1use super::{Call, LoadBlock, LoadPendingBlock, LoadState, LoadTransaction};
4use crate::FromEvmError;
5use alloy_consensus::{transaction::TxHashRef, 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 evm::EvmFactoryExt, system_calls::SystemCaller, tracing::TracingCtx, ConfigureEvm, Database,
13 Evm, EvmEnvFor, EvmFor, HaltReasonFor, InspectorFor, TxEnvFor,
14};
15use reth_primitives_traits::{BlockBody, Recovered, RecoveredBlock};
16use reth_revm::{database::StateProviderDatabase, db::State};
17use reth_rpc_eth_types::{
18 cache::db::{StateCacheDb, StateCacheDbRefMutWrapper, StateProviderTraitObjWrapper},
19 EthApiError,
20};
21use reth_storage_api::{ProviderBlock, ProviderTx};
22use revm::{context::Block, context_interface::result::ResultAndState, DatabaseCommit};
23use revm_inspectors::tracing::{TracingInspector, TracingInspectorConfig};
24use std::sync::Arc;
25
26pub trait Trace: LoadState<Error: FromEvmError<Self::Evm>> {
28 fn inspect<DB, I>(
31 &self,
32 db: DB,
33 evm_env: EvmEnvFor<Self::Evm>,
34 tx_env: TxEnvFor<Self::Evm>,
35 inspector: I,
36 ) -> Result<ResultAndState<HaltReasonFor<Self::Evm>>, Self::Error>
37 where
38 DB: Database<Error = ProviderError>,
39 I: InspectorFor<Self::Evm, DB>,
40 {
41 let mut evm = self.evm_config().evm_with_env_and_inspector(db, evm_env, inspector);
42 evm.transact(tx_env).map_err(Self::Error::from_evm_err)
43 }
44
45 fn trace_at<F, R>(
53 &self,
54 evm_env: EvmEnvFor<Self::Evm>,
55 tx_env: TxEnvFor<Self::Evm>,
56 config: TracingInspectorConfig,
57 at: BlockId,
58 f: F,
59 ) -> impl Future<Output = Result<R, Self::Error>> + Send
60 where
61 Self: Call,
62 R: Send + 'static,
63 F: FnOnce(
64 TracingInspector,
65 ResultAndState<HaltReasonFor<Self::Evm>>,
66 ) -> Result<R, Self::Error>
67 + Send
68 + 'static,
69 {
70 self.with_state_at_block(at, move |this, state| {
71 let mut db = State::builder().with_database(StateProviderDatabase::new(state)).build();
72 let mut inspector = TracingInspector::new(config);
73 let res = this.inspect(&mut db, evm_env, tx_env, &mut inspector)?;
74 f(inspector, res)
75 })
76 }
77
78 fn spawn_trace_at_with_state<F, R>(
86 &self,
87 evm_env: EvmEnvFor<Self::Evm>,
88 tx_env: TxEnvFor<Self::Evm>,
89 config: TracingInspectorConfig,
90 at: BlockId,
91 f: F,
92 ) -> impl Future<Output = Result<R, Self::Error>> + Send
93 where
94 Self: LoadPendingBlock + Call,
95 F: FnOnce(
96 TracingInspector,
97 ResultAndState<HaltReasonFor<Self::Evm>>,
98 StateCacheDb<'_>,
99 ) -> Result<R, Self::Error>
100 + Send
101 + 'static,
102 R: Send + 'static,
103 {
104 let this = self.clone();
105 self.spawn_with_state_at_block(at, move |state| {
106 let mut db = State::builder().with_database(StateProviderDatabase::new(state)).build();
107 let mut inspector = TracingInspector::new(config);
108 let res = this.inspect(&mut db, evm_env, tx_env, &mut inspector)?;
109 f(inspector, res, db)
110 })
111 }
112
113 fn spawn_trace_transaction_in_block<F, R>(
123 &self,
124 hash: B256,
125 config: TracingInspectorConfig,
126 f: F,
127 ) -> impl Future<Output = Result<Option<R>, Self::Error>> + Send
128 where
129 Self: LoadPendingBlock + LoadTransaction + Call,
130 F: FnOnce(
131 TransactionInfo,
132 TracingInspector,
133 ResultAndState<HaltReasonFor<Self::Evm>>,
134 StateCacheDb<'_>,
135 ) -> Result<R, Self::Error>
136 + Send
137 + 'static,
138 R: Send + 'static,
139 {
140 self.spawn_trace_transaction_in_block_with_inspector(hash, TracingInspector::new(config), f)
141 }
142
143 fn spawn_trace_transaction_in_block_with_inspector<Insp, F, R>(
153 &self,
154 hash: B256,
155 mut inspector: Insp,
156 f: F,
157 ) -> impl Future<Output = Result<Option<R>, Self::Error>> + Send
158 where
159 Self: LoadPendingBlock + LoadTransaction + Call,
160 F: FnOnce(
161 TransactionInfo,
162 Insp,
163 ResultAndState<HaltReasonFor<Self::Evm>>,
164 StateCacheDb<'_>,
165 ) -> Result<R, Self::Error>
166 + Send
167 + 'static,
168 Insp:
169 for<'a, 'b> InspectorFor<Self::Evm, StateCacheDbRefMutWrapper<'a, 'b>> + Send + 'static,
170 R: Send + 'static,
171 {
172 async move {
173 let (transaction, block) = match self.transaction_and_block(hash).await? {
174 None => return Ok(None),
175 Some(res) => res,
176 };
177 let (tx, tx_info) = transaction.split();
178
179 let (evm_env, _) = self.evm_env_at(block.hash().into()).await?;
180
181 let parent_block = block.parent_hash();
184
185 let this = self.clone();
186 self.spawn_with_state_at_block(parent_block.into(), move |state| {
187 let mut db =
188 State::builder().with_database(StateProviderDatabase::new(state)).build();
189 let block_txs = block.transactions_recovered();
190
191 this.apply_pre_execution_changes(&block, &mut db, &evm_env)?;
192
193 this.replay_transactions_until(&mut db, evm_env.clone(), block_txs, *tx.tx_hash())?;
195
196 let tx_env = this.evm_config().tx_env(tx);
197 let res = this.inspect(
198 StateCacheDbRefMutWrapper(&mut db),
199 evm_env,
200 tx_env,
201 &mut inspector,
202 )?;
203 f(tx_info, inspector, res, db)
204 })
205 .await
206 .map(Some)
207 }
208 }
209
210 fn trace_block_until<F, R>(
217 &self,
218 block_id: BlockId,
219 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
220 highest_index: Option<u64>,
221 config: TracingInspectorConfig,
222 f: F,
223 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
224 where
225 Self: LoadBlock,
226 F: Fn(
227 TransactionInfo,
228 TracingCtx<
229 '_,
230 Recovered<&ProviderTx<Self::Provider>>,
231 EvmFor<Self::Evm, StateCacheDbRefMutWrapper<'_, '_>, TracingInspector>,
232 >,
233 ) -> Result<R, Self::Error>
234 + Send
235 + 'static,
236 R: Send + 'static,
237 {
238 self.trace_block_until_with_inspector(
239 block_id,
240 block,
241 highest_index,
242 move || TracingInspector::new(config),
243 f,
244 )
245 }
246
247 fn trace_block_until_with_inspector<Setup, Insp, F, R>(
258 &self,
259 block_id: BlockId,
260 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
261 highest_index: Option<u64>,
262 mut inspector_setup: Setup,
263 f: F,
264 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
265 where
266 Self: LoadBlock,
267 F: Fn(
268 TransactionInfo,
269 TracingCtx<
270 '_,
271 Recovered<&ProviderTx<Self::Provider>>,
272 EvmFor<Self::Evm, StateCacheDbRefMutWrapper<'_, '_>, Insp>,
273 >,
274 ) -> Result<R, Self::Error>
275 + Send
276 + 'static,
277 Setup: FnMut() -> Insp + Send + 'static,
278 Insp: Clone + for<'a, 'b> InspectorFor<Self::Evm, StateCacheDbRefMutWrapper<'a, 'b>>,
279 R: Send + 'static,
280 {
281 async move {
282 let block = async {
283 if block.is_some() {
284 return Ok(block)
285 }
286 self.recovered_block(block_id).await
287 };
288
289 let ((evm_env, _), block) = futures::try_join!(self.evm_env_at(block_id), block)?;
290
291 let Some(block) = block else { return Ok(None) };
292
293 if block.body().transactions().is_empty() {
294 return Ok(Some(Vec::new()))
296 }
297
298 self.spawn_blocking_io_fut(move |this| async move {
300 let state_at = block.parent_hash();
303 let block_hash = block.hash();
304
305 let block_number = evm_env.block_env.number().saturating_to();
306 let base_fee = evm_env.block_env.basefee();
307
308 let state = this.state_at_block_id(state_at.into()).await?;
310 let mut db = State::builder()
311 .with_database(StateProviderDatabase::new(StateProviderTraitObjWrapper(&state)))
312 .build();
313
314 this.apply_pre_execution_changes(&block, &mut db, &evm_env)?;
315
316 let max_transactions = highest_index.map_or_else(
319 || block.body().transaction_count(),
320 |highest| {
321 highest as usize + 1
323 },
324 );
325
326 let mut idx = 0;
327
328 let results = this
329 .evm_config()
330 .evm_factory()
331 .create_tracer(StateCacheDbRefMutWrapper(&mut db), evm_env, inspector_setup())
332 .try_trace_many(block.transactions_recovered().take(max_transactions), |ctx| {
333 let tx_info = TransactionInfo {
334 hash: Some(*ctx.tx.tx_hash()),
335 index: Some(idx),
336 block_hash: Some(block_hash),
337 block_number: Some(block_number),
338 base_fee: Some(base_fee),
339 };
340 idx += 1;
341
342 f(tx_info, ctx)
343 })
344 .collect::<Result<_, _>>()?;
345
346 Ok(Some(results))
347 })
348 .await
349 }
350 }
351
352 fn trace_block_with<F, R>(
363 &self,
364 block_id: BlockId,
365 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
366 config: TracingInspectorConfig,
367 f: F,
368 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
369 where
370 Self: LoadBlock,
371 F: Fn(
374 TransactionInfo,
375 TracingCtx<
376 '_,
377 Recovered<&ProviderTx<Self::Provider>>,
378 EvmFor<Self::Evm, StateCacheDbRefMutWrapper<'_, '_>, TracingInspector>,
379 >,
380 ) -> Result<R, Self::Error>
381 + Send
382 + 'static,
383 R: Send + 'static,
384 {
385 self.trace_block_until(block_id, block, None, config, f)
386 }
387
388 fn trace_block_inspector<Setup, Insp, F, R>(
403 &self,
404 block_id: BlockId,
405 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
406 insp_setup: Setup,
407 f: F,
408 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
409 where
410 Self: LoadBlock,
411 F: Fn(
414 TransactionInfo,
415 TracingCtx<
416 '_,
417 Recovered<&ProviderTx<Self::Provider>>,
418 EvmFor<Self::Evm, StateCacheDbRefMutWrapper<'_, '_>, Insp>,
419 >,
420 ) -> Result<R, Self::Error>
421 + Send
422 + 'static,
423 Setup: FnMut() -> Insp + Send + 'static,
424 Insp: Clone + for<'a, 'b> InspectorFor<Self::Evm, StateCacheDbRefMutWrapper<'a, 'b>>,
425 R: Send + 'static,
426 {
427 self.trace_block_until_with_inspector(block_id, block, None, insp_setup, f)
428 }
429
430 fn apply_pre_execution_changes<DB: Send + Database + DatabaseCommit>(
436 &self,
437 block: &RecoveredBlock<ProviderBlock<Self::Provider>>,
438 db: &mut DB,
439 evm_env: &EvmEnvFor<Self::Evm>,
440 ) -> Result<(), Self::Error> {
441 let mut system_caller = SystemCaller::new(self.provider().chain_spec());
442
443 let mut evm = self.evm_config().evm_with_env(db, evm_env.clone());
445 system_caller.apply_pre_execution_changes(block.header(), &mut evm).map_err(|err| {
446 EthApiError::EvmCustom(format!("failed to apply 4788 system call {err}"))
447 })?;
448
449 Ok(())
450 }
451}