// This module tests our code against the blockifier to ensure they work in the same way. use assert_matches::assert_matches; use cairo_lang_starknet::contract_class::ContractClass as SierraContractClass; use cairo_vm::felt::{felt_str, Felt252}; use cairo_vm::vm::runners::builtin_runner::{HASH_BUILTIN_NAME, RANGE_CHECK_BUILTIN_NAME}; use cairo_vm::vm::{ errors::{ cairo_run_errors::CairoRunError, vm_errors::VirtualMachineError, vm_exception::VmException, }, runners::cairo_runner::ExecutionResources, }; use lazy_static::lazy_static; use num_bigint::BigUint; use num_traits::{Num, One, ToPrimitive, Zero}; use starknet_contract_class::EntryPointType; use starknet_rs::core::errors::state_errors::StateError; use starknet_rs::definitions::constants::{ DEFAULT_CAIRO_RESOURCE_FEE_WEIGHTS, VALIDATE_ENTRY_POINT_SELECTOR, }; use starknet_rs::execution::execution_entry_point::ExecutionEntryPoint; use starknet_rs::execution::TransactionExecutionContext; use starknet_rs::services::api::contract_classes::deprecated_contract_class::ContractClass; use starknet_rs::state::ExecutionResourcesManager; use starknet_rs::transaction::{DeclareV2, Deploy}; use starknet_rs::CasmContractClass; use starknet_rs::{ definitions::{ block_context::{BlockContext, StarknetChainId, StarknetOsConfig}, constants::{ CONSTRUCTOR_ENTRY_POINT_SELECTOR, EXECUTE_ENTRY_POINT_SELECTOR, TRANSACTION_VERSION, TRANSFER_ENTRY_POINT_SELECTOR, TRANSFER_EVENT_SELECTOR, VALIDATE_DECLARE_ENTRY_POINT_SELECTOR, VALIDATE_DEPLOY_ENTRY_POINT_SELECTOR, }, transaction_type::TransactionType, }, execution::{CallInfo, CallType, OrderedEvent, TransactionExecutionInfo}, state::in_memory_state_reader::InMemoryStateReader, state::{ cached_state::{CachedState, ContractClassCache}, state_api::{State, StateReader}, state_cache::StateCache, state_cache::StorageEntry, BlockInfo, }, transaction::{ error::TransactionError, DeployAccount, {invoke_function::InvokeFunction, Declare}, }, utils::{calculate_sn_keccak, felt_to_hash, Address, ClassHash}, }; use std::{ collections::{HashMap, HashSet}, path::PathBuf, }; const ACCOUNT_CONTRACT_PATH: &str = "starknet_programs/account_without_validation.json"; const ERC20_CONTRACT_PATH: &str = "starknet_programs/ERC20.json"; const TEST_CONTRACT_PATH: &str = "starknet_programs/test_contract.json"; const TEST_EMPTY_CONTRACT_PATH: &str = "starknet_programs/empty_contract.json"; lazy_static! { // Addresses. static ref TEST_ACCOUNT_CONTRACT_ADDRESS: Address = Address(felt_str!("257")); static ref TEST_CONTRACT_ADDRESS: Address = Address(felt_str!("256")); static ref TEST_FIB_CONTRACT_ADDRESS: Address = Address(felt_str!("27728")); pub static ref TEST_SEQUENCER_ADDRESS: Address = Address(felt_str!("4096")); pub static ref TEST_ERC20_CONTRACT_ADDRESS: Address = Address(felt_str!("4097")); // Class hashes. static ref TEST_ACCOUNT_CONTRACT_CLASS_HASH: Felt252 = felt_str!("273"); static ref TEST_CLASS_HASH: Felt252 = felt_str!("272"); static ref TEST_EMPTY_CONTRACT_CLASS_HASH: Felt252 = felt_str!("274"); static ref TEST_ERC20_CONTRACT_CLASS_HASH: Felt252 = felt_str!("4112"); static ref TEST_FIB_COMPILED_CONTRACT_CLASS_HASH: Felt252 = felt_str!("27727"); // Storage keys. static ref TEST_ERC20_ACCOUNT_BALANCE_KEY: Felt252 = felt_str!("1192211877881866289306604115402199097887041303917861778777990838480655617515"); static ref TEST_ERC20_SEQUENCER_BALANCE_KEY: Felt252 = felt_str!("3229073099929281304021185011369329892856197542079132996799046100564060768274"); static ref TEST_ERC20_BALANCE_KEY_1: Felt252 = felt_str!("1192211877881866289306604115402199097887041303917861778777990838480655617516"); static ref TEST_ERC20_BALANCE_KEY_2: Felt252 = felt_str!("3229073099929281304021185011369329892856197542079132996799046100564060768275"); static ref TEST_ERC20_DEPLOYED_ACCOUNT_BALANCE_KEY: Felt252 = felt_str!("2542253978940891427830343982984992363331567580652119103860970381451088310289"); // Others. // Blockifier had this value hardcoded to 2. static ref ACTUAL_FEE: Felt252 = Felt252::zero(); } fn get_contract_class

(path: P) -> Result> where P: Into, { Ok(ContractClass::try_from(path.into())?) } pub fn new_starknet_block_context_for_testing() -> BlockContext { BlockContext::new( StarknetOsConfig::new( StarknetChainId::TestNet, TEST_ERC20_CONTRACT_ADDRESS.clone(), 0, ), 0, 0, DEFAULT_CAIRO_RESOURCE_FEE_WEIGHTS.clone(), 1_000_000, 0, BlockInfo::empty(TEST_SEQUENCER_ADDRESS.clone()), HashMap::default(), true, ) } fn create_account_tx_test_state( ) -> Result<(BlockContext, CachedState), Box> { let block_context = new_starknet_block_context_for_testing(); let test_contract_class_hash = felt_to_hash(&TEST_CLASS_HASH.clone()); let test_account_contract_class_hash = felt_to_hash(&TEST_ACCOUNT_CONTRACT_CLASS_HASH.clone()); let test_erc20_class_hash = felt_to_hash(&TEST_ERC20_CONTRACT_CLASS_HASH.clone()); let class_hash_to_class = HashMap::from([ ( test_account_contract_class_hash, get_contract_class(ACCOUNT_CONTRACT_PATH)?, ), ( test_contract_class_hash, get_contract_class(TEST_CONTRACT_PATH)?, ), ( test_erc20_class_hash, get_contract_class(ERC20_CONTRACT_PATH)?, ), ]); let test_contract_address = TEST_CONTRACT_ADDRESS.clone(); let test_account_contract_address = TEST_ACCOUNT_CONTRACT_ADDRESS.clone(); let test_erc20_address = block_context .starknet_os_config() .fee_token_address() .clone(); let address_to_class_hash = HashMap::from([ (test_contract_address, test_contract_class_hash), ( test_account_contract_address, test_account_contract_class_hash, ), (test_erc20_address.clone(), test_erc20_class_hash), ]); let test_erc20_account_balance_key = TEST_ERC20_ACCOUNT_BALANCE_KEY.clone(); let storage_view = HashMap::from([( (test_erc20_address, test_erc20_account_balance_key), ACTUAL_FEE.clone(), )]); let cached_state = CachedState::new( { let mut state_reader = InMemoryStateReader::default(); for (contract_address, class_hash) in address_to_class_hash { let storage_keys: HashMap<(Address, ClassHash), Felt252> = storage_view .iter() .filter_map(|((address, storage_key), storage_value)| { (address == &contract_address).then_some(( (address.clone(), felt_to_hash(storage_key)), storage_value.clone(), )) }) .collect(); let stored: HashMap = storage_keys; state_reader .address_to_class_hash_mut() .insert(contract_address.clone(), class_hash); state_reader .address_to_nonce_mut() .insert(contract_address.clone(), Felt252::zero()); state_reader.address_to_storage_mut().extend(stored); } for (class_hash, contract_class) in class_hash_to_class { state_reader .class_hash_to_contract_class_mut() .insert(class_hash, contract_class); } state_reader }, Some(HashMap::new()), Some(HashMap::new()), ); Ok((block_context, cached_state)) } fn expected_state_before_tx() -> CachedState { let in_memory_state_reader = initial_in_memory_state_reader(); let state_cache = ContractClassCache::new(); CachedState::new( in_memory_state_reader, Some(state_cache), Some(HashMap::new()), ) } fn expected_state_after_tx() -> CachedState { let in_memory_state_reader = initial_in_memory_state_reader(); let contract_classes_cache = ContractClassCache::from([ ( felt_to_hash(&TEST_CLASS_HASH.clone()), get_contract_class(TEST_CONTRACT_PATH).unwrap(), ), ( felt_to_hash(&TEST_ACCOUNT_CONTRACT_CLASS_HASH.clone()), get_contract_class(ACCOUNT_CONTRACT_PATH).unwrap(), ), ( felt_to_hash(&TEST_ERC20_CONTRACT_CLASS_HASH.clone()), get_contract_class(ERC20_CONTRACT_PATH).unwrap(), ), ]); CachedState::new_for_testing( in_memory_state_reader, Some(contract_classes_cache), state_cache_after_invoke_tx(), Some(HashMap::new()), ) } fn state_cache_after_invoke_tx() -> StateCache { let class_hash_initial_values = HashMap::from([ ( TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_ACCOUNT_CONTRACT_CLASS_HASH.clone()), ), ( TEST_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_CLASS_HASH.clone()), ), ( TEST_ERC20_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_ERC20_CONTRACT_CLASS_HASH.clone()), ), ]); let nonce_initial_values = HashMap::from([(TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), Felt252::zero())]); let storage_initial_values = HashMap::from([ ( ( TEST_ERC20_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_ERC20_SEQUENCER_BALANCE_KEY.clone()), ), Felt252::zero(), ), ( ( TEST_ERC20_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_ERC20_ACCOUNT_BALANCE_KEY.clone()), ), Felt252::zero(), ), ( ( TEST_ERC20_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_ERC20_BALANCE_KEY_1.clone()), ), Felt252::zero(), ), ( ( TEST_ERC20_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_ERC20_BALANCE_KEY_2.clone()), ), Felt252::zero(), ), ]); let class_hash_writes = HashMap::new(); let nonce_writes = HashMap::from([(TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), Felt252::from(1))]); let storage_writes = HashMap::from([ ( ( TEST_ERC20_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_ERC20_SEQUENCER_BALANCE_KEY.clone()), ), Felt252::from(0), ), ( ( TEST_ERC20_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_ERC20_ACCOUNT_BALANCE_KEY.clone()), ), Felt252::from(0), ), ( ( TEST_ERC20_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_ERC20_BALANCE_KEY_1.clone()), ), Felt252::from(0), ), ( ( TEST_ERC20_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_ERC20_BALANCE_KEY_2.clone()), ), Felt252::from(0), ), ]); let compiled_class_hash_initial_values = HashMap::new(); let compiled_class_hash_writes = HashMap::new(); let compiled_class_hash = HashMap::new(); StateCache::new_for_testing( class_hash_initial_values, compiled_class_hash_initial_values, nonce_initial_values, storage_initial_values, class_hash_writes, compiled_class_hash_writes, nonce_writes, storage_writes, compiled_class_hash, ) } fn initial_in_memory_state_reader() -> InMemoryStateReader { InMemoryStateReader::new( HashMap::from([ ( TEST_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_CLASS_HASH), ), ( TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_ACCOUNT_CONTRACT_CLASS_HASH), ), ( TEST_ERC20_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_ERC20_CONTRACT_CLASS_HASH), ), ]), HashMap::from([ (TEST_CONTRACT_ADDRESS.clone(), Felt252::zero()), (TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), Felt252::zero()), (TEST_ERC20_CONTRACT_ADDRESS.clone(), Felt252::zero()), ]), HashMap::from([( ( TEST_ERC20_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_ERC20_ACCOUNT_BALANCE_KEY.clone()), ), Felt252::from(0), )]), HashMap::from([ ( felt_to_hash(&TEST_ERC20_CONTRACT_CLASS_HASH), get_contract_class(ERC20_CONTRACT_PATH).unwrap(), ), ( felt_to_hash(&TEST_ACCOUNT_CONTRACT_CLASS_HASH), get_contract_class(ACCOUNT_CONTRACT_PATH).unwrap(), ), ( felt_to_hash(&TEST_CLASS_HASH), get_contract_class(TEST_CONTRACT_PATH).unwrap(), ), ]), HashMap::new(), HashMap::new(), ) } fn expected_validate_call_info( entry_point_selector: Felt252, calldata: Vec, storage_address: Address, ) -> CallInfo { CallInfo { entry_point_type: EntryPointType::External.into(), entry_point_selector: entry_point_selector.into(), calldata, contract_address: storage_address, // Entries **not** in blockifier. class_hash: Some(felt_to_hash(&TEST_ACCOUNT_CONTRACT_CLASS_HASH)), call_type: Some(CallType::Call), execution_resources: ExecutionResources { n_steps: 13, ..Default::default() }, ..Default::default() } } fn expected_fee_transfer_call_info( block_context: &BlockContext, account_address: &Address, actual_fee: u64, ) -> CallInfo { CallInfo { entry_point_type: EntryPointType::External.into(), entry_point_selector: TRANSFER_ENTRY_POINT_SELECTOR.clone().into(), calldata: vec![ block_context.block_info().sequencer_address.0.clone(), actual_fee.into(), Felt252::zero(), ], contract_address: block_context .starknet_os_config() .fee_token_address() .clone(), caller_address: account_address.clone(), retdata: vec![Felt252::one()], events: vec![OrderedEvent { order: 0, keys: vec![TRANSFER_EVENT_SELECTOR.clone()], data: vec![ account_address.0.clone(), block_context.block_info().sequencer_address.0.clone(), actual_fee.into(), Felt252::zero(), ], }], // Entries **not** in blockifier. class_hash: Some(felt_to_hash(&TEST_ERC20_CONTRACT_CLASS_HASH)), call_type: Some(CallType::Call), accessed_storage_keys: HashSet::from([ [ 7, 35, 151, 50, 8, 99, 155, 120, 57, 206, 41, 143, 127, 254, 166, 30, 63, 149, 51, 135, 45, 239, 215, 171, 219, 145, 2, 61, 180, 101, 136, 19, ], [ 5, 158, 221, 96, 243, 245, 236, 116, 233, 4, 68, 137, 231, 149, 207, 133, 23, 150, 101, 24, 93, 212, 49, 126, 49, 102, 131, 144, 118, 15, 48, 18, ], [ 5, 158, 221, 96, 243, 245, 236, 116, 233, 4, 68, 137, 231, 149, 207, 133, 23, 150, 101, 24, 93, 212, 49, 126, 49, 102, 131, 144, 118, 15, 48, 17, ], [ 7, 35, 151, 50, 8, 99, 155, 120, 57, 206, 41, 143, 127, 254, 166, 30, 63, 149, 51, 135, 45, 239, 215, 171, 219, 145, 2, 61, 180, 101, 136, 18, ], ]), storage_read_values: vec![ Felt252::zero(), Felt252::zero(), Felt252::zero(), Felt252::zero(), ], execution_resources: ExecutionResources { n_steps: 529, n_memory_holes: 57, builtin_instance_counter: HashMap::from([ (RANGE_CHECK_BUILTIN_NAME.to_string(), 21), (HASH_BUILTIN_NAME.to_string(), 4), ]), }, ..Default::default() } } fn validate_final_balances( state: &mut S, block_context: &BlockContext, expected_sequencer_balance: Felt252, erc20_account_balance_storage_key: &ClassHash, ) where S: State + StateReader, { let account_balance = state .get_storage_at(&( block_context .starknet_os_config() .fee_token_address() .clone(), *erc20_account_balance_storage_key, )) .unwrap(); assert_eq!(account_balance, Felt252::zero()); let sequencer_balance = state .get_storage_at(&( block_context .starknet_os_config() .fee_token_address() .clone(), felt_to_hash(&TEST_ERC20_SEQUENCER_BALANCE_KEY), )) .unwrap(); assert_eq!(sequencer_balance, expected_sequencer_balance); } #[test] fn test_create_account_tx_test_state() { let (block_context, mut state) = create_account_tx_test_state().unwrap(); assert_eq!(state, expected_state_before_tx()); let value = state .get_storage_at(&( block_context .starknet_os_config() .fee_token_address() .clone(), felt_to_hash(&TEST_ERC20_ACCOUNT_BALANCE_KEY), )) .unwrap(); assert_eq!(value, *ACTUAL_FEE); let class_hash = state.get_class_hash_at(&TEST_CONTRACT_ADDRESS).unwrap(); assert_eq!(class_hash, felt_to_hash(&TEST_CLASS_HASH)); let contract_class: ContractClass = state .get_contract_class(&felt_to_hash(&TEST_ERC20_CONTRACT_CLASS_HASH)) .unwrap() .try_into() .unwrap(); assert_eq!( contract_class, get_contract_class(ERC20_CONTRACT_PATH).unwrap() ); } fn invoke_tx(calldata: Vec) -> InvokeFunction { InvokeFunction::new( TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), EXECUTE_ENTRY_POINT_SELECTOR.clone(), 2, TRANSACTION_VERSION.clone(), calldata, vec![], StarknetChainId::TestNet.to_felt(), Some(Felt252::zero()), None, ) .unwrap() } fn expected_fee_transfer_info() -> CallInfo { CallInfo { failure_flag: false, gas_consumed: 0, caller_address: TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), call_type: Some(CallType::Call), contract_address: Address(Felt252::from(4097)), code_address: None, class_hash: Some(felt_to_hash(&TEST_ERC20_CONTRACT_CLASS_HASH)), entry_point_selector: Some(TRANSFER_ENTRY_POINT_SELECTOR.clone()), entry_point_type: Some(EntryPointType::External), calldata: vec![Felt252::from(4096), Felt252::zero(), Felt252::zero()], retdata: vec![Felt252::from(1)], execution_resources: ExecutionResources { n_steps: 525, n_memory_holes: 59, builtin_instance_counter: HashMap::from([ (RANGE_CHECK_BUILTIN_NAME.to_string(), 21), (HASH_BUILTIN_NAME.to_string(), 4), ]), }, l2_to_l1_messages: vec![], internal_calls: vec![], events: vec![OrderedEvent { order: 0, keys: vec![TRANSFER_EVENT_SELECTOR.clone()], data: vec![ Felt252::from(257), Felt252::from(4096), Felt252::zero(), Felt252::zero(), ], }], storage_read_values: vec![ Felt252::zero(), Felt252::zero(), Felt252::zero(), Felt252::zero(), ], accessed_storage_keys: HashSet::from([ [ 7, 35, 151, 50, 8, 99, 155, 120, 57, 206, 41, 143, 127, 254, 166, 30, 63, 149, 51, 135, 45, 239, 215, 171, 219, 145, 2, 61, 180, 101, 136, 19, ], [ 2, 162, 196, 156, 77, 186, 13, 145, 179, 79, 42, 222, 133, 212, 29, 9, 86, 31, 154, 119, 136, 76, 21, 186, 42, 176, 242, 36, 27, 8, 13, 236, ], [ 7, 35, 151, 50, 8, 99, 155, 120, 57, 206, 41, 143, 127, 254, 166, 30, 63, 149, 51, 135, 45, 239, 215, 171, 219, 145, 2, 61, 180, 101, 136, 18, ], [ 2, 162, 196, 156, 77, 186, 13, 145, 179, 79, 42, 222, 133, 212, 29, 9, 86, 31, 154, 119, 136, 76, 21, 186, 42, 176, 242, 36, 27, 8, 13, 235, ], ]), } } fn expected_fib_fee_transfer_info() -> CallInfo { CallInfo { failure_flag: false, gas_consumed: 0, caller_address: TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), call_type: Some(CallType::Call), contract_address: Address(Felt252::from(4097)), code_address: None, class_hash: Some(felt_to_hash(&TEST_ERC20_CONTRACT_CLASS_HASH)), entry_point_selector: Some(TRANSFER_ENTRY_POINT_SELECTOR.clone()), entry_point_type: Some(EntryPointType::External), calldata: vec![Felt252::from(4096), Felt252::zero(), Felt252::zero()], retdata: vec![Felt252::from(1)], execution_resources: ExecutionResources { n_steps: 525, n_memory_holes: 59, builtin_instance_counter: HashMap::from([ ("range_check_builtin".to_string(), 21), ("pedersen_builtin".to_string(), 4), ]), }, l2_to_l1_messages: vec![], internal_calls: vec![], events: vec![OrderedEvent { order: 0, keys: vec![TRANSFER_EVENT_SELECTOR.clone()], data: vec![ Felt252::from(257), Felt252::from(4096), Felt252::zero(), Felt252::zero(), ], }], storage_read_values: vec![ Felt252::zero(), Felt252::zero(), Felt252::zero(), Felt252::zero(), ], accessed_storage_keys: HashSet::from([ [ 2, 162, 196, 156, 77, 186, 13, 145, 179, 79, 42, 222, 133, 212, 29, 9, 86, 31, 154, 119, 136, 76, 21, 186, 42, 176, 242, 36, 27, 8, 13, 235, ], [ 7, 35, 151, 50, 8, 99, 155, 120, 57, 206, 41, 143, 127, 254, 166, 30, 63, 149, 51, 135, 45, 239, 215, 171, 219, 145, 2, 61, 180, 101, 136, 19, ], [ 7, 35, 151, 50, 8, 99, 155, 120, 57, 206, 41, 143, 127, 254, 166, 30, 63, 149, 51, 135, 45, 239, 215, 171, 219, 145, 2, 61, 180, 101, 136, 18, ], [ 2, 162, 196, 156, 77, 186, 13, 145, 179, 79, 42, 222, 133, 212, 29, 9, 86, 31, 154, 119, 136, 76, 21, 186, 42, 176, 242, 36, 27, 8, 13, 236, ], ]), } } fn declare_tx() -> Declare { Declare { contract_class: get_contract_class(TEST_EMPTY_CONTRACT_PATH).unwrap(), class_hash: felt_to_hash(&TEST_EMPTY_CONTRACT_CLASS_HASH), sender_address: TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), tx_type: TransactionType::Declare, validate_entry_point_selector: VALIDATE_DECLARE_ENTRY_POINT_SELECTOR.clone(), version: 1.into(), max_fee: 2, signature: vec![], nonce: 0.into(), hash_value: 0.into(), } } fn declarev2_tx() -> DeclareV2 { let program_data = include_bytes!("../starknet_programs/cairo1/fibonacci.sierra"); let sierra_contract_class: SierraContractClass = serde_json::from_slice(program_data).unwrap(); DeclareV2 { sender_address: TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), tx_type: TransactionType::Declare, validate_entry_point_selector: VALIDATE_DECLARE_ENTRY_POINT_SELECTOR.clone(), version: 1.into(), max_fee: 2, signature: vec![], nonce: 0.into(), hash_value: 0.into(), compiled_class_hash: TEST_FIB_COMPILED_CONTRACT_CLASS_HASH.clone(), sierra_contract_class, casm_class: Default::default(), } } fn deploy_fib_syscall() -> Deploy { Deploy { hash_value: 0.into(), version: 1.into(), contract_address: TEST_FIB_CONTRACT_ADDRESS.clone(), contract_address_salt: 0.into(), contract_hash: felt_to_hash(&TEST_FIB_COMPILED_CONTRACT_CLASS_HASH.clone()), constructor_calldata: Vec::new(), tx_type: TransactionType::Deploy, } } fn expected_declare_fee_transfer_info() -> CallInfo { CallInfo { caller_address: TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), call_type: Some(CallType::Call), contract_address: TEST_ERC20_CONTRACT_ADDRESS.clone(), class_hash: Some(felt_to_hash(&TEST_ERC20_CONTRACT_CLASS_HASH)), entry_point_selector: Some(TRANSFER_ENTRY_POINT_SELECTOR.clone()), entry_point_type: Some(EntryPointType::External), calldata: vec![ TEST_SEQUENCER_ADDRESS.0.clone(), Felt252::zero(), Felt252::zero(), ], retdata: vec![1.into()], events: vec![OrderedEvent::new( 0, vec![felt_str!( "271746229759260285552388728919865295615886751538523744128730118297934206697" )], vec![ TEST_ACCOUNT_CONTRACT_ADDRESS.clone().0, TEST_SEQUENCER_ADDRESS.clone().0, 0.into(), 0.into(), ], )], storage_read_values: vec![ Felt252::zero(), Felt252::zero(), Felt252::zero(), Felt252::zero(), ], accessed_storage_keys: HashSet::from([ [ 2, 162, 196, 156, 77, 186, 13, 145, 179, 79, 42, 222, 133, 212, 29, 9, 86, 31, 154, 119, 136, 76, 21, 186, 42, 176, 242, 36, 27, 8, 13, 236, ], [ 7, 35, 151, 50, 8, 99, 155, 120, 57, 206, 41, 143, 127, 254, 166, 30, 63, 149, 51, 135, 45, 239, 215, 171, 219, 145, 2, 61, 180, 101, 136, 18, ], [ 7, 35, 151, 50, 8, 99, 155, 120, 57, 206, 41, 143, 127, 254, 166, 30, 63, 149, 51, 135, 45, 239, 215, 171, 219, 145, 2, 61, 180, 101, 136, 19, ], [ 2, 162, 196, 156, 77, 186, 13, 145, 179, 79, 42, 222, 133, 212, 29, 9, 86, 31, 154, 119, 136, 76, 21, 186, 42, 176, 242, 36, 27, 8, 13, 235, ], ]), execution_resources: ExecutionResources { n_steps: 525, n_memory_holes: 59, builtin_instance_counter: HashMap::from([ (RANGE_CHECK_BUILTIN_NAME.to_string(), 21), (HASH_BUILTIN_NAME.to_string(), 4), ]), }, ..Default::default() } } #[test] fn test_declare_tx() { let (block_context, mut state) = create_account_tx_test_state().unwrap(); assert_eq!(state, expected_state_before_tx()); let declare_tx = declare_tx(); // Check ContractClass is not set before the declare_tx assert!(state.get_contract_class(&declare_tx.class_hash).is_err()); // Execute declare_tx let result = declare_tx.execute(&mut state, &block_context).unwrap(); // Check ContractClass is set after the declare_tx assert!(state.get_contract_class(&declare_tx.class_hash).is_ok()); let expected_execution_info = TransactionExecutionInfo::new( Some(CallInfo { call_type: Some(CallType::Call), contract_address: TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), class_hash: Some(felt_to_hash(&TEST_ACCOUNT_CONTRACT_CLASS_HASH)), entry_point_selector: Some(VALIDATE_DECLARE_ENTRY_POINT_SELECTOR.clone()), entry_point_type: Some(EntryPointType::External), calldata: vec![TEST_EMPTY_CONTRACT_CLASS_HASH.clone()], execution_resources: ExecutionResources { n_steps: 12, ..Default::default() }, ..Default::default() }), None, Some(expected_declare_fee_transfer_info()), 0, HashMap::from([ ("range_check_builtin".to_string(), 57), ("pedersen_builtin".to_string(), 15), ("l1_gas_usage".to_string(), 0), ]), Some(TransactionType::Declare), ); assert_eq!(result, expected_execution_info); } #[test] fn test_declarev2_tx() { let (block_context, mut state) = create_account_tx_test_state().unwrap(); assert_eq!(state, expected_state_before_tx()); let declare_tx = declarev2_tx(); // Check ContractClass is not set before the declare_tx assert!(state .get_contract_class(&felt_to_hash(&declare_tx.compiled_class_hash)) .is_err()); // Execute declare_tx let result = declare_tx.execute(&mut state, &block_context).unwrap(); // Check ContractClass is set after the declare_tx assert!(state .get_contract_class(&declare_tx.compiled_class_hash.to_be_bytes()) .is_ok()); let expected_execution_info = TransactionExecutionInfo::new( Some(CallInfo { call_type: Some(CallType::Call), contract_address: TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), class_hash: Some(felt_to_hash(&TEST_ACCOUNT_CONTRACT_CLASS_HASH)), entry_point_selector: Some(VALIDATE_DECLARE_ENTRY_POINT_SELECTOR.clone()), entry_point_type: Some(EntryPointType::External), calldata: vec![TEST_FIB_COMPILED_CONTRACT_CLASS_HASH.clone()], execution_resources: ExecutionResources { n_steps: 12, ..Default::default() }, ..Default::default() }), None, Some(expected_declare_fee_transfer_info()), 0, HashMap::from([ ("range_check_builtin".to_string(), 57), ("pedersen_builtin".to_string(), 15), ("l1_gas_usage".to_string(), 0), ]), Some(TransactionType::Declare), ); assert_eq!(result, expected_execution_info); } fn expected_execute_call_info() -> CallInfo { CallInfo { caller_address: Address(Felt252::zero()), call_type: Some(CallType::Call), contract_address: TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), code_address: None, class_hash: Some(felt_to_hash(&TEST_ACCOUNT_CONTRACT_CLASS_HASH.clone())), entry_point_selector: Some(EXECUTE_ENTRY_POINT_SELECTOR.clone()), entry_point_type: Some(EntryPointType::External), calldata: vec![ Felt252::from(256), Felt252::from_str_radix( "039a1491f76903a16feed0a6433bec78de4c73194944e1118e226820ad479701", 16, ) .unwrap(), Felt252::from(1), Felt252::from(2), ], retdata: vec![Felt252::from(2)], l2_to_l1_messages: vec![], internal_calls: vec![CallInfo { caller_address: TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), call_type: Some(CallType::Call), class_hash: Some(felt_to_hash(&TEST_CLASS_HASH.clone())), entry_point_selector: Some( Felt252::from_str_radix( "039a1491f76903a16feed0a6433bec78de4c73194944e1118e226820ad479701", 16, ) .unwrap(), ), entry_point_type: Some(EntryPointType::External), calldata: vec![Felt252::from(2)], retdata: vec![Felt252::from(2)], events: vec![], l2_to_l1_messages: vec![], internal_calls: vec![], contract_address: TEST_CONTRACT_ADDRESS.clone(), code_address: None, execution_resources: ExecutionResources { n_steps: 22, ..Default::default() }, ..Default::default() }], events: vec![], execution_resources: ExecutionResources { n_steps: 61, n_memory_holes: 0, builtin_instance_counter: HashMap::from([(RANGE_CHECK_BUILTIN_NAME.to_string(), 1)]), }, ..Default::default() } } fn expected_fib_execute_call_info() -> CallInfo { CallInfo { caller_address: Address(Felt252::zero()), call_type: Some(CallType::Call), contract_address: TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), code_address: None, class_hash: Some(felt_to_hash(&TEST_ACCOUNT_CONTRACT_CLASS_HASH.clone())), entry_point_selector: Some(EXECUTE_ENTRY_POINT_SELECTOR.clone()), entry_point_type: Some(EntryPointType::External), calldata: vec![ Felt252::from(27728), Felt252::from_bytes_be(&calculate_sn_keccak(b"fib")), Felt252::from(3), Felt252::from(42), Felt252::from(0), Felt252::from(0), ], retdata: vec![Felt252::from(42)], execution_resources: ExecutionResources { n_steps: 160, n_memory_holes: 1, builtin_instance_counter: HashMap::from([("range_check_builtin".to_string(), 4)]), }, l2_to_l1_messages: vec![], internal_calls: vec![CallInfo { caller_address: TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), call_type: Some(CallType::Call), class_hash: Some(felt_to_hash(&TEST_FIB_COMPILED_CONTRACT_CLASS_HASH.clone())), entry_point_selector: Some(Felt252::from_bytes_be(&calculate_sn_keccak(b"fib"))), entry_point_type: Some(EntryPointType::External), calldata: vec![Felt252::from(42), Felt252::from(0), Felt252::from(0)], retdata: vec![Felt252::from(42)], events: vec![], l2_to_l1_messages: vec![], internal_calls: vec![], contract_address: TEST_FIB_CONTRACT_ADDRESS.clone(), code_address: None, gas_consumed: 4710, execution_resources: ExecutionResources { n_steps: 121, n_memory_holes: 1, builtin_instance_counter: HashMap::from([("range_check_builtin".to_string(), 3)]), }, ..Default::default() }], events: vec![], ..Default::default() } } fn expected_validate_call_info_2() -> CallInfo { CallInfo { caller_address: Address(Felt252::zero()), call_type: Some(CallType::Call), contract_address: TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), class_hash: Some(felt_to_hash(&TEST_ACCOUNT_CONTRACT_CLASS_HASH.clone())), entry_point_selector: Some(VALIDATE_ENTRY_POINT_SELECTOR.clone()), entry_point_type: Some(EntryPointType::External), calldata: vec![ Felt252::from(256), Felt252::from_str_radix( "039a1491f76903a16feed0a6433bec78de4c73194944e1118e226820ad479701", 16, ) .unwrap(), Felt252::from(1), Felt252::from(2), ], execution_resources: ExecutionResources { n_steps: 21, n_memory_holes: 0, builtin_instance_counter: HashMap::from([(RANGE_CHECK_BUILTIN_NAME.to_string(), 1)]), }, ..Default::default() } } fn expected_fib_validate_call_info_2() -> CallInfo { CallInfo { caller_address: Address(Felt252::zero()), call_type: Some(CallType::Call), contract_address: TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), class_hash: Some(felt_to_hash(&TEST_ACCOUNT_CONTRACT_CLASS_HASH.clone())), entry_point_selector: Some(VALIDATE_ENTRY_POINT_SELECTOR.clone()), entry_point_type: Some(EntryPointType::External), calldata: vec![ Felt252::from(27728), Felt252::from_bytes_be(&calculate_sn_keccak(b"fib")), Felt252::from(3), Felt252::from(42), Felt252::from(0), Felt252::from(0), ], execution_resources: ExecutionResources { n_steps: 21, n_memory_holes: 0, builtin_instance_counter: HashMap::from([("range_check_builtin".to_string(), 1)]), }, ..Default::default() } } fn expected_transaction_execution_info() -> TransactionExecutionInfo { TransactionExecutionInfo::new( Some(expected_validate_call_info_2()), Some(expected_execute_call_info()), Some(expected_fee_transfer_info()), 0, HashMap::from([ ("pedersen_builtin".to_string(), 16), ("l1_gas_usage".to_string(), 0), ("range_check_builtin".to_string(), 72), ]), Some(TransactionType::InvokeFunction), ) } fn expected_fib_transaction_execution_info() -> TransactionExecutionInfo { TransactionExecutionInfo::new( Some(expected_fib_validate_call_info_2()), Some(expected_fib_execute_call_info()), Some(expected_fib_fee_transfer_info()), 0, HashMap::from([ ("pedersen_builtin".to_string(), 16), ("l1_gas_usage".to_string(), 0), ("range_check_builtin".to_string(), 75), ]), Some(TransactionType::InvokeFunction), ) } #[test] fn test_invoke_tx() { let (starknet_general_context, state) = &mut create_account_tx_test_state().unwrap(); let Address(test_contract_address) = TEST_CONTRACT_ADDRESS.clone(); let calldata = vec![ test_contract_address, // CONTRACT_ADDRESS Felt252::from_bytes_be(&calculate_sn_keccak(b"return_result")), // CONTRACT FUNCTION SELECTOR Felt252::from(1), // CONTRACT_CALLDATA LEN Felt252::from(2), // CONTRACT_CALLDATA ]; let invoke_tx = invoke_tx(calldata); // Extract invoke transaction fields for testing, as it is consumed when creating an account // transaction. let result = invoke_tx .execute(state, starknet_general_context, 0) .unwrap(); let expected_execution_info = expected_transaction_execution_info(); assert_eq!(result, expected_execution_info); } #[test] fn test_invoke_tx_state() { let (starknet_general_context, state) = &mut create_account_tx_test_state().unwrap(); let expected_initial_state = expected_state_before_tx(); assert_eq!(state, &expected_initial_state); let Address(test_contract_address) = TEST_CONTRACT_ADDRESS.clone(); let calldata = vec![ test_contract_address, // CONTRACT_ADDRESS Felt252::from_bytes_be(&calculate_sn_keccak(b"return_result")), // CONTRACT FUNCTION SELECTOR Felt252::from(1), // CONTRACT_CALLDATA LEN Felt252::from(2), // CONTRACT_CALLDATA ]; let invoke_tx = invoke_tx(calldata); invoke_tx .execute(state, starknet_general_context, 0) .unwrap(); let expected_final_state = expected_state_after_tx(); assert_eq!(*state, expected_final_state); } #[test] fn test_invoke_with_declarev2_tx() { let (starknet_general_config, state) = &mut create_account_tx_test_state().unwrap(); let expected_initial_state = expected_state_before_tx(); assert_eq!(state, &expected_initial_state); // Declare the fibonacci contract let declare_tx = declarev2_tx(); declare_tx.execute(state, starknet_general_config).unwrap(); // Deploy the fibonacci contract let deploy = deploy_fib_syscall(); deploy.execute(state, starknet_general_config).unwrap(); let Address(test_contract_address) = TEST_FIB_CONTRACT_ADDRESS.clone(); let calldata = vec![ test_contract_address, // CONTRACT ADDRESS Felt252::from_bytes_be(&calculate_sn_keccak(b"fib")), // CONTRACT FUNCTION SELECTOR Felt252::from(3), // CONTRACT CALLDATA LEN Felt252::from(42), // a Felt252::from(0), // b Felt252::from(0), // n ]; let invoke_tx = invoke_tx(calldata); let expected_gas_consumed = 4710; let result = invoke_tx .execute(state, starknet_general_config, expected_gas_consumed) .unwrap(); let expected_execution_info = expected_fib_transaction_execution_info(); assert_eq!(result, expected_execution_info); } #[test] fn test_deploy_account() { let (block_context, mut state) = create_account_tx_test_state().unwrap(); let deploy_account_tx = DeployAccount::new( felt_to_hash(&TEST_ACCOUNT_CONTRACT_CLASS_HASH), 2, TRANSACTION_VERSION.clone(), Default::default(), Default::default(), Default::default(), Default::default(), StarknetChainId::TestNet.to_felt(), None, ) .unwrap(); state.set_storage_at( &( block_context .starknet_os_config() .fee_token_address() .clone(), TEST_ERC20_DEPLOYED_ACCOUNT_BALANCE_KEY.to_be_bytes(), ), ACTUAL_FEE.clone(), ); let (state_before, state_after) = expected_deploy_account_states(); assert_eq!(state, state_before); // Statement **not** in blockifier. state.cache_mut().nonce_initial_values_mut().insert( deploy_account_tx.contract_address().clone(), Felt252::zero(), ); let tx_info = deploy_account_tx .execute(&mut state, &block_context) .unwrap(); assert_eq!(state, state_after); let expected_validate_call_info = expected_validate_call_info( VALIDATE_DEPLOY_ENTRY_POINT_SELECTOR.clone(), [ Felt252::from_bytes_be(deploy_account_tx.class_hash()), deploy_account_tx.contract_address_salt().clone(), ] .into_iter() .chain(deploy_account_tx.constructor_calldata().clone()) .collect(), deploy_account_tx.contract_address().clone(), ); let expected_execute_call_info = CallInfo { entry_point_type: EntryPointType::Constructor.into(), entry_point_selector: CONSTRUCTOR_ENTRY_POINT_SELECTOR.clone().into(), contract_address: deploy_account_tx.contract_address().clone(), // Entries **not** in blockifier. class_hash: Some(TEST_ACCOUNT_CONTRACT_CLASS_HASH.to_be_bytes()), call_type: Some(CallType::Call), ..Default::default() }; let expected_fee_transfer_call_info = expected_fee_transfer_call_info( &block_context, deploy_account_tx.contract_address(), ACTUAL_FEE.to_u64().unwrap(), ); let expected_execution_info = TransactionExecutionInfo::new( expected_validate_call_info.into(), expected_execute_call_info.into(), expected_fee_transfer_call_info.into(), ACTUAL_FEE.to_u128().unwrap(), // Entry **not** in blockifier. // Default::default(), [ ("l1_gas_usage", 3672), ("range_check_builtin", 74), ("pedersen_builtin", 23), ] .into_iter() .map(|(k, v)| (k.to_string(), v)) .collect(), TransactionType::DeployAccount.into(), ); assert_eq!(tx_info, expected_execution_info); let nonce_from_state = state .get_nonce_at(deploy_account_tx.contract_address()) .unwrap(); assert_eq!(nonce_from_state, Felt252::one()); let hash = TEST_ERC20_DEPLOYED_ACCOUNT_BALANCE_KEY.to_be_bytes(); validate_final_balances(&mut state, &block_context, Felt252::zero(), &hash); let class_hash_from_state = state .get_class_hash_at(deploy_account_tx.contract_address()) .unwrap(); assert_eq!(class_hash_from_state, *deploy_account_tx.class_hash()); } fn expected_deploy_account_states() -> ( CachedState, CachedState, ) { let mut state_before = CachedState::new( InMemoryStateReader::new( HashMap::from([ ( Address(0x101.into()), felt_to_hash(&0x111.into()), ), ( Address(0x100.into()), felt_to_hash(&0x110.into()), ), ( Address(0x1001.into()), felt_to_hash(&0x1010.into()), )]), HashMap::from([ ( Address(0x101.into()), Default::default(), ), ( Address(0x100.into()), Default::default(), ), ( Address(0x1001.into()), Default::default(), )]), HashMap::from([ ( (Address(0x1001.into()), felt_to_hash(&felt_str!("1192211877881866289306604115402199097887041303917861778777990838480655617515"))), Felt252::zero(), ), ]), HashMap::from([ (felt_to_hash(&0x110.into()), ContractClass::try_from(PathBuf::from(TEST_CONTRACT_PATH)).unwrap()), (felt_to_hash(&0x111.into()), ContractClass::try_from(PathBuf::from(ACCOUNT_CONTRACT_PATH)).unwrap()), (felt_to_hash(&0x1010.into()), ContractClass::try_from(PathBuf::from(ERC20_CONTRACT_PATH)).unwrap()), ]), HashMap::new(), HashMap::new() ), Some(ContractClassCache::new()), Some(HashMap::new()) ); state_before.set_storage_at( &( Address(0x1001.into()), felt_to_hash(&felt_str!( "2542253978940891427830343982984992363331567580652119103860970381451088310289" )), ), 0.into(), ); let mut state_after = state_before.clone(); state_after.cache_mut().nonce_initial_values_mut().insert( Address(felt_str!( "386181506763903095743576862849245034886954647214831045800703908858571591162" )), Felt252::zero(), ); state_after .cache_mut() .class_hash_initial_values_mut() .insert(Address(0x1001.into()), felt_to_hash(&0x1010.into())); state_after .cache_mut() .class_hash_initial_values_mut() .insert( Address(felt_str!( "386181506763903095743576862849245034886954647214831045800703908858571591162" )), [0; 32], ); state_after.cache_mut().storage_initial_values_mut().insert( ( Address(0x1001.into()), felt_to_hash(&felt_str!( "2542253978940891427830343982984992363331567580652119103860970381451088310290" )), ), Felt252::zero(), ); state_after.cache_mut().storage_initial_values_mut().insert( ( Address(0x1001.into()), felt_to_hash(&TEST_ERC20_BALANCE_KEY_2), ), Felt252::zero(), ); state_after.cache_mut().storage_initial_values_mut().insert( ( Address(0x1001.into()), felt_to_hash(&felt_str!( "3229073099929281304021185011369329892856197542079132996799046100564060768274" )), ), Felt252::zero(), ); state_after.cache_mut().nonce_writes_mut().insert( Address(felt_str!( "386181506763903095743576862849245034886954647214831045800703908858571591162" )), 1.into(), ); state_after.cache_mut().class_hash_writes_mut().insert( Address(felt_str!( "386181506763903095743576862849245034886954647214831045800703908858571591162" )), felt_to_hash(&0x111.into()), ); state_after.cache_mut().storage_writes_mut().insert( ( Address(0x1001.into()), felt_to_hash(&felt_str!( "2542253978940891427830343982984992363331567580652119103860970381451088310290" )), ), Felt252::zero(), ); state_after.cache_mut().storage_writes_mut().insert( ( Address(0x1001.into()), felt_to_hash(&felt_str!( "2542253978940891427830343982984992363331567580652119103860970381451088310289" )), ), Felt252::zero(), ); state_after.cache_mut().storage_writes_mut().insert( ( Address(0x1001.into()), felt_to_hash(&TEST_ERC20_BALANCE_KEY_2), ), Felt252::zero(), ); state_after.cache_mut().storage_writes_mut().insert( ( Address(0x1001.into()), felt_to_hash(&felt_str!( "3229073099929281304021185011369329892856197542079132996799046100564060768274" )), ), Felt252::zero(), ); state_after .set_contract_class( &felt_to_hash(&0x1010.into()), &ContractClass::try_from(PathBuf::from(ERC20_CONTRACT_PATH)).unwrap(), ) .unwrap(); state_after .set_contract_class( &felt_to_hash(&0x111.into()), &ContractClass::try_from(PathBuf::from(ACCOUNT_CONTRACT_PATH)).unwrap(), ) .unwrap(); (state_before, state_after) } #[test] fn test_state_for_declare_tx() { let (block_context, mut state) = create_account_tx_test_state().unwrap(); let declare_tx = declare_tx(); // Check ContractClass is not set before the declare_tx assert!(state.get_contract_class(&declare_tx.class_hash).is_err()); assert!(state .get_nonce_at(&declare_tx.sender_address) .unwrap() .is_zero()); // Execute declare_tx assert!(declare_tx.execute(&mut state, &block_context).is_ok()); assert!(state .get_nonce_at(&declare_tx.sender_address) .unwrap() .is_one()); // Check state.state_reader let mut state_reader = state.state_reader().clone(); assert_eq!( state_reader.address_to_class_hash_mut(), &mut HashMap::from([ ( TEST_ERC20_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_ERC20_CONTRACT_CLASS_HASH) ), ( TEST_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_CLASS_HASH) ), ( TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_ACCOUNT_CONTRACT_CLASS_HASH) ), ]), ); assert_eq!( state_reader.address_to_nonce_mut(), &mut HashMap::from([ (TEST_ERC20_CONTRACT_ADDRESS.clone(), Felt252::zero()), (TEST_CONTRACT_ADDRESS.clone(), Felt252::zero()), (TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), Felt252::zero()), ]), ); assert_eq!( state_reader.address_to_storage_mut(), &mut HashMap::from([( ( TEST_ERC20_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_ERC20_ACCOUNT_BALANCE_KEY) ), Felt252::zero() ),]), ); assert_eq!( state_reader.class_hash_to_contract_class_mut(), &mut HashMap::from([ ( felt_to_hash(&TEST_ERC20_CONTRACT_CLASS_HASH), get_contract_class(ERC20_CONTRACT_PATH).unwrap() ), ( felt_to_hash(&TEST_CLASS_HASH), get_contract_class(TEST_CONTRACT_PATH).unwrap() ), ( felt_to_hash(&TEST_ACCOUNT_CONTRACT_CLASS_HASH), get_contract_class(ACCOUNT_CONTRACT_PATH).unwrap() ), ]) ); // Check state.cache assert_eq!( state.cache(), &StateCache::new( HashMap::from([ ( TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_ACCOUNT_CONTRACT_CLASS_HASH) ), ( TEST_ERC20_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_ERC20_CONTRACT_CLASS_HASH) ) ]), HashMap::new(), HashMap::from([( TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), 0.into() )]), HashMap::from([ ( ( TEST_ERC20_CONTRACT_ADDRESS.clone(), felt_to_hash(&felt_str!("3229073099929281304021185011369329892856197542079132996799046100564060768275")) ), 0.into() ), ( ( TEST_ERC20_CONTRACT_ADDRESS.clone(), felt_to_hash(&felt_str!("1192211877881866289306604115402199097887041303917861778777990838480655617516")) ), 0.into() ), ( ( TEST_ERC20_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_ERC20_SEQUENCER_BALANCE_KEY) ), 0.into() ), ( ( TEST_ERC20_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_ERC20_ACCOUNT_BALANCE_KEY) ), 0.into() ) ]), HashMap::new(), HashMap::new(), HashMap::from([( TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), 1.into() )]), HashMap::from([ ( ( TEST_ERC20_CONTRACT_ADDRESS.clone(), felt_to_hash(&felt_str!("3229073099929281304021185011369329892856197542079132996799046100564060768275")) ), 0.into() ), ( ( TEST_ERC20_CONTRACT_ADDRESS.clone(), felt_to_hash(&felt_str!("1192211877881866289306604115402199097887041303917861778777990838480655617516")) ), 0.into() ), ( ( TEST_ERC20_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_ERC20_SEQUENCER_BALANCE_KEY) ), 0.into() //Fee, 2 in blockifier ), ( ( TEST_ERC20_CONTRACT_ADDRESS.clone(), felt_to_hash(&TEST_ERC20_ACCOUNT_BALANCE_KEY) ), 0.into() ), ]), HashMap::new() ), ); // Check state.contract_classes assert_eq!( state.contract_classes(), &Some(HashMap::from([ ( felt_to_hash(&TEST_EMPTY_CONTRACT_CLASS_HASH), get_contract_class(TEST_EMPTY_CONTRACT_PATH).unwrap() ), ( felt_to_hash(&TEST_ERC20_CONTRACT_CLASS_HASH), get_contract_class(ERC20_CONTRACT_PATH).unwrap() ), ( felt_to_hash(&TEST_ACCOUNT_CONTRACT_CLASS_HASH), get_contract_class(ACCOUNT_CONTRACT_PATH).unwrap() ), ])) ); } #[test] fn test_invoke_tx_wrong_call_data() { let (starknet_general_context, state) = &mut create_account_tx_test_state().unwrap(); // Calldata with missing inputs let calldata = vec![ TEST_CONTRACT_ADDRESS.clone().0, // CONTRACT_ADDRESS Felt252::from_bytes_be(&calculate_sn_keccak(b"return_result")), // CONTRACT FUNCTION SELECTOR Felt252::from(1), // CONTRACT_CALLDATA LEN // CONTRACT_CALLDATA ]; let invoke_tx = invoke_tx(calldata); // Execute transaction let result = invoke_tx.execute(state, starknet_general_context, 0); // Assert error assert_matches!( result, Err(TransactionError::CairoRunner(CairoRunError::VmException( VmException { inner_exc: VirtualMachineError::DiffAssertValues(..), .. } ))) ); } #[test] fn test_invoke_tx_wrong_entrypoint() { let (starknet_general_context, state) = &mut create_account_tx_test_state().unwrap(); let Address(test_contract_address) = TEST_CONTRACT_ADDRESS.clone(); // Invoke transaction with an entrypoint that doesn't exists let invoke_tx = InvokeFunction::new( TEST_ACCOUNT_CONTRACT_ADDRESS.clone(), // Entrypoiont that doesnt exits in the contract Felt252::from_bytes_be(&calculate_sn_keccak(b"none_function")), 1, TRANSACTION_VERSION.clone(), vec![ test_contract_address, // CONTRACT_ADDRESS Felt252::from_bytes_be(&calculate_sn_keccak(b"return_result")), // CONTRACT FUNCTION SELECTOR Felt252::from(1), // CONTRACT_CALLDATA LEN Felt252::from(2), // CONTRACT_CALLDATA ], vec![], StarknetChainId::TestNet.to_felt(), Some(Felt252::zero()), None, ) .unwrap(); // Execute transaction let result = invoke_tx.execute(state, starknet_general_context, 0); // Assert error assert_matches!(result, Err(TransactionError::EntryPointNotFound)); } #[test] fn test_deploy_undeclared_account() { let (block_context, mut state) = create_account_tx_test_state().unwrap(); let not_deployed_class_hash = [1; 32]; // Deploy transaction with a not_deployed_class_hash class_hash let deploy_account_tx = DeployAccount::new( not_deployed_class_hash, 2, TRANSACTION_VERSION.clone(), Default::default(), Default::default(), Default::default(), Default::default(), StarknetChainId::TestNet.to_felt(), None, ) .unwrap(); // Check not_deployed_class_hash assert!(state.get_contract_class(¬_deployed_class_hash).is_err()); // Execute transaction let result = deploy_account_tx.execute(&mut state, &block_context); // Execute transaction assert_matches!( result, Err(TransactionError::State(StateError::NoneCompiledHash(_))) ); } #[test] fn test_library_call_with_declare_v2() { let (block_context, state) = &mut create_account_tx_test_state().unwrap(); // Declare the fibonacci contract let declare_tx = declarev2_tx(); declare_tx.execute(state, block_context).unwrap(); // Deploy the fibonacci contract let deploy = deploy_fib_syscall(); deploy.execute(state, block_context).unwrap(); // Create program and entry point types for contract class let program_data = include_bytes!("../starknet_programs/cairo1/fibonacci_dispatcher.casm"); let contract_class: CasmContractClass = serde_json::from_slice(program_data).unwrap(); let entrypoints = contract_class.clone().entry_points_by_type; let external_entrypoint_selector = &entrypoints.external.get(0).unwrap().selector; let address = Address(6666.into()); let mut class_hash: ClassHash = [0; 32]; class_hash[0] = 1; let nonce = Felt252::zero(); state .cache_mut() .class_hash_initial_values_mut() .insert(address.clone(), class_hash); state .cache_mut() .nonce_initial_values_mut() .insert(address.clone(), nonce); state .set_compiled_class(&Felt252::from_bytes_be(&class_hash), contract_class) .unwrap(); let create_execute_extrypoint = |selector: &BigUint, calldata: Vec, entry_point_type: EntryPointType| -> ExecutionEntryPoint { ExecutionEntryPoint::new( address.clone(), calldata, Felt252::new(selector.clone()), Address(0000.into()), entry_point_type, Some(CallType::Delegate), Some(class_hash), 1000000000, ) }; // Create an execution entry point let calldata = vec![ TEST_FIB_COMPILED_CONTRACT_CLASS_HASH.clone(), Felt252::from_bytes_be(&calculate_sn_keccak(b"fib")), 1.into(), 1.into(), 10.into(), ]; let send_message_exec_entry_point = create_execute_extrypoint( external_entrypoint_selector, calldata.clone(), EntryPointType::External, ); // Execute the entrypoint let block_context = BlockContext::default(); let mut tx_execution_context = TransactionExecutionContext::new( Address(0.into()), Felt252::zero(), Vec::new(), 100000000, 10.into(), block_context.invoke_tx_max_n_steps(), TRANSACTION_VERSION.clone(), ); let mut resources_manager = ExecutionResourcesManager::default(); // Run send_msg entrypoint let call_info = send_message_exec_entry_point .execute( state, &block_context, &mut resources_manager, &mut tx_execution_context, false, ) .unwrap(); let expected_internal_call_info = CallInfo { caller_address: Address(0.into()), call_type: Some(CallType::Delegate), contract_address: address.clone(), class_hash: Some(TEST_FIB_COMPILED_CONTRACT_CLASS_HASH.clone().to_be_bytes()), entry_point_selector: Some(external_entrypoint_selector.into()), entry_point_type: Some(EntryPointType::External), gas_consumed: 30410, calldata: vec![1.into(), 1.into(), 10.into()], retdata: vec![89.into()], // fib(10) execution_resources: ExecutionResources { n_steps: 371, n_memory_holes: 1, builtin_instance_counter: HashMap::from([("range_check_builtin".to_string(), 13)]), }, ..Default::default() }; let expected_call_info = CallInfo { caller_address: Address(0.into()), call_type: Some(CallType::Delegate), contract_address: address.clone(), class_hash: Some(class_hash), entry_point_selector: Some(external_entrypoint_selector.into()), entry_point_type: Some(EntryPointType::External), gas_consumed: 113480, calldata, retdata: vec![89.into()], // fib(10) execution_resources: ExecutionResources { n_steps: 587, n_memory_holes: 3, builtin_instance_counter: HashMap::from([("range_check_builtin".to_string(), 16)]), }, internal_calls: vec![expected_internal_call_info], ..Default::default() }; assert_eq!(call_info, expected_call_info); }