diff --git a/crates/revm/revm-primitives/src/env.rs b/crates/revm/revm-primitives/src/env.rs index 588586acf3..4a3ee32392 100644 --- a/crates/revm/revm-primitives/src/env.rs +++ b/crates/revm/revm-primitives/src/env.rs @@ -41,15 +41,28 @@ pub fn fill_cfg_env( cfg_env.perf_all_precompiles_have_balance = false; cfg_env.perf_analyse_created_bytecodes = AnalysisKind::Analyse; } + /// Fill block environment from Block. pub fn fill_block_env( block_env: &mut BlockEnv, chain_spec: &ChainSpec, header: &Header, after_merge: bool, +) { + let coinbase = block_coinbase(chain_spec, header, after_merge); + fill_block_env_with_coinbase(block_env, header, after_merge, coinbase); +} + +/// Fill block environment with coinbase. +#[inline] +pub fn fill_block_env_with_coinbase( + block_env: &mut BlockEnv, + header: &Header, + after_merge: bool, + coinbase: Address, ) { block_env.number = U256::from(header.number); - block_env.coinbase = block_coinbase(chain_spec, header, after_merge); + block_env.coinbase = coinbase; block_env.timestamp = U256::from(header.timestamp); if after_merge { block_env.prevrandao = Some(header.mix_hash); diff --git a/crates/rpc/rpc-builder/tests/it/http.rs b/crates/rpc/rpc-builder/tests/it/http.rs index 8810f801d2..cb86772dd4 100644 --- a/crates/rpc/rpc-builder/tests/it/http.rs +++ b/crates/rpc/rpc-builder/tests/it/http.rs @@ -162,11 +162,8 @@ where .await .err() .unwrap(); - TraceApiClient::trace_block(client, block_id).await.err().unwrap(); - TraceApiClient::replay_block_transactions(client, block_id, HashSet::default()) - .await - .err() - .unwrap(); + TraceApiClient::trace_block(client, block_id).await.unwrap(); + TraceApiClient::replay_block_transactions(client, block_id, HashSet::default()).await.unwrap(); assert!(is_unimplemented( TraceApiClient::trace_filter(client, trace_filter).await.err().unwrap() diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 9b604574a7..9f0f59350b 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -9,7 +9,7 @@ use crate::{ }; use async_trait::async_trait; use jsonrpsee::core::RpcResult; -use reth_primitives::{Block, BlockId, BlockNumberOrTag, Bytes, H256, U256}; +use reth_primitives::{Block, BlockId, BlockNumberOrTag, Bytes, TransactionSigned, H256, U256}; use reth_provider::{BlockProvider, HeaderProvider, StateProviderBox}; use reth_revm::{ database::{State, SubState}, @@ -27,6 +27,7 @@ use reth_rpc_types::{ BlockError, CallRequest, RichBlock, }; use revm::primitives::Env; +use revm_primitives::{BlockEnv, CfgEnv}; /// `debug` API implementation. /// @@ -37,7 +38,6 @@ pub struct DebugApi { client: Client, /// The implementation of `eth` API eth_api: Eth, - // restrict the number of concurrent calls to `debug_traceTransaction` tracing_call_guard: TracingCallGuard, } @@ -58,42 +58,15 @@ where Client: BlockProvider + HeaderProvider + 'static, Eth: EthTransactions + 'static, { - /// Replays the given block and returns the trace of each transaction. - /// - /// This expects a rlp encoded block - /// - /// Note, the parent of this block must be present, or it will fail. - pub async fn debug_trace_raw_block( + /// Trace the entire block + fn trace_block_with( &self, - rlp_block: Bytes, - _opts: GethDebugTracingOptions, - ) -> EthResult> { - let block = - Block::decode(&mut rlp_block.as_ref()).map_err(BlockError::RlpDecodeRawBlock)?; - let _parent = block.parent_hash; - - // TODO we need the state after the parent block - - todo!() - } - - /// Replays a block and returns the trace of each transaction. - pub async fn debug_trace_block( - &self, - block_id: BlockId, + at: BlockId, + transactions: Vec, + cfg: CfgEnv, + block_env: BlockEnv, opts: GethDebugTracingOptions, ) -> EthResult> { - let block_hash = self - .client - .block_hash_for_id(block_id)? - .ok_or_else(|| EthApiError::UnknownBlockNumber)?; - - let ((cfg, block_env, at), transactions) = futures::try_join!( - self.eth_api.evm_env_at(block_hash.into()), - self.eth_api.transactions_by_block(block_hash), - )?; - let transactions = transactions.ok_or_else(|| EthApiError::UnknownBlockNumber)?; - // replay all transactions of the block self.eth_api.with_state_at_block(at, move |state| { let mut results = Vec::with_capacity(transactions.len()); @@ -112,6 +85,50 @@ where }) } + /// Replays the given block and returns the trace of each transaction. + /// + /// This expects a rlp encoded block + /// + /// Note, the parent of this block must be present, or it will fail. + pub async fn debug_trace_raw_block( + &self, + rlp_block: Bytes, + opts: GethDebugTracingOptions, + ) -> EthResult> { + let block = + Block::decode(&mut rlp_block.as_ref()).map_err(BlockError::RlpDecodeRawBlock)?; + + 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; + self.trace_block_with(parent.into(), block.body, cfg, block_env, opts) + } + + /// Replays a block and returns the trace of each transaction. + pub async fn debug_trace_block( + &self, + block_id: BlockId, + opts: GethDebugTracingOptions, + ) -> EthResult> { + let block_hash = self + .client + .block_hash_for_id(block_id)? + .ok_or_else(|| EthApiError::UnknownBlockNumber)?; + + let ((cfg, block_env, _), block) = futures::try_join!( + self.eth_api.evm_env_at(block_hash.into()), + self.eth_api.block_by_id(block_id), + )?; + + let block = block.ok_or_else(|| EthApiError::UnknownBlockNumber)?; + // we need to get the state of the parent block because we're replaying this block on top of + // its parent block's state + let state_at = block.parent_hash; + + self.trace_block_with(state_at.into(), block.body, cfg, block_env, opts) + } + /// Trace the transaction according to the provided options. /// /// Ref: @@ -128,10 +145,10 @@ where // we need to get the state of the parent block because we're essentially replaying the // block the transaction is included in - let parent_block = block.parent_hash; + let state_at = block.parent_hash; let block_txs = block.body; - self.eth_api.with_state_at_block(parent_block.into(), |state| { + self.eth_api.with_state_at_block(state_at.into(), |state| { // configure env for the target transaction let tx = transaction.into_recovered(); diff --git a/crates/rpc/rpc/src/eth/api/transactions.rs b/crates/rpc/rpc/src/eth/api/transactions.rs index 1282d5d9ef..835ac950c7 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -11,15 +11,15 @@ use async_trait::async_trait; use reth_network_api::NetworkInfo; use reth_primitives::{ - Address, BlockId, BlockNumberOrTag, Bytes, FromRecoveredTransaction, IntoRecoveredTransaction, - Receipt, SealedBlock, + Address, BlockId, BlockNumberOrTag, Bytes, FromRecoveredTransaction, Header, + IntoRecoveredTransaction, Receipt, SealedBlock, TransactionKind::{Call, Create}, TransactionMeta, TransactionSigned, TransactionSignedEcRecovered, H256, U128, U256, U64, }; use reth_provider::{BlockProvider, EvmEnvProvider, StateProviderBox, StateProviderFactory}; use reth_revm::{ database::{State, SubState}, - env::tx_env_with_recovered, + env::{fill_block_env_with_coinbase, tx_env_with_recovered}, tracing::{TracingInspector, TracingInspectorConfig}, }; use reth_rpc_types::{ @@ -32,7 +32,7 @@ use revm::{ primitives::{BlockEnv, CfgEnv}, Inspector, }; -use revm_primitives::{utilities::create_address, Env, ResultAndState}; +use revm_primitives::{utilities::create_address, Env, ResultAndState, SpecId}; /// Commonly used transaction related functions for the [EthApi] type in the `eth_` namespace #[async_trait::async_trait] @@ -51,12 +51,22 @@ pub trait EthTransactions: Send + Sync { /// for. async fn evm_env_at(&self, at: BlockId) -> EthResult<(CfgEnv, BlockEnv, BlockId)>; + /// Returns the revm evm env for the raw block header + /// + /// This is used for tracing raw blocks + async fn evm_env_for_raw_block(&self, at: &Header) -> EthResult<(CfgEnv, BlockEnv)>; + /// Get all transactions in the block with the given hash. /// /// Returns `None` if block does not exist. async fn transactions_by_block(&self, block: H256) -> EthResult>>; + /// Get the entire block for the given id. + /// + /// Returns `None` if block does not exist. + async fn block_by_id(&self, id: BlockId) -> EthResult>; + /// Get all transactions in the block with the given hash. /// /// Returns `None` if block does not exist. @@ -205,6 +215,16 @@ where } } + async fn evm_env_for_raw_block(&self, header: &Header) -> EthResult<(CfgEnv, BlockEnv)> { + // get the parent config first + let (cfg, mut block_env, _) = self.evm_env_at(header.parent_hash.into()).await?; + + let after_merge = cfg.spec_id >= SpecId::MERGE; + fill_block_env_with_coinbase(&mut block_env, header, after_merge, header.beneficiary); + + Ok((cfg, block_env)) + } + async fn transactions_by_block( &self, block: H256, @@ -212,14 +232,15 @@ where Ok(self.cache().get_block_transactions(block).await?) } + async fn block_by_id(&self, id: BlockId) -> EthResult> { + self.block(id).await + } + async fn transactions_by_block_id( &self, block: BlockId, ) -> EthResult>> { - match self.client().block_hash_for_id(block)? { - None => Ok(None), - Some(hash) => self.transactions_by_block(hash).await, - } + self.block_by_id(block).await.map(|block| block.map(|block| block.body)) } async fn transaction_by_hash(&self, hash: H256) -> EthResult> { @@ -248,9 +269,9 @@ where async fn transaction_by_hash_at( &self, - hash: H256, + transaction_hash: H256, ) -> EthResult> { - match self.transaction_by_hash(hash).await? { + match self.transaction_by_hash(transaction_hash).await? { None => return Ok(None), Some(tx) => { let res = match tx { diff --git a/crates/rpc/rpc/src/trace.rs b/crates/rpc/rpc/src/trace.rs index 24946baa5c..9c17d30c35 100644 --- a/crates/rpc/rpc/src/trace.rs +++ b/crates/rpc/rpc/src/trace.rs @@ -213,20 +213,26 @@ where where F: Fn(TransactionInfo, TracingInspector, ResultAndState) -> EthResult + Send, { - let block_hash = match self.client.block_hash_for_id(block_id)? { - Some(hash) => hash, + let ((cfg, block_env, _), block) = futures::try_join!( + self.eth_api.evm_env_at(block_id), + self.eth_api.block_by_id(block_id), + )?; + + let block = match block { + Some(block) => block, None => return Ok(None), }; - let ((cfg, block_env, at), transactions) = futures::try_join!( - self.eth_api.evm_env_at(block_hash.into()), - self.eth_api.transactions_by_block(block_hash), - )?; - let transactions = transactions.ok_or_else(|| EthApiError::UnknownBlockNumber)?; + // we need to get the state of the parent block because we're replaying this block on top of + // its parent block's state + let state_at = block.parent_hash; + + let block_hash = block.hash; + let transactions = block.body; // replay all transactions of the block self.eth_api - .with_state_at_block(at, move |state| { + .with_state_at_block(state_at.into(), move |state| { let mut results = Vec::with_capacity(transactions.len()); let mut db = SubState::new(State::new(state));