1use super::{Call, LoadBlock, LoadState, LoadTransaction};
4use crate::{FromEthApiError, FromEvmError};
5use alloy_consensus::{transaction::TxHashRef, BlockHeader};
6use alloy_eip7928::bal::DecodedBal;
7use alloy_primitives::B256;
8use alloy_rpc_types_eth::{BlockId, TransactionInfo};
9use futures::Future;
10use reth_errors::RethError;
11use reth_evm::{
12 block::BlockExecutor, evm::EvmFactoryExt, tracing::TracingCtx, ConfigureEvm, Evm, EvmEnvFor,
13 EvmFor, HaltReasonFor, InspectorFor, IntoTxEnv, TxEnvFor,
14};
15use reth_primitives_traits::{BlockBody, BlockTy, Recovered, RecoveredBlock};
16use reth_rpc_eth_types::{
17 cache::db::{attach_bal_before_tx, StateCacheDb},
18 EthApiError,
19};
20use reth_storage_api::{ProviderBlock, ProviderTx};
21use reth_tasks::cancel::is_cancelled;
22use revm::{context::Block, context_interface::result::ResultAndState, state::bal::Bal as RevmBal};
23use revm_inspectors::tracing::{TracingInspector, TracingInspectorConfig};
24use std::sync::Arc;
25
26pub trait Trace: LoadState<Error: FromEvmError<Self::Evm>> + Call {
28 fn inspect<'a>(
31 &self,
32 db: &'a mut StateCacheDb,
33 evm_env: EvmEnvFor<Self::Evm>,
34 tx_env: impl IntoTxEnv<TxEnvFor<Self::Evm>>,
35 inspector: impl InspectorFor<Self::Evm, &'a mut StateCacheDb>,
36 ) -> Result<ResultAndState<HaltReasonFor<Self::Evm>>, Self::Error> {
37 self.evm_config()
38 .evm_with_env_and_inspector(db, evm_env, inspector)
39 .transact(tx_env)
40 .map_err(Self::Error::from_evm_err)
41 }
42
43 fn spawn_trace_transaction_in_block<F, R>(
56 &self,
57 hash: B256,
58 config: TracingInspectorConfig,
59 f: F,
60 ) -> impl Future<Output = Result<Option<R>, Self::Error>> + Send
61 where
62 Self: LoadTransaction,
63 F: FnOnce(
64 TransactionInfo,
65 TracingInspector,
66 ResultAndState<HaltReasonFor<Self::Evm>>,
67 StateCacheDb,
68 ) -> Result<R, Self::Error>
69 + Send
70 + 'static,
71 R: Send + 'static,
72 {
73 self.spawn_trace_transaction_in_block_with_inspector(hash, TracingInspector::new(config), f)
74 }
75
76 fn spawn_trace_transaction_in_block_with_inspector<Insp, F, R>(
89 &self,
90 hash: B256,
91 mut inspector: Insp,
92 f: F,
93 ) -> impl Future<Output = Result<Option<R>, Self::Error>> + Send
94 where
95 Self: LoadTransaction,
96 F: FnOnce(
97 TransactionInfo,
98 Insp,
99 ResultAndState<HaltReasonFor<Self::Evm>>,
100 StateCacheDb,
101 ) -> Result<R, Self::Error>
102 + Send
103 + 'static,
104 Insp: for<'a> InspectorFor<Self::Evm, &'a mut StateCacheDb> + Send + 'static,
105 R: Send + 'static,
106 {
107 async move {
108 let (transaction, block, bal) =
109 match self.transaction_and_block_and_maybe_bal(hash).await? {
110 None => return Ok(None),
111 Some(res) => res,
112 };
113 let (tx, tx_info) = transaction.split();
114
115 let parent_block = block.parent_hash();
118
119 self.spawn_with_state_at_block(parent_block, move |this, mut db| {
120 let (res, _) = this.inspect_transaction_in_block(
121 &block,
122 &mut db,
123 &mut inspector,
124 tx_info.index.expect("transaction_and_block only returns block transactions")
127 as usize,
128 tx,
129 bal.as_deref(),
130 )?;
131 f(tx_info, inspector, res, db)
132 })
133 .await
134 .map(Some)
135 }
136 }
137
138 fn replay_block_until(
148 &self,
149 db: &mut StateCacheDb,
150 block: &RecoveredBlock<BlockTy<Self::Primitives>>,
151 target_tx_index: usize,
152 bal: Option<&DecodedBal<Arc<RevmBal>>>,
153 ) -> Result<(), Self::Error> {
154 if let Some(bal) = bal {
155 attach_bal_before_tx(db, bal, target_tx_index);
156 return Ok(())
157 }
158
159 self.apply_pre_execution_changes(block, db)?;
160
161 let evm_env = self.evm_env_for_header(block.sealed_block().sealed_header())?;
162 let mut evm = self.evm_config().evm_with_env(db, evm_env);
163 self.replay_transactions_until_with_evm(
164 &mut evm,
165 block.transactions_recovered(),
166 target_tx_index,
167 )
168 }
169
170 #[expect(clippy::type_complexity)]
177 fn inspect_transaction_in_block<'a>(
178 &self,
179 block: &RecoveredBlock<BlockTy<Self::Primitives>>,
180 db: &'a mut StateCacheDb,
181 inspector: impl InspectorFor<Self::Evm, &'a mut StateCacheDb>,
182 target_tx_index: usize,
183 target_tx_env: impl IntoTxEnv<TxEnvFor<Self::Evm>>,
184 bal: Option<&DecodedBal<Arc<RevmBal>>>,
185 ) -> Result<(ResultAndState<HaltReasonFor<Self::Evm>>, EvmEnvFor<Self::Evm>), Self::Error> {
186 if let Some(bal) = bal {
187 attach_bal_before_tx(db, bal, target_tx_index);
189 } else {
190 self.apply_pre_execution_changes(block, db)?;
191 }
192
193 let evm_env = self.evm_env_for_header(block.sealed_block().sealed_header())?;
194 let mut evm = self.evm_config().evm_with_env_and_inspector(db, evm_env, inspector);
195
196 if bal.is_none() {
197 evm.disable_inspector();
198 self.replay_transactions_until_with_evm(
199 &mut evm,
200 block.transactions_recovered(),
201 target_tx_index,
202 )?;
203 evm.enable_inspector();
204 }
205
206 let res = evm.transact(target_tx_env).map_err(Self::Error::from_evm_err)?;
207
208 let (_, evm_env) = evm.finish();
209
210 Ok((res, evm_env))
211 }
212
213 fn trace_block_until<F, R>(
220 &self,
221 block_id: BlockId,
222 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
223 highest_index: Option<u64>,
224 config: TracingInspectorConfig,
225 f: F,
226 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
227 where
228 Self: LoadBlock,
229 F: Fn(
230 TransactionInfo,
231 TracingCtx<
232 '_,
233 Recovered<&ProviderTx<Self::Provider>>,
234 EvmFor<Self::Evm, &mut StateCacheDb, TracingInspector>,
235 >,
236 ) -> Result<R, Self::Error>
237 + Send
238 + 'static,
239 R: Send + 'static,
240 {
241 self.trace_block_until_with_inspector(
242 block_id,
243 block,
244 highest_index,
245 move || TracingInspector::new(config),
246 f,
247 )
248 }
249
250 fn trace_block_until_with_inspector<Setup, Insp, F, R>(
261 &self,
262 block_id: BlockId,
263 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
264 highest_index: Option<u64>,
265 mut inspector_setup: Setup,
266 f: F,
267 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
268 where
269 Self: LoadBlock,
270 F: Fn(
271 TransactionInfo,
272 TracingCtx<
273 '_,
274 Recovered<&ProviderTx<Self::Provider>>,
275 EvmFor<Self::Evm, &mut StateCacheDb, Insp>,
276 >,
277 ) -> Result<R, Self::Error>
278 + Send
279 + 'static,
280 Setup: FnMut() -> Insp + Send + 'static,
281 Insp: Clone + for<'a> InspectorFor<Self::Evm, &'a mut StateCacheDb>,
282 R: Send + 'static,
283 {
284 async move {
285 let block =
286 if block.is_some() { block } else { self.recovered_block(block_id).await? };
287
288 let Some(block) = block else { return Ok(None) };
289 let evm_env = self.evm_env_for_header(block.sealed_block().sealed_header())?;
290
291 if block.body().transactions().is_empty() {
292 return Ok(Some(Vec::new()))
294 }
295
296 self.spawn_with_state_at_block(block.parent_hash(), move |this, mut db| {
300 let block_hash = block.hash();
301
302 let block_number = evm_env.block_env.number().saturating_to();
303 let block_timestamp = evm_env.block_env.timestamp().saturating_to();
304 let base_fee = evm_env.block_env.basefee();
305
306 this.apply_pre_execution_changes(&block, &mut db)?;
307
308 let max_transactions = highest_index.map_or_else(
311 || block.body().transaction_count(),
312 |highest| {
313 highest as usize + 1
315 },
316 );
317
318 let mut idx = 0;
319
320 let results = this
321 .evm_config()
322 .evm_factory()
323 .create_tracer(&mut db, evm_env, inspector_setup())
324 .try_trace_many(block.transactions_recovered().take(max_transactions), |ctx| {
325 if is_cancelled() {
326 return Err(EthApiError::InternalEthError.into())
327 }
328 let tx_info = TransactionInfo {
329 hash: Some(*ctx.tx.tx_hash()),
330 index: Some(idx),
331 block_hash: Some(block_hash),
332 block_number: Some(block_number),
333 block_timestamp: Some(block_timestamp),
334 base_fee: Some(base_fee),
335 };
336 idx += 1;
337
338 f(tx_info, ctx)
339 })
340 .collect::<Result<_, _>>()?;
341
342 Ok(Some(results))
343 })
344 .await
345 }
346 }
347
348 fn trace_block_with<F, R>(
359 &self,
360 block_id: BlockId,
361 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
362 config: TracingInspectorConfig,
363 f: F,
364 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
365 where
366 Self: LoadBlock,
367 F: Fn(
370 TransactionInfo,
371 TracingCtx<
372 '_,
373 Recovered<&ProviderTx<Self::Provider>>,
374 EvmFor<Self::Evm, &mut StateCacheDb, TracingInspector>,
375 >,
376 ) -> Result<R, Self::Error>
377 + Send
378 + 'static,
379 R: Send + 'static,
380 {
381 self.trace_block_until(block_id, block, None, config, f)
382 }
383
384 fn trace_block_inspector<Setup, Insp, F, R>(
399 &self,
400 block_id: BlockId,
401 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
402 insp_setup: Setup,
403 f: F,
404 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
405 where
406 Self: LoadBlock,
407 F: Fn(
410 TransactionInfo,
411 TracingCtx<
412 '_,
413 Recovered<&ProviderTx<Self::Provider>>,
414 EvmFor<Self::Evm, &mut StateCacheDb, Insp>,
415 >,
416 ) -> Result<R, Self::Error>
417 + Send
418 + 'static,
419 Setup: FnMut() -> Insp + Send + 'static,
420 Insp: Clone + for<'a> InspectorFor<Self::Evm, &'a mut StateCacheDb>,
421 R: Send + 'static,
422 {
423 self.trace_block_until_with_inspector(block_id, block, None, insp_setup, f)
424 }
425
426 fn apply_pre_execution_changes(
432 &self,
433 block: &RecoveredBlock<ProviderBlock<Self::Provider>>,
434 db: &mut StateCacheDb,
435 ) -> Result<(), Self::Error> {
436 self.evm_config()
437 .executor_for_block(db, block.sealed_block())
438 .map_err(RethError::other)
439 .map_err(Self::Error::from_eth_err)?
440 .apply_pre_execution_changes()
441 .map_err(Self::Error::from_eth_err)?;
442 Ok(())
443 }
444}