1use super::{Call, LoadBlock, 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::{
17 database::StateProviderDatabase,
18 db::{bal::EvmDatabaseError, State},
19};
20use reth_rpc_eth_types::{cache::db::StateCacheDb, EthApiError};
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>> + Call {
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 = EvmDatabaseError<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 R: Send + 'static,
62 F: FnOnce(
63 TracingInspector,
64 ResultAndState<HaltReasonFor<Self::Evm>>,
65 ) -> Result<R, Self::Error>
66 + Send
67 + 'static,
68 {
69 self.with_state_at_block(at, move |this, state| {
70 let mut db = State::builder().with_database(StateProviderDatabase::new(state)).build();
71 let mut inspector = TracingInspector::new(config);
72 let res = this.inspect(&mut db, evm_env, tx_env, &mut inspector)?;
73 f(inspector, res)
74 })
75 }
76
77 fn spawn_trace_at_with_state<F, R>(
85 &self,
86 evm_env: EvmEnvFor<Self::Evm>,
87 tx_env: TxEnvFor<Self::Evm>,
88 config: TracingInspectorConfig,
89 at: BlockId,
90 f: F,
91 ) -> impl Future<Output = Result<R, Self::Error>> + Send
92 where
93 F: FnOnce(
94 TracingInspector,
95 ResultAndState<HaltReasonFor<Self::Evm>>,
96 StateCacheDb,
97 ) -> Result<R, Self::Error>
98 + Send
99 + 'static,
100 R: Send + 'static,
101 {
102 self.spawn_with_state_at_block(at, move |this, mut db| {
103 let mut inspector = TracingInspector::new(config);
104 let res = this.inspect(&mut db, evm_env, tx_env, &mut inspector)?;
105 f(inspector, res, db)
106 })
107 }
108
109 fn spawn_trace_transaction_in_block<F, R>(
119 &self,
120 hash: B256,
121 config: TracingInspectorConfig,
122 f: F,
123 ) -> impl Future<Output = Result<Option<R>, Self::Error>> + Send
124 where
125 Self: LoadTransaction,
126 F: FnOnce(
127 TransactionInfo,
128 TracingInspector,
129 ResultAndState<HaltReasonFor<Self::Evm>>,
130 StateCacheDb,
131 ) -> Result<R, Self::Error>
132 + Send
133 + 'static,
134 R: Send + 'static,
135 {
136 self.spawn_trace_transaction_in_block_with_inspector(hash, TracingInspector::new(config), f)
137 }
138
139 fn spawn_trace_transaction_in_block_with_inspector<Insp, F, R>(
149 &self,
150 hash: B256,
151 mut inspector: Insp,
152 f: F,
153 ) -> impl Future<Output = Result<Option<R>, Self::Error>> + Send
154 where
155 Self: LoadTransaction,
156 F: FnOnce(
157 TransactionInfo,
158 Insp,
159 ResultAndState<HaltReasonFor<Self::Evm>>,
160 StateCacheDb,
161 ) -> Result<R, Self::Error>
162 + Send
163 + 'static,
164 Insp: for<'a> InspectorFor<Self::Evm, &'a mut StateCacheDb> + Send + 'static,
165 R: Send + 'static,
166 {
167 async move {
168 let (transaction, block) = match self.transaction_and_block(hash).await? {
169 None => return Ok(None),
170 Some(res) => res,
171 };
172 let (tx, tx_info) = transaction.split();
173
174 let evm_env = self.evm_env_for_header(block.sealed_block().sealed_header())?;
175
176 let parent_block = block.parent_hash();
179
180 self.spawn_with_state_at_block(parent_block, move |this, mut db| {
181 let block_txs = block.transactions_recovered();
182
183 this.apply_pre_execution_changes(&block, &mut db, &evm_env)?;
184
185 this.replay_transactions_until(&mut db, evm_env.clone(), block_txs, *tx.tx_hash())?;
187
188 let tx_env = this.evm_config().tx_env(tx);
189 let res = this.inspect(&mut db, evm_env, tx_env, &mut inspector)?;
190 f(tx_info, inspector, res, db)
191 })
192 .await
193 .map(Some)
194 }
195 }
196
197 fn trace_block_until<F, R>(
204 &self,
205 block_id: BlockId,
206 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
207 highest_index: Option<u64>,
208 config: TracingInspectorConfig,
209 f: F,
210 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
211 where
212 Self: LoadBlock,
213 F: Fn(
214 TransactionInfo,
215 TracingCtx<
216 '_,
217 Recovered<&ProviderTx<Self::Provider>>,
218 EvmFor<Self::Evm, &mut StateCacheDb, TracingInspector>,
219 >,
220 ) -> Result<R, Self::Error>
221 + Send
222 + 'static,
223 R: Send + 'static,
224 {
225 self.trace_block_until_with_inspector(
226 block_id,
227 block,
228 highest_index,
229 move || TracingInspector::new(config),
230 f,
231 )
232 }
233
234 fn trace_block_until_with_inspector<Setup, Insp, F, R>(
245 &self,
246 block_id: BlockId,
247 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
248 highest_index: Option<u64>,
249 mut inspector_setup: Setup,
250 f: F,
251 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
252 where
253 Self: LoadBlock,
254 F: Fn(
255 TransactionInfo,
256 TracingCtx<
257 '_,
258 Recovered<&ProviderTx<Self::Provider>>,
259 EvmFor<Self::Evm, &mut StateCacheDb, Insp>,
260 >,
261 ) -> Result<R, Self::Error>
262 + Send
263 + 'static,
264 Setup: FnMut() -> Insp + Send + 'static,
265 Insp: Clone + for<'a> InspectorFor<Self::Evm, &'a mut StateCacheDb>,
266 R: Send + 'static,
267 {
268 async move {
269 let block = async {
270 if block.is_some() {
271 return Ok(block)
272 }
273 self.recovered_block(block_id).await
274 };
275
276 let ((evm_env, _), block) = futures::try_join!(self.evm_env_at(block_id), block)?;
277
278 let Some(block) = block else { return Ok(None) };
279
280 if block.body().transactions().is_empty() {
281 return Ok(Some(Vec::new()))
283 }
284
285 self.spawn_with_state_at_block(block.parent_hash(), move |this, mut db| {
289 let block_hash = block.hash();
290
291 let block_number = evm_env.block_env.number().saturating_to();
292 let base_fee = evm_env.block_env.basefee();
293
294 this.apply_pre_execution_changes(&block, &mut db, &evm_env)?;
295
296 let max_transactions = highest_index.map_or_else(
299 || block.body().transaction_count(),
300 |highest| {
301 highest as usize + 1
303 },
304 );
305
306 let mut idx = 0;
307
308 let results = this
309 .evm_config()
310 .evm_factory()
311 .create_tracer(&mut db, evm_env, inspector_setup())
312 .try_trace_many(block.transactions_recovered().take(max_transactions), |ctx| {
313 #[allow(clippy::needless_update)]
314 let tx_info = TransactionInfo {
315 hash: Some(*ctx.tx.tx_hash()),
316 index: Some(idx),
317 block_hash: Some(block_hash),
318 block_number: Some(block_number),
319 base_fee: Some(base_fee),
320 ..Default::default()
321 };
322 idx += 1;
323
324 f(tx_info, ctx)
325 })
326 .collect::<Result<_, _>>()?;
327
328 Ok(Some(results))
329 })
330 .await
331 }
332 }
333
334 fn trace_block_with<F, R>(
345 &self,
346 block_id: BlockId,
347 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
348 config: TracingInspectorConfig,
349 f: F,
350 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
351 where
352 Self: LoadBlock,
353 F: Fn(
356 TransactionInfo,
357 TracingCtx<
358 '_,
359 Recovered<&ProviderTx<Self::Provider>>,
360 EvmFor<Self::Evm, &mut StateCacheDb, TracingInspector>,
361 >,
362 ) -> Result<R, Self::Error>
363 + Send
364 + 'static,
365 R: Send + 'static,
366 {
367 self.trace_block_until(block_id, block, None, config, f)
368 }
369
370 fn trace_block_inspector<Setup, Insp, F, R>(
385 &self,
386 block_id: BlockId,
387 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
388 insp_setup: Setup,
389 f: F,
390 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
391 where
392 Self: LoadBlock,
393 F: Fn(
396 TransactionInfo,
397 TracingCtx<
398 '_,
399 Recovered<&ProviderTx<Self::Provider>>,
400 EvmFor<Self::Evm, &mut StateCacheDb, Insp>,
401 >,
402 ) -> Result<R, Self::Error>
403 + Send
404 + 'static,
405 Setup: FnMut() -> Insp + Send + 'static,
406 Insp: Clone + for<'a> InspectorFor<Self::Evm, &'a mut StateCacheDb>,
407 R: Send + 'static,
408 {
409 self.trace_block_until_with_inspector(block_id, block, None, insp_setup, f)
410 }
411
412 fn apply_pre_execution_changes<DB: Send + Database + DatabaseCommit>(
418 &self,
419 block: &RecoveredBlock<ProviderBlock<Self::Provider>>,
420 db: &mut DB,
421 evm_env: &EvmEnvFor<Self::Evm>,
422 ) -> Result<(), Self::Error> {
423 let mut system_caller = SystemCaller::new(self.provider().chain_spec());
424
425 let mut evm = self.evm_config().evm_with_env(db, evm_env.clone());
427 system_caller.apply_pre_execution_changes(block.header(), &mut evm).map_err(|err| {
428 EthApiError::EvmCustom(format!("failed to apply 4788 system call {err}"))
429 })?;
430
431 Ok(())
432 }
433}