From cf5a1114d0c4342d0c60c5bb9c789a49a8779687 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 4 Apr 2023 21:39:07 +0200 Subject: [PATCH] feat(rpc): add replay transaction (#2107) Co-authored-by: Dan Cline <6798349+Rjected@users.noreply.github.com> --- crates/rpc/rpc-builder/tests/it/http.rs | 10 ++-- crates/rpc/rpc/src/eth/api/transactions.rs | 34 ++++++++++++++ crates/rpc/rpc/src/trace.rs | 53 ++++++++++++++-------- 3 files changed, 71 insertions(+), 26 deletions(-) diff --git a/crates/rpc/rpc-builder/tests/it/http.rs b/crates/rpc/rpc-builder/tests/it/http.rs index 3eec4370c6..b95e111216 100644 --- a/crates/rpc/rpc-builder/tests/it/http.rs +++ b/crates/rpc/rpc-builder/tests/it/http.rs @@ -161,6 +161,10 @@ where .await .unwrap_err(); TraceApiClient::trace_call_many(client, vec![], None).await.err().unwrap(); + TraceApiClient::replay_transaction(client, H256::default(), HashSet::default()) + .await + .err() + .unwrap(); assert!(is_unimplemented( TraceApiClient::replay_block_transactions(client, block_id, HashSet::default()) @@ -168,12 +172,6 @@ where .err() .unwrap() )); - assert!(is_unimplemented( - TraceApiClient::replay_transaction(client, H256::default(), HashSet::default()) - .await - .err() - .unwrap() - )); assert!(is_unimplemented(TraceApiClient::trace_block(client, block_id).await.err().unwrap())); assert!(is_unimplemented( TraceApiClient::trace_filter(client, trace_filter).await.err().unwrap() diff --git a/crates/rpc/rpc/src/eth/api/transactions.rs b/crates/rpc/rpc/src/eth/api/transactions.rs index 6e1aa99fb1..c7f35e1601 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -19,6 +19,7 @@ use reth_primitives::{ use reth_provider::{BlockProvider, EvmEnvProvider, StateProviderBox, StateProviderFactory}; use reth_revm::{ database::{State, SubState}, + env::tx_env_with_recovered, tracing::{TracingInspector, TracingInspectorConfig}, }; use reth_rpc_types::{ @@ -121,6 +122,16 @@ pub trait EthTransactions: Send + Sync { ) -> EthResult where F: FnOnce(TracingInspector, ResultAndState) -> EthResult; + + /// Retrieves the transaction if it exists and returns its trace + async fn trace_transaction( + &self, + hash: H256, + config: TracingInspectorConfig, + f: F, + ) -> EthResult> + where + F: FnOnce(TransactionInfo, TracingInspector, ResultAndState) -> EthResult + Send; } #[async_trait] @@ -376,6 +387,29 @@ where f(inspector, res) }) } + + async fn trace_transaction( + &self, + hash: H256, + config: TracingInspectorConfig, + f: F, + ) -> EthResult> + where + F: FnOnce(TransactionInfo, TracingInspector, ResultAndState) -> EthResult + Send, + { + let (transaction, at) = match self.transaction_by_hash_at(hash).await? { + None => return Ok(None), + Some(res) => res, + }; + + let (cfg, block, at) = self.evm_env_at(at).await?; + let (tx, tx_info) = transaction.split(); + let tx = tx_env_with_recovered(&tx); + let env = Env { cfg, block, tx }; + + // execute the trace + self.trace_at(env, config, at, move |insp, res| f(tx_info, insp, res)).map(Some) + } } // === impl EthApi === diff --git a/crates/rpc/rpc/src/trace.rs b/crates/rpc/rpc/src/trace.rs index 195f034b6a..231eb24c3b 100644 --- a/crates/rpc/rpc/src/trace.rs +++ b/crates/rpc/rpc/src/trace.rs @@ -1,7 +1,7 @@ use crate::{ eth::{ cache::EthStateCache, - error::EthResult, + error::{EthApiError, EthResult}, revm_utils::{inspect, prepare_call_env}, utils::recover_raw_transaction, EthTransactions, @@ -147,6 +147,24 @@ where }) } + /// Replays a transaction, returning the traces. + pub async fn replay_transaction( + &self, + hash: H256, + trace_types: HashSet, + ) -> EthResult { + let config = tracing_config(&trace_types); + self.eth_api + .trace_transaction(hash, config, |_, inspector, res| { + let trace_res = + inspector.into_parity_builder().into_trace_results(res.result, &trace_types); + Ok(trace_res) + }) + .await + .transpose() + .ok_or_else(|| EthApiError::TransactionNotFound)? + } + /// Returns transaction trace with the given address. pub async fn trace_get( &self, @@ -172,22 +190,17 @@ where ) -> EthResult>> { let _permit = self.acquire_trace_permit().await; - let (transaction, at) = match self.eth_api.transaction_by_hash_at(hash).await? { - None => return Ok(None), - Some(res) => res, - }; - - let (cfg, block, at) = self.eth_api.evm_env_at(at).await?; - - let (tx, tx_info) = transaction.split(); - let tx = tx_env_with_recovered(&tx); - let env = Env { cfg, block, tx }; - - // execute the trace - self.eth_api.trace_at(env, TracingInspectorConfig::default_parity(), at, |inspector, _| { - let traces = inspector.into_parity_builder().into_localized_transaction_traces(tx_info); - Ok(Some(traces)) - }) + self.eth_api + .trace_transaction( + hash, + TracingInspectorConfig::default_parity(), + |tx_info, inspector, _| { + let traces = + inspector.into_parity_builder().into_localized_transaction_traces(tx_info); + Ok(traces) + }, + ) + .await } } @@ -240,10 +253,10 @@ where /// Handler for `trace_replayTransaction` async fn replay_transaction( &self, - _transaction: H256, - _trace_types: HashSet, + transaction: H256, + trace_types: HashSet, ) -> Result { - Err(internal_rpc_err("unimplemented")) + Ok(TraceApi::replay_transaction(self, transaction, trace_types).await?) } /// Handler for `trace_block`