feat: move transact and inspect calls to trait (#7438)

This commit is contained in:
Matthias Seitz
2024-04-03 19:40:46 +02:00
committed by GitHub
parent 68c753b79b
commit f892b5c511
5 changed files with 284 additions and 195 deletions

View File

@ -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<Provider, Eth> DebugApi<Provider, Eth> {
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);

View File

@ -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,22 +450,21 @@ 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<S>(
/// 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<S>(
&self,
env_gas_limit: U256,
mut env: EnvWithHandlerCfg,
mut db: &mut CacheDB<StateProviderDatabase<S>>,
) -> EthApiError
where
) -> 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) {
let (res, _) = match self.transact(&mut db, env) {
Ok(res) => res,
Err(err) => return err,
};
@ -478,7 +478,10 @@ where
// reverted again after bumping the limit
RpcInvalidTransactionError::Revert(RevertError::new(output)).into()
}
ExecutionResult::Halt { reason, .. } => RpcInvalidTransactionError::EvmHalt(reason).into(),
ExecutionResult::Halt { reason, .. } => {
RpcInvalidTransactionError::EvmHalt(reason).into()
}
}
}
}

View File

@ -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<StateProviderDatabase<StateProviderBox>>;
/// 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<StateProviderDatabase<StateProviderBox>>;
/// 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<DB>(
&self,
db: DB,
env: EnvWithHandlerCfg,
) -> EthResult<(ResultAndState, EnvWithHandlerCfg)>
where
DB: Database,
<DB as Database>::Error: Into<EthApiError>;
/// Executes the [EnvWithHandlerCfg] against the given [Database] without committing state
/// changes.
fn inspect<DB, I>(
&self,
db: DB,
env: EnvWithHandlerCfg,
inspector: I,
) -> EthResult<(ResultAndState, EnvWithHandlerCfg)>
where
DB: Database,
<DB as Database>::Error: Into<EthApiError>,
I: GetInspector<DB>;
/// 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<DB, I>(
&self,
db: DB,
env: EnvWithHandlerCfg,
inspector: I,
) -> EthResult<(ResultAndState, EnvWithHandlerCfg, DB)>
where
DB: Database,
<DB as Database>::Error: Into<EthApiError>,
I: GetInspector<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.
fn replay_transactions_until<DB, I, Tx>(
&self,
db: &mut CacheDB<DB>,
cfg: CfgEnvWithHandlerCfg,
block_env: BlockEnv,
transactions: I,
target_tx_hash: B256,
) -> Result<usize, EthApiError>
where
DB: DatabaseRef,
EthApiError: From<<DB as DatabaseRef>::Error>,
I: IntoIterator<Item = Tx>,
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<DB>(
&self,
db: DB,
env: EnvWithHandlerCfg,
) -> EthResult<(ResultAndState, EnvWithHandlerCfg)>
where
DB: Database,
<DB as Database>::Error: Into<EthApiError>,
{
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<DB, I>(
&self,
db: DB,
env: EnvWithHandlerCfg,
inspector: I,
) -> EthResult<(ResultAndState, EnvWithHandlerCfg)>
where
DB: Database,
<DB as Database>::Error: Into<EthApiError>,
I: GetInspector<DB>,
{
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<DB, I>(
&self,
db: DB,
env: EnvWithHandlerCfg,
inspector: I,
) -> EthResult<(ResultAndState, EnvWithHandlerCfg, DB)>
where
DB: Database,
<DB as Database>::Error: Into<EthApiError>,
I: GetInspector<DB>,
{
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<DB, I, Tx>(
&self,
db: &mut CacheDB<DB>,
cfg: CfgEnvWithHandlerCfg,
block_env: BlockEnv,
transactions: I,
target_tx_hash: B256,
) -> Result<usize, EthApiError>
where
DB: DatabaseRef,
EthApiError: From<<DB as DatabaseRef>::Error>,
I: IntoIterator<Item = Tx>,
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,7 +1107,10 @@ where
at: BlockId,
overrides: EvmOverrides,
) -> EthResult<(ResultAndState, EnvWithHandlerCfg)> {
self.spawn_with_call_at(request, at, overrides, move |mut db, env| transact(&mut db, env))
let this = self.clone();
self.spawn_with_call_at(request, at, overrides, move |mut db, env| {
this.transact(&mut db, env)
})
.await
}
@ -964,7 +1124,10 @@ where
where
I: Inspector<StateCacheDB> + Send + 'static,
{
self.spawn_with_call_at(request, at, overrides, move |db, env| inspect(db, env, inspector))
let this = self.clone();
self.spawn_with_call_at(request, at, overrides, move |db, env| {
this.inspect(db, env, inspector)
})
.await
}
@ -978,11 +1141,12 @@ where
where
F: FnOnce(TracingInspector, ResultAndState) -> EthResult<R>,
{
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<R> + 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)?);

View File

@ -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<Option<StateOverride>> 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<Item = Addre
Precompiles::new(spec).addresses().copied().map(Address::from)
}
/// Executes the [EnvWithHandlerCfg] against the given [Database] without committing state changes.
pub(crate) fn transact<DB>(
db: DB,
env: EnvWithHandlerCfg,
) -> EthResult<(ResultAndState, EnvWithHandlerCfg)>
where
DB: Database,
<DB as Database>::Error: Into<EthApiError>,
{
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, I>(
db: DB,
env: EnvWithHandlerCfg,
inspector: I,
) -> EthResult<(ResultAndState, EnvWithHandlerCfg)>
where
DB: Database,
<DB as Database>::Error: Into<EthApiError>,
I: GetInspector<DB>,
{
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, I>(
db: DB,
env: EnvWithHandlerCfg,
inspector: I,
) -> EthResult<(ResultAndState, EnvWithHandlerCfg, DB)>
where
DB: Database,
<DB as Database>::Error: Into<EthApiError>,
I: GetInspector<DB>,
{
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, I, Tx>(
db: &mut CacheDB<DB>,
cfg: CfgEnvWithHandlerCfg,
block_env: BlockEnv,
transactions: I,
target_tx_hash: B256,
) -> Result<usize, EthApiError>
where
DB: DatabaseRef,
EthApiError: From<<DB as DatabaseRef>::Error>,
I: IntoIterator<Item = Tx>,
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.

View File

@ -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<Provider, Eth> TraceApi<Provider, Eth> {
) -> std::result::Result<OwnedSemaphorePermit, AcquireError> {
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,