1use super::{Call, LoadBlock, LoadState, LoadTransaction};
4use crate::{FromEthApiError, FromEvmError};
5use alloy_consensus::{transaction::TxHashRef, BlockHeader};
6use alloy_primitives::B256;
7use alloy_rpc_types_eth::{BlockId, TransactionInfo};
8use futures::Future;
9use reth_errors::RethError;
10use reth_evm::{
11 block::BlockExecutor, evm::EvmFactoryExt, tracing::TracingCtx, ConfigureEvm, Evm, EvmEnvFor,
12 EvmFor, HaltReasonFor, InspectorFor, IntoTxEnv, TxEnvFor,
13};
14use reth_primitives_traits::{BlockBody, BlockTy, Recovered, RecoveredBlock};
15use reth_rpc_eth_types::cache::db::StateCacheDb;
16use reth_storage_api::{ProviderBlock, ProviderTx};
17use revm::{context::Block, context_interface::result::ResultAndState};
18use revm_inspectors::tracing::{TracingInspector, TracingInspectorConfig};
19use std::sync::Arc;
20
21pub trait Trace: LoadState<Error: FromEvmError<Self::Evm>> + Call {
23 fn inspect<'a>(
26 &self,
27 db: &'a mut StateCacheDb,
28 evm_env: EvmEnvFor<Self::Evm>,
29 tx_env: impl IntoTxEnv<TxEnvFor<Self::Evm>>,
30 inspector: impl InspectorFor<Self::Evm, &'a mut StateCacheDb>,
31 ) -> Result<ResultAndState<HaltReasonFor<Self::Evm>>, Self::Error> {
32 self.evm_config()
33 .evm_with_env_and_inspector(db, evm_env, inspector)
34 .transact(tx_env)
35 .map_err(Self::Error::from_evm_err)
36 }
37
38 fn spawn_trace_transaction_in_block<F, R>(
48 &self,
49 hash: B256,
50 config: TracingInspectorConfig,
51 f: F,
52 ) -> impl Future<Output = Result<Option<R>, Self::Error>> + Send
53 where
54 Self: LoadTransaction,
55 F: FnOnce(
56 TransactionInfo,
57 TracingInspector,
58 ResultAndState<HaltReasonFor<Self::Evm>>,
59 StateCacheDb,
60 ) -> Result<R, Self::Error>
61 + Send
62 + 'static,
63 R: Send + 'static,
64 {
65 self.spawn_trace_transaction_in_block_with_inspector(hash, TracingInspector::new(config), f)
66 }
67
68 fn spawn_trace_transaction_in_block_with_inspector<Insp, F, R>(
78 &self,
79 hash: B256,
80 mut inspector: Insp,
81 f: F,
82 ) -> impl Future<Output = Result<Option<R>, Self::Error>> + Send
83 where
84 Self: LoadTransaction,
85 F: FnOnce(
86 TransactionInfo,
87 Insp,
88 ResultAndState<HaltReasonFor<Self::Evm>>,
89 StateCacheDb,
90 ) -> Result<R, Self::Error>
91 + Send
92 + 'static,
93 Insp: for<'a> InspectorFor<Self::Evm, &'a mut StateCacheDb> + Send + 'static,
94 R: Send + 'static,
95 {
96 async move {
97 let (transaction, block) = match self.transaction_and_block(hash).await? {
98 None => return Ok(None),
99 Some(res) => res,
100 };
101 let (tx, tx_info) = transaction.split();
102
103 let parent_block = block.parent_hash();
106
107 self.spawn_with_state_at_block(parent_block, move |this, mut db| {
108 let (res, _) = this.inspect_transaction_in_block(
109 &block,
110 &mut db,
111 &mut inspector,
112 tx_info.index.expect("transaction_and_block only returns block transactions")
115 as usize,
116 tx,
117 )?;
118 f(tx_info, inspector, res, db)
119 })
120 .await
121 .map(Some)
122 }
123 }
124
125 fn replay_block_until(
133 &self,
134 db: &mut StateCacheDb,
135 block: &RecoveredBlock<BlockTy<Self::Primitives>>,
136 target_tx_index: usize,
137 ) -> Result<(), Self::Error> {
138 self.apply_pre_execution_changes(block, db)?;
139
140 let evm_env = self.evm_env_for_header(block.sealed_block().sealed_header())?;
141 let mut evm = self.evm_config().evm_with_env(db, evm_env);
142 self.replay_transactions_until_with_evm(
143 &mut evm,
144 block.transactions_recovered(),
145 target_tx_index,
146 )
147 }
148
149 #[expect(clippy::type_complexity)]
152 fn inspect_transaction_in_block<'a>(
153 &self,
154 block: &RecoveredBlock<BlockTy<Self::Primitives>>,
155 db: &'a mut StateCacheDb,
156 inspector: impl InspectorFor<Self::Evm, &'a mut StateCacheDb>,
157 target_tx_index: usize,
158 target_tx_env: impl IntoTxEnv<TxEnvFor<Self::Evm>>,
159 ) -> Result<(ResultAndState<HaltReasonFor<Self::Evm>>, EvmEnvFor<Self::Evm>), Self::Error> {
160 let block_txs = block.transactions_recovered();
161
162 self.apply_pre_execution_changes(block, db)?;
163
164 let evm_env = self.evm_env_for_header(block.sealed_block().sealed_header())?;
165 let mut evm = self.evm_config().evm_with_env_and_inspector(db, evm_env, inspector);
166
167 evm.disable_inspector();
168 self.replay_transactions_until_with_evm(&mut evm, block_txs, target_tx_index)?;
169 evm.enable_inspector();
170
171 let res = evm.transact(target_tx_env).map_err(Self::Error::from_evm_err)?;
172
173 let (_, evm_env) = evm.finish();
174
175 Ok((res, evm_env))
176 }
177
178 fn trace_block_until<F, R>(
185 &self,
186 block_id: BlockId,
187 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
188 highest_index: Option<u64>,
189 config: TracingInspectorConfig,
190 f: F,
191 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
192 where
193 Self: LoadBlock,
194 F: Fn(
195 TransactionInfo,
196 TracingCtx<
197 '_,
198 Recovered<&ProviderTx<Self::Provider>>,
199 EvmFor<Self::Evm, &mut StateCacheDb, TracingInspector>,
200 >,
201 ) -> Result<R, Self::Error>
202 + Send
203 + 'static,
204 R: Send + 'static,
205 {
206 self.trace_block_until_with_inspector(
207 block_id,
208 block,
209 highest_index,
210 move || TracingInspector::new(config),
211 f,
212 )
213 }
214
215 fn trace_block_until_with_inspector<Setup, Insp, F, R>(
226 &self,
227 block_id: BlockId,
228 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
229 highest_index: Option<u64>,
230 mut inspector_setup: Setup,
231 f: F,
232 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
233 where
234 Self: LoadBlock,
235 F: Fn(
236 TransactionInfo,
237 TracingCtx<
238 '_,
239 Recovered<&ProviderTx<Self::Provider>>,
240 EvmFor<Self::Evm, &mut StateCacheDb, Insp>,
241 >,
242 ) -> Result<R, Self::Error>
243 + Send
244 + 'static,
245 Setup: FnMut() -> Insp + Send + 'static,
246 Insp: Clone + for<'a> InspectorFor<Self::Evm, &'a mut StateCacheDb>,
247 R: Send + 'static,
248 {
249 async move {
250 let block =
251 if block.is_some() { block } else { self.recovered_block(block_id).await? };
252
253 let Some(block) = block else { return Ok(None) };
254 let evm_env = self.evm_env_for_header(block.sealed_block().sealed_header())?;
255
256 if block.body().transactions().is_empty() {
257 return Ok(Some(Vec::new()))
259 }
260
261 self.spawn_with_state_at_block(block.parent_hash(), move |this, mut db| {
265 let block_hash = block.hash();
266
267 let block_number = evm_env.block_env.number().saturating_to();
268 let block_timestamp = evm_env.block_env.timestamp().saturating_to();
269 let base_fee = evm_env.block_env.basefee();
270
271 this.apply_pre_execution_changes(&block, &mut db)?;
272
273 let max_transactions = highest_index.map_or_else(
276 || block.body().transaction_count(),
277 |highest| {
278 highest as usize + 1
280 },
281 );
282
283 let mut idx = 0;
284
285 let results = this
286 .evm_config()
287 .evm_factory()
288 .create_tracer(&mut db, evm_env, inspector_setup())
289 .try_trace_many(block.transactions_recovered().take(max_transactions), |ctx| {
290 let tx_info = TransactionInfo {
291 hash: Some(*ctx.tx.tx_hash()),
292 index: Some(idx),
293 block_hash: Some(block_hash),
294 block_number: Some(block_number),
295 block_timestamp: Some(block_timestamp),
296 base_fee: Some(base_fee),
297 };
298 idx += 1;
299
300 f(tx_info, ctx)
301 })
302 .collect::<Result<_, _>>()?;
303
304 Ok(Some(results))
305 })
306 .await
307 }
308 }
309
310 fn trace_block_with<F, R>(
321 &self,
322 block_id: BlockId,
323 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
324 config: TracingInspectorConfig,
325 f: F,
326 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
327 where
328 Self: LoadBlock,
329 F: Fn(
332 TransactionInfo,
333 TracingCtx<
334 '_,
335 Recovered<&ProviderTx<Self::Provider>>,
336 EvmFor<Self::Evm, &mut StateCacheDb, TracingInspector>,
337 >,
338 ) -> Result<R, Self::Error>
339 + Send
340 + 'static,
341 R: Send + 'static,
342 {
343 self.trace_block_until(block_id, block, None, config, f)
344 }
345
346 fn trace_block_inspector<Setup, Insp, F, R>(
361 &self,
362 block_id: BlockId,
363 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
364 insp_setup: Setup,
365 f: F,
366 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
367 where
368 Self: LoadBlock,
369 F: Fn(
372 TransactionInfo,
373 TracingCtx<
374 '_,
375 Recovered<&ProviderTx<Self::Provider>>,
376 EvmFor<Self::Evm, &mut StateCacheDb, Insp>,
377 >,
378 ) -> Result<R, Self::Error>
379 + Send
380 + 'static,
381 Setup: FnMut() -> Insp + Send + 'static,
382 Insp: Clone + for<'a> InspectorFor<Self::Evm, &'a mut StateCacheDb>,
383 R: Send + 'static,
384 {
385 self.trace_block_until_with_inspector(block_id, block, None, insp_setup, f)
386 }
387
388 fn apply_pre_execution_changes(
394 &self,
395 block: &RecoveredBlock<ProviderBlock<Self::Provider>>,
396 db: &mut StateCacheDb,
397 ) -> Result<(), Self::Error> {
398 self.evm_config()
399 .executor_for_block(db, block.sealed_block())
400 .map_err(RethError::other)
401 .map_err(Self::Error::from_eth_err)?
402 .apply_pre_execution_changes()
403 .map_err(Self::Error::from_eth_err)?;
404 Ok(())
405 }
406}