mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: move transact and inspect calls to trait (#7438)
This commit is contained in:
@ -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);
|
||||
|
||||
@ -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<S>(
|
||||
env_gas_limit: U256,
|
||||
mut env: EnvWithHandlerCfg,
|
||||
mut db: &mut CacheDB<StateProviderDatabase<S>>,
|
||||
) -> 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<S>(
|
||||
&self,
|
||||
env_gas_limit: U256,
|
||||
mut env: EnvWithHandlerCfg,
|
||||
mut db: &mut CacheDB<StateProviderDatabase<S>>,
|
||||
) -> 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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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,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<I>(
|
||||
@ -964,8 +1124,11 @@ where
|
||||
where
|
||||
I: Inspector<StateCacheDB> + 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<F, R>(
|
||||
@ -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)?);
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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,
|
||||
|
||||
Reference in New Issue
Block a user