1use crate::{FromEvmError, RpcNodeCore};
4use alloy_consensus::{BlockHeader, Typed2718};
5use alloy_primitives::B256;
6use alloy_rpc_types_eth::{BlockId, TransactionInfo};
7use futures::Future;
8use reth_chainspec::ChainSpecProvider;
9use reth_evm::{system_calls::SystemCaller, ConfigureEvm, ConfigureEvmEnv};
10use reth_primitives::SealedBlockWithSenders;
11use reth_primitives_traits::{BlockBody, SignedTransaction};
12use reth_provider::{BlockReader, ProviderBlock, ProviderHeader, ProviderTx};
13use reth_revm::database::StateProviderDatabase;
14use reth_rpc_eth_types::{
15 cache::db::{StateCacheDb, StateCacheDbRefMutWrapper, StateProviderTraitObjWrapper},
16 EthApiError,
17};
18use revm::{db::CacheDB, Database, DatabaseCommit, GetInspector, Inspector};
19use revm_inspectors::tracing::{TracingInspector, TracingInspectorConfig};
20use revm_primitives::{
21 BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, EvmState, ExecutionResult, ResultAndState,
22};
23use std::{fmt::Display, sync::Arc};
24
25use super::{Call, LoadBlock, LoadPendingBlock, LoadState, LoadTransaction};
26
27pub trait Trace:
29 LoadState<
30 Provider: BlockReader,
31 Evm: ConfigureEvm<
32 Header = ProviderHeader<Self::Provider>,
33 Transaction = ProviderTx<Self::Provider>,
34 >,
35>
36{
37 fn inspect<DB, I>(
40 &self,
41 db: DB,
42 env: EnvWithHandlerCfg,
43 inspector: I,
44 ) -> Result<(ResultAndState, EnvWithHandlerCfg), Self::Error>
45 where
46 DB: Database,
47 EthApiError: From<DB::Error>,
48 I: GetInspector<DB>,
49 {
50 self.inspect_and_return_db(db, env, inspector).map(|(res, env, _)| (res, env))
51 }
52
53 fn inspect_and_return_db<DB, I>(
59 &self,
60 db: DB,
61 env: EnvWithHandlerCfg,
62 inspector: I,
63 ) -> Result<(ResultAndState, EnvWithHandlerCfg, DB), Self::Error>
64 where
65 DB: Database,
66 EthApiError: From<DB::Error>,
67
68 I: GetInspector<DB>,
69 {
70 let mut evm = self.evm_config().evm_with_env_and_inspector(db, env, inspector);
71 let res = evm.transact().map_err(Self::Error::from_evm_err)?;
72 let (db, env) = evm.into_db_and_env_with_handler_cfg();
73 Ok((res, env, db))
74 }
75
76 fn trace_at<F, R>(
84 &self,
85 env: EnvWithHandlerCfg,
86 config: TracingInspectorConfig,
87 at: BlockId,
88 f: F,
89 ) -> Result<R, Self::Error>
90 where
91 Self: Call,
92 F: FnOnce(TracingInspector, ResultAndState) -> Result<R, Self::Error>,
93 {
94 self.with_state_at_block(at, |state| {
95 let mut db = CacheDB::new(StateProviderDatabase::new(state));
96 let mut inspector = TracingInspector::new(config);
97 let (res, _) = self.inspect(&mut db, env, &mut inspector)?;
98 f(inspector, res)
99 })
100 }
101
102 fn spawn_trace_at_with_state<F, R>(
110 &self,
111 env: EnvWithHandlerCfg,
112 config: TracingInspectorConfig,
113 at: BlockId,
114 f: F,
115 ) -> impl Future<Output = Result<R, Self::Error>> + Send
116 where
117 Self: LoadPendingBlock + Call,
118 F: FnOnce(TracingInspector, ResultAndState, StateCacheDb<'_>) -> Result<R, Self::Error>
119 + Send
120 + 'static,
121 R: Send + 'static,
122 {
123 let this = self.clone();
124 self.spawn_with_state_at_block(at, move |state| {
125 let mut db = CacheDB::new(StateProviderDatabase::new(state));
126 let mut inspector = TracingInspector::new(config);
127 let (res, _) = this.inspect(&mut db, env, &mut inspector)?;
128 f(inspector, res, db)
129 })
130 }
131
132 fn spawn_trace_transaction_in_block<F, R>(
142 &self,
143 hash: B256,
144 config: TracingInspectorConfig,
145 f: F,
146 ) -> impl Future<Output = Result<Option<R>, Self::Error>> + Send
147 where
148 Self: LoadPendingBlock + LoadTransaction + Call,
149 F: FnOnce(
150 TransactionInfo,
151 TracingInspector,
152 ResultAndState,
153 StateCacheDb<'_>,
154 ) -> Result<R, Self::Error>
155 + Send
156 + 'static,
157 R: Send + 'static,
158 {
159 self.spawn_trace_transaction_in_block_with_inspector(hash, TracingInspector::new(config), f)
160 }
161
162 fn spawn_trace_transaction_in_block_with_inspector<Insp, F, R>(
172 &self,
173 hash: B256,
174 mut inspector: Insp,
175 f: F,
176 ) -> impl Future<Output = Result<Option<R>, Self::Error>> + Send
177 where
178 Self: LoadPendingBlock + LoadTransaction + Call,
179 F: FnOnce(
180 TransactionInfo,
181 Insp,
182 ResultAndState,
183 StateCacheDb<'_>,
184 ) -> Result<R, Self::Error>
185 + Send
186 + 'static,
187 Insp: for<'a, 'b> Inspector<StateCacheDbRefMutWrapper<'a, 'b>> + Send + 'static,
188 R: Send + 'static,
189 {
190 async move {
191 let (transaction, block) = match self.transaction_and_block(hash).await? {
192 None => return Ok(None),
193 Some(res) => res,
194 };
195 let (tx, tx_info) = transaction.split();
196
197 let (cfg, block_env, _) = self.evm_env_at(block.hash().into()).await?;
198
199 let parent_block = block.parent_hash();
202
203 let this = self.clone();
204 self.spawn_with_state_at_block(parent_block.into(), move |state| {
205 let mut db = CacheDB::new(StateProviderDatabase::new(state));
206 let block_txs = block.transactions_with_sender();
207
208 this.apply_pre_execution_changes(&block, &mut db, &cfg, &block_env)?;
209
210 this.replay_transactions_until(
212 &mut db,
213 cfg.clone(),
214 block_env.clone(),
215 block_txs,
216 *tx.tx_hash(),
217 )?;
218
219 let env = EnvWithHandlerCfg::new_with_cfg_env(
220 cfg,
221 block_env,
222 RpcNodeCore::evm_config(&this)
223 .tx_env(tx.as_signed(), tx.signer())
224 .map_err(|_| EthApiError::FailedToDecodeSignedTransaction)?,
225 );
226 let (res, _) =
227 this.inspect(StateCacheDbRefMutWrapper(&mut db), env, &mut inspector)?;
228 f(tx_info, inspector, res, db)
229 })
230 .await
231 .map(Some)
232 }
233 }
234
235 fn trace_block_until<F, R>(
242 &self,
243 block_id: BlockId,
244 block: Option<Arc<SealedBlockWithSenders<ProviderBlock<Self::Provider>>>>,
245 highest_index: Option<u64>,
246 config: TracingInspectorConfig,
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 TracingInspector,
254 ExecutionResult,
255 &EvmState,
256 &StateCacheDb<'_>,
257 ) -> Result<R, Self::Error>
258 + Send
259 + 'static,
260 R: Send + 'static,
261 {
262 self.trace_block_until_with_inspector(
263 block_id,
264 block,
265 highest_index,
266 move || TracingInspector::new(config),
267 f,
268 )
269 }
270
271 fn trace_block_until_with_inspector<Setup, Insp, F, R>(
282 &self,
283 block_id: BlockId,
284 block: Option<Arc<SealedBlockWithSenders<ProviderBlock<Self::Provider>>>>,
285 highest_index: Option<u64>,
286 mut inspector_setup: Setup,
287 f: F,
288 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
289 where
290 Self: LoadBlock,
291 F: Fn(
292 TransactionInfo,
293 Insp,
294 ExecutionResult,
295 &EvmState,
296 &StateCacheDb<'_>,
297 ) -> Result<R, Self::Error>
298 + Send
299 + 'static,
300 Setup: FnMut() -> Insp + Send + 'static,
301 Insp: for<'a, 'b> Inspector<StateCacheDbRefMutWrapper<'a, 'b>> + Send + 'static,
302 R: Send + 'static,
303 {
304 async move {
305 let block = async {
306 if block.is_some() {
307 return Ok(block)
308 }
309 self.block_with_senders(block_id).await
310 };
311
312 let ((cfg, block_env, _), block) =
313 futures::try_join!(self.evm_env_at(block_id), block)?;
314
315 let Some(block) = block else { return Ok(None) };
316
317 if block.body.transactions().is_empty() {
318 return Ok(Some(Vec::new()))
320 }
321
322 self.spawn_tracing(move |this| {
324 let state_at = block.parent_hash();
327 let block_hash = block.hash();
328
329 let block_number = block_env.number.saturating_to::<u64>();
330 let base_fee = block_env.basefee.saturating_to::<u128>();
331
332 let state = this.state_at_block_id(state_at.into())?;
334 let mut db =
335 CacheDB::new(StateProviderDatabase::new(StateProviderTraitObjWrapper(&state)));
336
337 this.apply_pre_execution_changes(&block, &mut db, &cfg, &block_env)?;
338
339 let max_transactions =
342 highest_index.map_or(block.body.transactions().len(), |highest| {
343 highest as usize + 1
345 });
346 let mut results = Vec::with_capacity(max_transactions);
347
348 let mut transactions = block
349 .transactions_with_sender()
350 .take(max_transactions)
351 .enumerate()
352 .map(|(idx, (signer, tx))| {
353 let tx_info = TransactionInfo {
354 hash: Some(*tx.tx_hash()),
355 index: Some(idx as u64),
356 block_hash: Some(block_hash),
357 block_number: Some(block_number),
358 base_fee: Some(base_fee),
359 tx_type: Some(tx.ty() as isize),
360 };
361 let tx_env = this
362 .evm_config()
363 .tx_env(tx, *signer)
364 .map_err(|_| EthApiError::FailedToDecodeSignedTransaction)?;
365 Ok((tx_info, tx_env))
366 })
367 .peekable();
368
369 while let Some(res) = transactions.next() {
370 match res {
371 Ok((tx_info, tx)) => {
372 let env = EnvWithHandlerCfg::new_with_cfg_env(
373 cfg.clone(),
374 block_env.clone(),
375 tx,
376 );
377
378 let mut inspector = inspector_setup();
379 let (res, _) = this.inspect(
380 StateCacheDbRefMutWrapper(&mut db),
381 env,
382 &mut inspector,
383 )?;
384 let ResultAndState { result, state } = res;
385 results.push(f(tx_info, inspector, result, &state, &db)?);
386
387 if transactions.peek().is_some() {
391 db.commit(state)
393 }
394 }
395 Err(err) => return Err(err),
396 }
397 }
398
399 Ok(Some(results))
400 })
401 .await
402 }
403 }
404
405 fn trace_block_with<F, R>(
416 &self,
417 block_id: BlockId,
418 block: Option<Arc<SealedBlockWithSenders<ProviderBlock<Self::Provider>>>>,
419 config: TracingInspectorConfig,
420 f: F,
421 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
422 where
423 Self: LoadBlock,
424 F: Fn(
427 TransactionInfo,
428 TracingInspector,
429 ExecutionResult,
430 &EvmState,
431 &StateCacheDb<'_>,
432 ) -> Result<R, Self::Error>
433 + Send
434 + 'static,
435 R: Send + 'static,
436 {
437 self.trace_block_until(block_id, block, None, config, f)
438 }
439
440 fn trace_block_inspector<Setup, Insp, F, R>(
455 &self,
456 block_id: BlockId,
457 block: Option<Arc<SealedBlockWithSenders<ProviderBlock<Self::Provider>>>>,
458 insp_setup: Setup,
459 f: F,
460 ) -> impl Future<Output = Result<Option<Vec<R>>, Self::Error>> + Send
461 where
462 Self: LoadBlock,
463 F: Fn(
466 TransactionInfo,
467 Insp,
468 ExecutionResult,
469 &EvmState,
470 &StateCacheDb<'_>,
471 ) -> Result<R, Self::Error>
472 + Send
473 + 'static,
474 Setup: FnMut() -> Insp + Send + 'static,
475 Insp: for<'a, 'b> Inspector<StateCacheDbRefMutWrapper<'a, 'b>> + Send + 'static,
476 R: Send + 'static,
477 {
478 self.trace_block_until_with_inspector(block_id, block, None, insp_setup, f)
479 }
480
481 fn apply_pre_execution_changes<DB: Send + Database<Error: Display> + DatabaseCommit>(
487 &self,
488 block: &SealedBlockWithSenders<ProviderBlock<Self::Provider>>,
489 db: &mut DB,
490 cfg: &CfgEnvWithHandlerCfg,
491 block_env: &BlockEnv,
492 ) -> Result<(), Self::Error> {
493 let mut system_caller =
494 SystemCaller::new(self.evm_config().clone(), self.provider().chain_spec());
495 system_caller
497 .pre_block_beacon_root_contract_call(
498 db,
499 cfg,
500 block_env,
501 block.header.parent_beacon_block_root(),
502 )
503 .map_err(|_| EthApiError::EvmCustom("failed to apply 4788 system call".to_string()))?;
504
505 system_caller
506 .pre_block_blockhashes_contract_call(db, cfg, block_env, block.header.parent_hash())
507 .map_err(|_| {
508 EthApiError::EvmCustom("failed to apply blockhashes system call".to_string())
509 })?;
510
511 Ok(())
512 }
513}