Skip to content

Commit

Permalink
fix: support of trace API
Browse files Browse the repository at this point in the history
  • Loading branch information
pythonberg1997 committed Jan 10, 2025
1 parent 833886f commit 875ac0e
Show file tree
Hide file tree
Showing 5 changed files with 260 additions and 34 deletions.
12 changes: 10 additions & 2 deletions crates/rpc/rpc-eth-api/src/helpers/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ pub trait Trace: LoadState<Evm: ConfigureEvm<Header = Header>> {
Self: LoadPendingBlock + LoadTransaction + Call,
F: FnOnce(
TransactionInfo,
u64, // tx gas limit
TracingInspector,
ResultAndState,
StateCacheDb<'_>,
Expand Down Expand Up @@ -168,6 +169,7 @@ pub trait Trace: LoadState<Evm: ConfigureEvm<Header = Header>> {
Self: LoadPendingBlock + LoadTransaction + Call,
F: FnOnce(
TransactionInfo,
u64, // tx gas limit
Insp,
ResultAndState,
StateCacheDb<'_>,
Expand Down Expand Up @@ -237,10 +239,11 @@ pub trait Trace: LoadState<Evm: ConfigureEvm<Header = Header>> {
tx_env
};

let tx_gas_limit = tx_env.gas_limit;
let env = EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, tx_env);
let (res, _) =
this.inspect(StateCacheDbRefMutWrapper(&mut db), env, &mut inspector)?;
f(tx_info, inspector, res, db)
f(tx_info, tx_gas_limit, inspector, res, db)
})
.await
.map(Some)
Expand All @@ -265,6 +268,7 @@ pub trait Trace: LoadState<Evm: ConfigureEvm<Header = Header>> {
Self: LoadBlock,
F: Fn(
TransactionInfo,
u64, // tx gas limit
TracingInspector,
ExecutionResult,
&EvmState,
Expand Down Expand Up @@ -305,6 +309,7 @@ pub trait Trace: LoadState<Evm: ConfigureEvm<Header = Header>> {
Self: LoadBlock,
F: Fn(
TransactionInfo,
u64, // tx gas limit
Insp,
ExecutionResult,
&EvmState,
Expand Down Expand Up @@ -388,14 +393,15 @@ pub trait Trace: LoadState<Evm: ConfigureEvm<Header = Header>> {
.peekable();

while let Some((tx_info, tx)) = transactions.next() {
let tx_gas_limit = tx.gas_limit;
let env =
EnvWithHandlerCfg::new_with_cfg_env(cfg.clone(), block_env.clone(), tx);

let mut inspector = inspector_setup();
let (res, _) =
this.inspect(StateCacheDbRefMutWrapper(&mut db), env, &mut inspector)?;
let ResultAndState { result, state } = res;
results.push(f(tx_info, inspector, result, &state, &db)?);
results.push(f(tx_info, tx_gas_limit, inspector, result, &state, &db)?);

// need to apply the state changes of this transaction before executing the
// next transaction, but only if there's a next transaction
Expand Down Expand Up @@ -434,6 +440,7 @@ pub trait Trace: LoadState<Evm: ConfigureEvm<Header = Header>> {
// state and db
F: Fn(
TransactionInfo,
u64, // tx gas limit
TracingInspector,
ExecutionResult,
&EvmState,
Expand Down Expand Up @@ -473,6 +480,7 @@ pub trait Trace: LoadState<Evm: ConfigureEvm<Header = Header>> {
// state and db
F: Fn(
TransactionInfo,
u64, // tx gas limit
Insp,
ExecutionResult,
&EvmState,
Expand Down
4 changes: 2 additions & 2 deletions crates/rpc/rpc/src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -543,11 +543,11 @@ where
.inner
.eth_api
.spawn_with_call_at(call, at, overrides, move |db, env| {
let (_res, env) =
this.eth_api().inspect(db, env, &mut inspector)?;
let (res, env) = this.eth_api().inspect(db, env, &mut inspector)?;
let tx_info = TransactionInfo::default();
let frame: FlatCallFrame = inspector
.with_transaction_gas_limit(env.tx.gas_limit)
.with_transaction_gas_used(res.result.gas_used())
.into_parity_builder()
.into_localized_transaction_traces(tx_info);
Ok(frame)
Expand Down
201 changes: 195 additions & 6 deletions crates/rpc/rpc/src/eth/helpers/trace.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,201 @@
//! Contains RPC handler implementations specific to tracing.
use reth_evm::ConfigureEvm;
use reth_primitives::Header;
use reth_rpc_eth_api::helpers::{LoadState, Trace};

use crate::EthApi;
use alloy_eips::BlockId;
use alloy_rpc_types_eth::TransactionInfo;
use reth_bsc_primitives::system_contracts::is_system_transaction;
use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks};
use reth_evm::{system_calls::SystemCaller, ConfigureEvm};
use reth_primitives::{Header, SealedBlockWithSenders};
use reth_revm::database::StateProviderDatabase;
use reth_rpc_eth_api::helpers::{LoadBlock, LoadState, SpawnBlocking, Trace};
use reth_rpc_eth_types::{
cache::db::{StateCacheDbRefMutWrapper, StateProviderTraitObjWrapper},
EthApiError, StateCacheDb,
};
use revm::{db::CacheDB, Inspector};
use revm_primitives::{
db::DatabaseCommit, EnvWithHandlerCfg, EvmState, ExecutionResult, ResultAndState,
};
use std::{future::Future, sync::Arc};

impl<Provider, Pool, Network, EvmConfig> Trace for EthApi<Provider, Pool, Network, EvmConfig> where
Self: LoadState<Evm: ConfigureEvm<Header = Header>>
impl<Provider, Pool, Network, EvmConfig> Trace for EthApi<Provider, Pool, Network, EvmConfig>
where
Self: LoadState<Evm: ConfigureEvm<Header = Header>>,
Provider: ChainSpecProvider<ChainSpec: EthChainSpec + EthereumHardforks>,
EvmConfig: ConfigureEvm<Header = Header>,
{
fn trace_block_until_with_inspector<Setup, Insp, F, R>(
&self,
block_id: BlockId,
block: Option<Arc<SealedBlockWithSenders>>,
highest_index: Option<u64>,
mut inspector_setup: Setup,
f: F,
) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
where
Self: LoadBlock,
F: Fn(
TransactionInfo,
u64, // tx gas limit
Insp,
ExecutionResult,
&EvmState,
&StateCacheDb<'_>,
) -> Result<R, Self::Error>
+ Send
+ 'static,
Setup: FnMut() -> Insp + Send + 'static,
Insp: for<'a, 'b> Inspector<StateCacheDbRefMutWrapper<'a, 'b>> + Send + 'static,
R: Send + 'static,
{
async move {
let block = async {
if block.is_some() {
return Ok(block)
}
self.block_with_senders(block_id).await
};

let ((cfg, block_env, _), block) =
futures::try_join!(self.evm_env_at(block_id), block)?;

let Some(block) = block else { return Ok(None) };

if block.body.transactions.is_empty() {
// nothing to trace
return Ok(Some(Vec::new()))
}

let parent_timestamp = self
.block_with_senders(block.parent_hash.into())
.await?
.ok_or(EthApiError::HeaderNotFound(block.parent_hash.into()))?
.timestamp;

// replay all transactions of the block
self.spawn_tracing(move |this| {
// 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 block_number = block_env.number.saturating_to::<u64>();
let base_fee = block_env.basefee.saturating_to::<u128>();

// now get the state
let state = this.state_at_block_id(state_at.into())?;
let mut db =
CacheDB::new(StateProviderDatabase::new(StateProviderTraitObjWrapper(&state)));

// apply relevant system calls
SystemCaller::new(this.evm_config().clone(), this.provider().chain_spec())
.pre_block_beacon_root_contract_call(
&mut db,
&cfg,
&block_env,
block.header().parent_beacon_block_root,
)
.map_err(|_| {
EthApiError::EvmCustom("failed to apply 4788 system call".to_string())
})?;

// prepare transactions, we do everything upfront to reduce time spent with open
// state
let max_transactions =
highest_index.map_or(block.body.transactions.len(), |highest| {
// we need + 1 because the index is 0-based
highest as usize + 1
});
let mut results = Vec::with_capacity(max_transactions);

let mut transactions = block
.transactions_with_sender()
.take(max_transactions)
.enumerate()
.map(|(idx, (signer, tx))| {
let tx_info = TransactionInfo {
hash: Some(tx.hash()),
index: Some(idx as u64),
block_hash: Some(block_hash),
block_number: Some(block_number),
base_fee: Some(base_fee),
};
(tx_info, signer, tx)
})
.peekable();

let is_bsc = this.bsc_trace_helper.is_some();
let mut before_system_tx = is_bsc;

// try to upgrade system contracts for bsc before all txs if feynman is not active
if is_bsc {
if let Some(trace_helper) = this.bsc_trace_helper.as_ref() {
trace_helper
.upgrade_system_contracts(&mut db, &block_env, parent_timestamp, true)
.map_err(|e| e.into())?;
}
}

while let Some((tx_info, sender, tx)) = transactions.next() {
// check if the transaction is a system transaction
// this should be done before return
if is_bsc &&
before_system_tx &&
is_system_transaction(tx, *sender, block_env.coinbase)
{
if let Some(trace_helper) = this.bsc_trace_helper.as_ref() {
// move block reward from the system address to the coinbase
trace_helper
.add_block_reward(&mut db, &block_env)
.map_err(|e| e.into())?;

// try to upgrade system contracts between normal txs and system txs
// if feynman is active
trace_helper
.upgrade_system_contracts(
&mut db,
&block_env,
parent_timestamp,
false,
)
.map_err(|e| e.into())?;
}

before_system_tx = false;
}

let tx_env = this.evm_config().tx_env(tx, *sender);
#[cfg(feature = "bsc")]
let tx_env = {
let mut tx_env = tx_env;
if !before_system_tx {
tx_env.bsc.is_system_transaction = Some(true);
};
tx_env
};

let tx_gas_limit = tx_env.gas_limit;
let env =
EnvWithHandlerCfg::new_with_cfg_env(cfg.clone(), block_env.clone(), tx_env);

let mut inspector = inspector_setup();
let (res, _) =
this.inspect(StateCacheDbRefMutWrapper(&mut db), env, &mut inspector)?;
let ResultAndState { result, state } = res;
results.push(f(tx_info, tx_gas_limit, inspector, result, &state, &db)?);

// need to apply the state changes of this transaction before executing the
// next transaction, but only if there's a next transaction
if transactions.peek().is_some() {
// commit the state changes to the DB
db.commit(state)
}
}

Ok(Some(results))
})
.await
}
}
}
8 changes: 5 additions & 3 deletions crates/rpc/rpc/src/otterscan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ where
.spawn_trace_transaction_in_block_with_inspector(
tx_hash,
TransferInspector::new(false),
|_tx_info, inspector, _, _| Ok(inspector.into_transfers()),
|_tx_info, _tx_gas_limit, inspector, _, _| Ok(inspector.into_transfers()),
)
.await
.map_err(Into::into)?
Expand Down Expand Up @@ -139,7 +139,9 @@ where
.spawn_trace_transaction_in_block(
tx_hash,
TracingInspectorConfig::default_parity(),
move |_tx_info, inspector, _, _| Ok(inspector.into_traces().into_nodes()),
move |_tx_info, _tx_gas_limit, inspector, _, _| {
Ok(inspector.into_traces().into_nodes())
},
)
.await
.map_err(Into::into)?
Expand Down Expand Up @@ -336,7 +338,7 @@ where
num.into(),
None,
TracingInspectorConfig::default_parity(),
|tx_info, inspector, _, _, _| {
|tx_info, _, inspector, _, _, _| {
Ok(inspector.into_parity_builder().into_localized_transaction_traces(tx_info))
},
)
Expand Down
Loading

0 comments on commit 875ac0e

Please sign in to comment.