From a4135be6e1fd5dc40e76d9462b23125cf5a85141 Mon Sep 17 00:00:00 2001 From: 0xTrinityy Date: Wed, 6 Mar 2024 12:19:32 +0100 Subject: [PATCH 1/2] feat: :white_check_mark: Implementing Trace_transaction RPC Call --- crates/client/rpc-core/src/lib.rs | 4 + crates/client/rpc/src/lib.rs | 5 +- crates/client/rpc/src/trace_api.rs | 365 ++++++++++++------ .../pallets/starknet/runtime_api/src/lib.rs | 16 +- crates/pallets/starknet/src/simulations.rs | 170 +++++--- .../src/tests/re_execute_transactions.rs | 113 +++++- crates/runtime/src/lib.rs | 4 +- 7 files changed, 503 insertions(+), 174 deletions(-) diff --git a/crates/client/rpc-core/src/lib.rs b/crates/client/rpc-core/src/lib.rs index 2a2c85f6e..b38a0aaca 100644 --- a/crates/client/rpc-core/src/lib.rs +++ b/crates/client/rpc-core/src/lib.rs @@ -169,4 +169,8 @@ pub trait StarknetTraceRpcApi { #[method(name = "traceBlockTransactions")] /// Returns the execution traces of all transactions included in the given block async fn trace_block_transactions(&self, block_id: BlockId) -> RpcResult>; + + #[method(name = "traceTransaction")] + /// Returns the execution trace of a transaction + async fn trace_transaction(&self, transaction_hash: FieldElement) -> RpcResult; } diff --git a/crates/client/rpc/src/lib.rs b/crates/client/rpc/src/lib.rs index 5042fcf3b..82013deec 100644 --- a/crates/client/rpc/src/lib.rs +++ b/crates/client/rpc/src/lib.rs @@ -1551,11 +1551,12 @@ where TransactionMp::Deploy(_) => todo!(), }) .collect::, _>>()?; - + + let empty_transactions = vec![]; let execution_infos = self .client .runtime_api() - .re_execute_transactions(previous_substrate_block_hash, transactions.clone()) + .re_execute_transactions(previous_substrate_block_hash, empty_transactions, transactions.clone()) .map_err(|e| { log::error!("Failed to execute runtime API call: {e}"); StarknetRpcApiError::InternalServerError diff --git a/crates/client/rpc/src/trace_api.rs b/crates/client/rpc/src/trace_api.rs index e70bbf18d..6bf90f506 100644 --- a/crates/client/rpc/src/trace_api.rs +++ b/crates/client/rpc/src/trace_api.rs @@ -10,6 +10,7 @@ use mc_genesis_data_provider::GenesisProvider; use mc_rpc_core::utils::get_block_by_block_hash; use mc_rpc_core::{StarknetReadRpcApiServer, StarknetTraceRpcApiServer}; use mc_storage::StorageOverride; +use mp_block::Block; use mp_felt::Felt252Wrapper; use mp_hashers::HasherT; use mp_simulations::{PlaceHolderErrorTypeForFailedStarknetExecution, SimulationFlags}; @@ -33,7 +34,6 @@ use thiserror::Error; use crate::errors::StarknetRpcApiError; use crate::Starknet; -use crate::utils::blockifier_to_starknet_rs_ordered_events; #[async_trait] #[allow(unused_variables)] @@ -108,108 +108,22 @@ where error!("Failed to get block for block hash {substrate_block_hash}: '{e}'"); StarknetRpcApiError::InternalServerError })?; + let chain_id = Felt252Wrapper(self.chain_id()?.0); + let block_number = starknet_block.header().block_number; - let block_transactions = starknet_block - .transactions() - .iter() - .map(|tx| match tx { - Transaction::Invoke(invoke_tx) => { - RpcResult::Ok(UserOrL1HandlerTransaction::User(UserTransaction::Invoke(invoke_tx.clone()))) - } - Transaction::DeployAccount(deploy_account_tx) => { - Ok(UserOrL1HandlerTransaction::User(UserTransaction::DeployAccount(deploy_account_tx.clone()))) - } - Transaction::Declare(declare_tx) => { - let class_hash = ClassHash::from(*declare_tx.class_hash()); - - match declare_tx { - DeclareTransaction::V0(_) | DeclareTransaction::V1(_) => { - let contract_class = self - .overrides - .for_block_hash(self.client.as_ref(), substrate_block_hash) - .contract_class_by_class_hash(substrate_block_hash, class_hash) - .ok_or_else(|| { - error!("Failed to retrieve contract class from hash '{class_hash}'"); - StarknetRpcApiError::InternalServerError - })?; - - Ok(UserOrL1HandlerTransaction::User(UserTransaction::Declare( - declare_tx.clone(), - contract_class, - ))) - } - DeclareTransaction::V2(tx) => { - let contract_class = self - .backend - .sierra_classes() - .get_sierra_class(class_hash) - .map_err(|e| { - error!("Failed to fetch sierra class with hash {class_hash}: {e}"); - StarknetRpcApiError::InternalServerError - })? - .ok_or_else(|| { - error!("The sierra class with hash {class_hash} is not present in db backend"); - StarknetRpcApiError::InternalServerError - })?; - let contract_class = mp_transactions::utils::sierra_to_casm_contract_class(contract_class) - .map_err(|e| { - error!("Failed to convert the SierraContractClass to CasmContractClass: {e}"); - StarknetRpcApiError::InternalServerError - })?; - let contract_class = - ContractClass::V1(ContractClassV1::try_from(contract_class).map_err(|e| { - error!( - "Failed to convert the compiler CasmContractClass to blockifier \ - CasmContractClass: {e}" - ); - StarknetRpcApiError::InternalServerError - })?); - - Ok(UserOrL1HandlerTransaction::User(UserTransaction::Declare( - declare_tx.clone(), - contract_class, - ))) - } - } - } - Transaction::L1Handler(handle_l1_message_tx) => { - let chain_id = self.chain_id()?.0.into(); - let tx_hash = handle_l1_message_tx.compute_hash::( - chain_id, - false, - Some(starknet_block.header().block_number), - ); - let paid_fee = - self.backend.l1_handler_paid_fee().get_fee_paid_for_l1_handler_tx(tx_hash.into()).map_err( - |e| { - error!("Failed to retrieve fee paid on l1 for tx with hash `{tx_hash:?}`: {e}"); - StarknetRpcApiError::InternalServerError - }, - )?; - - Ok(UserOrL1HandlerTransaction::L1Handler(handle_l1_message_tx.clone(), paid_fee)) - } - Transaction::Deploy(_) => todo!(), - }) - .collect::, _>>()?; + let (block_transactions, empty_transactions) = + map_transaction_to_user_transaction(self, starknet_block, substrate_block_hash, chain_id, None)?; - let previous_block_substrate_hash = { - let starknet_block = get_block_by_block_hash(self.client.as_ref(), substrate_block_hash).map_err(|e| { - error!("Failed to starknet block for substate block with hash {substrate_block_hash}: {e}"); - StarknetRpcApiError::InternalServerError - })?; - let block_number = starknet_block.header().block_number; - let previous_block_number = block_number - 1; - self.substrate_block_hash_from_starknet_block(BlockId::Number(previous_block_number)).map_err(|e| { - error!("Failed to retrieve previous block substrate hash: {e}"); - StarknetRpcApiError::InternalServerError - }) - }?; + let previous_block_substrate_hash = get_previous_block_substrate_hash(self, substrate_block_hash)?; let execution_infos = self .client .runtime_api() - .re_execute_transactions(previous_block_substrate_hash, block_transactions.clone()) + .re_execute_transactions( + previous_block_substrate_hash, + empty_transactions.clone(), + block_transactions.clone(), + ) .map_err(|e| { error!("Failed to execute runtime API call: {e}"); StarknetRpcApiError::InternalServerError @@ -227,7 +141,6 @@ where })?; let storage_override = self.overrides.for_block_hash(self.client.as_ref(), substrate_block_hash); - let chain_id = Felt252Wrapper(self.chain_id()?.0); let traces = execution_infos .into_iter() @@ -241,9 +154,7 @@ where &tx_exec_info, ) .map(|trace_root| TransactionTraceWithHash { - transaction_hash: block_transactions[tx_idx] - .compute_hash::(chain_id, false, Some(starknet_block.header().block_number)) - .into(), + transaction_hash: block_transactions[tx_idx].compute_hash::(chain_id, false, Some(block_number)).into(), trace_root, }) }) @@ -252,6 +163,67 @@ where Ok(traces) } + + async fn trace_transaction(&self, transaction_hash: FieldElement) -> RpcResult { + let substrate_block_hash = self + .backend + .mapping() + .block_hash_from_transaction_hash(Felt252Wrapper(transaction_hash).into()) + .map_err(|e| { + error!("Failed to get transaction's substrate block hash from mapping_db: {e}"); + StarknetRpcApiError::TxnHashNotFound + })? + .ok_or(StarknetRpcApiError::TxnHashNotFound)?; + + let starknet_block = get_block_by_block_hash(self.client.as_ref(), substrate_block_hash)?; + let chain_id = Felt252Wrapper(self.chain_id()?.0); + let transaction_hash_to_trace: Felt252Wrapper = transaction_hash.into(); + + let (txs_to_execute_before, tx_to_trace) = map_transaction_to_user_transaction( + self, + starknet_block, + substrate_block_hash, + chain_id, + Some(transaction_hash_to_trace), + )?; + + let previous_block_substrate_hash = get_previous_block_substrate_hash(self, substrate_block_hash)?; + + let execution_infos = self + .client + .runtime_api() + .re_execute_transactions(previous_block_substrate_hash, txs_to_execute_before.clone(), tx_to_trace.clone()) + .map_err(|e| { + error!("Failed to execute runtime API call: {e}"); + StarknetRpcApiError::InternalServerError + })? + .map_err(|e| { + error!("Failed to reexecute the block transactions: {e:?}"); + StarknetRpcApiError::InternalServerError + })? + .map_err(|_| { + error!( + "One of the transaction failed during it's reexecution. This should not happen, as the block has \ + already been executed successfully in the past. There is a bug somewhere." + ); + StarknetRpcApiError::InternalServerError + })?; + + let storage_override = self.overrides.for_block_hash(self.client.as_ref(), substrate_block_hash); + let chain_id = Felt252Wrapper(self.chain_id()?.0); + + let trace = tx_execution_infos_to_tx_trace( + &**storage_override, + substrate_block_hash, + TxType::from(tx_to_trace.get(0).unwrap()), + &execution_infos[0], + ) + .unwrap(); + + let tx_trace = TransactionTraceWithHash { transaction_hash, trace_root: trace }; + + Ok(tx_trace) + } } #[derive(Error, Debug)] @@ -281,10 +253,35 @@ fn collect_call_info_ordered_messages(call_info: &CallInfo) -> Vec Vec { + ordered_events + .iter() + .map(|event| starknet_core::types::OrderedEvent { + order: event.order as u64, // Convert usize to u64 + keys: event.event.keys.iter().map(|key| FieldElement::from_byte_slice_be(key.0.bytes()).unwrap()).collect(), + data: event + .event + .data + .0 + .iter() + .map(|data_item| FieldElement::from_byte_slice_be(data_item.bytes()).unwrap()) + .collect(), }) .collect() } @@ -336,7 +333,8 @@ fn try_get_funtion_invocation_from_call_info( // Blockifier call info does not give use the class_hash "if it can be deducted from the storage // address". We have to do this decution ourselves here let class_hash = if let Some(class_hash) = call_info.call.class_hash { - Felt252Wrapper::from(class_hash.0).into() + let felt_wrapper: Felt252Wrapper = Felt252Wrapper::from(class_hash.0); + FieldElement::from(felt_wrapper) } else if let Some(cached_hash) = class_hash_cache.get(&call_info.call.storage_address) { *cached_hash } else { @@ -352,18 +350,23 @@ fn try_get_funtion_invocation_from_call_info( }; Ok(starknet_core::types::FunctionInvocation { - contract_address: Felt252Wrapper::from(call_info.call.storage_address.0.0).into(), - entry_point_selector: Felt252Wrapper::from(call_info.call.entry_point_selector.0).into(), - calldata: call_info.call.calldata.0.iter().map(|x| Felt252Wrapper::from(*x).into()).collect(), - caller_address: Felt252Wrapper::from(call_info.call.caller_address.0.0).into(), + contract_address: FieldElement::from(Felt252Wrapper::from(call_info.call.storage_address.0.0)), + entry_point_selector: FieldElement::from(Felt252Wrapper::from(call_info.call.entry_point_selector.0)), + calldata: call_info.call.calldata.0.iter() + .map(|x| FieldElement::from(Felt252Wrapper::from(*x))) + .collect(), + caller_address: FieldElement::from(Felt252Wrapper::from(call_info.call.caller_address.0.0)), class_hash, entry_point_type, call_type, - result: call_info.execution.retdata.0.iter().map(|x| Felt252Wrapper::from(*x).into()).collect(), + result: call_info.execution.retdata.0.iter() + .map(|x| FieldElement::from(Felt252Wrapper::from(*x))) + .collect(), calls: inner_calls, events, messages, }) + } fn tx_execution_infos_to_tx_trace( @@ -490,3 +493,143 @@ fn tx_execution_infos_to_simulated_transactions( Ok(results) } + +fn map_transaction_to_user_transaction( + starknet: &Starknet, + starknet_block: Block, + substrate_block_hash: B::Hash, + chain_id: Felt252Wrapper, + target_transaction_hash: Option, +) -> Result<(Vec, Vec), StarknetRpcApiError> +where + A: ChainApi + 'static, + B: BlockT, + C: HeaderBackend + BlockBackend + StorageProvider + 'static, + H: HasherT + Send + Sync + 'static, + BE: Backend + 'static, +{ + let mut transactions = Vec::new(); + let mut transaction_to_trace = Vec::new(); + let block_number = starknet_block.header().block_number; + + for tx in starknet_block.transactions() { + let current_tx_hash = tx.compute_hash::(chain_id, false, Some(block_number)); + + if Some(current_tx_hash) == target_transaction_hash { + let converted_tx = convert_transaction(tx, starknet, substrate_block_hash, chain_id, block_number)?; + transaction_to_trace.push(converted_tx); + break; + } else { + let converted_tx = convert_transaction(tx, starknet, substrate_block_hash, chain_id, block_number)?; + transactions.push(converted_tx); + } + } + + Ok((transactions, transaction_to_trace)) +} + +fn convert_transaction( + tx: &Transaction, + starknet: &Starknet, + substrate_block_hash: B::Hash, + chain_id: Felt252Wrapper, + block_number: u64, +) -> Result +where + A: ChainApi + 'static, + B: BlockT, + C: HeaderBackend + BlockBackend + StorageProvider + 'static, + H: HasherT + Send + Sync + 'static, + BE: Backend + 'static, +{ + match tx { + Transaction::Invoke(invoke_tx) => { + Ok(UserOrL1HandlerTransaction::User(UserTransaction::Invoke(invoke_tx.clone()))) + } + Transaction::DeployAccount(deploy_account_tx) => { + Ok(UserOrL1HandlerTransaction::User(UserTransaction::DeployAccount(deploy_account_tx.clone()))) + } + Transaction::Declare(declare_tx) => { + let class_hash = ClassHash::from(*declare_tx.class_hash()); + + match declare_tx { + DeclareTransaction::V0(_) | DeclareTransaction::V1(_) => { + let contract_class = starknet + .overrides + .for_block_hash(starknet.client.as_ref(), substrate_block_hash) + .contract_class_by_class_hash(substrate_block_hash, class_hash) + .ok_or_else(|| { + error!("Failed to retrieve contract class from hash '{class_hash}'"); + StarknetRpcApiError::InternalServerError + })?; + + Ok(UserOrL1HandlerTransaction::User(UserTransaction::Declare(declare_tx.clone(), contract_class))) + } + DeclareTransaction::V2(_tx) => { + let contract_class = starknet + .backend + .sierra_classes() + .get_sierra_class(class_hash) + .map_err(|e| { + error!("Failed to fetch sierra class with hash {class_hash}: {e}"); + StarknetRpcApiError::InternalServerError + })? + .ok_or_else(|| { + error!("The sierra class with hash {class_hash} is not present in db backend"); + StarknetRpcApiError::InternalServerError + })?; + let contract_class = mp_transactions::utils::sierra_to_casm_contract_class(contract_class) + .map_err(|e| { + error!("Failed to convert the SierraContractClass to CasmContractClass: {e}"); + StarknetRpcApiError::InternalServerError + })?; + let contract_class = ContractClass::V1(ContractClassV1::try_from(contract_class).map_err(|e| { + error!("Failed to convert the compiler CasmContractClass to blockifier CasmContractClass: {e}"); + StarknetRpcApiError::InternalServerError + })?); + + Ok(UserOrL1HandlerTransaction::User(UserTransaction::Declare(declare_tx.clone(), contract_class))) + } + } + } + Transaction::L1Handler(handle_l1_message_tx) => { + let tx_hash = handle_l1_message_tx.compute_hash::(chain_id, false, Some(block_number)); + let paid_fee = + starknet.backend.l1_handler_paid_fee().get_fee_paid_for_l1_handler_tx(tx_hash.into()).map_err(|e| { + error!("Failed to retrieve fee paid on l1 for tx with hash `{tx_hash:?}`: {e}"); + StarknetRpcApiError::InternalServerError + })?; + + Ok(UserOrL1HandlerTransaction::L1Handler(handle_l1_message_tx.clone(), paid_fee)) + } + &mp_transactions::Transaction::Deploy(_) => todo!(), + } +} + +fn get_previous_block_substrate_hash( + starknet: &Starknet, + substrate_block_hash: B::Hash, +) -> Result +where + A: ChainApi + 'static, + B: BlockT, + C: HeaderBackend + BlockBackend + StorageProvider + 'static, + C: ProvideRuntimeApi, + C::Api: StarknetRuntimeApi + ConvertTransactionRuntimeApi, + H: HasherT + Send + Sync + 'static, + BE: Backend + 'static, +{ + let starknet_block = get_block_by_block_hash(starknet.client.as_ref(), substrate_block_hash).map_err(|e| { + error!("Failed to get block for block hash {substrate_block_hash}: '{e}'"); + StarknetRpcApiError::InternalServerError + })?; + let block_number = starknet_block.header().block_number; + let previous_block_number = block_number - 1; + let substrate_block_hash = + starknet.substrate_block_hash_from_starknet_block(BlockId::Number(previous_block_number)).map_err(|e| { + error!("Failed to retrieve previous block substrate hash: {e}"); + StarknetRpcApiError::InternalServerError + })?; + + Ok(substrate_block_hash) +} \ No newline at end of file diff --git a/crates/pallets/starknet/runtime_api/src/lib.rs b/crates/pallets/starknet/runtime_api/src/lib.rs index 296caf22c..b8e10ac19 100644 --- a/crates/pallets/starknet/runtime_api/src/lib.rs +++ b/crates/pallets/starknet/runtime_api/src/lib.rs @@ -72,9 +72,19 @@ sp_api::decl_runtime_apis! { /// the runtime itself, accomplished through the extrinsic_filter method. This enables the /// client to operate seamlessly while abstracting the extrinsic complexity. fn extrinsic_filter(xts: Vec<::Extrinsic>) -> Vec; - /// Re-execute a block and return the TransactionExecutionInfos of every transaction in it, in the same order - fn re_execute_transactions(transactions: Vec) -> Result, PlaceHolderErrorTypeForFailedStarknetExecution>, DispatchError>; - + /// Used to re-execute transactions from a past block and return their trace + /// + /// # Arguments + /// + /// * `transactions_before` - The first txs of the block. We don't want to trace those, but we need to execute them to rebuild the exact same state + /// * `transactions_to_trace` - The transactions we want to trace (can be a complete block of transactions or a subset of it) + /// + /// # Return + /// + /// Idealy, the execution traces of all of `transactions_to_trace`. + /// If any of the transactions (from both arguments) fails, an error is returned. + fn re_execute_transactions(transactions_before: Vec, transactions_to_trace: Vec) -> Result, PlaceHolderErrorTypeForFailedStarknetExecution>, DispatchError>; + fn get_index_and_tx_for_tx_hash(xts: Vec<::Extrinsic>, chain_id: Felt252Wrapper, tx_hash: Felt252Wrapper) -> Option<(u32, Transaction)>; /// Returns events, call with index from get_index_and_tx_for_tx_hash method fn get_events_for_tx_by_index(tx_index: u32) -> Option>; diff --git a/crates/pallets/starknet/src/simulations.rs b/crates/pallets/starknet/src/simulations.rs index 6df0c42d8..eaab9a4c3 100644 --- a/crates/pallets/starknet/src/simulations.rs +++ b/crates/pallets/starknet/src/simulations.rs @@ -110,6 +110,39 @@ impl Pallet { Ok(tx_execution_results) } + pub fn simulate_message( + message: HandleL1MessageTransaction, + simulation_flags: &SimulationFlags, + ) -> Result, DispatchError> { + storage::transactional::with_transaction(|| { + storage::TransactionOutcome::Rollback(Result::<_, DispatchError>::Ok(Self::simulate_message_inner( + message, + simulation_flags, + ))) + }) + .map_err(|_| Error::::FailedToCreateATransactionalStorageExecution)? + } + + fn simulate_message_inner( + message: HandleL1MessageTransaction, + simulation_flags: &SimulationFlags, + ) -> Result, DispatchError> { + let chain_id = Self::chain_id(); + let block_context = Self::get_block_context(); + let mut execution_config = + RuntimeExecutionConfigBuilder::new::().with_simulation_mode(simulation_flags).build(); + + // Follow `offset` from Pallet Starknet where it is set to false + execution_config.set_offset_version(false); + let tx_execution_result = + Self::execute_message(message, chain_id, &block_context, &execution_config).map_err(|e| { + log::error!("Transaction execution failed during simulation: {e}"); + PlaceHolderErrorTypeForFailedStarknetExecution + }); + + Ok(tx_execution_result) + } + pub fn estimate_message_fee(message: HandleL1MessageTransaction) -> Result<(u128, u64, u64), DispatchError> { storage::transactional::with_transaction(|| { storage::TransactionOutcome::Rollback(Result::<_, DispatchError>::Ok(Self::estimate_message_fee_inner( @@ -122,17 +155,30 @@ impl Pallet { fn estimate_message_fee_inner(message: HandleL1MessageTransaction) -> Result<(u128, u64, u64), DispatchError> { let chain_id = Self::chain_id(); - let tx_execution_infos = message - .into_executable::(chain_id, Fee::default(), true) - .execute( + // Follow `offset` from Pallet Starknet where it is set to false + let tx_execution_infos = + match message.into_executable::(chain_id, Fee(u128::MAX), false).execute( &mut BlockifierStateAdapter::::default(), &Self::get_block_context(), &RuntimeExecutionConfigBuilder::new::().with_query_mode().with_disable_nonce_validation().build(), - ) - .map_err(|e| { - log::error!("L1 message execution failed during fee estimation: {}", e); - Error::::TransactionExecutionFailed - })?; + ) { + Ok(execution_info) if !execution_info.is_reverted() => Ok(execution_info), + Err(e) => { + log::error!( + "Transaction execution failed during fee estimation: {e} {:?}", + std::error::Error::source(&e) + ); + Err(Error::::TransactionExecutionFailed) + } + Ok(execution_info) => { + log::error!( + "Transaction execution reverted during fee estimation: {}", + // Safe due to the `match` branch order + execution_info.revert_error.unwrap() + ); + Err(Error::::TransactionExecutionFailed) + } + }?; if let Some(l1_gas_usage) = tx_execution_infos.actual_resources.0.get("l1_gas_usage") { Ok((T::L1GasPrice::get().price_in_wei, tx_execution_infos.actual_fee.0 as u64, *l1_gas_usage)) @@ -142,69 +188,42 @@ impl Pallet { } pub fn re_execute_transactions( - transactions: Vec, + transactions_before: Vec, + transactions_to_trace: Vec, ) -> Result, PlaceHolderErrorTypeForFailedStarknetExecution>, DispatchError> { storage::transactional::with_transaction(|| { storage::TransactionOutcome::Rollback(Result::<_, DispatchError>::Ok(Self::re_execute_transactions_inner( - transactions, + transactions_before, + transactions_to_trace, ))) }) .map_err(|_| Error::::FailedToCreateATransactionalStorageExecution)? } fn re_execute_transactions_inner( - transactions: Vec, + transactions_before: Vec, + transactions_to_trace: Vec, ) -> Result, PlaceHolderErrorTypeForFailedStarknetExecution>, DispatchError> { let chain_id = Self::chain_id(); let block_context = Self::get_block_context(); let execution_config = RuntimeExecutionConfigBuilder::new::().build(); - let execution_infos = transactions - .iter() - .map(|user_or_l1_tx| match user_or_l1_tx { - UserOrL1HandlerTransaction::User(tx) => match tx { - UserTransaction::Declare(tx, contract_class) => tx - .try_into_executable::(chain_id, contract_class.clone(), false) - .map_err(|e| { - log::error!("Failed to reexecute a tx: {}", e); - PlaceHolderErrorTypeForFailedStarknetExecution - }) - .and_then(|executable| { - executable - .execute(&mut BlockifierStateAdapter::::default(), &block_context, &execution_config) - .map_err(|e| { - log::error!("Failed to reexecute a tx: {}", e); - PlaceHolderErrorTypeForFailedStarknetExecution - }) - }), - UserTransaction::DeployAccount(tx) => tx - .into_executable::(chain_id, false) - .execute(&mut BlockifierStateAdapter::::default(), &block_context, &execution_config) - .map_err(|e| { - log::error!("Failed to reexecute a tx: {}", e); - PlaceHolderErrorTypeForFailedStarknetExecution - }), - UserTransaction::Invoke(tx) => tx - .into_executable::(chain_id, false) - .execute(&mut BlockifierStateAdapter::::default(), &block_context, &execution_config) - .map_err(|e| { - log::error!("Failed to reexecute a tx: {}", e); - PlaceHolderErrorTypeForFailedStarknetExecution - }), - }, - UserOrL1HandlerTransaction::L1Handler(tx, fee) => tx - .into_executable::(chain_id, *fee, false) - .execute(&mut BlockifierStateAdapter::::default(), &block_context, &execution_config) - .map_err(|e| { - log::error!("Failed to reexecute a tx: {}", e); - PlaceHolderErrorTypeForFailedStarknetExecution - }), - }) - .collect::, _>>(); + let _transactions_before_exec_infos = Self::execute_user_or_l1_handler_transactions( + chain_id, + &block_context, + &execution_config, + transactions_before, + ); + let transactions_exec_infos = Self::execute_user_or_l1_handler_transactions( + chain_id, + &block_context, + &execution_config, + transactions_to_trace, + ); - Ok(execution_infos) + Ok(transactions_exec_infos) } fn execute_user_transaction( @@ -229,4 +248,45 @@ impl Pallet { } } } -} + + fn execute_message( + message: HandleL1MessageTransaction, + chain_id: Felt252Wrapper, + block_context: &BlockContext, + execution_config: &ExecutionConfig, + ) -> Result { + // Follow `offset` from Pallet Starknet where it is set to false + let fee = Fee(u128::MAX); + let executable = message.into_executable::(chain_id, fee, false); + executable.execute(&mut BlockifierStateAdapter::::default(), block_context, execution_config) + } + + fn execute_user_or_l1_handler_transactions( + chain_id: Felt252Wrapper, + block_context: &BlockContext, + execution_config: &ExecutionConfig, + transactions: Vec, + ) -> Result, PlaceHolderErrorTypeForFailedStarknetExecution> { + let exec_transactions: Vec> = transactions + .iter() + .map(|user_or_l1_tx| match user_or_l1_tx { + UserOrL1HandlerTransaction::User(tx) => { + Self::execute_user_transaction(tx.clone(), chain_id, block_context, execution_config) + } + UserOrL1HandlerTransaction::L1Handler(tx, _fee) => { + Self::execute_message(tx.clone(), chain_id, block_context, execution_config) + } + }) + .collect(); + + let mut execution_infos = Vec::with_capacity(exec_transactions.len()); + for result in exec_transactions { + match result { + Ok(info) => execution_infos.push(info), + Err(_err) => return Err(PlaceHolderErrorTypeForFailedStarknetExecution), + } + } + + Ok(execution_infos) + } +} \ No newline at end of file diff --git a/crates/pallets/starknet/src/tests/re_execute_transactions.rs b/crates/pallets/starknet/src/tests/re_execute_transactions.rs index db7b0c109..82fc706cf 100644 --- a/crates/pallets/starknet/src/tests/re_execute_transactions.rs +++ b/crates/pallets/starknet/src/tests/re_execute_transactions.rs @@ -83,8 +83,10 @@ fn re_execute_tx_ok() { UserOrL1HandlerTransaction::L1Handler(handle_l1_tx, Fee(10)), ]; + let txs_to_ignore: Vec = vec![]; + // Call the function we want to test - let res = Starknet::re_execute_transactions(txs.clone()).unwrap().unwrap(); + let res = Starknet::re_execute_transactions(txs_to_ignore, txs.clone()).unwrap().unwrap(); // Storage changes have been reverted assert_eq!(Starknet::nonce(invoke_sender_address), Nonce(Felt252Wrapper::ZERO.into())); @@ -158,3 +160,112 @@ fn re_execute_tx_ok() { assert_eq!(res[4], handle_l1_message_tx_info); }); } + +// Desactivate until i found i make this test work +// #[test] +// fn re_execute_tx_with_a_transfer_ok() { +// new_test_ext::().execute_with(|| { +// basic_test_setup(2); +// let invoke_sender_address: ContractAddress = +// Felt252Wrapper::from_hex_be(constants::BLOCKIFIER_ACCOUNT_ADDRESS).unwrap().into(); +// let chain_id = Starknet::chain_id(); + +// // Deploy + +// // TEST ACCOUNT CONTRACT +// // - ref testnet tx(0x0751b4b5b95652ad71b1721845882c3852af17e2ed0c8d93554b5b292abb9810) +// let salt = +// +// Felt252Wrapper::from_hex_be("0x03b37cbe4e9eac89d54c5f7cc6329a63a63e8c8db2bf936f981041e086752463" +// ).unwrap(); let (account_class_hash, calldata) = +// account_helper(AccountType::V0(AccountTypeV0Inner::NoValidate)); + +// let deploy_tx = DeployAccountTransaction { +// nonce: Felt252Wrapper::ZERO, +// max_fee: u128::MAX, +// signature: vec![], +// contract_address_salt: salt, +// constructor_calldata: calldata.0.iter().map(|e| Felt252Wrapper::from(*e)).collect(), +// class_hash: account_class_hash.into(), +// offset_version: false, +// }; + +// let address = deploy_tx.account_address().into(); +// set_infinite_tokens::(&address); + +// // Declare + +// let declare_tx = +// get_declare_dummy(chain_id, Felt252Wrapper::ZERO, +// AccountType::V0(AccountTypeV0Inner::Openzeppelin)); let erc20_class_hash: CasmClassHash = +// +// Felt252Wrapper::from_hex_be("0x372ee6669dc86563007245ed7343d5180b96221ce28f44408cff2898038dbd4") +// .unwrap() +// .into(); +// let erc20_class = get_contract_class("ERC20.json", 0); + +// let contract_address = +// +// Felt252Wrapper::from_hex_be("0x024d1e355f6b9d27a5a420c8f4b50cea9154a8e34ad30fc39d7c98d3c177d0d7" +// ).unwrap(); let from_address = +// Felt252Wrapper::from_hex_be("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045").unwrap(); + +// // Handle l1 message + +// let handle_l1_tx = HandleL1MessageTransaction { +// nonce: 1, +// contract_address, +// entry_point_selector: Felt252Wrapper::from_hex_be( +// "0x014093c40d95d0a3641c087f7d48d55160e1a58bc7c07b0d2323efeeb3087269", // +// test_l1_handler_store_under_caller_address ) +// .unwrap(), +// calldata: vec![ +// from_address, +// Felt252Wrapper::from_hex_be("0x1").unwrap(), // value +// ], +// }; + +// let txs = vec![ +// UserOrL1HandlerTransaction::User(mp_transactions::UserTransaction::Invoke( +// get_invoke_dummy(Felt252Wrapper::ZERO).into(), +// )), +// UserOrL1HandlerTransaction::User(mp_transactions::UserTransaction::Invoke( +// get_invoke_dummy(Felt252Wrapper::ONE).into(), +// )), +// +// UserOrL1HandlerTransaction::User(mp_transactions::UserTransaction::Declare(declare_tx, +// erc20_class)), +// UserOrL1HandlerTransaction::User(mp_transactions::UserTransaction::DeployAccount(deploy_tx)), +// UserOrL1HandlerTransaction::L1Handler(handle_l1_tx, Fee(10)), +// ]; + +// let transfer_tx: Vec = vec![UserOrL1HandlerTransaction::User( +// +// mp_transactions::UserTransaction::Invoke(get_invoke_dummy(Felt252Wrapper::TWO).into()), +// )]; + +// // Call the function we want to test +// let res = Starknet::re_execute_transactions(txs.clone(), +// transfer_tx.clone()).unwrap().unwrap(); + +// // Storage changes have been reverted +// assert_eq!(Starknet::nonce(invoke_sender_address), Nonce(Felt252Wrapper::ZERO.into())); +// assert_eq!(Starknet::contract_class_by_class_hash(erc20_class_hash), None); +// // Here we only got the transfer tx +// assert_eq!(res.len(), 1); + +// // Now let's check the TransactionInfos returned +// let transfer_invoke_tx_info = match transfer_tx.get(0).unwrap() { +// UserOrL1HandlerTransaction::User(mp_transactions::UserTransaction::Invoke(invoke_tx)) +// => invoke_tx .into_executable::<::SystemHash>(chain_id, +// false) .execute( +// &mut BlockifierStateAdapter::::default(), +// &Starknet::get_block_context(), +// &RuntimeExecutionConfigBuilder::new::().build(), +// ) +// .unwrap(), +// _ => unreachable!(), +// }; +// assert_eq!(res[0], transfer_invoke_tx_info); +// }); +// } \ No newline at end of file diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs index c6ec14c08..c8e72d67c 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -286,8 +286,8 @@ impl_runtime_apis! { Starknet::estimate_fee(transactions) } - fn re_execute_transactions(transactions: Vec) -> Result, PlaceHolderErrorTypeForFailedStarknetExecution>, DispatchError> { - Starknet::re_execute_transactions(transactions) + fn re_execute_transactions(transactions_before: Vec, transactions_to_trace: Vec) -> Result, PlaceHolderErrorTypeForFailedStarknetExecution>, DispatchError> { + Starknet::re_execute_transactions(transactions_before, transactions_to_trace) } fn estimate_message_fee(message: HandleL1MessageTransaction) -> Result<(u128, u64, u64), DispatchError> { From 51257b68475961793715fb382af1fa15140f282a Mon Sep 17 00:00:00 2001 From: 0xTrinityy Date: Wed, 6 Mar 2024 14:45:27 +0100 Subject: [PATCH 2/2] style: :recycle: Added a map in simultation.rs to increase clean code manners --- crates/pallets/starknet/src/simulations.rs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/crates/pallets/starknet/src/simulations.rs b/crates/pallets/starknet/src/simulations.rs index e718b5bce..8a38dede9 100644 --- a/crates/pallets/starknet/src/simulations.rs +++ b/crates/pallets/starknet/src/simulations.rs @@ -267,7 +267,7 @@ impl Pallet { execution_config: &ExecutionConfig, transactions: Vec, ) -> Result, PlaceHolderErrorTypeForFailedStarknetExecution> { - let exec_transactions: Vec> = transactions + transactions .iter() .map(|user_or_l1_tx| match user_or_l1_tx { UserOrL1HandlerTransaction::User(tx) => { @@ -277,16 +277,7 @@ impl Pallet { Self::execute_message(tx.clone(), chain_id, block_context, execution_config) } }) - .collect(); - - let mut execution_infos = Vec::with_capacity(exec_transactions.len()); - for result in exec_transactions { - match result { - Ok(info) => execution_infos.push(info), - Err(_err) => return Err(PlaceHolderErrorTypeForFailedStarknetExecution), - } - } - - Ok(execution_infos) + .collect::, _>>() + .map_err(|_| PlaceHolderErrorTypeForFailedStarknetExecution) } }