diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 1e41b6c5e6..182d6ceeff 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -1,7 +1,7 @@ use crate::{ eth::{ error::{EthApiError, EthResult}, - revm_utils::{inspect, replay_transactions_until}, + revm_utils::{inspect, replay_transactions_until, EvmOverrides}, EthTransactions, TransactionSource, }, result::{internal_rpc_err, ToRpcResult}, @@ -254,9 +254,9 @@ where opts: GethDebugTracingCallOptions, ) -> EthResult { let at = block_id.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)); - // TODO(mattsse) apply block overrides - let GethDebugTracingCallOptions { tracing_options, state_overrides, block_overrides: _ } = + let GethDebugTracingCallOptions { tracing_options, state_overrides, block_overrides } = opts; + let overrides = EvmOverrides::new(state_overrides, block_overrides); let GethDebugTracingOptions { config, tracer, tracer_config, .. } = tracing_options; if let Some(tracer) = tracer { @@ -274,7 +274,7 @@ where let (_res, _) = self .inner .eth_api - .inspect_call_at(call, at, state_overrides, &mut inspector) + .inspect_call_at(call, at, overrides, &mut inspector) .await?; return Ok(FourByteFrame::from(inspector).into()) } @@ -290,7 +290,7 @@ where let _ = self .inner .eth_api - .inspect_call_at(call, at, state_overrides, &mut inspector) + .inspect_call_at(call, at, overrides, &mut inspector) .await?; let frame = inspector.into_geth_builder().geth_call_traces(call_config); @@ -314,7 +314,7 @@ where let mut inspector = TracingInspector::new(inspector_config); let (res, _) = - self.inner.eth_api.inspect_call_at(call, at, state_overrides, &mut inspector).await?; + self.inner.eth_api.inspect_call_at(call, at, overrides, &mut inspector).await?; let gas_used = res.result.gas_used(); let frame = inspector.into_geth_builder().geth_traces(gas_used, config); diff --git a/crates/rpc/rpc/src/eth/api/call.rs b/crates/rpc/rpc/src/eth/api/call.rs index d857b42ad7..9eadd0f490 100644 --- a/crates/rpc/rpc/src/eth/api/call.rs +++ b/crates/rpc/rpc/src/eth/api/call.rs @@ -59,7 +59,7 @@ where .transact_call_at( request, block_number.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)), - state_overrides, + state_overrides.into(), ) .await?; diff --git a/crates/rpc/rpc/src/eth/api/transactions.rs b/crates/rpc/rpc/src/eth/api/transactions.rs index 85d7b9273d..d0b9bbe0c5 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -9,6 +9,7 @@ use crate::{ }; use async_trait::async_trait; +use crate::eth::revm_utils::EvmOverrides; use reth_network_api::NetworkInfo; use reth_primitives::{ Address, BlockId, BlockNumberOrTag, Bytes, FromRecoveredTransaction, Header, @@ -23,8 +24,8 @@ use reth_revm::{ tracing::{TracingInspector, TracingInspectorConfig}, }; use reth_rpc_types::{ - state::StateOverride, CallRequest, Index, Log, Transaction, TransactionInfo, - TransactionReceipt, TransactionRequest, TypedTransactionRequest, + CallRequest, Index, Log, Transaction, TransactionInfo, TransactionReceipt, TransactionRequest, + TypedTransactionRequest, }; use reth_transaction_pool::{TransactionOrigin, TransactionPool}; use revm::{ @@ -117,7 +118,7 @@ pub trait EthTransactions: Send + Sync { &self, request: CallRequest, at: BlockId, - state_overrides: Option, + overrides: EvmOverrides, f: F, ) -> EthResult where @@ -128,7 +129,7 @@ pub trait EthTransactions: Send + Sync { &self, request: CallRequest, at: BlockId, - state_overrides: Option, + overrides: EvmOverrides, ) -> EthResult<(ResultAndState, Env)>; /// Executes the call request at the given [BlockId] @@ -136,7 +137,7 @@ pub trait EthTransactions: Send + Sync { &self, request: CallRequest, at: BlockId, - state_overrides: Option, + overrides: EvmOverrides, inspector: I, ) -> EthResult<(ResultAndState, Env)> where @@ -431,7 +432,7 @@ where &self, request: CallRequest, at: BlockId, - state_overrides: Option, + overrides: EvmOverrides, f: F, ) -> EthResult where @@ -441,7 +442,7 @@ where let state = self.state_at(at)?; let mut db = SubState::new(State::new(state)); - let env = prepare_call_env(cfg, block_env, request, &mut db, state_overrides)?; + let env = prepare_call_env(cfg, block_env, request, &mut db, overrides)?; f(db, env) } @@ -449,22 +450,22 @@ where &self, request: CallRequest, at: BlockId, - state_overrides: Option, + overrides: EvmOverrides, ) -> EthResult<(ResultAndState, Env)> { - self.with_call_at(request, at, state_overrides, |mut db, env| transact(&mut db, env)).await + self.with_call_at(request, at, overrides, |mut db, env| transact(&mut db, env)).await } async fn inspect_call_at( &self, request: CallRequest, at: BlockId, - state_overrides: Option, + overrides: EvmOverrides, inspector: I, ) -> EthResult<(ResultAndState, Env)> where I: for<'r> Inspector>>> + Send, { - self.with_call_at(request, at, state_overrides, |db, env| inspect(db, env, inspector)).await + self.with_call_at(request, at, overrides, |db, env| inspect(db, env, inspector)).await } fn trace_at( diff --git a/crates/rpc/rpc/src/eth/revm_utils.rs b/crates/rpc/rpc/src/eth/revm_utils.rs index 3d47939ef9..a79be4b0ad 100644 --- a/crates/rpc/rpc/src/eth/revm_utils.rs +++ b/crates/rpc/rpc/src/eth/revm_utils.rs @@ -7,7 +7,7 @@ use reth_primitives::{ use reth_revm::env::{fill_tx_env, fill_tx_env_with_recovered}; use reth_rpc_types::{ state::{AccountOverride, StateOverride}, - CallRequest, + BlockOverrides, CallRequest, }; use revm::{ db::CacheDB, @@ -21,10 +21,42 @@ use revm_primitives::{ }; use tracing::trace; +/// Helper type that bundles various overrides for EVM Execution. +/// +/// By `Default`, no overrides are included. +#[derive(Debug, Clone, Default)] +pub struct EvmOverrides { + /// Applies overrides to the state before execution. + pub state: Option, + /// Applies overrides to the block before execution. + /// + /// This is a `Box` because less common and only available in debug trace endpoints. + pub block: Option>, +} + +impl EvmOverrides { + /// Creates a new instance with the given overrides + pub fn new(state: Option, block: Option) -> Self { + Self { state, block: block.map(Box::new) } + } + + /// Creates a new instance with the given state overrides. + pub fn state(state: Option) -> Self { + Self { state, block: None } + } +} + +impl From> for EvmOverrides { + fn from(state: Option) -> Self { + Self::state(state) + } +} + /// Helper type to work with different transaction types when configuring the EVM env. /// /// This makes it easier to handle errors. pub(crate) trait FillableTransaction { + /// Returns the hash of the transaction. fn hash(&self) -> TxHash; /// Fill the transaction environment with the given transaction. @@ -145,7 +177,7 @@ pub(crate) fn prepare_call_env( block: BlockEnv, request: CallRequest, db: &mut CacheDB, - state_overrides: Option, + overrides: EvmOverrides, ) -> EthResult where DB: DatabaseRef, @@ -169,10 +201,15 @@ where let mut env = build_call_evm_env(cfg, block, request)?; // apply state overrides - if let Some(state_overrides) = state_overrides { + if let Some(state_overrides) = overrides.state { apply_state_overrides(state_overrides, db)?; } + // apply block overrides + if let Some(block_overrides) = overrides.block { + apply_block_overrides(*block_overrides, &mut env.block); + } + if request_gas.is_none() && env.tx.gas_price > U256::ZERO { trace!(target: "rpc::eth::call", ?env, "Applying gas limit cap"); // no gas limit was provided in the request, so we need to cap the request's gas limit @@ -324,6 +361,34 @@ impl CallFees { } } +/// Applies the given block overrides to the env +fn apply_block_overrides(overrides: BlockOverrides, env: &mut BlockEnv) { + let BlockOverrides { number, difficulty, time, gas_limit, coinbase, random, base_fee } = + overrides; + + if let Some(number) = number { + env.number = number; + } + if let Some(difficulty) = difficulty { + env.difficulty = difficulty; + } + if let Some(time) = time { + env.timestamp = U256::from(time.as_u64()); + } + if let Some(gas_limit) = gas_limit { + env.gas_limit = U256::from(gas_limit.as_u64()); + } + if let Some(coinbase) = coinbase { + env.coinbase = coinbase; + } + if let Some(random) = random { + env.prevrandao = Some(random); + } + if let Some(base_fee) = base_fee { + env.basefee = base_fee; + } +} + /// Applies the given state overrides (a set of [AccountOverride]) to the [CacheDB]. fn apply_state_overrides(overrides: StateOverride, db: &mut CacheDB) -> EthResult<()> where diff --git a/crates/rpc/rpc/src/trace.rs b/crates/rpc/rpc/src/trace.rs index e203f11981..77cf0e3bde 100644 --- a/crates/rpc/rpc/src/trace.rs +++ b/crates/rpc/rpc/src/trace.rs @@ -117,7 +117,11 @@ where let config = tracing_config(&trace_types); let mut inspector = TracingInspector::new(config); - let (res, _) = self.inner.eth_api.inspect_call_at(call, at, None, &mut inspector).await?; + let (res, _) = self + .inner + .eth_api + .inspect_call_at(call, at, Default::default(), &mut inspector) + .await?; let trace_res = inspector.into_parity_builder().into_trace_results(res.result, &trace_types); @@ -172,8 +176,13 @@ where let mut db = SubState::new(State::new(state)); for (call, trace_types) in calls { - let env = - prepare_call_env(cfg.clone(), block_env.clone(), call, &mut db, None)?; + let env = prepare_call_env( + cfg.clone(), + block_env.clone(), + call, + &mut db, + Default::default(), + )?; let config = tracing_config(&trace_types); let mut inspector = TracingInspector::new(config); let (res, _) = inspect(&mut db, env, &mut inspector)?;