Skip to content
This repository has been archived by the owner on Nov 5, 2023. It is now read-only.

Commit

Permalink
feat(rpc): move trace_block_with and trace_block_until to `EthTra…
Browse files Browse the repository at this point in the history
…nsactions` (paradigmxyz#5021)
  • Loading branch information
tcoratger authored Oct 14, 2023
1 parent 48cf69d commit 3bf2c88
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 148 deletions.
161 changes: 158 additions & 3 deletions crates/rpc/rpc/src/eth/api/transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ use reth_revm::{
tracing::{TracingInspector, TracingInspectorConfig},
};
use reth_rpc_types::{
CallRequest, Index, Log, Transaction, TransactionInfo, TransactionReceipt, TransactionRequest,
TypedTransactionRequest,
BlockError, CallRequest, Index, Log, Transaction, TransactionInfo, TransactionReceipt,
TransactionRequest, TypedTransactionRequest,
};
use reth_rpc_types_compat::from_recovered_with_block_context;
use reth_transaction_pool::{TransactionOrigin, TransactionPool};
Expand All @@ -39,7 +39,10 @@ use revm::{
primitives::{BlockEnv, CfgEnv},
Inspector,
};
use revm_primitives::{utilities::create_address, Env, ResultAndState, SpecId};
use revm_primitives::{
db::DatabaseCommit, utilities::create_address, Env, ExecutionResult, ResultAndState, SpecId,
State,
};

/// Helper alias type for the state's [CacheDB]
pub(crate) type StateCacheDB<'r> = CacheDB<StateProviderDatabase<StateProviderBox<'r>>>;
Expand Down Expand Up @@ -236,6 +239,59 @@ pub trait EthTransactions: Send + Sync {
+ Send
+ 'static,
R: Send + 'static;

/// 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 [StateProviderDatabase] and the database that points to the state
/// right _before_ the transaction.
async fn trace_block_with<F, R>(
&self,
block_id: BlockId,
config: TracingInspectorConfig,
f: F,
) -> EthResult<Option<Vec<R>>>
where
// This is the callback that's invoked for each transaction with
F: for<'a> Fn(
TransactionInfo,
TracingInspector,
ExecutionResult,
&'a State,
&'a CacheDB<StateProviderDatabase<StateProviderBox<'a>>>,
) -> EthResult<R>
+ Send
+ 'static,
R: Send + 'static;

/// 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.
async fn trace_block_until<F, R>(
&self,
block_id: BlockId,
highest_index: Option<u64>,
config: TracingInspectorConfig,
f: F,
) -> EthResult<Option<Vec<R>>>
where
F: for<'a> Fn(
TransactionInfo,
TracingInspector,
ExecutionResult,
&'a State,
&'a CacheDB<StateProviderDatabase<StateProviderBox<'a>>>,
) -> EthResult<R>
+ Send
+ 'static,
R: Send + 'static;
}

#[async_trait]
Expand Down Expand Up @@ -687,6 +743,105 @@ where
.await
.map(Some)
}

async fn trace_block_with<F, R>(
&self,
block_id: BlockId,
config: TracingInspectorConfig,
f: F,
) -> EthResult<Option<Vec<R>>>
where
// This is the callback that's invoked for each transaction with
F: for<'a> Fn(
TransactionInfo,
TracingInspector,
ExecutionResult,
&'a State,
&'a CacheDB<StateProviderDatabase<StateProviderBox<'a>>>,
) -> EthResult<R>
+ Send
+ 'static,
R: Send + 'static,
{
self.trace_block_until(block_id, None, config, f).await
}

async fn trace_block_until<F, R>(
&self,
block_id: BlockId,
highest_index: Option<u64>,
config: TracingInspectorConfig,
f: F,
) -> EthResult<Option<Vec<R>>>
where
F: for<'a> Fn(
TransactionInfo,
TracingInspector,
ExecutionResult,
&'a State,
&'a CacheDB<StateProviderDatabase<StateProviderBox<'a>>>,
) -> EthResult<R>
+ Send
+ 'static,
R: Send + 'static,
{
let ((cfg, block_env, _), block) =
futures::try_join!(self.evm_env_at(block_id), self.block_by_id(block_id),)?;

let block = match block {
Some(block) => block,
None => return Ok(None),
};

// we need to get the state of the parent block because we're replaying this block on top of
// its parent block's state
let state_at = block.parent_hash;

let block_hash = block.hash;
let transactions = block.body;

// replay all transactions of the block
self.spawn_with_state_at_block(state_at.into(), move |state| {
let mut results = Vec::with_capacity(transactions.len());
let mut db = CacheDB::new(StateProviderDatabase::new(state));

let max_transactions =
highest_index.map_or(transactions.len(), |highest| highest as usize);
let mut transactions =
transactions.into_iter().take(max_transactions).enumerate().peekable();

while let Some((idx, tx)) = transactions.next() {
let tx = tx.into_ecrecovered().ok_or(BlockError::InvalidSignature)?;
let tx_info = TransactionInfo {
hash: Some(tx.hash()),
index: Some(idx as u64),
block_hash: Some(block_hash),
block_number: Some(block_env.number.try_into().unwrap_or(u64::MAX)),
base_fee: Some(block_env.basefee.try_into().unwrap_or(u64::MAX)),
};

let tx = tx_env_with_recovered(&tx);
let env = Env { cfg: cfg.clone(), block: block_env.clone(), tx };

let mut inspector = TracingInspector::new(config);
let (res, _) = inspect(&mut db, env, &mut inspector)?;
let ResultAndState { result, state } = res;
results.push(f(tx_info, inspector, result, &state, &db)?);

// need to apply the state changes of this transaction before executing the
// next transaction
if transactions.peek().is_some() {
// need to apply the state changes of this transaction before executing
// the next transaction
db.commit(state)
}
}

Ok(results)
})
.await
.map(Some)
}
}

// === impl EthApi ===
Expand Down
Loading

0 comments on commit 3bf2c88

Please sign in to comment.