feat(rpc): add replay transaction (#2107)

Co-authored-by: Dan Cline <6798349+Rjected@users.noreply.github.com>
This commit is contained in:
Matthias Seitz
2023-04-04 21:39:07 +02:00
committed by GitHub
parent a49dca0ce6
commit cf5a1114d0
3 changed files with 71 additions and 26 deletions

View File

@ -161,6 +161,10 @@ where
.await .await
.unwrap_err(); .unwrap_err();
TraceApiClient::trace_call_many(client, vec![], None).await.err().unwrap(); TraceApiClient::trace_call_many(client, vec![], None).await.err().unwrap();
TraceApiClient::replay_transaction(client, H256::default(), HashSet::default())
.await
.err()
.unwrap();
assert!(is_unimplemented( assert!(is_unimplemented(
TraceApiClient::replay_block_transactions(client, block_id, HashSet::default()) TraceApiClient::replay_block_transactions(client, block_id, HashSet::default())
@ -168,12 +172,6 @@ where
.err() .err()
.unwrap() .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_block(client, block_id).await.err().unwrap()));
assert!(is_unimplemented( assert!(is_unimplemented(
TraceApiClient::trace_filter(client, trace_filter).await.err().unwrap() TraceApiClient::trace_filter(client, trace_filter).await.err().unwrap()

View File

@ -19,6 +19,7 @@ use reth_primitives::{
use reth_provider::{BlockProvider, EvmEnvProvider, StateProviderBox, StateProviderFactory}; use reth_provider::{BlockProvider, EvmEnvProvider, StateProviderBox, StateProviderFactory};
use reth_revm::{ use reth_revm::{
database::{State, SubState}, database::{State, SubState},
env::tx_env_with_recovered,
tracing::{TracingInspector, TracingInspectorConfig}, tracing::{TracingInspector, TracingInspectorConfig},
}; };
use reth_rpc_types::{ use reth_rpc_types::{
@ -121,6 +122,16 @@ pub trait EthTransactions: Send + Sync {
) -> EthResult<R> ) -> EthResult<R>
where where
F: FnOnce(TracingInspector, ResultAndState) -> EthResult<R>; F: FnOnce(TracingInspector, ResultAndState) -> EthResult<R>;
/// Retrieves the transaction if it exists and returns its trace
async fn trace_transaction<F, R>(
&self,
hash: H256,
config: TracingInspectorConfig,
f: F,
) -> EthResult<Option<R>>
where
F: FnOnce(TransactionInfo, TracingInspector, ResultAndState) -> EthResult<R> + Send;
} }
#[async_trait] #[async_trait]
@ -376,6 +387,29 @@ where
f(inspector, res) f(inspector, res)
}) })
} }
async fn trace_transaction<F, R>(
&self,
hash: H256,
config: TracingInspectorConfig,
f: F,
) -> EthResult<Option<R>>
where
F: FnOnce(TransactionInfo, TracingInspector, ResultAndState) -> EthResult<R> + 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 === // === impl EthApi ===

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
eth::{ eth::{
cache::EthStateCache, cache::EthStateCache,
error::EthResult, error::{EthApiError, EthResult},
revm_utils::{inspect, prepare_call_env}, revm_utils::{inspect, prepare_call_env},
utils::recover_raw_transaction, utils::recover_raw_transaction,
EthTransactions, EthTransactions,
@ -147,6 +147,24 @@ where
}) })
} }
/// Replays a transaction, returning the traces.
pub async fn replay_transaction(
&self,
hash: H256,
trace_types: HashSet<TraceType>,
) -> EthResult<TraceResults> {
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. /// Returns transaction trace with the given address.
pub async fn trace_get( pub async fn trace_get(
&self, &self,
@ -172,22 +190,17 @@ where
) -> EthResult<Option<Vec<LocalizedTransactionTrace>>> { ) -> EthResult<Option<Vec<LocalizedTransactionTrace>>> {
let _permit = self.acquire_trace_permit().await; let _permit = self.acquire_trace_permit().await;
let (transaction, at) = match self.eth_api.transaction_by_hash_at(hash).await? { self.eth_api
None => return Ok(None), .trace_transaction(
Some(res) => res, hash,
}; TracingInspectorConfig::default_parity(),
|tx_info, inspector, _| {
let (cfg, block, at) = self.eth_api.evm_env_at(at).await?; let traces =
inspector.into_parity_builder().into_localized_transaction_traces(tx_info);
let (tx, tx_info) = transaction.split(); Ok(traces)
let tx = tx_env_with_recovered(&tx); },
let env = Env { cfg, block, tx }; )
.await
// 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))
})
} }
} }
@ -240,10 +253,10 @@ where
/// Handler for `trace_replayTransaction` /// Handler for `trace_replayTransaction`
async fn replay_transaction( async fn replay_transaction(
&self, &self,
_transaction: H256, transaction: H256,
_trace_types: HashSet<TraceType>, trace_types: HashSet<TraceType>,
) -> Result<TraceResults> { ) -> Result<TraceResults> {
Err(internal_rpc_err("unimplemented")) Ok(TraceApi::replay_transaction(self, transaction, trace_types).await?)
} }
/// Handler for `trace_block` /// Handler for `trace_block`