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::{database::StateProviderDatabase, db::State};
17use reth_rpc_eth_types::{cache::db::StateCacheDb, EthApiError};
18use reth_storage_api::{ProviderBlock, ProviderTx};
19use revm::{context::Block, context_interface::result::ResultAndState, DatabaseCommit};
20use revm_inspectors::tracing::{TracingInspector, TracingInspectorConfig};
21use std::sync::Arc;
22
23pub trait Trace: LoadState<Error: FromEvmError<Self::Evm>> + Call {
25 fn inspect<DB, I>(
28 &self,
29 db: DB,
30 evm_env: EvmEnvFor<Self::Evm>,
31 tx_env: TxEnvFor<Self::Evm>,
32 inspector: I,
33 ) -> Result<ResultAndState<HaltReasonFor<Self::Evm>>, Self::Error>
34 where
35 DB: Database<Error = ProviderError>,
36 I: InspectorFor<Self::Evm, DB>,
37 {
38 let mut evm = self.evm_config().evm_with_env_and_inspector(db, evm_env, inspector);
39 evm.transact(tx_env).map_err(Self::Error::from_evm_err)
40 }
41
42 fn trace_at<F, R>(
50 &self,
51 evm_env: EvmEnvFor<Self::Evm>,
52 tx_env: TxEnvFor<Self::Evm>,
53 config: TracingInspectorConfig,
54 at: BlockId,
55 f: F,
56 ) -> impl Future<Output = Result<R, Self::Error>> + Send
57 where
58 R: Send + 'static,
59 F: FnOnce(
60 TracingInspector,
61 ResultAndState<HaltReasonFor<Self::Evm>>,
62 ) -> Result<R, Self::Error>
63 + Send
64 + 'static,
65 {
66 self.with_state_at_block(at, move |this, state| {
67 let mut db = State::builder().with_database(StateProviderDatabase::new(state)).build();
68 let mut inspector = TracingInspector::new(config);
69 let res = this.inspect(&mut db, evm_env, tx_env, &mut inspector)?;
70 f(inspector, res)
71 })
72 }
73
74 fn spawn_trace_at_with_state<F, R>(
82 &self,
83 evm_env: EvmEnvFor<Self::Evm>,
84 tx_env: TxEnvFor<Self::Evm>,
85 config: TracingInspectorConfig,
86 at: BlockId,
87 f: F,
88 ) -> impl Future<Output = Result<R, Self::Error>> + Send
89 where
90 F: FnOnce(
91 TracingInspector,
92 ResultAndState<HaltReasonFor<Self::Evm>>,
93 StateCacheDb,
94 ) -> Result<R, Self::Error>
95 + Send
96 + 'static,
97 R: Send + 'static,
98 {
99 self.spawn_with_state_at_block(at, move |this, mut db| {
100 let mut inspector = TracingInspector::new(config);
101 let res = this.inspect(&mut db, evm_env, tx_env, &mut inspector)?;
102 f(inspector, res, db)
103 })
104 }
105
106 fn spawn_trace_transaction_in_block<F, R>(
116 &self,
117 hash: B256,
118 config: TracingInspectorConfig,
119 f: F,
120 ) -> impl Future<Output = Result<Option<R>, Self::Error>> + Send
121 where
122 Self: LoadTransaction,
123 F: FnOnce(
124 TransactionInfo,
125 TracingInspector,
126 ResultAndState<HaltReasonFor<Self::Evm>>,
127 StateCacheDb,
128 ) -> Result<R, Self::Error>
129 + Send
130 + 'static,
131 R: Send + 'static,
132 {
133 self.spawn_trace_transaction_in_block_with_inspector(hash, TracingInspector::new(config), f)
134 }
135
136 fn spawn_trace_transaction_in_block_with_inspector<Insp, F, R>(
146 &self,
147 hash: B256,
148 mut inspector: Insp,
149 f: F,
150 ) -> impl Future<Output = Result<Option<R>, Self::Error>> + Send
151 where
152 Self: LoadTransaction,
153 F: FnOnce(
154 TransactionInfo,
155 Insp,
156 ResultAndState<HaltReasonFor<Self::Evm>>,
157 StateCacheDb,
158 ) -> Result<R, Self::Error>
159 + Send
160 + 'static,
161 Insp: for<'a> InspectorFor<Self::Evm, &'a mut StateCacheDb> + Send + 'static,
162 R: Send + 'static,
163 {
164 async move {
165 let (transaction, block) = match self.transaction_and_block(hash).await? {
166 None => return Ok(None),
167 Some(res) => res,
168 };
169 let (tx, tx_info) = transaction.split();
170
171 let (evm_env, _) = self.evm_env_at(block.hash().into()).await?;
172
173 let parent_block = block.parent_hash();
176
177 self.spawn_with_state_at_block(parent_block, move |this, mut db| {
178 let block_txs = block.transactions_recovered();
179
180 this.apply_pre_execution_changes(&block, &mut db, &evm_env)?;
181
182 this.replay_transactions_until(&mut db, evm_env.clone(), block_txs, *tx.tx_hash())?;
184
185 let tx_env = this.evm_config().tx_env(tx);
186 let res = this.inspect(&mut db, evm_env, tx_env, &mut inspector)?;
187 f(tx_info, inspector, res, db)
188 })
189 .await
190 .map(Some)
191 }
192 }
193
194 fn trace_block_until<F, R>(
201 &self,
202 block_id: BlockId,
203 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
204 highest_index: Option<u64>,
205 config: TracingInspectorConfig,
206 f: F,
207 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
208 where
209 Self: LoadBlock,
210 F: Fn(
211 TransactionInfo,
212 TracingCtx<
213 '_,
214 Recovered<&ProviderTx<Self::Provider>>,
215 EvmFor<Self::Evm, &mut StateCacheDb, TracingInspector>,
216 >,
217 ) -> Result<R, Self::Error>
218 + Send
219 + 'static,
220 R: Send + 'static,
221 {
222 self.trace_block_until_with_inspector(
223 block_id,
224 block,
225 highest_index,
226 move || TracingInspector::new(config),
227 f,
228 )
229 }
230
231 fn trace_block_until_with_inspector<Setup, Insp, F, R>(
242 &self,
243 block_id: BlockId,
244 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
245 highest_index: Option<u64>,
246 mut inspector_setup: Setup,
247 f: F,
248 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
249 where
250 Self: LoadBlock,
251 F: Fn(
252 TransactionInfo,
253 TracingCtx<
254 '_,
255 Recovered<&ProviderTx<Self::Provider>>,
256 EvmFor<Self::Evm, &mut StateCacheDb, Insp>,
257 >,
258 ) -> Result<R, Self::Error>
259 + Send
260 + 'static,
261 Setup: FnMut() -> Insp + Send + 'static,
262 Insp: Clone + for<'a> InspectorFor<Self::Evm, &'a mut StateCacheDb>,
263 R: Send + 'static,
264 {
265 async move {
266 let block = async {
267 if block.is_some() {
268 return Ok(block)
269 }
270 self.recovered_block(block_id).await
271 };
272
273 let ((evm_env, _), block) = futures::try_join!(self.evm_env_at(block_id), block)?;
274
275 let Some(block) = block else { return Ok(None) };
276
277 if block.body().transactions().is_empty() {
278 return Ok(Some(Vec::new()))
280 }
281
282 self.spawn_with_state_at_block(block.parent_hash(), move |this, mut db| {
286 let block_hash = block.hash();
287
288 let block_number = evm_env.block_env.number().saturating_to();
289 let base_fee = evm_env.block_env.basefee();
290
291 this.apply_pre_execution_changes(&block, &mut db, &evm_env)?;
292
293 let max_transactions = highest_index.map_or_else(
296 || block.body().transaction_count(),
297 |highest| {
298 highest as usize + 1
300 },
301 );
302
303 let mut idx = 0;
304
305 let results = this
306 .evm_config()
307 .evm_factory()
308 .create_tracer(&mut db, evm_env, inspector_setup())
309 .try_trace_many(block.transactions_recovered().take(max_transactions), |ctx| {
310 let tx_info = TransactionInfo {
311 hash: Some(*ctx.tx.tx_hash()),
312 index: Some(idx),
313 block_hash: Some(block_hash),
314 block_number: Some(block_number),
315 base_fee: Some(base_fee),
316 };
317 idx += 1;
318
319 f(tx_info, ctx)
320 })
321 .collect::<Result<_, _>>()?;
322
323 Ok(Some(results))
324 })
325 .await
326 }
327 }
328
329 fn trace_block_with<F, R>(
340 &self,
341 block_id: BlockId,
342 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
343 config: TracingInspectorConfig,
344 f: F,
345 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
346 where
347 Self: LoadBlock,
348 F: Fn(
351 TransactionInfo,
352 TracingCtx<
353 '_,
354 Recovered<&ProviderTx<Self::Provider>>,
355 EvmFor<Self::Evm, &mut StateCacheDb, TracingInspector>,
356 >,
357 ) -> Result<R, Self::Error>
358 + Send
359 + 'static,
360 R: Send + 'static,
361 {
362 self.trace_block_until(block_id, block, None, config, f)
363 }
364
365 fn trace_block_inspector<Setup, Insp, F, R>(
380 &self,
381 block_id: BlockId,
382 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
383 insp_setup: Setup,
384 f: F,
385 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
386 where
387 Self: LoadBlock,
388 F: Fn(
391 TransactionInfo,
392 TracingCtx<
393 '_,
394 Recovered<&ProviderTx<Self::Provider>>,
395 EvmFor<Self::Evm, &mut StateCacheDb, Insp>,
396 >,
397 ) -> Result<R, Self::Error>
398 + Send
399 + 'static,
400 Setup: FnMut() -> Insp + Send + 'static,
401 Insp: Clone + for<'a> InspectorFor<Self::Evm, &'a mut StateCacheDb>,
402 R: Send + 'static,
403 {
404 self.trace_block_until_with_inspector(block_id, block, None, insp_setup, f)
405 }
406
407 fn apply_pre_execution_changes<DB: Send + Database + DatabaseCommit>(
413 &self,
414 block: &RecoveredBlock<ProviderBlock<Self::Provider>>,
415 db: &mut DB,
416 evm_env: &EvmEnvFor<Self::Evm>,
417 ) -> Result<(), Self::Error> {
418 let mut system_caller = SystemCaller::new(self.provider().chain_spec());
419
420 let mut evm = self.evm_config().evm_with_env(db, evm_env.clone());
422 system_caller.apply_pre_execution_changes(block.header(), &mut evm).map_err(|err| {
423 EthApiError::EvmCustom(format!("failed to apply 4788 system call {err}"))
424 })?;
425
426 Ok(())
427 }
428}