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::{ProviderError, RethError};
10use reth_evm::{
11 block::BlockExecutor, evm::EvmFactoryExt, tracing::TracingCtx, ConfigureEvm, Database, Evm,
12 EvmEnvFor, EvmFor, HaltReasonFor, InspectorFor, TxEnvFor,
13};
14use reth_primitives_traits::{BlockBody, Recovered, RecoveredBlock};
15use reth_revm::{
16 database::StateProviderDatabase,
17 db::{bal::EvmDatabaseError, State},
18};
19use reth_rpc_eth_types::cache::db::StateCacheDb;
20use reth_storage_api::{ProviderBlock, ProviderTx};
21use revm::{context::Block, context_interface::result::ResultAndState};
22use revm_inspectors::tracing::{TracingInspector, TracingInspectorConfig};
23use std::sync::Arc;
24
25pub trait Trace: LoadState<Error: FromEvmError<Self::Evm>> + Call {
27 fn inspect<DB, I>(
30 &self,
31 db: DB,
32 evm_env: EvmEnvFor<Self::Evm>,
33 tx_env: TxEnvFor<Self::Evm>,
34 inspector: I,
35 ) -> Result<ResultAndState<HaltReasonFor<Self::Evm>>, Self::Error>
36 where
37 DB: Database<Error = EvmDatabaseError<ProviderError>>,
38 I: InspectorFor<Self::Evm, DB>,
39 {
40 let mut evm = self.evm_config().evm_with_env_and_inspector(db, evm_env, inspector);
41 evm.transact(tx_env).map_err(Self::Error::from_evm_err)
42 }
43
44 fn trace_at<F, R>(
52 &self,
53 evm_env: EvmEnvFor<Self::Evm>,
54 tx_env: TxEnvFor<Self::Evm>,
55 config: TracingInspectorConfig,
56 at: BlockId,
57 f: F,
58 ) -> impl Future<Output = Result<R, Self::Error>> + Send
59 where
60 R: Send + 'static,
61 F: FnOnce(
62 TracingInspector,
63 ResultAndState<HaltReasonFor<Self::Evm>>,
64 ) -> Result<R, Self::Error>
65 + Send
66 + 'static,
67 {
68 self.with_state_at_block(at, move |this, state| {
69 let mut db = State::builder().with_database(StateProviderDatabase::new(state)).build();
70 let mut inspector = TracingInspector::new(config);
71 let res = this.inspect(&mut db, evm_env, tx_env, &mut inspector)?;
72 f(inspector, res)
73 })
74 }
75
76 fn spawn_trace_at_with_state<F, R>(
84 &self,
85 evm_env: EvmEnvFor<Self::Evm>,
86 tx_env: TxEnvFor<Self::Evm>,
87 config: TracingInspectorConfig,
88 at: BlockId,
89 f: F,
90 ) -> impl Future<Output = Result<R, Self::Error>> + Send
91 where
92 F: FnOnce(
93 TracingInspector,
94 ResultAndState<HaltReasonFor<Self::Evm>>,
95 StateCacheDb,
96 ) -> Result<R, Self::Error>
97 + Send
98 + 'static,
99 R: Send + 'static,
100 {
101 self.spawn_with_state_at_block(at, move |this, mut db| {
102 let mut inspector = TracingInspector::new(config);
103 let res = this.inspect(&mut db, evm_env, tx_env, &mut inspector)?;
104 f(inspector, res, db)
105 })
106 }
107
108 fn spawn_trace_transaction_in_block<F, R>(
118 &self,
119 hash: B256,
120 config: TracingInspectorConfig,
121 f: F,
122 ) -> impl Future<Output = Result<Option<R>, Self::Error>> + Send
123 where
124 Self: LoadTransaction,
125 F: FnOnce(
126 TransactionInfo,
127 TracingInspector,
128 ResultAndState<HaltReasonFor<Self::Evm>>,
129 StateCacheDb,
130 ) -> Result<R, Self::Error>
131 + Send
132 + 'static,
133 R: Send + 'static,
134 {
135 self.spawn_trace_transaction_in_block_with_inspector(hash, TracingInspector::new(config), f)
136 }
137
138 fn spawn_trace_transaction_in_block_with_inspector<Insp, F, R>(
148 &self,
149 hash: B256,
150 mut inspector: Insp,
151 f: F,
152 ) -> impl Future<Output = Result<Option<R>, Self::Error>> + Send
153 where
154 Self: LoadTransaction,
155 F: FnOnce(
156 TransactionInfo,
157 Insp,
158 ResultAndState<HaltReasonFor<Self::Evm>>,
159 StateCacheDb,
160 ) -> Result<R, Self::Error>
161 + Send
162 + 'static,
163 Insp: for<'a> InspectorFor<Self::Evm, &'a mut StateCacheDb> + Send + 'static,
164 R: Send + 'static,
165 {
166 async move {
167 let (transaction, block) = match self.transaction_and_block(hash).await? {
168 None => return Ok(None),
169 Some(res) => res,
170 };
171 let (tx, tx_info) = transaction.split();
172
173 let evm_env = self.evm_env_for_header(block.sealed_block().sealed_header())?;
174
175 let parent_block = block.parent_hash();
178
179 self.spawn_with_state_at_block(parent_block, move |this, mut db| {
180 let block_txs = block.transactions_recovered();
181
182 this.apply_pre_execution_changes(&block, &mut db)?;
183
184 this.replay_transactions_until(&mut db, evm_env.clone(), block_txs, *tx.tx_hash())?;
186
187 let tx_env = this.evm_config().tx_env(tx);
188 let res = this.inspect(&mut db, evm_env, tx_env, &mut inspector)?;
189 f(tx_info, inspector, res, db)
190 })
191 .await
192 .map(Some)
193 }
194 }
195
196 fn trace_block_until<F, R>(
203 &self,
204 block_id: BlockId,
205 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
206 highest_index: Option<u64>,
207 config: TracingInspectorConfig,
208 f: F,
209 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
210 where
211 Self: LoadBlock,
212 F: Fn(
213 TransactionInfo,
214 TracingCtx<
215 '_,
216 Recovered<&ProviderTx<Self::Provider>>,
217 EvmFor<Self::Evm, &mut StateCacheDb, TracingInspector>,
218 >,
219 ) -> Result<R, Self::Error>
220 + Send
221 + 'static,
222 R: Send + 'static,
223 {
224 self.trace_block_until_with_inspector(
225 block_id,
226 block,
227 highest_index,
228 move || TracingInspector::new(config),
229 f,
230 )
231 }
232
233 fn trace_block_until_with_inspector<Setup, Insp, F, R>(
244 &self,
245 block_id: BlockId,
246 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
247 highest_index: Option<u64>,
248 mut inspector_setup: Setup,
249 f: F,
250 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
251 where
252 Self: LoadBlock,
253 F: Fn(
254 TransactionInfo,
255 TracingCtx<
256 '_,
257 Recovered<&ProviderTx<Self::Provider>>,
258 EvmFor<Self::Evm, &mut StateCacheDb, Insp>,
259 >,
260 ) -> Result<R, Self::Error>
261 + Send
262 + 'static,
263 Setup: FnMut() -> Insp + Send + 'static,
264 Insp: Clone + for<'a> InspectorFor<Self::Evm, &'a mut StateCacheDb>,
265 R: Send + 'static,
266 {
267 async move {
268 let block =
269 if block.is_some() { block } else { self.recovered_block(block_id).await? };
270
271 let Some(block) = block else { return Ok(None) };
272 let evm_env = self.evm_env_for_header(block.sealed_block().sealed_header())?;
273
274 if block.body().transactions().is_empty() {
275 return Ok(Some(Vec::new()))
277 }
278
279 self.spawn_with_state_at_block(block.parent_hash(), move |this, mut db| {
283 let block_hash = block.hash();
284
285 let block_number = evm_env.block_env.number().saturating_to();
286 let base_fee = evm_env.block_env.basefee();
287
288 this.apply_pre_execution_changes(&block, &mut db)?;
289
290 let max_transactions = highest_index.map_or_else(
293 || block.body().transaction_count(),
294 |highest| {
295 highest as usize + 1
297 },
298 );
299
300 let mut idx = 0;
301
302 let results = this
303 .evm_config()
304 .evm_factory()
305 .create_tracer(&mut db, evm_env, inspector_setup())
306 .try_trace_many(block.transactions_recovered().take(max_transactions), |ctx| {
307 let tx_info = TransactionInfo {
308 hash: Some(*ctx.tx.tx_hash()),
309 index: Some(idx),
310 block_hash: Some(block_hash),
311 block_number: Some(block_number),
312 base_fee: Some(base_fee),
313 ..Default::default()
314 };
315 idx += 1;
316
317 f(tx_info, ctx)
318 })
319 .collect::<Result<_, _>>()?;
320
321 Ok(Some(results))
322 })
323 .await
324 }
325 }
326
327 fn trace_block_with<F, R>(
338 &self,
339 block_id: BlockId,
340 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
341 config: TracingInspectorConfig,
342 f: F,
343 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
344 where
345 Self: LoadBlock,
346 F: Fn(
349 TransactionInfo,
350 TracingCtx<
351 '_,
352 Recovered<&ProviderTx<Self::Provider>>,
353 EvmFor<Self::Evm, &mut StateCacheDb, TracingInspector>,
354 >,
355 ) -> Result<R, Self::Error>
356 + Send
357 + 'static,
358 R: Send + 'static,
359 {
360 self.trace_block_until(block_id, block, None, config, f)
361 }
362
363 fn trace_block_inspector<Setup, Insp, F, R>(
378 &self,
379 block_id: BlockId,
380 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
381 insp_setup: Setup,
382 f: F,
383 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
384 where
385 Self: LoadBlock,
386 F: Fn(
389 TransactionInfo,
390 TracingCtx<
391 '_,
392 Recovered<&ProviderTx<Self::Provider>>,
393 EvmFor<Self::Evm, &mut StateCacheDb, Insp>,
394 >,
395 ) -> Result<R, Self::Error>
396 + Send
397 + 'static,
398 Setup: FnMut() -> Insp + Send + 'static,
399 Insp: Clone + for<'a> InspectorFor<Self::Evm, &'a mut StateCacheDb>,
400 R: Send + 'static,
401 {
402 self.trace_block_until_with_inspector(block_id, block, None, insp_setup, f)
403 }
404
405 fn apply_pre_execution_changes(
411 &self,
412 block: &RecoveredBlock<ProviderBlock<Self::Provider>>,
413 db: &mut StateCacheDb,
414 ) -> Result<(), Self::Error> {
415 self.evm_config()
416 .executor_for_block(db, block.sealed_block())
417 .map_err(RethError::other)
418 .map_err(Self::Error::from_eth_err)?
419 .apply_pre_execution_changes()
420 .map_err(Self::Error::from_eth_err)?;
421 Ok(())
422 }
423}