From f2da01fd88c8518196733eb6ea79b0c09dd0029d Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 5 Mar 2024 21:52:40 +0100 Subject: [PATCH] feat: support any Inspector for block + tx tracing (#6978) Co-authored-by: Dan Cline <6798349+Rjected@users.noreply.github.com> --- crates/rpc/rpc/src/eth/api/transactions.rs | 159 ++++++++++++++++----- 1 file changed, 123 insertions(+), 36 deletions(-) diff --git a/crates/rpc/rpc/src/eth/api/transactions.rs b/crates/rpc/rpc/src/eth/api/transactions.rs index bb664e6db2..6944caaf05 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -287,6 +287,32 @@ pub trait EthTransactions: Send + Sync { F: FnOnce(TransactionInfo, TracingInspector, ResultAndState, StateCacheDB) -> EthResult + Send + 'static, + R: Send + 'static, + { + self.spawn_trace_transaction_in_block_with_inspector(hash, TracingInspector::new(config), f) + .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_trace_transaction_in_block_with_inspector( + &self, + hash: B256, + inspector: Insp, + f: F, + ) -> EthResult> + where + F: FnOnce(TransactionInfo, Insp, ResultAndState, StateCacheDB) -> EthResult + + Send + + 'static, + Insp: for<'a> Inspector<&'a mut StateCacheDB> + Send + 'static, R: Send + 'static; /// Executes all transactions of a block and returns a list of callback results invoked for each @@ -306,17 +332,60 @@ pub trait EthTransactions: Send + Sync { f: F, ) -> EthResult>> where - // This is the callback that's invoked for each transaction with + // This is the callback that's invoked for each transaction with the inspector, the result, + // state and db F: for<'a> Fn( TransactionInfo, TracingInspector, ExecutionResult, &'a State, - &'a CacheDB>, + &'a StateCacheDB, ) -> EthResult + Send + 'static, - R: Send + 'static; + R: Send + 'static, + { + self.trace_block_until(block_id, None, config, f).await + } + + /// Executes all transactions of a block and returns a list of callback results invoked for each + /// transaction in the block. + /// + /// This + /// 1. fetches all transactions of the block + /// 2. configures the EVM evn + /// 3. loops over all transactions and executes them + /// 4. calls the callback with the transaction info, the execution result, the changed state + /// _after_ the transaction [State] and the database that points to the state + /// right _before_ the transaction, in other words the state the transaction was + /// executed on: `changed_state = tx(cached_state)` + /// + /// This accepts a `inspector_setup` closure that returns the inspector to be used for tracing + /// a transaction. This is invoked for each transaction. + async fn trace_block_with_inspector( + &self, + block_id: BlockId, + insp_setup: Setup, + f: F, + ) -> EthResult>> + where + // This is the callback that's invoked for each transaction with the inspector, the result, + // state and db + F: for<'a> Fn( + TransactionInfo, + Insp, + ExecutionResult, + &'a State, + &'a StateCacheDB, + ) -> EthResult + + Send + + 'static, + Setup: FnMut() -> Insp + Send + 'static, + Insp: for<'a> Inspector<&'a mut StateCacheDB> + Send + 'static, + R: Send + 'static, + { + self.trace_block_until_with_inspector(block_id, None, insp_setup, f).await + } /// Executes all transactions of a block. /// @@ -336,10 +405,48 @@ pub trait EthTransactions: Send + Sync { TracingInspector, ExecutionResult, &'a State, - &'a CacheDB>, + &'a StateCacheDB, ) -> EthResult + Send + 'static, + R: Send + 'static, + { + self.trace_block_until_with_inspector( + block_id, + highest_index, + move || TracingInspector::new(config), + f, + ) + .await + } + + /// Executes all transactions of a block. + /// + /// If a `highest_index` is given, this will only execute the first `highest_index` + /// transactions, in other words, it will stop executing transactions after the + /// `highest_index`th transaction. + /// + /// This accepts a `inspector_setup` closure that returns the inspector to be used for tracing + /// the transactions. + async fn trace_block_until_with_inspector( + &self, + block_id: BlockId, + highest_index: Option, + inspector_setup: Setup, + f: F, + ) -> EthResult>> + where + F: for<'a> Fn( + TransactionInfo, + Insp, + ExecutionResult, + &'a State, + &'a StateCacheDB, + ) -> EthResult + + Send + + 'static, + Setup: FnMut() -> Insp + Send + 'static, + Insp: for<'a> Inspector<&'a mut StateCacheDB> + Send + 'static, R: Send + 'static; } @@ -883,16 +990,17 @@ where Ok(block.map(|block| (transaction, block.seal(block_hash)))) } - async fn spawn_trace_transaction_in_block( + async fn spawn_trace_transaction_in_block_with_inspector( &self, hash: B256, - config: TracingInspectorConfig, + mut inspector: Insp, f: F, ) -> EthResult> where - F: FnOnce(TransactionInfo, TracingInspector, ResultAndState, StateCacheDB) -> EthResult + F: FnOnce(TransactionInfo, Insp, ResultAndState, StateCacheDB) -> EthResult + Send + 'static, + Insp: for<'a> Inspector<&'a mut StateCacheDB> + Send + 'static, R: Send + 'static, { let (transaction, block) = match self.transaction_and_block(hash).await? { @@ -917,53 +1025,32 @@ where let env = EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, tx_env_with_recovered(&tx)); - let mut inspector = TracingInspector::new(config); - let (res, _, db) = inspect_and_return_db(db, env, &mut inspector)?; + let (res, _) = inspect(&mut db, env, &mut inspector)?; f(tx_info, inspector, res, db) }) .await .map(Some) } - async fn trace_block_with( - &self, - block_id: BlockId, - config: TracingInspectorConfig, - f: F, - ) -> EthResult>> - where - // This is the callback that's invoked for each transaction with - F: for<'a> Fn( - TransactionInfo, - TracingInspector, - ExecutionResult, - &'a State, - &'a CacheDB>, - ) -> EthResult - + Send - + 'static, - R: Send + 'static, - { - self.trace_block_until(block_id, None, config, f).await - } - - async fn trace_block_until( + async fn trace_block_until_with_inspector( &self, block_id: BlockId, highest_index: Option, - config: TracingInspectorConfig, + mut inspector_setup: Setup, f: F, ) -> EthResult>> where F: for<'a> Fn( TransactionInfo, - TracingInspector, + Insp, ExecutionResult, &'a State, - &'a CacheDB>, + &'a StateCacheDB, ) -> EthResult + Send + 'static, + Setup: FnMut() -> Insp + Send + 'static, + Insp: for<'a> Inspector<&'a mut StateCacheDB> + Send + 'static, R: Send + 'static, { let ((cfg, block_env, _), block) = @@ -1010,7 +1097,7 @@ where while let Some((tx_info, tx)) = transactions.next() { let env = EnvWithHandlerCfg::new_with_cfg_env(cfg.clone(), block_env.clone(), tx); - let mut inspector = TracingInspector::new(config); + let mut inspector = inspector_setup(); let (res, _) = inspect(&mut db, env, &mut inspector)?; let ResultAndState { result, state } = res; results.push(f(tx_info, inspector, result, &state, &db)?);