diff --git a/README.md b/README.md index d11991ec..03d280d9 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,7 @@
-## [Overview](https://hackmd.io/@pragma/ByP-iux1T) - -Rust Library for running the Starknet OS via the [Cairo VM](https://github.com/lambdaclass/cairo-vm). +Rust Library for running the [Starknet OS](https://hackmd.io/@pragma/ByP-iux1T) via the [Cairo VM](https://github.com/lambdaclass/cairo-vm). ## Test Setup @@ -17,15 +15,14 @@ Rust Library for running the Starknet OS via the [Cairo VM](https://github.com/l cargo test ``` -### Reset Tests +**Reset Tests** ```bash ./scripts/teardown-tests.sh ``` -### Debug Single Cairo Program +**Debug Single Cairo Program** ```bash ./scripts/debug-hint.sh load_deprecated_class ``` - \ No newline at end of file diff --git a/src/config.rs b/src/config.rs index ae6e16c9..8ae6184d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,13 +10,15 @@ use serde::{Deserialize, Serialize}; use serde_with::serde_as; use starknet_api::block::{BlockNumber, BlockTimestamp}; use starknet_api::core::{ChainId, ContractAddress, PatriciaKey}; -use starknet_api::hash::StarkHash; +use starknet_api::hash::{pedersen_hash_array, StarkFelt, StarkHash}; use starknet_api::{contract_address, patricia_key}; +use starknet_crypto::FieldElement; use crate::error::SnOsError; const DEFAULT_CONFIG_PATH: &str = "cairo-lang/src/starkware/starknet/definitions/general_config.yml"; +pub const STARKNET_OS_CONFIG_HASH_VERSION: &str = "StarknetOsConfig1"; pub const DEFAULT_LAYOUT: &str = "starknet_with_keccak"; pub const DEFAULT_COMPILED_OS: &str = "build/os_latest.json"; pub const DEFAULT_INPUT_PATH: &str = "build/input.json"; @@ -35,6 +37,16 @@ pub struct StarknetOsConfig { pub fee_token_address: ContractAddress, } +impl StarknetOsConfig { + pub fn hash(&self) -> StarkHash { + pedersen_hash_array(&[ + StarkFelt::from(FieldElement::from_byte_slice_be(STARKNET_OS_CONFIG_HASH_VERSION.as_bytes()).unwrap()), + StarkFelt::from(u128::from_str_radix(&self.chain_id.0, 16).unwrap()), + *self.fee_token_address.0.key(), + ]) + } +} + #[derive(Debug, Serialize, Clone, Deserialize)] pub struct StarknetGeneralConfig { pub starknet_os_config: StarknetOsConfig, diff --git a/src/error.rs b/src/error.rs index 7e5b0c43..6f402cd6 100644 --- a/src/error.rs +++ b/src/error.rs @@ -15,6 +15,8 @@ pub enum SnOsError { SharpRequest(String), #[error("Starknet Os Runner Error: {0}")] Runner(CairoRunError), + #[error("SnOs Output Error: {0}")] + Output(String), } #[derive(thiserror::Error, Clone, Debug)] diff --git a/src/io/mod.rs b/src/io/mod.rs index a9a6a25c..00001a5f 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -1,4 +1,5 @@ pub mod classes; +pub mod output; use std::collections::HashMap; use std::io::Write; @@ -32,6 +33,19 @@ pub struct StarknetOsInput { pub block_hash: Felt252, } +impl StarknetOsInput { + pub fn load(path: &str) -> Self { + let raw_input = fs::read_to_string(path::PathBuf::from(path)).unwrap(); + serde_json::from_str(&raw_input).unwrap() + } + pub fn dump(&self, path: &str) -> Result<(), SnOsError> { + fs::File::create(path) + .unwrap() + .write_all(&serde_json::to_vec(&self).unwrap()) + .map_err(|e| SnOsError::CatchAll(format!("{e}"))) + } +} + #[serde_as] #[derive(Debug, Clone, Default, Deserialize, Serialize)] pub struct CommitmentInfo { @@ -127,43 +141,3 @@ pub struct StarknetOsOutput { /// List of the newly declared contract classes. pub contract_class_diff: Vec, } - -impl StarknetOsOutput { - #[allow(clippy::too_many_arguments)] - pub fn new( - prev_state_root: Felt252, - new_state_root: Felt252, - block_number: Felt252, - block_hash: Felt252, - config_hash: Felt252, - messages_to_l1: Vec, - messages_to_l2: Vec, - state_updates: Vec, - contract_class_diff: Vec, - ) -> Self { - Self { - prev_state_root, - new_state_root, - block_number, - block_hash, - config_hash, - messages_to_l1, - messages_to_l2, - state_updates, - contract_class_diff, - } - } -} - -impl StarknetOsInput { - pub fn load(path: &str) -> Self { - let raw_input = fs::read_to_string(path::PathBuf::from(path)).unwrap(); - serde_json::from_str(&raw_input).unwrap() - } - pub fn dump(&self, path: &str) -> Result<(), SnOsError> { - fs::File::create(path) - .unwrap() - .write_all(&serde_json::to_vec(&self).unwrap()) - .map_err(|e| SnOsError::CatchAll(format!("{e}"))) - } -} diff --git a/src/io/output.rs b/src/io/output.rs new file mode 100644 index 00000000..0d6aa6bd --- /dev/null +++ b/src/io/output.rs @@ -0,0 +1,66 @@ +use cairo_felt::Felt252; +use cairo_vm::types::relocatable::MaybeRelocatable; +use cairo_vm::vm::runners::builtin_runner::BuiltinRunner; +use cairo_vm::vm::vm_core::VirtualMachine; + +use super::StarknetOsOutput; +use crate::error::SnOsError; +use crate::utils::felt_vm2usize; + +const PREVIOUS_MERKLE_UPDATE_OFFSET: usize = 0; +const NEW_MERKLE_UPDATE_OFFSET: usize = 1; +const BLOCK_NUMBER_OFFSET: usize = 2; +const BLOCK_HASH_OFFSET: usize = 3; +const CONFIG_HASH_OFFSET: usize = 4; +const HEADER_SIZE: usize = 5; + +impl StarknetOsOutput { + pub fn from_run(vm: &VirtualMachine) -> Result { + // os_output = runner.vm_memory.get_range_as_ints( + // addr=runner.output_builtin.base, size=builtin_end_ptrs[0] - runner.output_builtin.base + // ) + let builtin_end_ptrs = vm.get_return_values(8).map_err(|e| SnOsError::CatchAll(e.to_string()))?; + let output_base = vm + .get_builtin_runners() + .iter() + .find(|&elt| matches!(elt, BuiltinRunner::Output(_))) + .expect("Os vm should have the output builtin") + .base(); + let size_bound_up = match builtin_end_ptrs.last().unwrap() { + MaybeRelocatable::Int(val) => val, + _ => panic!("Value should be an int"), + }; + + // Get is input and check that everything is an integer. + let raw_output = vm + .get_range((output_base as isize, 0).into(), felt_vm2usize(Some(&(size_bound_up.clone() - output_base)))?); + let raw_output: Vec = raw_output + .iter() + .map(|x| { + if let MaybeRelocatable::Int(val) = x.clone().unwrap().into_owned() { + val + } else { + panic!("Output should be all integers") + } + }) + .collect(); + + decode_output(raw_output) + } +} + +pub fn decode_output(mut os_output: Vec) -> Result { + let header: Vec = os_output.drain(..HEADER_SIZE).collect(); + + Ok(StarknetOsOutput { + prev_state_root: header[PREVIOUS_MERKLE_UPDATE_OFFSET].clone(), + new_state_root: header[NEW_MERKLE_UPDATE_OFFSET].clone(), + block_number: header[BLOCK_NUMBER_OFFSET].clone(), + block_hash: header[BLOCK_HASH_OFFSET].clone(), + config_hash: header[CONFIG_HASH_OFFSET].clone(), + messages_to_l1: os_output.drain(1..felt_vm2usize(os_output.first())?).collect(), + messages_to_l2: os_output.drain(1..felt_vm2usize(os_output.first())?).collect(), + state_updates: os_output.drain(1..felt_vm2usize(os_output.first())?).collect(), + contract_class_diff: os_output.drain(1..felt_vm2usize(os_output.first())?).collect(), + }) +} diff --git a/src/lib.rs b/src/lib.rs index 415ad40a..fd69333f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,26 +6,21 @@ pub mod sharp; pub mod state; pub mod utils; -use core::panic; use std::fs; use blockifier::block_context::BlockContext; use blockifier::state::state_api::StateReader; -use cairo_felt::Felt252; use cairo_vm::cairo_run::CairoRunConfig; use cairo_vm::types::program::Program; -use cairo_vm::types::relocatable::MaybeRelocatable; use cairo_vm::vm::errors::vm_exception::VmException; -use cairo_vm::vm::runners::builtin_runner::BuiltinRunner; use cairo_vm::vm::runners::cairo_pie::CairoPie; use cairo_vm::vm::runners::cairo_runner::CairoRunner; use cairo_vm::vm::vm_core::VirtualMachine; use config::StarknetGeneralConfig; use error::SnOsError; +use io::StarknetOsOutput; use state::SharedState; -use crate::io::StarknetOsOutput; - pub struct SnOsRunner { layout: String, os_path: String, @@ -34,22 +29,6 @@ pub struct SnOsRunner { } impl SnOsRunner { - pub fn with_layout(layout: &str) -> Self { - Self { layout: layout.to_string(), ..Self::default() } - } - - pub fn with_os_path(os_path: &str) -> Self { - Self { os_path: os_path.to_string(), ..Self::default() } - } - - pub fn with_input_path(input_path: &str) -> Self { - Self { input_path: input_path.to_string(), ..Self::default() } - } - - pub fn with_block_context(block_context: BlockContext) -> Self { - Self { block_context, ..Self::default() } - } - pub fn run(&self, shared_state: SharedState) -> Result { // Init CairoRunConfig let cairo_run_config = CairoRunConfig { @@ -91,68 +70,9 @@ impl SnOsRunner { } // Prepare and check expected output. - // os_output = runner.vm_memory.get_range_as_ints( - // addr=runner.output_builtin.base, size=builtin_end_ptrs[0] - runner.output_builtin.base - // ) - let builtin_end_ptrs = vm.get_return_values(8).map_err(|e| SnOsError::CatchAll(e.to_string()))?; - let output_base = vm - .get_builtin_runners() - .iter() - .find(|&elt| matches!(elt, BuiltinRunner::Output(_))) - .expect("Os vm should have the output builtin") - .base(); - let size_bound_up = match builtin_end_ptrs.last().unwrap() { - MaybeRelocatable::Int(val) => val, - _ => panic!("Value should be an int"), - }; - // Get is input and check that everything is an integer. - let os_output = vm.get_range( - (output_base as isize, 0).into(), - ::from_be_bytes((size_bound_up.clone() - output_base).to_be_bytes()[..8].try_into().unwrap()), - ); - let os_output: Vec = os_output - .iter() - .map(|x| { - if let MaybeRelocatable::Int(val) = x.clone().unwrap().into_owned() { - val - } else { - panic!("Output should be all integers") - } - }) - .collect(); - - let prev_state_root = os_output[0].clone(); - let new_state_root = os_output[1].clone(); - let block_number = os_output[2].clone(); - let block_hash = os_output[3].clone(); - let config_hash = os_output[4].clone(); - let os_output = &os_output[5..]; - let messages_to_l1_size = ::from_be_bytes(os_output[0].to_be_bytes()[..8].try_into().unwrap()); - let messages_to_l1 = os_output[1..1 + messages_to_l1_size].to_vec(); - - let os_output = &os_output[messages_to_l1_size + 1..]; - let messages_to_l2_size = ::from_be_bytes(os_output[0].to_be_bytes()[..8].try_into().unwrap()); - let messages_to_l2 = os_output[1..1 + messages_to_l2_size].to_vec(); - let os_output = &os_output[messages_to_l2_size + 1..]; - - let state_updates_size = ::from_be_bytes(os_output[0].to_be_bytes()[..8].try_into().unwrap()); - let state_updates = os_output[1..1 + state_updates_size].to_vec(); - let os_output = &os_output[state_updates_size + 1..]; - - let contract_class_diff_size = ::from_be_bytes(os_output[0].to_be_bytes()[..8].try_into().unwrap()); - let contract_class_diff = os_output[1..1 + contract_class_diff_size].to_vec(); - let real_output = StarknetOsOutput::new( - prev_state_root, - new_state_root, - block_number, - block_hash, - config_hash, - messages_to_l1, - messages_to_l2, - state_updates, - contract_class_diff, - ); - println!("{:?}", real_output); + let _os_output = StarknetOsOutput::from_run(&vm)?; + + println!("{:?}", _os_output); vm.verify_auto_deductions().map_err(|e| SnOsError::Runner(e.into()))?; cairo_runner.read_return_values(&mut vm).map_err(|e| SnOsError::Runner(e.into()))?; @@ -163,6 +83,22 @@ impl SnOsRunner { Ok(pie) } + + pub fn with_layout(layout: &str) -> Self { + Self { layout: layout.to_string(), ..Self::default() } + } + + pub fn with_os_path(os_path: &str) -> Self { + Self { os_path: os_path.to_string(), ..Self::default() } + } + + pub fn with_input_path(input_path: &str) -> Self { + Self { input_path: input_path.to_string(), ..Self::default() } + } + + pub fn with_block_context(block_context: BlockContext) -> Self { + Self { block_context, ..Self::default() } + } } impl Default for SnOsRunner { diff --git a/src/utils.rs b/src/utils.rs index 5f3cc254..fd3d88ae 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -18,6 +18,7 @@ use starknet_api::hash::{pedersen_hash, StarkFelt, StarkHash}; use starknet_api::stark_felt; use crate::config::DEFAULT_COMPILER_VERSION; +use crate::error::SnOsError; lazy_static! { static ref RE: Regex = Regex::new(r"^[A-Fa-f0-9]+$").unwrap(); @@ -60,6 +61,17 @@ pub fn felt_api2vm(felt: StarkFelt) -> Felt252 { felt_str!(felt.to_string().trim_start_matches("0x"), 16) } +pub fn felt_vm2usize(felt_op: Option<&Felt252>) -> Result { + match felt_op { + Some(felt) => { + let big_num: u16 = felt.to_bigint().try_into().map_err(|e| SnOsError::Output(format!("{e}")))?; + + Ok(big_num.into()) + } + None => Err(SnOsError::CatchAll("no length available".to_string())), + } +} + pub fn deprecated_class_vm2api(class: ContractClassV0) -> DeprecatedContractClass { DeprecatedContractClass { abi: None, diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 916b8dd5..2f3d9715 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,4 +1,4 @@ -pub mod serde; +pub mod serde_utils; pub mod utils; use std::collections::HashMap; @@ -27,7 +27,8 @@ use cairo_vm::vm::runners::cairo_runner::CairoRunner; use cairo_vm::vm::vm_core::VirtualMachine; use rstest::{fixture, rstest}; use snos::config::{StarknetGeneralConfig, DEFAULT_FEE_TOKEN_ADDR, DEFAULT_INPUT_PATH}; -use snos::io::StarknetOsInput; +use snos::io::output::decode_output; +use snos::io::{StarknetOsInput, StarknetOsOutput}; use snos::state::SharedState; use starknet_api::block::{BlockNumber, BlockTimestamp}; use starknet_api::core::{calculate_contract_address, ClassHash, ContractAddress, PatriciaKey}; @@ -71,7 +72,7 @@ pub const EXPECTED_UPDATED_ROOT: &str = "482c9ce8a99afddc9777ff048520fcbfab6c038 #[fixture] #[once] pub fn load_and_write_input() { - let os_input = serde::StarknetOsInputUtil::load("tests/common/os_input.json"); + let os_input = serde_utils::StarknetOsInputUtil::load("tests/common/os_input.json"); os_input.dump(DEFAULT_INPUT_PATH).unwrap(); } @@ -501,48 +502,10 @@ pub fn prepare_os_test( (initial_state, exec_info) } -#[rstest] -fn validate_prepare(prepare_os_test: (SharedState, Vec)) { - let mut shared_state = prepare_os_test.0; - let diff = shared_state.cache.to_state_diff(); - - let addr_1 = contract_address!("46fd0893101585e0c7ebd3caf8097b179f774102d6373760c8f60b1a5ef8c92"); - let addr_1_updates = diff.storage_updates.get(&addr_1).unwrap(); - assert_eq!(5, addr_1_updates.len()); - assert_eq!(&stark_felt!(47_u32), addr_1_updates.get(&StorageKey(patricia_key!(85_u32))).unwrap()); - assert_eq!(&stark_felt!(543_u32), addr_1_updates.get(&StorageKey(patricia_key!(321_u32))).unwrap()); - assert_eq!(&stark_felt!(666_u32), addr_1_updates.get(&StorageKey(patricia_key!(444_u32))).unwrap()); - - let addr_2 = contract_address!("4e9665675ca1ac12820b7aff2f44fec713e272efcd3f20aa0fd8ca277f25dc6"); - let addr_2_updates = diff.storage_updates.get(&addr_2).unwrap(); - assert_eq!(&stark_felt!(1_u32), addr_2_updates.get(&StorageKey(patricia_key!(15_u32))).unwrap()); - assert_eq!(&stark_felt!(987_u32), addr_2_updates.get(&StorageKey(patricia_key!(111_u32))).unwrap()); - assert_eq!(&stark_felt!(888_u32), addr_2_updates.get(&StorageKey(patricia_key!(555_u32))).unwrap()); - assert_eq!(&stark_felt!(999_u32), addr_2_updates.get(&StorageKey(patricia_key!(666_u32))).unwrap()); - - let delegate_addr = contract_address!("238e6b5dffc9f0eb2fe476855d0cd1e9e034e5625663c7eda2d871bd4b6eac0"); - let delegate_addr_updates = diff.storage_updates.get(&delegate_addr).unwrap(); - // assert_eq!(6, delegate_addr_updates.len()); - assert_eq!(&stark_felt!(456_u32), delegate_addr_updates.get(&StorageKey(patricia_key!(123_u32))).unwrap()); - assert_eq!( - &stark_felt!("4e5e39d16e565bacdbc7d8d13b9bc2b51a32c8b2b49062531688dcd2f6ec834"), - delegate_addr_updates.get(&StorageKey(patricia_key!(300_u32))).unwrap() - ); - assert_eq!( - &stark_felt!(1536727068981429685321_u128), - delegate_addr_updates.get(&StorageKey(patricia_key!(311_u32))).unwrap() - ); - assert_eq!(&stark_felt!(19_u32), delegate_addr_updates.get(&StorageKey(patricia_key!(322_u32))).unwrap()); - assert_eq!( - &stark_felt!(TESTING_HASH_0_12_2), - delegate_addr_updates - .get(&StorageKey(patricia_key!("2e9111f912ea3746e28b8e693578fdbcc18d64a3380d03bd67c0c04f5715ed1"))) - .unwrap() - ); - assert_eq!( - &stark_felt!(2_u8), - delegate_addr_updates - .get(&StorageKey(patricia_key!("1cda892019d02a987cdc80f1500179f0e33fbd6cac8cb2ffef5d6d05101a8dc"))) - .unwrap() - ); +#[fixture] +pub fn load_output() -> StarknetOsOutput { + let buf = fs::read_to_string("tests/common/os_output.json").unwrap(); + let raw_output: serde_utils::RawOsOutput = serde_json::from_str(&buf).unwrap(); + + decode_output(raw_output.0).unwrap() } diff --git a/tests/common/os_output.json b/tests/common/os_output.json new file mode 100644 index 00000000..5b83c024 --- /dev/null +++ b/tests/common/os_output.json @@ -0,0 +1,66 @@ +[ + 125777881657840305468919655792243043894346744037226223335092204105986408733, + 2040334332115293258607805604894929469377060974617729443381753056905784954023, + 1, + 2535437458273622887584459710067137978693525181086955024571735059458497227738, + 310876289256536046287137994578069209749202099665831078341805439700916543594, + 5, + 2006810680437147719782949677362224138923732654511812452693656965873469983890, + 85, + 2, + 12, + 34, + 6, + 85, + 1005161437792794307757078281996005783125378987969285529172328835577592343232, + 0, + 352040181584456735608515580760888541466059565068553383579463728554843487745, + 1, + 2, + 5, + 1005161437792794307757078281996005783125378987969285529172328835577592343232, + 340282366920938463463374607431768211462, + 692694963414257194264020892248745848197431340753065024006875718260869872089, + 123, + 456, + 300, + 2215430303710902791540996484823030809971452078551498009603035870583464052788, + 311, + 1536727068981429685321, + 322, + 19, + 815679926571212018227195848707562322348558067406060931041239273854107494620, + 2, + 1316419243995606702889870694869183679645676506242823596326912212936248352465, + 3262122051170176624039908867798875903980511552421730070376672653403179864416, + 2006810680437147719782949677362224138923732654511812452693656965873469983890, + 340282366920938463463374607431768211461, + 2084524728099985327606460172540572310995923661656859675449923268913980850263, + 85, + 47, + 321, + 543, + 444, + 666, + 1715425246256821823855536409958992540846451989567087551676457027799652256186, + 100, + 1715425246256821823855536409958992540846451989567087551676457027799652256187, + 200, + 2221633069513894212967173919871301977519426338681819384231748898933664013766, + 340282366920938463463374607431768211460, + 3262122051170176624039908867798875903980511552421730070376672653403179864416, + 15, + 1, + 111, + 987, + 555, + 888, + 666, + 999, + 2618767603815038378512366346550627731109766804643583016834052353912473402832, + 442721857769029238784, + 3302098605493938887217934688678356071939708546668669666319008757914002811976, + 340282366920938463463374607431768211456, + 3262122051170176624039908867798875903980511552421730070376672653403179864416, + 0 +] \ No newline at end of file diff --git a/tests/common/serde.rs b/tests/common/serde_utils.rs similarity index 95% rename from tests/common/serde.rs rename to tests/common/serde_utils.rs index e988a02d..55f36569 100644 --- a/tests/common/serde.rs +++ b/tests/common/serde_utils.rs @@ -65,3 +65,7 @@ impl StarknetOsInputUtil { .map_err(|e| SnOsError::CatchAll(format!("{e}"))) } } + +#[serde_as] +#[derive(Debug, Deserialize)] +pub struct RawOsOutput(#[serde_as(as = "Vec")] pub Vec); diff --git a/tests/snos.rs b/tests/snos.rs index 954ad386..96a71e61 100644 --- a/tests/snos.rs +++ b/tests/snos.rs @@ -1,18 +1,22 @@ mod common; +use blockifier::state::state_api::State; use blockifier::test_utils::DictStateReader; use blockifier::transaction::objects::TransactionExecutionInfo; -use cairo_felt::felt_str; +use cairo_felt::{felt_str, Felt252}; use common::{ - initial_state, load_input, prepare_os_test, EXPECTED_PREV_ROOT, EXPECTED_UPDATED_ROOT, TESTING_BLOCK_HASH, + initial_state, load_input, load_output, prepare_os_test, EXPECTED_PREV_ROOT, EXPECTED_UPDATED_ROOT, + TESTING_BLOCK_HASH, TESTING_HASH_0_12_2, }; -use rstest::*; -use snos::io::StarknetOsInput; +use rstest::rstest; +use snos::io::{StarknetOsInput, StarknetOsOutput}; use snos::state::SharedState; +use snos::utils::felt_api2vm; use snos::SnOsRunner; use starknet_api::block::BlockNumber; use starknet_api::core::{ContractAddress, PatriciaKey}; use starknet_api::hash::{StarkFelt, StarkHash}; +use starknet_api::state::StorageKey; use starknet_api::{contract_address, patricia_key, stark_felt}; #[rstest] @@ -25,6 +29,48 @@ fn snos_run_test(_load_input: &StarknetOsInput, initial_state: SharedState, Vec)) { let (mut prepare_os_test, _) = prepare_os_test; + let diff = prepare_os_test.cache.to_state_diff(); + + let addr_1 = contract_address!("46fd0893101585e0c7ebd3caf8097b179f774102d6373760c8f60b1a5ef8c92"); + let addr_1_updates = diff.storage_updates.get(&addr_1).unwrap(); + assert_eq!(5, addr_1_updates.len()); + assert_eq!(&stark_felt!(47_u32), addr_1_updates.get(&StorageKey(patricia_key!(85_u32))).unwrap()); + assert_eq!(&stark_felt!(543_u32), addr_1_updates.get(&StorageKey(patricia_key!(321_u32))).unwrap()); + assert_eq!(&stark_felt!(666_u32), addr_1_updates.get(&StorageKey(patricia_key!(444_u32))).unwrap()); + + let addr_2 = contract_address!("4e9665675ca1ac12820b7aff2f44fec713e272efcd3f20aa0fd8ca277f25dc6"); + let addr_2_updates = diff.storage_updates.get(&addr_2).unwrap(); + assert_eq!(&stark_felt!(1_u32), addr_2_updates.get(&StorageKey(patricia_key!(15_u32))).unwrap()); + assert_eq!(&stark_felt!(987_u32), addr_2_updates.get(&StorageKey(patricia_key!(111_u32))).unwrap()); + assert_eq!(&stark_felt!(888_u32), addr_2_updates.get(&StorageKey(patricia_key!(555_u32))).unwrap()); + assert_eq!(&stark_felt!(999_u32), addr_2_updates.get(&StorageKey(patricia_key!(666_u32))).unwrap()); + + let delegate_addr = contract_address!("238e6b5dffc9f0eb2fe476855d0cd1e9e034e5625663c7eda2d871bd4b6eac0"); + let delegate_addr_updates = diff.storage_updates.get(&delegate_addr).unwrap(); + // assert_eq!(6, delegate_addr_updates.len()); + assert_eq!(&stark_felt!(456_u32), delegate_addr_updates.get(&StorageKey(patricia_key!(123_u32))).unwrap()); + assert_eq!( + &stark_felt!("4e5e39d16e565bacdbc7d8d13b9bc2b51a32c8b2b49062531688dcd2f6ec834"), + delegate_addr_updates.get(&StorageKey(patricia_key!(300_u32))).unwrap() + ); + assert_eq!( + &stark_felt!(1536727068981429685321_u128), + delegate_addr_updates.get(&StorageKey(patricia_key!(311_u32))).unwrap() + ); + assert_eq!(&stark_felt!(19_u32), delegate_addr_updates.get(&StorageKey(patricia_key!(322_u32))).unwrap()); + assert_eq!( + &stark_felt!(TESTING_HASH_0_12_2), + delegate_addr_updates + .get(&StorageKey(patricia_key!("2e9111f912ea3746e28b8e693578fdbcc18d64a3380d03bd67c0c04f5715ed1"))) + .unwrap() + ); + assert_eq!( + &stark_felt!(2_u8), + delegate_addr_updates + .get(&StorageKey(patricia_key!("1cda892019d02a987cdc80f1500179f0e33fbd6cac8cb2ffef5d6d05101a8dc"))) + .unwrap() + ); + let _commitment = prepare_os_test.apply_state(); assert_eq!(BlockNumber(2), prepare_os_test.get_block_num()); @@ -49,3 +95,16 @@ fn parse_os_input(load_input: &StarknetOsInput) { assert_eq!(felt_str!(EXPECTED_UPDATED_ROOT, 16), load_input.contract_state_commitment_info.updated_root); assert!(!load_input.transactions.is_empty()); } + +#[rstest] +fn parse_os_output(load_input: &StarknetOsInput, load_output: StarknetOsOutput) { + assert_eq!(load_input.contract_state_commitment_info.previous_root, load_output.prev_state_root); + assert_eq!(load_input.contract_state_commitment_info.updated_root, load_output.new_state_root); + assert_eq!(Felt252::from(1), load_output.block_number); + assert_eq!(load_input.block_hash, load_output.block_hash); + assert_eq!(felt_api2vm(load_input.general_config.starknet_os_config.hash()), load_output.config_hash); + assert_eq!(4, load_output.messages_to_l1.len()); + assert_eq!(4, load_output.messages_to_l2.len()); + assert_eq!(4, load_output.state_updates.len()); + assert_eq!(4, load_output.contract_class_diff.len()); +}