feat: add spawn_replay_transaction to EthTransactions (#8036)

Co-authored-by: Oliver Nordbjerg <onbjerg@users.noreply.github.com>
This commit is contained in:
Daniel Ramirez
2024-05-02 10:30:04 -04:00
committed by GitHub
parent 2eee1920ea
commit 78f62dd34c
2 changed files with 59 additions and 9 deletions

View File

@ -376,6 +376,20 @@ pub trait EthTransactions: Send + Sync {
.await
}
/// 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
/// state by executing them first.
/// The callback `f` is invoked with the [ResultAndState] after the transaction was executed and
/// the database that points to the beginning of the transaction.
///
/// Note: Implementers should use a threadpool where blocking is allowed, such as
/// [BlockingTaskPool](reth_tasks::pool::BlockingTaskPool).
async fn spawn_replay_transaction<F, R>(&self, hash: B256, f: F) -> EthResult<Option<R>>
where
F: FnOnce(TransactionInfo, ResultAndState, StateCacheDB) -> EthResult<R> + Send + 'static,
R: Send + 'static;
/// 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
@ -1173,6 +1187,47 @@ where
Ok(block.map(|block| (transaction, block.seal(block_hash))))
}
async fn spawn_replay_transaction<F, R>(&self, hash: B256, f: F) -> EthResult<Option<R>>
where
F: FnOnce(TransactionInfo, ResultAndState, StateCacheDB) -> EthResult<R> + Send + 'static,
R: Send + 'static,
{
let (transaction, block) = match self.transaction_and_block(hash).await? {
None => return Ok(None),
Some(res) => res,
};
let (tx, tx_info) = transaction.split();
let (cfg, block_env, _) = self.evm_env_at(block.hash().into()).await?;
// 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.into_transactions_ecrecovered();
let this = self.clone();
self.spawn_with_state_at_block(parent_block.into(), move |state| {
let mut db = CacheDB::new(StateProviderDatabase::new(state));
// replay all transactions prior to the targeted transaction
this.replay_transactions_until(
&mut db,
cfg.clone(),
block_env.clone(),
block_txs,
tx.hash,
)?;
let env =
EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, tx_env_with_recovered(&tx));
let (res, _) = this.transact(&mut db, env)?;
f(tx_info, res, db)
})
.await
.map(Some)
}
async fn spawn_trace_transaction_in_block_with_inspector<Insp, F, R>(
&self,
hash: B256,

View File

@ -1,7 +1,6 @@
use alloy_primitives::Bytes;
use async_trait::async_trait;
use jsonrpsee::core::RpcResult;
use revm::inspectors::NoOpInspector;
use revm_inspectors::transfer::{TransferInspector, TransferKind};
use revm_primitives::ExecutionResult;
@ -81,14 +80,10 @@ where
async fn get_transaction_error(&self, tx_hash: TxHash) -> RpcResult<Option<Bytes>> {
let maybe_revert = self
.eth
.spawn_trace_transaction_in_block_with_inspector(
tx_hash,
NoOpInspector,
|_tx_info, _inspector, res, _| match res.result {
ExecutionResult::Revert { output, .. } => Ok(Some(output)),
_ => Ok(None),
},
)
.spawn_replay_transaction(tx_hash, |_tx_info, res, _| match res.result {
ExecutionResult::Revert { output, .. } => Ok(Some(output)),
_ => Ok(None),
})
.await
.map(Option::flatten)?;
Ok(maybe_revert)