diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 843f2bcb02..9b604574a7 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -120,18 +120,18 @@ where tx_hash: H256, opts: GethDebugTracingOptions, ) -> EthResult { - let (transaction, at) = match self.eth_api.transaction_by_hash_at(tx_hash).await? { + let (transaction, block) = match self.eth_api.transaction_and_block(tx_hash).await? { None => return Err(EthApiError::TransactionNotFound), Some(res) => res, }; + let (cfg, block_env, _) = self.eth_api.evm_env_at(block.hash.into()).await?; - let block_env = self.eth_api.evm_env_at(at); - let block_txs = self.eth_api.transactions_by_block_id(at); - let (block_env, block_txs) = futures::try_join!(block_env, block_txs)?; - let block_txs = block_txs.unwrap_or_default(); - let (cfg, block_env, at) = block_env; + // 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 block_txs = block.body; - self.eth_api.with_state_at_block(at, |state| { + self.eth_api.with_state_at_block(parent_block.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 467993220b..4354f73d82 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -8,10 +8,11 @@ use crate::{ EthApi, EthApiSpec, }; use async_trait::async_trait; + use reth_network_api::NetworkInfo; use reth_primitives::{ Address, BlockId, BlockNumberOrTag, Bytes, FromRecoveredTransaction, IntoRecoveredTransaction, - Receipt, Transaction as PrimitiveTransaction, + Receipt, SealedBlock, Transaction as PrimitiveTransaction, TransactionKind::{Call, Create}, TransactionMeta, TransactionSigned, TransactionSignedEcRecovered, TxEip1559, TxEip2930, TxLegacy, H256, U128, U256, U64, @@ -73,11 +74,19 @@ pub trait EthTransactions: Send + Sync { async fn transaction_by_hash(&self, hash: H256) -> EthResult>; /// Returns the transaction by including its corresponding [BlockId] + /// + /// Note: this supports pending transactions async fn transaction_by_hash_at( &self, hash: H256, ) -> EthResult>; + /// Returns the _historical_ transaction and the block it was mined in + async fn historical_transaction_by_hash_at( + &self, + hash: H256, + ) -> EthResult>; + /// Returns the transaction receipt for the given hash. /// /// Returns None if the transaction does not exist or is pending @@ -139,6 +148,12 @@ pub trait EthTransactions: Send + Sync { where F: FnOnce(TracingInspector, ResultAndState) -> EthResult; + /// Fetches the transaction and the transaction's block + async fn transaction_and_block( + &self, + hash: H256, + ) -> EthResult>; + /// Retrieves the transaction if it exists and returns its trace. /// /// Before the transaction is traced, all previous transaction in the block are applied to the @@ -263,6 +278,16 @@ where } } + async fn historical_transaction_by_hash_at( + &self, + hash: H256, + ) -> EthResult> { + match self.transaction_by_hash_at(hash).await? { + None => Ok(None), + Some((tx, at)) => Ok(at.as_block_hash().map(|hash| (tx, hash))), + } + } + async fn transaction_receipt(&self, hash: H256) -> EthResult> { let (tx, meta) = match self.client().transaction_by_hash_with_meta(hash)? { Some((tx, meta)) => (tx, meta), @@ -424,6 +449,24 @@ where }) } + async fn transaction_and_block( + &self, + hash: H256, + ) -> EthResult> { + let (transaction, at) = match self.transaction_by_hash_at(hash).await? { + None => return Ok(None), + Some(res) => res, + }; + + // Note: this is always either hash or pending + let block_hash = match at { + BlockId::Hash(hash) => hash.block_hash, + _ => return Ok(None), + }; + let block = self.cache().get_block(block_hash).await?; + Ok(block.map(|block| (transaction, block.seal(block_hash)))) + } + async fn trace_transaction_in_block( &self, hash: H256, @@ -433,19 +476,20 @@ where where F: FnOnce(TransactionInfo, TracingInspector, ResultAndState) -> EthResult + Send, { - let (transaction, at) = match self.transaction_by_hash_at(hash).await? { + let (transaction, block) = match self.transaction_and_block(hash).await? { None => return Ok(None), Some(res) => res, }; let (tx, tx_info) = transaction.split(); - let block_env = self.evm_env_at(at); - let block_txs = self.transactions_by_block_id(at); - let (block_env, block_txs) = futures::try_join!(block_env, block_txs)?; - let block_txs = block_txs.unwrap_or_default(); - let (cfg, block_env, at) = block_env; + let (cfg, block_env, _) = self.evm_env_at(block.hash.into()).await?; - self.with_state_at_block(at, |state| { + // 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 block_txs = block.body; + + self.with_state_at_block(parent_block.into(), |state| { let mut db = SubState::new(State::new(state)); // replay all transactions prior to the targeted transaction