diff --git a/rpc-state-reader/src/execution.rs b/rpc-state-reader/src/execution.rs index 65859f2..c2db76f 100644 --- a/rpc-state-reader/src/execution.rs +++ b/rpc-state-reader/src/execution.rs @@ -1,7 +1,4 @@ -use crate::{ - objects::{RpcTransactionReceipt, RpcTransactionTrace}, - reader::{PairRpcStateReader, RpcChain, RpcStateReader}, -}; +use crate::reader::{PairRpcStateReader, RpcChain, RpcStateReader}; use anyhow::Context; use blockifier::{ bouncer::BouncerConfig, @@ -9,272 +6,19 @@ use blockifier::{ state::{cached_state::CachedState, state_api::StateReader}, test_utils::MAX_FEE, transaction::{ - account_transaction::ExecutionFlags, - objects::{TransactionExecutionInfo, TransactionExecutionResult}, + account_transaction::ExecutionFlags, objects::TransactionExecutionInfo, transaction_execution::Transaction as BlockiTransaction, transactions::ExecutableTransaction, }, versioned_constants::{VersionedConstants, VersionedConstantsOverrides}, }; use starknet_api::{ - block::{BlockInfo, BlockNumber}, + block::BlockNumber, core::ContractAddress, - felt, - hash::StarkHash, patricia_key, transaction::{Transaction as SNTransaction, TransactionHash}, }; -pub fn execute_tx( - tx_hash: &str, - network: RpcChain, - block_number: BlockNumber, -) -> ( - TransactionExecutionInfo, - RpcTransactionTrace, - RpcTransactionReceipt, -) { - let tx_hash = TransactionHash(StarkHash::from_hex(tx_hash).unwrap()); - - // Instantiate the RPC StateReader and the CachedState - let reader = RpcStateReader::new(network, block_number); - - let block_info = reader.get_block_info().unwrap(); - - // Get transaction before giving ownership of the reader - let tx = reader.get_transaction(&tx_hash).unwrap(); - - let trace = reader.get_transaction_trace(&tx_hash).unwrap(); - let receipt = reader.get_transaction_receipt(&tx_hash).unwrap(); - - // Create state from RPC reader - let mut state = CachedState::new(reader); - - let fee_token_addresses = FeeTokenAddresses { - strk_fee_token_address: ContractAddress(patricia_key!( - "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d" - )), - eth_fee_token_address: ContractAddress(patricia_key!( - "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7" - )), - }; - let chain_info = ChainInfo { - chain_id: network.into(), - fee_token_addresses, - }; - let mut versioned_constants = - VersionedConstants::get_versioned_constants(VersionedConstantsOverrides { - validate_max_n_steps: u32::MAX, - invoke_tx_max_n_steps: u32::MAX, - max_recursion_depth: usize::MAX, - }); - versioned_constants.disable_cairo0_redeclaration = false; - - let block_context = BlockContext::new( - block_info, - chain_info, - versioned_constants, - BouncerConfig::max(), - ); - - let exec_flags = ExecutionFlags { - charge_fee: false, - only_query: false, - validate: true, - }; - - // Map starknet_api transaction to blockifier's - let blockifier_tx = match tx { - SNTransaction::Invoke(_) | SNTransaction::DeployAccount(_) => { - BlockiTransaction::from_api(tx, tx_hash, None, None, None, exec_flags).unwrap() - } - // SNTransaction::Declare(ref declare_tx) => { - // let block_number = block_context.block_info().block_number; - // let network = parse_to_rpc_chain(&block_context.chain_info().chain_id.to_string()); - // // we need to retrieve the next block in order to get the contract_class - // let next_reader = RpcStateReader::new(network, block_number.next().unwrap()); - // let contract_class = next_reader - // .get_contract_class(&declare_tx.class_hash()) - // .unwrap(); - // let class_info = calculate_class_info_for_testing(contract_class); - - // BlockiTransaction::from_api(tx, tx_hash, Some(class_info), None, None, exec_flags) - // .unwrap() - // } - SNTransaction::L1Handler(_) => { - BlockiTransaction::from_api(tx, tx_hash, None, Some(MAX_FEE), None, exec_flags).unwrap() - } - _ => todo!(), - }; - - ( - blockifier_tx.execute(&mut state, &block_context).unwrap(), - trace, - receipt, - ) -} - -pub fn execute_tx_configurable_with_state( - tx_hash: TransactionHash, - tx: SNTransaction, - block_info: BlockInfo, - _skip_validate: bool, - _skip_nonce_check: bool, - charge_fee: bool, - state: &mut CachedState, -) -> TransactionExecutionResult { - let fee_token_address = FeeTokenAddresses { - strk_fee_token_address: ContractAddress( - felt!("0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d") - .try_into() - .unwrap(), - ), - eth_fee_token_address: ContractAddress( - felt!("0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7") - .try_into() - .unwrap(), - ), - }; - - // Get values for block context before giving ownership of the reader - let chain_id = state.state.get_chain_id(); - - let chain_info = ChainInfo { - chain_id: chain_id.clone(), - fee_token_addresses: fee_token_address, - }; - let mut versioned_constants = - VersionedConstants::get_versioned_constants(VersionedConstantsOverrides { - validate_max_n_steps: u32::MAX, - invoke_tx_max_n_steps: u32::MAX, - max_recursion_depth: usize::MAX, - }); - versioned_constants.disable_cairo0_redeclaration = false; - - let block_context = BlockContext::new( - block_info, - chain_info, - versioned_constants, - BouncerConfig::max(), - ); - - let exec_flags = ExecutionFlags { - charge_fee, - only_query: false, - validate: true, - }; - - // Get transaction before giving ownership of the reader - let blockifier_tx = match tx { - SNTransaction::Invoke(_) | SNTransaction::DeployAccount(_) => { - BlockiTransaction::from_api(tx, tx_hash, None, None, None, exec_flags).unwrap() - } - // SNTransaction::Declare(ref declare_tx) => { - // let block_number = block_context.block_info().block_number; - // let network = parse_to_rpc_chain(&chain_id.to_string()); - // // we need to retrieve the next block in order to get the contract_class - // let next_reader = RpcStateReader::new(network, block_number.next().unwrap()); - // let contract_class = next_reader - // .get_contract_class(&declare_tx.class_hash()) - // .unwrap(); - // let class_info = calculate_class_info_for_testing(contract_class); - - // BlockiTransaction::from_api(tx, tx_hash, Some(class_info), None, None, exec_flags) - // .unwrap() - // } - SNTransaction::L1Handler(_) => { - BlockiTransaction::from_api(tx, tx_hash, None, Some(MAX_FEE), None, exec_flags).unwrap() - } - _ => unimplemented!(), - }; - - blockifier_tx.execute(state, &block_context) -} - -pub fn execute_tx_configurable( - state: &mut CachedState, - tx_hash: &str, - _block_number: BlockNumber, - skip_validate: bool, - skip_nonce_check: bool, - charge_fee: bool, -) -> TransactionExecutionResult { - let tx_hash = TransactionHash(StarkHash::from_hex(tx_hash).unwrap()); - let tx = state.state.get_transaction(&tx_hash).unwrap(); - let block_info = state.state.get_block_info().unwrap(); - let blockifier_exec_info = execute_tx_configurable_with_state( - tx_hash, - tx, - block_info, - skip_validate, - skip_nonce_check, - charge_fee, - state, - )?; - Ok(blockifier_exec_info) -} - -/// Executes a transaction with blockifier -/// -/// Unlike `execute_tx_configurable`, it does not depend on our state reader -/// and can be used with any cached state. It already receives all context information -/// needed to execute the transaction. -pub fn execute_tx_with_blockifier( - state: &mut CachedState, - context: BlockContext, - transaction: SNTransaction, - transaction_hash: TransactionHash, -) -> TransactionExecutionResult { - let exec_flags = ExecutionFlags { - charge_fee: false, - only_query: false, - validate: true, - }; - - let account_transaction: BlockiTransaction = match transaction { - SNTransaction::Invoke(_) | SNTransaction::DeployAccount(_) => BlockiTransaction::from_api( - transaction, - transaction_hash, - None, - None, - None, - exec_flags, - )?, - // SNTransaction::Declare(ref declare_tx) => { - // let block_number = context.block_info().block_number; - // let network = parse_to_rpc_chain(&context.chain_info().chain_id.to_string()); - // // we need to retrieve the next block in order to get the contract_class - // let contract_class = state - // .get_compiled_class(&declare_tx.class_hash()) - // .unwrap(); - // let class_info = calculate_class_info_for_testing(contract_class); - - // BlockiTransaction::from_api( - // transaction, - // transaction_hash, - // Some(class_info), - // None, - // None, - // exec_flags, - // )? - // } - SNTransaction::L1Handler(_) => { - // As L1Hanlder is not an account transaction we execute it here and return the result - BlockiTransaction::from_api( - transaction, - transaction_hash, - None, - Some(MAX_FEE), - None, - exec_flags, - )? - } - _ => unimplemented!(), - }; - - account_transaction.execute(state, &context) -} - pub fn fetch_block_context(reader: &RpcStateReader) -> anyhow::Result { let block_info = reader.get_block_info()?; let mut versioned_constants = @@ -324,8 +68,7 @@ pub fn fetch_blockifier_transaction( None }; - let transaction = - BlockiTransaction::from_api(transaction, hash.clone(), class_info, fee, None, flags)?; + let transaction = BlockiTransaction::from_api(transaction, hash, class_info, fee, None, flags)?; Ok(transaction) } @@ -374,7 +117,7 @@ pub fn fetch_transaction( next: next_reader, }; - let transaction = fetch_blockifier_transaction(&pair_reader, flags, hash.clone())?; + let transaction = fetch_blockifier_transaction(&pair_reader, flags, *hash)?; let context = fetch_block_context(&pair_reader.current)?; Ok((transaction, context)) @@ -385,7 +128,7 @@ pub fn execute_transaction_with_state( transaction: &BlockiTransaction, context: &BlockContext, ) -> anyhow::Result { - let execution_info = transaction.execute(state, &context)?; + let execution_info = transaction.execute(state, context)?; Ok(execution_info) } @@ -401,6 +144,7 @@ mod tests { execution::call_info::{CallInfo, ChargedResources, EventSummary, ExecutionSummary}, fee::resources::{StarknetResources, StateResources}, state::cached_state::StateChangesCount, + transaction::account_transaction::ExecutionFlags, }; use cairo_vm::{ types::builtin_name::BuiltinName, vm::runners::cairo_runner::ExecutionResources, @@ -410,6 +154,7 @@ mod tests { block::BlockNumber, class_hash, execution_resources::{GasAmount, GasVector}, + felt, state::StorageKey, }; use test_case::test_case; @@ -430,9 +175,19 @@ mod tests { => ignore["broken on both due to a cairo-vm error"] )] fn blockifier_test_case_reverted_tx(hash: &str, block_number: u64, chain: RpcChain) { - // To reexecute a transaction, we must use the state from its previous block - let previous_block = BlockNumber(block_number - 1); - let (tx_info, trace, _) = execute_tx(hash, chain, previous_block); + let hash = TransactionHash(felt!(hash)); + let block_number = BlockNumber(block_number); + let flags = ExecutionFlags { + only_query: false, + charge_fee: false, + validate: true, + }; + + let tx_info = execute_transaction(&hash, block_number, chain, flags).unwrap(); + + let next_reader = RpcStateReader::new(chain, block_number); + let trace = next_reader.get_transaction_trace(&hash).unwrap(); + assert_eq!( tx_info.revert_error.map(|r| r.to_string()), trace.execute_invocation.unwrap().revert_reason @@ -599,9 +354,18 @@ mod tests { => ignore )] fn blockifier_tx(hash: &str, block_number: u64, chain: RpcChain) { - // To reexecute a transaction, we must use the state from its previous block - let previous_block = BlockNumber(block_number - 1); - let (tx_info, trace, _receipt) = execute_tx(hash, chain, previous_block); + let hash = TransactionHash(felt!(hash)); + let block_number = BlockNumber(block_number); + let flags = ExecutionFlags { + only_query: false, + charge_fee: false, + validate: true, + }; + + let tx_info = execute_transaction(&hash, block_number, chain, flags).unwrap(); + + let next_reader = RpcStateReader::new(chain, block_number); + let trace = next_reader.get_transaction_trace(&hash).unwrap(); // We cannot currently check fee & resources @@ -3318,8 +3082,15 @@ mod tests { n_allocated_keys: usize, execution_summary: ExecutionSummary, ) { - let previous_block = BlockNumber(block_number - 1); - let (tx_info, _, _) = execute_tx(hash, chain, previous_block); + let hash = TransactionHash(felt!(hash)); + let block_number = BlockNumber(block_number); + let flags = ExecutionFlags { + only_query: false, + charge_fee: false, + validate: true, + }; + let tx_info = execute_transaction(&hash, block_number, chain, flags).unwrap(); + let starknet_resources = tx_info.clone().receipt.resources.starknet_resources; let state_resources = StateResources::new_for_testing(starknet_chg, n_allocated_keys); let starknet_rsc = StarknetResources::new( @@ -3353,10 +3124,14 @@ mod tests { /// transaction (therefore, the same contracts) multiple times at the same /// time, helping to uncover any possible concurrency bug that we may have fn test_concurrency(tx_hash: &str, block_number: u64, chain: RpcChain) { - let reader = RpcStateReader::new(RpcChain::MainNet, BlockNumber(block_number - 1)); - let context = fetch_block_context(&reader).unwrap(); - let tx_hash = TransactionHash(felt!(tx_hash)); - let tx = reader.get_transaction(&tx_hash).unwrap(); + let hash = TransactionHash(felt!(tx_hash)); + let block_number = BlockNumber(block_number); + let flags = ExecutionFlags { + only_query: false, + charge_fee: false, + validate: true, + }; + let (tx, context) = fetch_transaction(&hash, block_number, chain, flags).unwrap(); let mut handles = Vec::new(); @@ -3364,12 +3139,13 @@ mod tests { let context = context.clone(); let tx = tx.clone(); - handles.push(thread::spawn(move || { - let reader = RpcStateReader::new(chain, BlockNumber(block_number - 1)); - let mut cache = CachedState::new(reader); + let previous_block_number = block_number.prev().unwrap(); + let current_reader = RpcStateReader::new(chain, previous_block_number); + let mut cache = CachedState::new(current_reader); + handles.push(thread::spawn(move || { let execution_info = - execute_tx_with_blockifier(&mut cache, context, tx, tx_hash).unwrap(); + execute_transaction_with_state(&mut cache, &tx, &context).unwrap(); assert!( !execution_info.is_reverted(),