From f892b5c5114a72c28a289d7a95747ff47223cb12 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 3 Apr 2024 19:40:46 +0200 Subject: [PATCH] feat: move transact and inspect calls to trait (#7438) --- crates/rpc/rpc/src/debug.rs | 61 ++++--- crates/rpc/rpc/src/eth/api/call.rs | 81 +++++---- crates/rpc/rpc/src/eth/api/transactions.rs | 202 +++++++++++++++++++-- crates/rpc/rpc/src/eth/revm_utils.rs | 116 +----------- crates/rpc/rpc/src/trace.rs | 19 +- 5 files changed, 284 insertions(+), 195 deletions(-) diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 3d2466b4e..500f786d3 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -1,10 +1,7 @@ use crate::{ eth::{ error::{EthApiError, EthResult}, - revm_utils::{ - inspect, inspect_and_return_db, prepare_call_env, replay_transactions_until, transact, - EvmOverrides, - }, + revm_utils::{prepare_call_env, EvmOverrides}, EthTransactions, }, result::{internal_rpc_err, ToRpcResult}, @@ -56,6 +53,11 @@ impl DebugApi { let inner = Arc::new(DebugApiInner { provider, eth_api: eth, blocking_task_guard }); Self { inner } } + + /// Access the underlying `Eth` API. + pub fn eth_api(&self) -> &Eth { + &self.inner.eth_api + } } // === impl DebugApi === @@ -86,8 +88,7 @@ where // replay all transactions of the block let this = self.clone(); - self.inner - .eth_api + self.eth_api() .spawn_with_state_at_block(at, move |state| { let block_hash = at.as_block_hash(); let mut results = Vec::with_capacity(transactions.len()); @@ -145,7 +146,7 @@ where let block = Block::decode(&mut rlp_block.as_ref()).map_err(BlockError::RlpDecodeRawBlock)?; - let (cfg, block_env) = self.inner.eth_api.evm_env_for_raw_block(&block.header).await?; + let (cfg, block_env) = self.eth_api().evm_env_for_raw_block(&block.header).await?; // we trace on top the block's parent block let parent = block.parent_hash; @@ -235,7 +236,7 @@ where let mut db = CacheDB::new(StateProviderDatabase::new(state)); // replay all transactions prior to the targeted transaction - let index = replay_transactions_until( + let index = this.eth_api().replay_transactions_until( &mut db, cfg.clone(), block_env.clone(), @@ -280,6 +281,7 @@ where let overrides = EvmOverrides::new(state_overrides, block_overrides.map(Box::new)); let GethDebugTracingOptions { config, tracer, tracer_config, .. } = tracing_options; + let this = self.clone(); if let Some(tracer) = tracer { return match tracer { GethDebugTracerType::BuiltInTracer(tracer) => match tracer { @@ -289,7 +291,7 @@ where .inner .eth_api .spawn_with_call_at(call, at, overrides, move |db, env| { - inspect(db, env, &mut inspector)?; + this.eth_api().inspect(db, env, &mut inspector)?; Ok(inspector) }) .await?; @@ -308,7 +310,7 @@ where .inner .eth_api .spawn_with_call_at(call, at, overrides, move |db, env| { - let (res, _) = inspect(db, env, &mut inspector)?; + let (res, _) = this.eth_api().inspect(db, env, &mut inspector)?; let frame = inspector .into_geth_builder() .geth_call_traces(call_config, res.result.gas_used()); @@ -329,8 +331,11 @@ where self.inner .eth_api .spawn_with_call_at(call, at, overrides, move |db, env| { - let (res, _, db) = - inspect_and_return_db(db, env, &mut inspector)?; + let (res, _, db) = this.eth_api().inspect_and_return_db( + db, + env, + &mut inspector, + )?; let frame = inspector .into_geth_builder() .geth_prestate_traces(&res, prestate_config, &db)?; @@ -351,7 +356,11 @@ where .inner .eth_api .spawn_with_call_at(call, at, overrides, move |db, env| { - let (res, _, db) = inspect_and_return_db(db, env, &mut inspector)?; + let (res, _, db) = this.eth_api().inspect_and_return_db( + db, + env, + &mut inspector, + )?; let frame = inspector.try_into_mux_frame(&res, &db)?; Ok(frame.into()) }) @@ -369,8 +378,11 @@ where .eth_api .spawn_with_call_at(call, at, overrides, move |db, env| { let mut inspector = JsInspector::new(code, config)?; - let (res, _, db) = - inspect_and_return_db(db, env.clone(), &mut inspector)?; + let (res, _, db) = this.eth_api().inspect_and_return_db( + db, + env.clone(), + &mut inspector, + )?; Ok(inspector.json_result(res, &env, &db)?) }) .await?; @@ -389,7 +401,7 @@ where .inner .eth_api .spawn_with_call_at(call, at, overrides, move |db, env| { - let (res, _) = inspect(db, env, &mut inspector)?; + let (res, _) = this.eth_api().inspect(db, env, &mut inspector)?; Ok((res, inspector)) }) .await?; @@ -462,7 +474,7 @@ where env: Env::boxed(cfg.cfg_env.clone(), block_env.clone(), tx), handler_cfg: cfg.handler_cfg, }; - let (res, _) = transact(&mut db, env)?; + let (res, _) = this.inner.eth_api.transact(&mut db, env)?; db.commit(res.state); } } @@ -532,7 +544,7 @@ where GethDebugTracerType::BuiltInTracer(tracer) => match tracer { GethDebugBuiltInTracerType::FourByteTracer => { let mut inspector = FourByteInspector::default(); - let (res, _) = inspect(db, env, &mut inspector)?; + let (res, _) = self.eth_api().inspect(db, env, &mut inspector)?; return Ok((FourByteFrame::from(inspector).into(), res.state)) } GethDebugBuiltInTracerType::CallTracer => { @@ -544,7 +556,7 @@ where TracingInspectorConfig::from_geth_call_config(&call_config), ); - let (res, _) = inspect(db, env, &mut inspector)?; + let (res, _) = self.eth_api().inspect(db, env, &mut inspector)?; let frame = inspector .into_geth_builder() @@ -560,7 +572,8 @@ where let mut inspector = TracingInspector::new( TracingInspectorConfig::from_geth_prestate_config(&prestate_config), ); - let (res, _, db) = inspect_and_return_db(db, env, &mut inspector)?; + let (res, _, db) = + self.eth_api().inspect_and_return_db(db, env, &mut inspector)?; let frame = inspector.into_geth_builder().geth_prestate_traces( &res, @@ -580,7 +593,8 @@ where let mut inspector = MuxInspector::try_from_config(mux_config)?; - let (res, _, db) = inspect_and_return_db(db, env, &mut inspector)?; + let (res, _, db) = + self.eth_api().inspect_and_return_db(db, env, &mut inspector)?; let frame = inspector.try_into_mux_frame(&res, db)?; return Ok((frame.into(), res.state)) } @@ -592,7 +606,8 @@ where config, transaction_context.unwrap_or_default(), )?; - let (res, env, db) = inspect_and_return_db(db, env, &mut inspector)?; + let (res, env, db) = + self.eth_api().inspect_and_return_db(db, env, &mut inspector)?; let state = res.state.clone(); let result = inspector.json_result(res, &env, db)?; @@ -606,7 +621,7 @@ where let mut inspector = TracingInspector::new(inspector_config); - let (res, _) = inspect(db, env, &mut inspector)?; + let (res, _) = self.eth_api().inspect(db, env, &mut inspector)?; let gas_used = res.result.gas_used(); let return_value = res.result.into_output().unwrap_or_default(); let frame = inspector.into_geth_builder().geth_traces(gas_used, return_value, config); diff --git a/crates/rpc/rpc/src/eth/api/call.rs b/crates/rpc/rpc/src/eth/api/call.rs index a94ae0fd0..274cd75c5 100644 --- a/crates/rpc/rpc/src/eth/api/call.rs +++ b/crates/rpc/rpc/src/eth/api/call.rs @@ -5,8 +5,8 @@ use crate::{ error::{ensure_success, EthApiError, EthResult, RevertError, RpcInvalidTransactionError}, revm_utils::{ apply_state_overrides, build_call_evm_env, caller_gas_allowance, - cap_tx_gas_limit_with_caller_allowance, get_precompiles, inspect, prepare_call_env, - transact, EvmOverrides, + cap_tx_gas_limit_with_caller_allowance, get_precompiles, prepare_call_env, + EvmOverrides, }, EthTransactions, }, @@ -123,6 +123,7 @@ where replay_block_txs = false; } + let this = self.clone(); self.spawn_with_state_at_block(at.into(), move |state| { let mut results = Vec::with_capacity(transactions.len()); let mut db = CacheDB::new(StateProviderDatabase::new(state)); @@ -135,7 +136,7 @@ where let tx = tx_env_with_recovered(&tx); let env = EnvWithHandlerCfg::new_with_cfg_env(cfg.clone(), block_env.clone(), tx); - let (res, _) = transact(&mut db, env)?; + let (res, _) = this.transact(&mut db, env)?; db.commit(res.state); } } @@ -156,7 +157,7 @@ where &mut db, overrides, )?; - let (res, _) = transact(&mut db, env)?; + let (res, _) = this.transact(&mut db, env)?; match ensure_success(res.result) { Ok(output) => { @@ -255,7 +256,7 @@ where trace!(target: "rpc::eth::estimate", ?env, "Starting gas estimation"); // transact with the highest __possible__ gas limit - let ethres = transact(&mut db, env.clone()); + let ethres = self.transact(&mut db, env.clone()); // Exceptional case: init used too much gas, we need to increase the gas limit and try // again @@ -266,7 +267,7 @@ where // if price or limit was included in the request then we can execute the request // again with the block's gas limit to check if revert is gas related or not if request_gas.is_some() || request_gas_price.is_some() { - return Err(map_out_of_gas_err(env_gas_limit, env, &mut db)) + return Err(self.map_out_of_gas_err(env_gas_limit, env, &mut db)) } } @@ -284,7 +285,7 @@ where // if price or limit was included in the request then we can execute the request // again with the block's gas limit to check if revert is gas related or not return if request_gas.is_some() || request_gas_price.is_some() { - Err(map_out_of_gas_err(env_gas_limit, env, &mut db)) + Err(self.map_out_of_gas_err(env_gas_limit, env, &mut db)) } else { // the transaction did revert Err(RpcInvalidTransactionError::Revert(RevertError::new(output)).into()) @@ -311,7 +312,7 @@ where let optimistic_gas_limit = (gas_used + gas_refund) * 64 / 63; if optimistic_gas_limit < highest_gas_limit { env.tx.gas_limit = optimistic_gas_limit; - (res, env) = transact(&mut db, env)?; + (res, env) = self.transact(&mut db, env)?; update_estimated_gas_range( res.result, optimistic_gas_limit, @@ -340,7 +341,7 @@ where }; env.tx.gas_limit = mid_gas_limit; - let ethres = transact(&mut db, env); + let ethres = self.transact(&mut db, env); // Exceptional case: init used too much gas, we need to increase the gas limit and try // again @@ -424,7 +425,7 @@ where let precompiles = get_precompiles(env.handler_cfg.spec_id); let mut inspector = AccessListInspector::new(initial, from, to, precompiles); - let (result, env) = inspect(&mut db, env, &mut inspector)?; + let (result, env) = self.inspect(&mut db, env, &mut inspector)?; match result.result { ExecutionResult::Halt { reason, .. } => Err(match reason { @@ -449,36 +450,38 @@ where Ok(AccessListWithGasUsed { access_list, gas_used }) } -} - -/// Executes the requests again after an out of gas error to check if the error is gas related or -/// not -#[inline] -fn map_out_of_gas_err( - env_gas_limit: U256, - mut env: EnvWithHandlerCfg, - mut db: &mut CacheDB>, -) -> EthApiError -where - S: StateProvider, -{ - let req_gas_limit = env.tx.gas_limit; - env.tx.gas_limit = env_gas_limit.try_into().unwrap_or(u64::MAX); - let (res, _) = match transact(&mut db, env) { - Ok(res) => res, - Err(err) => return err, - }; - match res.result { - ExecutionResult::Success { .. } => { - // transaction succeeded by manually increasing the gas limit to - // highest, which means the caller lacks funds to pay for the tx - RpcInvalidTransactionError::BasicOutOfGas(U256::from(req_gas_limit)).into() + /// Executes the requests again after an out of gas error to check if the error is gas related + /// or not + #[inline] + fn map_out_of_gas_err( + &self, + env_gas_limit: U256, + mut env: EnvWithHandlerCfg, + mut db: &mut CacheDB>, + ) -> EthApiError + where + S: StateProvider, + { + let req_gas_limit = env.tx.gas_limit; + env.tx.gas_limit = env_gas_limit.try_into().unwrap_or(u64::MAX); + let (res, _) = match self.transact(&mut db, env) { + Ok(res) => res, + Err(err) => return err, + }; + match res.result { + ExecutionResult::Success { .. } => { + // transaction succeeded by manually increasing the gas limit to + // highest, which means the caller lacks funds to pay for the tx + RpcInvalidTransactionError::BasicOutOfGas(U256::from(req_gas_limit)).into() + } + ExecutionResult::Revert { output, .. } => { + // reverted again after bumping the limit + RpcInvalidTransactionError::Revert(RevertError::new(output)).into() + } + ExecutionResult::Halt { reason, .. } => { + RpcInvalidTransactionError::EvmHalt(reason).into() + } } - ExecutionResult::Revert { output, .. } => { - // reverted again after bumping the limit - RpcInvalidTransactionError::Revert(RevertError::new(output)).into() - } - ExecutionResult::Halt { reason, .. } => RpcInvalidTransactionError::EvmHalt(reason).into(), } } diff --git a/crates/rpc/rpc/src/eth/api/transactions.rs b/crates/rpc/rpc/src/eth/api/transactions.rs index 6f1981de0..a6e501a5e 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -3,10 +3,7 @@ use crate::{ eth::{ api::pending_block::PendingBlockEnv, error::{EthApiError, EthResult, RpcInvalidTransactionError, SignError}, - revm_utils::{ - inspect, inspect_and_return_db, prepare_call_env, replay_transactions_until, transact, - EvmOverrides, - }, + revm_utils::{prepare_call_env, EvmOverrides}, utils::recover_raw_transaction, }, EthApi, EthApiSpec, @@ -41,11 +38,12 @@ use reth_rpc_types_compat::transaction::from_recovered_with_block_context; use reth_transaction_pool::{TransactionOrigin, TransactionPool}; use revm::{ db::CacheDB, + inspector_handle_register, primitives::{ db::DatabaseCommit, BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, ExecutionResult, ResultAndState, SpecId, State, }, - Inspector, + GetInspector, Inspector, }; use std::future::Future; @@ -53,22 +51,25 @@ use std::future::Future; use crate::eth::api::optimism::OptimismTxMeta; #[cfg(feature = "optimism")] use crate::eth::optimism::OptimismEthApiError; +use crate::eth::revm_utils::FillableTransaction; #[cfg(feature = "optimism")] use reth_revm::optimism::RethL1BlockInfo; #[cfg(feature = "optimism")] use reth_rpc_types::OptimismTransactionReceiptFields; #[cfg(feature = "optimism")] use revm::L1BlockInfo; +use revm_primitives::db::{Database, DatabaseRef}; /// Helper alias type for the state's [CacheDB] pub(crate) type StateCacheDB = CacheDB>; /// Commonly used transaction related functions for the [EthApi] type in the `eth_` namespace. /// +/// This includes utilities for transaction tracing, transacting and inspection. +/// /// Async functions that are spawned onto the /// [BlockingTaskPool](reth_tasks::pool::BlockingTaskPool) begin with `spawn_` /// -/// /// ## Calls /// /// There are subtle differences between when transacting [TransactionRequest]: @@ -86,6 +87,67 @@ pub(crate) type StateCacheDB = CacheDB>; /// This implementation follows the behaviour of Geth and disables the basefee check for tracing. #[async_trait::async_trait] pub trait EthTransactions: Send + Sync { + /// Executes the [EnvWithHandlerCfg] against the given [Database] without committing state + /// changes. + fn transact( + &self, + db: DB, + env: EnvWithHandlerCfg, + ) -> EthResult<(ResultAndState, EnvWithHandlerCfg)> + where + DB: Database, + ::Error: Into; + + /// Executes the [EnvWithHandlerCfg] against the given [Database] without committing state + /// changes. + fn inspect( + &self, + db: DB, + env: EnvWithHandlerCfg, + inspector: I, + ) -> EthResult<(ResultAndState, EnvWithHandlerCfg)> + where + DB: Database, + ::Error: Into, + I: GetInspector; + + /// Same as [Self::inspect] but also returns the database again. + /// + /// Even though [Database] is also implemented on `&mut` + /// this is still useful if there are certain trait bounds on the Inspector's database generic + /// type + fn inspect_and_return_db( + &self, + db: DB, + env: EnvWithHandlerCfg, + inspector: I, + ) -> EthResult<(ResultAndState, EnvWithHandlerCfg, DB)> + where + DB: Database, + ::Error: Into, + I: GetInspector; + + /// Replays all the transactions until the target transaction is found. + /// + /// All transactions before the target transaction are executed and their changes are written to + /// the _runtime_ db ([CacheDB]). + /// + /// Note: This assumes the target transaction is in the given iterator. + /// Returns the index of the target transaction in the given iterator. + fn replay_transactions_until( + &self, + db: &mut CacheDB, + cfg: CfgEnvWithHandlerCfg, + block_env: BlockEnv, + transactions: I, + target_tx_hash: B256, + ) -> Result + where + DB: DatabaseRef, + EthApiError: From<::Error>, + I: IntoIterator, + Tx: FillableTransaction; + /// Returns default gas limit to use for `eth_call` and tracing RPC methods. fn call_gas_limit(&self) -> u64; @@ -481,6 +543,101 @@ where Network: NetworkInfo + Send + Sync + 'static, EvmConfig: ConfigureEvmEnv + 'static, { + fn transact( + &self, + db: DB, + env: EnvWithHandlerCfg, + ) -> EthResult<(ResultAndState, EnvWithHandlerCfg)> + where + DB: Database, + ::Error: Into, + { + let mut evm = revm::Evm::builder().with_db(db).with_env_with_handler_cfg(env).build(); + let res = evm.transact()?; + let (_, env) = evm.into_db_and_env_with_handler_cfg(); + Ok((res, env)) + } + + fn inspect( + &self, + db: DB, + env: EnvWithHandlerCfg, + inspector: I, + ) -> EthResult<(ResultAndState, EnvWithHandlerCfg)> + where + DB: Database, + ::Error: Into, + I: GetInspector, + { + let mut evm = revm::Evm::builder() + .with_db(db) + .with_external_context(inspector) + .with_env_with_handler_cfg(env) + .append_handler_register(inspector_handle_register) + .build(); + let res = evm.transact()?; + let (_, env) = evm.into_db_and_env_with_handler_cfg(); + Ok((res, env)) + } + + fn inspect_and_return_db( + &self, + db: DB, + env: EnvWithHandlerCfg, + inspector: I, + ) -> EthResult<(ResultAndState, EnvWithHandlerCfg, DB)> + where + DB: Database, + ::Error: Into, + I: GetInspector, + { + let mut evm = revm::Evm::builder() + .with_external_context(inspector) + .with_db(db) + .with_env_with_handler_cfg(env) + .append_handler_register(inspector_handle_register) + .build(); + let res = evm.transact()?; + let (db, env) = evm.into_db_and_env_with_handler_cfg(); + Ok((res, env, db)) + } + + fn replay_transactions_until( + &self, + db: &mut CacheDB, + cfg: CfgEnvWithHandlerCfg, + block_env: BlockEnv, + transactions: I, + target_tx_hash: B256, + ) -> Result + where + DB: DatabaseRef, + EthApiError: From<::Error>, + I: IntoIterator, + Tx: FillableTransaction, + { + let mut evm = revm::Evm::builder() + .with_db(db) + .with_env_with_handler_cfg(EnvWithHandlerCfg::new_with_cfg_env( + cfg, + block_env, + Default::default(), + )) + .build(); + let mut index = 0; + for tx in transactions.into_iter() { + if tx.hash() == target_tx_hash { + // reached the target transaction + break + } + + tx.try_fill_tx_env(evm.tx_mut())?; + evm.transact_commit()?; + index += 1; + } + Ok(index) + } + fn call_gas_limit(&self) -> u64 { self.inner.gas_cap } @@ -950,8 +1107,11 @@ where at: BlockId, overrides: EvmOverrides, ) -> EthResult<(ResultAndState, EnvWithHandlerCfg)> { - self.spawn_with_call_at(request, at, overrides, move |mut db, env| transact(&mut db, env)) - .await + let this = self.clone(); + self.spawn_with_call_at(request, at, overrides, move |mut db, env| { + this.transact(&mut db, env) + }) + .await } async fn spawn_inspect_call_at( @@ -964,8 +1124,11 @@ where where I: Inspector + Send + 'static, { - self.spawn_with_call_at(request, at, overrides, move |db, env| inspect(db, env, inspector)) - .await + let this = self.clone(); + self.spawn_with_call_at(request, at, overrides, move |db, env| { + this.inspect(db, env, inspector) + }) + .await } fn trace_at( @@ -978,11 +1141,12 @@ where where F: FnOnce(TracingInspector, ResultAndState) -> EthResult, { + let this = self.clone(); self.with_state_at_block(at, |state| { let db = CacheDB::new(StateProviderDatabase::new(state)); let mut inspector = TracingInspector::new(config); - let (res, _) = inspect(db, env, &mut inspector)?; + let (res, _) = this.inspect(db, env, &mut inspector)?; f(inspector, res) }) @@ -999,10 +1163,11 @@ where F: FnOnce(TracingInspector, ResultAndState, StateCacheDB) -> EthResult + Send + 'static, R: Send + 'static, { + let this = self.clone(); self.spawn_with_state_at_block(at, move |state| { let db = CacheDB::new(StateProviderDatabase::new(state)); let mut inspector = TracingInspector::new(config); - let (res, _, db) = inspect_and_return_db(db, env, &mut inspector)?; + let (res, _, db) = this.inspect_and_return_db(db, env, &mut inspector)?; f(inspector, res, db) }) @@ -1053,16 +1218,23 @@ where let parent_block = block.parent_hash; let block_txs = block.into_transactions_ecrecovered(); + let this = self.clone(); self.spawn_with_state_at_block(parent_block.into(), move |state| { let mut db = CacheDB::new(StateProviderDatabase::new(state)); // replay all transactions prior to the targeted transaction - replay_transactions_until(&mut db, cfg.clone(), block_env.clone(), block_txs, tx.hash)?; + this.replay_transactions_until( + &mut db, + cfg.clone(), + block_env.clone(), + block_txs, + tx.hash, + )?; let env = EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, tx_env_with_recovered(&tx)); - let (res, _) = inspect(&mut db, env, &mut inspector)?; + let (res, _) = this.inspect(&mut db, env, &mut inspector)?; f(tx_info, inspector, res, db) }) .await @@ -1142,7 +1314,7 @@ where let env = EnvWithHandlerCfg::new_with_cfg_env(cfg.clone(), block_env.clone(), tx); let mut inspector = inspector_setup(); - let (res, _) = inspect(&mut db, env, &mut inspector)?; + let (res, _) = this.inspect(&mut db, env, &mut inspector)?; let ResultAndState { result, state } = res; results.push(f(tx_info, inspector, result, &state, &db)?); diff --git a/crates/rpc/rpc/src/eth/revm_utils.rs b/crates/rpc/rpc/src/eth/revm_utils.rs index 7305e1a35..5d14756b2 100644 --- a/crates/rpc/rpc/src/eth/revm_utils.rs +++ b/crates/rpc/rpc/src/eth/revm_utils.rs @@ -1,7 +1,5 @@ //! utilities for working with revm -use std::cmp::min; - use crate::eth::error::{EthApiError, EthResult, RpcInvalidTransactionError}; #[cfg(feature = "optimism")] use reth_primitives::revm::env::fill_op_tx_env; @@ -19,14 +17,14 @@ use reth_rpc_types::{ use revm::primitives::{Bytes, OptimismFields}; use revm::{ db::CacheDB, - inspector_handle_register, precompile::{PrecompileSpecId, Precompiles}, primitives::{ - db::DatabaseRef, BlockEnv, Bytecode, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, - ResultAndState, SpecId, TransactTo, TxEnv, + db::DatabaseRef, BlockEnv, Bytecode, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, SpecId, + TransactTo, TxEnv, }, - Database, GetInspector, + Database, }; +use std::cmp::min; use tracing::trace; /// Helper type that bundles various overrides for EVM Execution. @@ -68,7 +66,7 @@ impl From> for EvmOverrides { /// Helper type to work with different transaction types when configuring the EVM env. /// /// This makes it easier to handle errors. -pub(crate) trait FillableTransaction { +pub trait FillableTransaction { /// Returns the hash of the transaction. fn hash(&self) -> TxHash; @@ -122,110 +120,6 @@ pub(crate) fn get_precompiles(spec_id: SpecId) -> impl IntoIterator( - db: DB, - env: EnvWithHandlerCfg, -) -> EthResult<(ResultAndState, EnvWithHandlerCfg)> -where - DB: Database, - ::Error: Into, -{ - let mut evm = revm::Evm::builder().with_db(db).with_env_with_handler_cfg(env).build(); - let res = evm.transact()?; - let (_, env) = evm.into_db_and_env_with_handler_cfg(); - Ok((res, env)) -} - -/// Executes the [EnvWithHandlerCfg] against the given [Database] without committing state changes. -pub(crate) fn inspect( - db: DB, - env: EnvWithHandlerCfg, - inspector: I, -) -> EthResult<(ResultAndState, EnvWithHandlerCfg)> -where - DB: Database, - ::Error: Into, - I: GetInspector, -{ - let mut evm = revm::Evm::builder() - .with_db(db) - .with_external_context(inspector) - .with_env_with_handler_cfg(env) - .append_handler_register(inspector_handle_register) - .build(); - let res = evm.transact()?; - let (_, env) = evm.into_db_and_env_with_handler_cfg(); - Ok((res, env)) -} - -/// Same as [inspect] but also returns the database again. -/// -/// Even though [Database] is also implemented on `&mut` -/// this is still useful if there are certain trait bounds on the Inspector's database generic type -pub(crate) fn inspect_and_return_db( - db: DB, - env: EnvWithHandlerCfg, - inspector: I, -) -> EthResult<(ResultAndState, EnvWithHandlerCfg, DB)> -where - DB: Database, - ::Error: Into, - I: GetInspector, -{ - let mut evm = revm::Evm::builder() - .with_external_context(inspector) - .with_db(db) - .with_env_with_handler_cfg(env) - .append_handler_register(inspector_handle_register) - .build(); - let res = evm.transact()?; - let (db, env) = evm.into_db_and_env_with_handler_cfg(); - Ok((res, env, db)) -} - -/// Replays all the transactions until the target transaction is found. -/// -/// All transactions before the target transaction are executed and their changes are written to the -/// _runtime_ db ([CacheDB]). -/// -/// Note: This assumes the target transaction is in the given iterator. -/// Returns the index of the target transaction in the given iterator. -pub(crate) fn replay_transactions_until( - db: &mut CacheDB, - cfg: CfgEnvWithHandlerCfg, - block_env: BlockEnv, - transactions: I, - target_tx_hash: B256, -) -> Result -where - DB: DatabaseRef, - EthApiError: From<::Error>, - I: IntoIterator, - Tx: FillableTransaction, -{ - let mut evm = revm::Evm::builder() - .with_db(db) - .with_env_with_handler_cfg(EnvWithHandlerCfg::new_with_cfg_env( - cfg, - block_env, - Default::default(), - )) - .build(); - let mut index = 0; - for tx in transactions.into_iter() { - if tx.hash() == target_tx_hash { - // reached the target transaction - break - } - - tx.try_fill_tx_env(evm.tx_mut())?; - evm.transact_commit()?; - index += 1; - } - Ok(index) -} - /// Prepares the [EnvWithHandlerCfg] for execution. /// /// Does not commit any changes to the underlying database. diff --git a/crates/rpc/rpc/src/trace.rs b/crates/rpc/rpc/src/trace.rs index fd1e76ab5..ade8291c3 100644 --- a/crates/rpc/rpc/src/trace.rs +++ b/crates/rpc/rpc/src/trace.rs @@ -1,6 +1,6 @@ use crate::eth::{ error::{EthApiError, EthResult}, - revm_utils::{inspect, inspect_and_return_db, prepare_call_env, EvmOverrides}, + revm_utils::{prepare_call_env, EvmOverrides}, utils::recover_raw_transaction, EthTransactions, }; @@ -62,6 +62,11 @@ impl TraceApi { ) -> std::result::Result { self.inner.blocking_task_guard.clone().acquire_owned().await } + + /// Access the underlying `Eth` API. + pub fn eth_api(&self) -> &Eth { + &self.inner.eth_api + } } // === impl TraceApi === @@ -78,10 +83,10 @@ where let overrides = EvmOverrides::new(trace_request.state_overrides, trace_request.block_overrides); let mut inspector = TracingInspector::new(config); - self.inner - .eth_api + let this = self.clone(); + self.eth_api() .spawn_with_call_at(trace_request.call, at, overrides, move |db, env| { - let (res, _, db) = inspect_and_return_db(db, env, &mut inspector)?; + let (res, _, db) = this.eth_api().inspect_and_return_db(db, env, &mut inspector)?; let trace_res = inspector.into_parity_builder().into_trace_results_with_state( &res, &trace_request.trace_types, @@ -136,9 +141,9 @@ where let (cfg, block_env, at) = self.inner.eth_api.evm_env_at(at).await?; let gas_limit = self.inner.eth_api.call_gas_limit(); + let this = self.clone(); // execute all transactions on top of each other and record the traces - self.inner - .eth_api + self.eth_api() .spawn_with_state_at_block(at, move |state| { let mut results = Vec::with_capacity(calls.len()); let mut db = CacheDB::new(StateProviderDatabase::new(state)); @@ -156,7 +161,7 @@ where )?; let config = TracingInspectorConfig::from_parity_config(&trace_types); let mut inspector = TracingInspector::new(config); - let (res, _) = inspect(&mut db, env, &mut inspector)?; + let (res, _) = this.eth_api().inspect(&mut db, env, &mut inspector)?; let trace_res = inspector.into_parity_builder().into_trace_results_with_state( &res,