Skip to content

Commit bae0593

Browse files
fix: support of trace API
1 parent 14f6308 commit bae0593

File tree

5 files changed

+260
-34
lines changed

5 files changed

+260
-34
lines changed

crates/rpc/rpc-eth-api/src/helpers/trace.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ pub trait Trace: LoadState<Evm: ConfigureEvm<Header = Header>> {
138138
Self: LoadPendingBlock + LoadTransaction + Call,
139139
F: FnOnce(
140140
TransactionInfo,
141+
u64, // tx gas limit
141142
TracingInspector,
142143
ResultAndState,
143144
StateCacheDb<'_>,
@@ -168,6 +169,7 @@ pub trait Trace: LoadState<Evm: ConfigureEvm<Header = Header>> {
168169
Self: LoadPendingBlock + LoadTransaction + Call,
169170
F: FnOnce(
170171
TransactionInfo,
172+
u64, // tx gas limit
171173
Insp,
172174
ResultAndState,
173175
StateCacheDb<'_>,
@@ -237,10 +239,11 @@ pub trait Trace: LoadState<Evm: ConfigureEvm<Header = Header>> {
237239
tx_env
238240
};
239241

242+
let tx_gas_limit = tx_env.gas_limit;
240243
let env = EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, tx_env);
241244
let (res, _) =
242245
this.inspect(StateCacheDbRefMutWrapper(&mut db), env, &mut inspector)?;
243-
f(tx_info, inspector, res, db)
246+
f(tx_info, tx_gas_limit, inspector, res, db)
244247
})
245248
.await
246249
.map(Some)
@@ -265,6 +268,7 @@ pub trait Trace: LoadState<Evm: ConfigureEvm<Header = Header>> {
265268
Self: LoadBlock,
266269
F: Fn(
267270
TransactionInfo,
271+
u64, // tx gas limit
268272
TracingInspector,
269273
ExecutionResult,
270274
&EvmState,
@@ -305,6 +309,7 @@ pub trait Trace: LoadState<Evm: ConfigureEvm<Header = Header>> {
305309
Self: LoadBlock,
306310
F: Fn(
307311
TransactionInfo,
312+
u64, // tx gas limit
308313
Insp,
309314
ExecutionResult,
310315
&EvmState,
@@ -388,14 +393,15 @@ pub trait Trace: LoadState<Evm: ConfigureEvm<Header = Header>> {
388393
.peekable();
389394

390395
while let Some((tx_info, tx)) = transactions.next() {
396+
let tx_gas_limit = tx.gas_limit;
391397
let env =
392398
EnvWithHandlerCfg::new_with_cfg_env(cfg.clone(), block_env.clone(), tx);
393399

394400
let mut inspector = inspector_setup();
395401
let (res, _) =
396402
this.inspect(StateCacheDbRefMutWrapper(&mut db), env, &mut inspector)?;
397403
let ResultAndState { result, state } = res;
398-
results.push(f(tx_info, inspector, result, &state, &db)?);
404+
results.push(f(tx_info, tx_gas_limit, inspector, result, &state, &db)?);
399405

400406
// need to apply the state changes of this transaction before executing the
401407
// next transaction, but only if there's a next transaction
@@ -434,6 +440,7 @@ pub trait Trace: LoadState<Evm: ConfigureEvm<Header = Header>> {
434440
// state and db
435441
F: Fn(
436442
TransactionInfo,
443+
u64, // tx gas limit
437444
TracingInspector,
438445
ExecutionResult,
439446
&EvmState,
@@ -473,6 +480,7 @@ pub trait Trace: LoadState<Evm: ConfigureEvm<Header = Header>> {
473480
// state and db
474481
F: Fn(
475482
TransactionInfo,
483+
u64, // tx gas limit
476484
Insp,
477485
ExecutionResult,
478486
&EvmState,

crates/rpc/rpc/src/debug.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -543,11 +543,11 @@ where
543543
.inner
544544
.eth_api
545545
.spawn_with_call_at(call, at, overrides, move |db, env| {
546-
let (_res, env) =
547-
this.eth_api().inspect(db, env, &mut inspector)?;
546+
let (res, env) = this.eth_api().inspect(db, env, &mut inspector)?;
548547
let tx_info = TransactionInfo::default();
549548
let frame: FlatCallFrame = inspector
550549
.with_transaction_gas_limit(env.tx.gas_limit)
550+
.with_transaction_gas_used(res.result.gas_used())
551551
.into_parity_builder()
552552
.into_localized_transaction_traces(tx_info);
553553
Ok(frame)
Lines changed: 195 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,201 @@
11
//! Contains RPC handler implementations specific to tracing.
22
3-
use reth_evm::ConfigureEvm;
4-
use reth_primitives::Header;
5-
use reth_rpc_eth_api::helpers::{LoadState, Trace};
6-
73
use crate::EthApi;
4+
use alloy_eips::BlockId;
5+
use alloy_rpc_types_eth::TransactionInfo;
6+
use reth_bsc_primitives::system_contracts::is_system_transaction;
7+
use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks};
8+
use reth_evm::{system_calls::SystemCaller, ConfigureEvm};
9+
use reth_primitives::{Header, SealedBlockWithSenders};
10+
use reth_revm::database::StateProviderDatabase;
11+
use reth_rpc_eth_api::helpers::{LoadBlock, LoadState, SpawnBlocking, Trace};
12+
use reth_rpc_eth_types::{
13+
cache::db::{StateCacheDbRefMutWrapper, StateProviderTraitObjWrapper},
14+
EthApiError, StateCacheDb,
15+
};
16+
use revm::{db::CacheDB, Inspector};
17+
use revm_primitives::{
18+
db::DatabaseCommit, EnvWithHandlerCfg, EvmState, ExecutionResult, ResultAndState,
19+
};
20+
use std::{future::Future, sync::Arc};
821

9-
impl<Provider, Pool, Network, EvmConfig> Trace for EthApi<Provider, Pool, Network, EvmConfig> where
10-
Self: LoadState<Evm: ConfigureEvm<Header = Header>>
22+
impl<Provider, Pool, Network, EvmConfig> Trace for EthApi<Provider, Pool, Network, EvmConfig>
23+
where
24+
Self: LoadState<Evm: ConfigureEvm<Header = Header>>,
25+
Provider: ChainSpecProvider<ChainSpec: EthChainSpec + EthereumHardforks>,
26+
EvmConfig: ConfigureEvm<Header = Header>,
1127
{
28+
fn trace_block_until_with_inspector<Setup, Insp, F, R>(
29+
&self,
30+
block_id: BlockId,
31+
block: Option<Arc<SealedBlockWithSenders>>,
32+
highest_index: Option<u64>,
33+
mut inspector_setup: Setup,
34+
f: F,
35+
) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
36+
where
37+
Self: LoadBlock,
38+
F: Fn(
39+
TransactionInfo,
40+
u64, // tx gas limit
41+
Insp,
42+
ExecutionResult,
43+
&EvmState,
44+
&StateCacheDb<'_>,
45+
) -> Result<R, Self::Error>
46+
+ Send
47+
+ 'static,
48+
Setup: FnMut() -> Insp + Send + 'static,
49+
Insp: for<'a, 'b> Inspector<StateCacheDbRefMutWrapper<'a, 'b>> + Send + 'static,
50+
R: Send + 'static,
51+
{
52+
async move {
53+
let block = async {
54+
if block.is_some() {
55+
return Ok(block)
56+
}
57+
self.block_with_senders(block_id).await
58+
};
59+
60+
let ((cfg, block_env, _), block) =
61+
futures::try_join!(self.evm_env_at(block_id), block)?;
62+
63+
let Some(block) = block else { return Ok(None) };
64+
65+
if block.body.transactions.is_empty() {
66+
// nothing to trace
67+
return Ok(Some(Vec::new()))
68+
}
69+
70+
let parent_timestamp = self
71+
.block_with_senders(block.parent_hash.into())
72+
.await?
73+
.ok_or(EthApiError::HeaderNotFound(block.parent_hash.into()))?
74+
.timestamp;
75+
76+
// replay all transactions of the block
77+
self.spawn_tracing(move |this| {
78+
// we need to get the state of the parent block because we're replaying this block
79+
// on top of its parent block's state
80+
let state_at = block.parent_hash;
81+
let block_hash = block.hash();
82+
83+
let block_number = block_env.number.saturating_to::<u64>();
84+
let base_fee = block_env.basefee.saturating_to::<u128>();
85+
86+
// now get the state
87+
let state = this.state_at_block_id(state_at.into())?;
88+
let mut db =
89+
CacheDB::new(StateProviderDatabase::new(StateProviderTraitObjWrapper(&state)));
90+
91+
// apply relevant system calls
92+
SystemCaller::new(this.evm_config().clone(), this.provider().chain_spec())
93+
.pre_block_beacon_root_contract_call(
94+
&mut db,
95+
&cfg,
96+
&block_env,
97+
block.header().parent_beacon_block_root,
98+
)
99+
.map_err(|_| {
100+
EthApiError::EvmCustom("failed to apply 4788 system call".to_string())
101+
})?;
102+
103+
// prepare transactions, we do everything upfront to reduce time spent with open
104+
// state
105+
let max_transactions =
106+
highest_index.map_or(block.body.transactions.len(), |highest| {
107+
// we need + 1 because the index is 0-based
108+
highest as usize + 1
109+
});
110+
let mut results = Vec::with_capacity(max_transactions);
111+
112+
let mut transactions = block
113+
.transactions_with_sender()
114+
.take(max_transactions)
115+
.enumerate()
116+
.map(|(idx, (signer, tx))| {
117+
let tx_info = TransactionInfo {
118+
hash: Some(tx.hash()),
119+
index: Some(idx as u64),
120+
block_hash: Some(block_hash),
121+
block_number: Some(block_number),
122+
base_fee: Some(base_fee),
123+
};
124+
(tx_info, signer, tx)
125+
})
126+
.peekable();
127+
128+
let is_bsc = this.bsc_trace_helper.is_some();
129+
let mut before_system_tx = is_bsc;
130+
131+
// try to upgrade system contracts for bsc before all txs if feynman is not active
132+
if is_bsc {
133+
if let Some(trace_helper) = this.bsc_trace_helper.as_ref() {
134+
trace_helper
135+
.upgrade_system_contracts(&mut db, &block_env, parent_timestamp, true)
136+
.map_err(|e| e.into())?;
137+
}
138+
}
139+
140+
while let Some((tx_info, sender, tx)) = transactions.next() {
141+
// check if the transaction is a system transaction
142+
// this should be done before return
143+
if is_bsc &&
144+
before_system_tx &&
145+
is_system_transaction(tx, *sender, block_env.coinbase)
146+
{
147+
if let Some(trace_helper) = this.bsc_trace_helper.as_ref() {
148+
// move block reward from the system address to the coinbase
149+
trace_helper
150+
.add_block_reward(&mut db, &block_env)
151+
.map_err(|e| e.into())?;
152+
153+
// try to upgrade system contracts between normal txs and system txs
154+
// if feynman is active
155+
trace_helper
156+
.upgrade_system_contracts(
157+
&mut db,
158+
&block_env,
159+
parent_timestamp,
160+
false,
161+
)
162+
.map_err(|e| e.into())?;
163+
}
164+
165+
before_system_tx = false;
166+
}
167+
168+
let tx_env = this.evm_config().tx_env(tx, *sender);
169+
#[cfg(feature = "bsc")]
170+
let tx_env = {
171+
let mut tx_env = tx_env;
172+
if !before_system_tx {
173+
tx_env.bsc.is_system_transaction = Some(true);
174+
};
175+
tx_env
176+
};
177+
178+
let tx_gas_limit = tx_env.gas_limit;
179+
let env =
180+
EnvWithHandlerCfg::new_with_cfg_env(cfg.clone(), block_env.clone(), tx_env);
181+
182+
let mut inspector = inspector_setup();
183+
let (res, _) =
184+
this.inspect(StateCacheDbRefMutWrapper(&mut db), env, &mut inspector)?;
185+
let ResultAndState { result, state } = res;
186+
results.push(f(tx_info, tx_gas_limit, inspector, result, &state, &db)?);
187+
188+
// need to apply the state changes of this transaction before executing the
189+
// next transaction, but only if there's a next transaction
190+
if transactions.peek().is_some() {
191+
// commit the state changes to the DB
192+
db.commit(state)
193+
}
194+
}
195+
196+
Ok(Some(results))
197+
})
198+
.await
199+
}
200+
}
12201
}

crates/rpc/rpc/src/otterscan.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ where
9393
.spawn_trace_transaction_in_block_with_inspector(
9494
tx_hash,
9595
TransferInspector::new(false),
96-
|_tx_info, inspector, _, _| Ok(inspector.into_transfers()),
96+
|_tx_info, _tx_gas_limit, inspector, _, _| Ok(inspector.into_transfers()),
9797
)
9898
.await
9999
.map_err(Into::into)?
@@ -139,7 +139,9 @@ where
139139
.spawn_trace_transaction_in_block(
140140
tx_hash,
141141
TracingInspectorConfig::default_parity(),
142-
move |_tx_info, inspector, _, _| Ok(inspector.into_traces().into_nodes()),
142+
move |_tx_info, _tx_gas_limit, inspector, _, _| {
143+
Ok(inspector.into_traces().into_nodes())
144+
},
143145
)
144146
.await
145147
.map_err(Into::into)?
@@ -336,7 +338,7 @@ where
336338
num.into(),
337339
None,
338340
TracingInspectorConfig::default_parity(),
339-
|tx_info, inspector, _, _, _| {
341+
|tx_info, _, inspector, _, _, _| {
340342
Ok(inspector.into_parity_builder().into_localized_transaction_traces(tx_info))
341343
},
342344
)

0 commit comments

Comments
 (0)