reth_rpc_eth_api/helpers/
trace.rs1use 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 #[allow(clippy::needless_update)]
308 let tx_info = TransactionInfo {
309 hash: Some(*ctx.tx.tx_hash()),
310 index: Some(idx),
311 block_hash: Some(block_hash),
312 block_number: Some(block_number),
313 base_fee: Some(base_fee),
314 ..Default::default()
315 };
316 idx += 1;
317
318 f(tx_info, ctx)
319 })
320 .collect::<Result<_, _>>()?;
321
322 Ok(Some(results))
323 })
324 .await
325 }
326 }
327
328 fn trace_block_with<F, R>(
339 &self,
340 block_id: BlockId,
341 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
342 config: TracingInspectorConfig,
343 f: F,
344 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
345 where
346 Self: LoadBlock,
347 F: Fn(
350 TransactionInfo,
351 TracingCtx<
352 '_,
353 Recovered<&ProviderTx<Self::Provider>>,
354 EvmFor<Self::Evm, &mut StateCacheDb, TracingInspector>,
355 >,
356 ) -> Result<R, Self::Error>
357 + Send
358 + 'static,
359 R: Send + 'static,
360 {
361 self.trace_block_until(block_id, block, None, config, f)
362 }
363
364 fn trace_block_inspector<Setup, Insp, F, R>(
379 &self,
380 block_id: BlockId,
381 block: Option<Arc<RecoveredBlock<ProviderBlock<Self::Provider>>>>,
382 insp_setup: Setup,
383 f: F,
384 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
385 where
386 Self: LoadBlock,
387 F: Fn(
390 TransactionInfo,
391 TracingCtx<
392 '_,
393 Recovered<&ProviderTx<Self::Provider>>,
394 EvmFor<Self::Evm, &mut StateCacheDb, Insp>,
395 >,
396 ) -> Result<R, Self::Error>
397 + Send
398 + 'static,
399 Setup: FnMut() -> Insp + Send + 'static,
400 Insp: Clone + for<'a> InspectorFor<Self::Evm, &'a mut StateCacheDb>,
401 R: Send + 'static,
402 {
403 self.trace_block_until_with_inspector(block_id, block, None, insp_setup, f)
404 }
405
406 fn apply_pre_execution_changes(
412 &self,
413 block: &RecoveredBlock<ProviderBlock<Self::Provider>>,
414 db: &mut StateCacheDb,
415 ) -> Result<(), Self::Error> {
416 self.evm_config()
417 .executor_for_block(db, block.sealed_block())
418 .map_err(RethError::other)
419 .map_err(Self::Error::from_eth_err)?
420 .apply_pre_execution_changes()
421 .map_err(Self::Error::from_eth_err)?;
422 Ok(())
423 }
424}