1use super::{Call, LoadBlock, LoadPendingBlock, LoadState, LoadTransaction};
4use crate::FromEvmError;
5use alloy_consensus::BlockHeader;
6use alloy_primitives::B256;
7use alloy_rpc_types_eth::{BlockId, TransactionInfo};
8use futures::Future;
9use reth_chainspec::ChainSpecProvider;
10use reth_errors::ProviderError;
11use reth_evm::{
12 system_calls::SystemCaller, ConfigureEvm, Database, Evm, EvmEnvFor, HaltReasonFor,
13 InspectorFor, TxEnvFor,
14};
15use reth_node_api::NodePrimitives;
16use reth_primitives_traits::{BlockBody, RecoveredBlock, SignedTransaction};
17use reth_revm::{database::StateProviderDatabase, db::CacheDB};
18use reth_rpc_eth_types::{
19 cache::db::{StateCacheDb, StateCacheDbRefMutWrapper, StateProviderTraitObjWrapper},
20 EthApiError,
21};
22use reth_storage_api::{BlockReader, ProviderBlock, ProviderHeader, ProviderTx};
23use revm::{
24 context_interface::result::{ExecutionResult, ResultAndState},
25 state::EvmState,
26 DatabaseCommit,
27};
28use revm_inspectors::tracing::{TracingInspector, TracingInspectorConfig};
29use std::sync::Arc;
30
31pub trait Trace:
33 LoadState<
34 Provider: BlockReader,
35 Evm: ConfigureEvm<
36 Primitives: NodePrimitives<
37 BlockHeader = ProviderHeader<Self::Provider>,
38 SignedTx = ProviderTx<Self::Provider>,
39 >,
40 >,
41 Error: FromEvmError<Self::Evm>,
42>
43{
44 #[expect(clippy::type_complexity)]
47 fn inspect<DB, I>(
48 &self,
49 db: DB,
50 evm_env: EvmEnvFor<Self::Evm>,
51 tx_env: TxEnvFor<Self::Evm>,
52 inspector: I,
53 ) -> Result<
54 (ResultAndState<HaltReasonFor<Self::Evm>>, (EvmEnvFor<Self::Evm>, TxEnvFor<Self::Evm>)),
55 Self::Error,
56 >
57 where
58 DB: Database<Error = ProviderError>,
59 I: InspectorFor<Self::Evm, DB>,
60 {
61 let mut evm = self.evm_config().evm_with_env_and_inspector(db, evm_env.clone(), inspector);
62 let res = evm.transact(tx_env.clone()).map_err(Self::Error::from_evm_err)?;
63 Ok((res, (evm_env, tx_env)))
64 }
65
66 fn trace_at<F, R>(
74 &self,
75 evm_env: EvmEnvFor<Self::Evm>,
76 tx_env: TxEnvFor<Self::Evm>,
77 config: TracingInspectorConfig,
78 at: BlockId,
79 f: F,
80 ) -> Result<R, Self::Error>
81 where
82 Self: Call,
83 F: FnOnce(
84 TracingInspector,
85 ResultAndState<HaltReasonFor<Self::Evm>>,
86 ) -> Result<R, Self::Error>,
87 {
88 self.with_state_at_block(at, |state| {
89 let mut db = CacheDB::new(StateProviderDatabase::new(state));
90 let mut inspector = TracingInspector::new(config);
91 let (res, _) = self.inspect(&mut db, evm_env, tx_env, &mut inspector)?;
92 f(inspector, res)
93 })
94 }
95
96 fn spawn_trace_at_with_state<F, R>(
104 &self,
105 evm_env: EvmEnvFor<Self::Evm>,
106 tx_env: TxEnvFor<Self::Evm>,
107 config: TracingInspectorConfig,
108 at: BlockId,
109 f: F,
110 ) -> impl Future<Output = Result<R, Self::Error>> + Send
111 where
112 Self: LoadPendingBlock + Call,
113 F: FnOnce(
114 TracingInspector,
115 ResultAndState<HaltReasonFor<Self::Evm>>,
116 StateCacheDb<'_>,
117 ) -> Result<R, Self::Error>
118 + Send
119 + 'static,
120 R: Send + 'static,
121 {
122 let this = self.clone();
123 self.spawn_with_state_at_block(at, move |state| {
124 let mut db = CacheDB::new(StateProviderDatabase::new(state));
125 let mut inspector = TracingInspector::new(config);
126 let (res, _) = this.inspect(&mut db, evm_env, tx_env, &mut inspector)?;
127 f(inspector, res, db)
128 })
129 }
130
131 fn spawn_trace_transaction_in_block<F, R>(
141 &self,
142 hash: B256,
143 config: TracingInspectorConfig,
144 f: F,
145 ) -> impl Future<Output = Result<Option<R>, Self::Error>> + Send
146 where
147 Self: LoadPendingBlock + LoadTransaction + Call,
148 F: FnOnce(
149 TransactionInfo,
150 TracingInspector,
151 ResultAndState<HaltReasonFor<Self::Evm>>,
152 StateCacheDb<'_>,
153 ) -> Result<R, Self::Error>
154 + Send
155 + 'static,
156 R: Send + 'static,
157 {
158 self.spawn_trace_transaction_in_block_with_inspector(hash, TracingInspector::new(config), f)
159 }
160
161 fn spawn_trace_transaction_in_block_with_inspector<Insp, F, R>(
171 &self,
172 hash: B256,
173 mut inspector: Insp,
174 f: F,
175 ) -> impl Future<Output = Result<Option<R>, Self::Error>> + Send
176 where
177 Self: LoadPendingBlock + LoadTransaction + Call,
178 F: FnOnce(
179 TransactionInfo,
180 Insp,
181 ResultAndState<HaltReasonFor<Self::Evm>>,
182 StateCacheDb<'_>,
183 ) -> Result<R, Self::Error>
184 + Send
185 + 'static,
186 Insp:
187 for<'a, 'b> InspectorFor<Self::Evm, StateCacheDbRefMutWrapper<'a, 'b>> + Send + 'static,
188 R: Send + 'static,
189 {
190 async move {
191 let (transaction, block) = match self.transaction_and_block(hash).await? {
192 None => return Ok(None),
193 Some(res) => res,
194 };
195 let (tx, tx_info) = transaction.split();
196
197 let (evm_env, _) = self.evm_env_at(block.hash().into()).await?;
198
199 let parent_block = block.parent_hash();
202
203 let this = self.clone();
204 self.spawn_with_state_at_block(parent_block.into(), move |state| {
205 let mut db = CacheDB::new(StateProviderDatabase::new(state));
206 let block_txs = block.transactions_recovered();
207
208 this.apply_pre_execution_changes(&block, &mut db, &evm_env)?;
209
210 this.replay_transactions_until(&mut db, evm_env.clone(), block_txs, *tx.tx_hash())?;
212
213 let tx_env = this.evm_config().tx_env(tx);
214 let (res, _) = this.inspect(
215 StateCacheDbRefMutWrapper(&mut db),
216 evm_env,
217 tx_env,
218 &mut inspector,
219 )?;
220 f(tx_info, inspector, res, db)
221 })
222 .await
223 .map(Some)
224 }
225 }
226
227 fn trace_block_until<F, R>(
234 &self,
235 block_id: BlockId,
236 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
237 highest_index: Option<u64>,
238 config: TracingInspectorConfig,
239 f: F,
240 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
241 where
242 Self: LoadBlock,
243 F: Fn(
244 TransactionInfo,
245 TracingInspector,
246 ExecutionResult<HaltReasonFor<Self::Evm>>,
247 &EvmState,
248 &StateCacheDb<'_>,
249 ) -> Result<R, Self::Error>
250 + Send
251 + 'static,
252 R: Send + 'static,
253 {
254 self.trace_block_until_with_inspector(
255 block_id,
256 block,
257 highest_index,
258 move || TracingInspector::new(config),
259 f,
260 )
261 }
262
263 fn trace_block_until_with_inspector<Setup, Insp, F, R>(
274 &self,
275 block_id: BlockId,
276 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
277 highest_index: Option<u64>,
278 mut inspector_setup: Setup,
279 f: F,
280 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
281 where
282 Self: LoadBlock,
283 F: Fn(
284 TransactionInfo,
285 Insp,
286 ExecutionResult<HaltReasonFor<Self::Evm>>,
287 &EvmState,
288 &StateCacheDb<'_>,
289 ) -> Result<R, Self::Error>
290 + Send
291 + 'static,
292 Setup: FnMut() -> Insp + Send + 'static,
293 Insp: for<'a, 'b> InspectorFor<Self::Evm, StateCacheDbRefMutWrapper<'a, 'b>>,
294 R: Send + 'static,
295 {
296 async move {
297 let block = async {
298 if block.is_some() {
299 return Ok(block)
300 }
301 self.recovered_block(block_id).await
302 };
303
304 let ((evm_env, _), block) = futures::try_join!(self.evm_env_at(block_id), block)?;
305
306 let Some(block) = block else { return Ok(None) };
307
308 if block.body().transactions().is_empty() {
309 return Ok(Some(Vec::new()))
311 }
312
313 self.spawn_tracing(move |this| {
315 let state_at = block.parent_hash();
318 let block_hash = block.hash();
319
320 let block_number = evm_env.block_env.number;
321 let base_fee = evm_env.block_env.basefee;
322
323 let state = this.state_at_block_id(state_at.into())?;
325 let mut db =
326 CacheDB::new(StateProviderDatabase::new(StateProviderTraitObjWrapper(&state)));
327
328 this.apply_pre_execution_changes(&block, &mut db, &evm_env)?;
329
330 let max_transactions =
333 highest_index.map_or(block.body().transaction_count(), |highest| {
334 highest as usize + 1
336 });
337 let mut results = Vec::with_capacity(max_transactions);
338
339 let mut transactions = block
340 .transactions_recovered()
341 .take(max_transactions)
342 .enumerate()
343 .map(|(idx, tx)| {
344 let tx_info = TransactionInfo {
345 hash: Some(*tx.tx_hash()),
346 index: Some(idx as u64),
347 block_hash: Some(block_hash),
348 block_number: Some(block_number),
349 base_fee: Some(base_fee),
350 };
351 let tx_env = this.evm_config().tx_env(tx);
352 (tx_info, tx_env)
353 })
354 .peekable();
355
356 while let Some((tx_info, tx)) = transactions.next() {
357 let mut inspector = inspector_setup();
358 let (res, _) = this.inspect(
359 StateCacheDbRefMutWrapper(&mut db),
360 evm_env.clone(),
361 tx,
362 &mut inspector,
363 )?;
364 let ResultAndState { result, state } = res;
365 results.push(f(tx_info, inspector, result, &state, &db)?);
366
367 if transactions.peek().is_some() {
370 db.commit(state)
372 }
373 }
374
375 Ok(Some(results))
376 })
377 .await
378 }
379 }
380
381 fn trace_block_with<F, R>(
392 &self,
393 block_id: BlockId,
394 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
395 config: TracingInspectorConfig,
396 f: F,
397 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
398 where
399 Self: LoadBlock,
400 F: Fn(
403 TransactionInfo,
404 TracingInspector,
405 ExecutionResult<HaltReasonFor<Self::Evm>>,
406 &EvmState,
407 &StateCacheDb<'_>,
408 ) -> Result<R, Self::Error>
409 + Send
410 + 'static,
411 R: Send + 'static,
412 {
413 self.trace_block_until(block_id, block, None, config, f)
414 }
415
416 fn trace_block_inspector<Setup, Insp, F, R>(
431 &self,
432 block_id: BlockId,
433 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
434 insp_setup: Setup,
435 f: F,
436 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
437 where
438 Self: LoadBlock,
439 F: Fn(
442 TransactionInfo,
443 Insp,
444 ExecutionResult<HaltReasonFor<Self::Evm>>,
445 &EvmState,
446 &StateCacheDb<'_>,
447 ) -> Result<R, Self::Error>
448 + Send
449 + 'static,
450 Setup: FnMut() -> Insp + Send + 'static,
451 Insp: for<'a, 'b> InspectorFor<Self::Evm, StateCacheDbRefMutWrapper<'a, 'b>>,
452 R: Send + 'static,
453 {
454 self.trace_block_until_with_inspector(block_id, block, None, insp_setup, f)
455 }
456
457 fn apply_pre_execution_changes<DB: Send + Database + DatabaseCommit>(
463 &self,
464 block: &RecoveredBlock<ProviderBlock<Self::Provider>>,
465 db: &mut DB,
466 evm_env: &EvmEnvFor<Self::Evm>,
467 ) -> Result<(), Self::Error> {
468 let mut system_caller = SystemCaller::new(self.provider().chain_spec());
469
470 let mut evm = self.evm_config().evm_with_env(db, evm_env.clone());
472 system_caller.apply_pre_execution_changes(block.header(), &mut evm).map_err(|err| {
473 EthApiError::EvmCustom(format!("failed to apply 4788 system call {err}"))
474 })?;
475
476 Ok(())
477 }
478}