1use alloy_consensus::BlockHeader as _;
2use alloy_eips::BlockId;
3use alloy_evm::block::calc::{base_block_reward_pre_merge, block_reward, ommer_reward};
4use alloy_primitives::{
5 map::{HashMap, HashSet},
6 Address, BlockHash, Bytes, B256, U256,
7};
8use alloy_rpc_types_eth::{
9 state::{EvmOverrides, StateOverride},
10 BlockOverrides, Index,
11};
12use alloy_rpc_types_trace::{
13 filter::TraceFilter,
14 opcode::{BlockOpcodeGas, TransactionOpcodeGas},
15 parity::*,
16 tracerequest::TraceCallRequest,
17};
18use async_trait::async_trait;
19use jsonrpsee::core::RpcResult;
20use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardfork, MAINNET, SEPOLIA};
21use reth_evm::ConfigureEvm;
22use reth_primitives_traits::{BlockBody, BlockHeader};
23use reth_revm::{database::StateProviderDatabase, State};
24use reth_rpc_api::TraceApiServer;
25use reth_rpc_convert::RpcTxReq;
26use reth_rpc_eth_api::{
27 helpers::{Call, LoadPendingBlock, LoadTransaction, Trace, TraceExt},
28 FromEthApiError, RpcNodeCore,
29};
30use reth_rpc_eth_types::{error::EthApiError, utils::recover_raw_transaction, EthConfig};
31use reth_storage_api::{BlockNumReader, BlockReader};
32use reth_tasks::pool::BlockingTaskGuard;
33use reth_transaction_pool::{PoolPooledTx, PoolTransaction, TransactionPool};
34use revm::DatabaseCommit;
35use revm_inspectors::{
36 opcode::OpcodeGasInspector,
37 storage::StorageInspector,
38 tracing::{parity::populate_state_diff, TracingInspector, TracingInspectorConfig},
39};
40use serde::{Deserialize, Serialize};
41use std::sync::Arc;
42use tokio::sync::{AcquireError, OwnedSemaphorePermit};
43
44pub struct TraceApi<Eth> {
48 inner: Arc<TraceApiInner<Eth>>,
49}
50
51impl<Eth> TraceApi<Eth> {
54 pub fn new(
56 eth_api: Eth,
57 blocking_task_guard: BlockingTaskGuard,
58 eth_config: EthConfig,
59 ) -> Self {
60 let inner = Arc::new(TraceApiInner { eth_api, blocking_task_guard, eth_config });
61 Self { inner }
62 }
63
64 async fn acquire_trace_permit(
66 &self,
67 ) -> std::result::Result<OwnedSemaphorePermit, AcquireError> {
68 self.inner.blocking_task_guard.clone().acquire_owned().await
69 }
70
71 pub fn eth_api(&self) -> &Eth {
73 &self.inner.eth_api
74 }
75}
76
77impl<Eth: RpcNodeCore> TraceApi<Eth> {
78 pub fn provider(&self) -> &Eth::Provider {
80 self.inner.eth_api.provider()
81 }
82}
83
84impl<Eth> TraceApi<Eth>
87where
88 Eth: Trace + Call + LoadPendingBlock + LoadTransaction + 'static,
91{
92 pub async fn trace_call(
94 &self,
95 trace_request: TraceCallRequest<RpcTxReq<Eth::NetworkTypes>>,
96 ) -> Result<TraceResults, Eth::Error> {
97 let at = trace_request.block_id.unwrap_or_default();
98 let config = TracingInspectorConfig::from_parity_config(&trace_request.trace_types);
99 let overrides =
100 EvmOverrides::new(trace_request.state_overrides, trace_request.block_overrides);
101 let mut inspector = TracingInspector::new(config);
102 let this = self.clone();
103 self.eth_api()
104 .spawn_with_call_at(trace_request.call, at, overrides, move |db, evm_env, tx_env| {
105 let db = db.0;
108
109 let res = this.eth_api().inspect(&mut *db, evm_env, tx_env, &mut inspector)?;
110 let trace_res = inspector
111 .into_parity_builder()
112 .into_trace_results_with_state(&res, &trace_request.trace_types, &db)
113 .map_err(Eth::Error::from_eth_err)?;
114 Ok(trace_res)
115 })
116 .await
117 }
118
119 pub async fn trace_raw_transaction(
121 &self,
122 tx: Bytes,
123 trace_types: HashSet<TraceType>,
124 block_id: Option<BlockId>,
125 ) -> Result<TraceResults, Eth::Error> {
126 let tx = recover_raw_transaction::<PoolPooledTx<Eth::Pool>>(&tx)?
127 .map(<Eth::Pool as TransactionPool>::Transaction::pooled_into_consensus);
128
129 let (evm_env, at) = self.eth_api().evm_env_at(block_id.unwrap_or_default()).await?;
130 let tx_env = self.eth_api().evm_config().tx_env(tx);
131
132 let config = TracingInspectorConfig::from_parity_config(&trace_types);
133
134 self.eth_api()
135 .spawn_trace_at_with_state(evm_env, tx_env, config, at, move |inspector, res, db| {
136 inspector
137 .into_parity_builder()
138 .into_trace_results_with_state(&res, &trace_types, &db)
139 .map_err(Eth::Error::from_eth_err)
140 })
141 .await
142 }
143
144 pub async fn trace_call_many(
149 &self,
150 calls: Vec<(RpcTxReq<Eth::NetworkTypes>, HashSet<TraceType>)>,
151 block_id: Option<BlockId>,
152 ) -> Result<Vec<TraceResults>, Eth::Error> {
153 let at = block_id.unwrap_or(BlockId::pending());
154 let (evm_env, at) = self.eth_api().evm_env_at(at).await?;
155
156 let this = self.clone();
157 self.eth_api()
159 .spawn_with_state_at_block(at, move |state| {
160 let mut results = Vec::with_capacity(calls.len());
161 let mut db =
162 State::builder().with_database(StateProviderDatabase::new(state)).build();
163
164 let mut calls = calls.into_iter().peekable();
165
166 while let Some((call, trace_types)) = calls.next() {
167 let (evm_env, tx_env) = this.eth_api().prepare_call_env(
168 evm_env.clone(),
169 call,
170 &mut db,
171 Default::default(),
172 )?;
173 let config = TracingInspectorConfig::from_parity_config(&trace_types);
174 let mut inspector = TracingInspector::new(config);
175 let res = this.eth_api().inspect(&mut db, evm_env, tx_env, &mut inspector)?;
176
177 let trace_res = inspector
178 .into_parity_builder()
179 .into_trace_results_with_state(&res, &trace_types, &db)
180 .map_err(Eth::Error::from_eth_err)?;
181
182 results.push(trace_res);
183
184 if calls.peek().is_some() {
187 db.commit(res.state)
190 }
191 }
192
193 Ok(results)
194 })
195 .await
196 }
197
198 pub async fn replay_transaction(
200 &self,
201 hash: B256,
202 trace_types: HashSet<TraceType>,
203 ) -> Result<TraceResults, Eth::Error> {
204 let config = TracingInspectorConfig::from_parity_config(&trace_types);
205 self.eth_api()
206 .spawn_trace_transaction_in_block(hash, config, move |_, inspector, res, db| {
207 let trace_res = inspector
208 .into_parity_builder()
209 .into_trace_results_with_state(&res, &trace_types, &db)
210 .map_err(Eth::Error::from_eth_err)?;
211 Ok(trace_res)
212 })
213 .await
214 .transpose()
215 .ok_or(EthApiError::TransactionNotFound)?
216 }
217
218 pub async fn trace_get(
225 &self,
226 hash: B256,
227 indices: Vec<usize>,
228 ) -> Result<Option<LocalizedTransactionTrace>, Eth::Error> {
229 if indices.len() != 1 {
230 return Ok(None)
232 }
233 self.trace_get_index(hash, indices[0]).await
234 }
235
236 pub async fn trace_get_index(
240 &self,
241 hash: B256,
242 index: usize,
243 ) -> Result<Option<LocalizedTransactionTrace>, Eth::Error> {
244 Ok(self.trace_transaction(hash).await?.and_then(|traces| traces.into_iter().nth(index)))
245 }
246
247 pub async fn trace_transaction(
249 &self,
250 hash: B256,
251 ) -> Result<Option<Vec<LocalizedTransactionTrace>>, Eth::Error> {
252 self.eth_api()
253 .spawn_trace_transaction_in_block(
254 hash,
255 TracingInspectorConfig::default_parity(),
256 move |tx_info, inspector, _, _| {
257 let traces =
258 inspector.into_parity_builder().into_localized_transaction_traces(tx_info);
259 Ok(traces)
260 },
261 )
262 .await
263 }
264
265 pub async fn trace_transaction_opcode_gas(
268 &self,
269 tx_hash: B256,
270 ) -> Result<Option<TransactionOpcodeGas>, Eth::Error> {
271 self.eth_api()
272 .spawn_trace_transaction_in_block_with_inspector(
273 tx_hash,
274 OpcodeGasInspector::default(),
275 move |_tx_info, inspector, _res, _| {
276 let trace = TransactionOpcodeGas {
277 transaction_hash: tx_hash,
278 opcode_gas: inspector.opcode_gas_iter().collect(),
279 };
280 Ok(trace)
281 },
282 )
283 .await
284 }
285
286 fn calculate_base_block_reward<H: BlockHeader>(
292 &self,
293 header: &H,
294 ) -> Result<Option<u128>, Eth::Error> {
295 let chain_spec = self.provider().chain_spec();
296 let is_paris_activated = if chain_spec.chain() == MAINNET.chain() {
297 Some(header.number()) >= EthereumHardfork::Paris.mainnet_activation_block()
298 } else if chain_spec.chain() == SEPOLIA.chain() {
299 Some(header.number()) >= EthereumHardfork::Paris.sepolia_activation_block()
300 } else {
301 true
302 };
303
304 if is_paris_activated {
305 return Ok(None)
306 }
307
308 Ok(Some(base_block_reward_pre_merge(&chain_spec, header.number())))
309 }
310
311 fn extract_reward_traces<H: BlockHeader>(
315 &self,
316 header: &H,
317 ommers: Option<&[H]>,
318 base_block_reward: u128,
319 ) -> Vec<LocalizedTransactionTrace> {
320 let ommers_cnt = ommers.map(|o| o.len()).unwrap_or_default();
321 let mut traces = Vec::with_capacity(ommers_cnt + 1);
322
323 let block_reward = block_reward(base_block_reward, ommers_cnt);
324 traces.push(reward_trace(
325 header,
326 RewardAction {
327 author: header.beneficiary(),
328 reward_type: RewardType::Block,
329 value: U256::from(block_reward),
330 },
331 ));
332
333 let Some(ommers) = ommers else { return traces };
334
335 for uncle in ommers {
336 let uncle_reward = ommer_reward(base_block_reward, header.number(), uncle.number());
337 traces.push(reward_trace(
338 header,
339 RewardAction {
340 author: uncle.beneficiary(),
341 reward_type: RewardType::Uncle,
342 value: U256::from(uncle_reward),
343 },
344 ));
345 }
346 traces
347 }
348}
349
350impl<Eth> TraceApi<Eth>
351where
352 Eth: TraceExt + 'static,
355{
356 pub async fn trace_filter(
361 &self,
362 filter: TraceFilter,
363 ) -> Result<Vec<LocalizedTransactionTrace>, Eth::Error> {
364 let matcher = Arc::new(filter.matcher());
366 let TraceFilter { from_block, to_block, after, count, .. } = filter;
367 let start = from_block.unwrap_or(0);
368
369 let latest_block = self.provider().best_block_number().map_err(Eth::Error::from_eth_err)?;
370 if start > latest_block {
371 return Err(EthApiError::HeaderNotFound(start.into()).into());
373 }
374 let end = to_block.unwrap_or(latest_block);
375
376 if start > end {
377 return Err(EthApiError::InvalidParams(
378 "invalid parameters: fromBlock cannot be greater than toBlock".to_string(),
379 )
380 .into())
381 }
382
383 let distance = end.saturating_sub(start);
385 if distance > self.inner.eth_config.max_trace_filter_blocks {
386 return Err(EthApiError::InvalidParams(
387 "Block range too large; currently limited to 100 blocks".to_string(),
388 )
389 .into())
390 }
391
392 let blocks = self
394 .provider()
395 .recovered_block_range(start..=end)
396 .map_err(Eth::Error::from_eth_err)?
397 .into_iter()
398 .map(Arc::new)
399 .collect::<Vec<_>>();
400
401 let mut block_traces = Vec::with_capacity(blocks.len());
403 for block in &blocks {
404 let matcher = matcher.clone();
405 let traces = self.eth_api().trace_block_until(
406 block.hash().into(),
407 Some(block.clone()),
408 None,
409 TracingInspectorConfig::default_parity(),
410 move |tx_info, mut ctx| {
411 let mut traces = ctx
412 .take_inspector()
413 .into_parity_builder()
414 .into_localized_transaction_traces(tx_info);
415 traces.retain(|trace| matcher.matches(&trace.trace));
416 Ok(Some(traces))
417 },
418 );
419 block_traces.push(traces);
420 }
421
422 let block_traces = futures::future::try_join_all(block_traces).await?;
423 let mut all_traces = block_traces
424 .into_iter()
425 .flatten()
426 .flat_map(|traces| traces.into_iter().flatten().flat_map(|traces| traces.into_iter()))
427 .collect::<Vec<_>>();
428
429 for block in &blocks {
431 if let Some(base_block_reward) = self.calculate_base_block_reward(block.header())? {
432 all_traces.extend(
433 self.extract_reward_traces(
434 block.header(),
435 block.body().ommers(),
436 base_block_reward,
437 )
438 .into_iter()
439 .filter(|trace| matcher.matches(&trace.trace)),
440 );
441 } else {
442 break
445 }
446 }
447
448 if let Some(after) = after.map(|a| a as usize) {
452 if after < all_traces.len() {
453 all_traces.drain(..after);
454 } else {
455 return Ok(vec![])
456 }
457 }
458
459 if let Some(count) = count {
461 let count = count as usize;
462 if count < all_traces.len() {
463 all_traces.truncate(count);
464 }
465 };
466
467 Ok(all_traces)
468 }
469
470 pub async fn trace_block(
472 &self,
473 block_id: BlockId,
474 ) -> Result<Option<Vec<LocalizedTransactionTrace>>, Eth::Error> {
475 let traces = self.eth_api().trace_block_with(
476 block_id,
477 None,
478 TracingInspectorConfig::default_parity(),
479 |tx_info, mut ctx| {
480 let traces = ctx
481 .take_inspector()
482 .into_parity_builder()
483 .into_localized_transaction_traces(tx_info);
484 Ok(traces)
485 },
486 );
487
488 let block = self.eth_api().recovered_block(block_id);
489 let (maybe_traces, maybe_block) = futures::try_join!(traces, block)?;
490
491 let mut maybe_traces =
492 maybe_traces.map(|traces| traces.into_iter().flatten().collect::<Vec<_>>());
493
494 if let (Some(block), Some(traces)) = (maybe_block, maybe_traces.as_mut()) &&
495 let Some(base_block_reward) = self.calculate_base_block_reward(block.header())?
496 {
497 traces.extend(self.extract_reward_traces(
498 block.header(),
499 block.body().ommers(),
500 base_block_reward,
501 ));
502 }
503
504 Ok(maybe_traces)
505 }
506
507 pub async fn replay_block_transactions(
509 &self,
510 block_id: BlockId,
511 trace_types: HashSet<TraceType>,
512 ) -> Result<Option<Vec<TraceResultsWithTransactionHash>>, Eth::Error> {
513 self.eth_api()
514 .trace_block_with(
515 block_id,
516 None,
517 TracingInspectorConfig::from_parity_config(&trace_types),
518 move |tx_info, mut ctx| {
519 let mut full_trace = ctx
520 .take_inspector()
521 .into_parity_builder()
522 .into_trace_results(&ctx.result, &trace_types);
523
524 if let Some(ref mut state_diff) = full_trace.state_diff {
527 populate_state_diff(state_diff, &ctx.db, ctx.state.iter())
528 .map_err(Eth::Error::from_eth_err)?;
529 }
530
531 let trace = TraceResultsWithTransactionHash {
532 transaction_hash: tx_info.hash.expect("tx hash is set"),
533 full_trace,
534 };
535 Ok(trace)
536 },
537 )
538 .await
539 }
540
541 pub async fn trace_block_opcode_gas(
546 &self,
547 block_id: BlockId,
548 ) -> Result<Option<BlockOpcodeGas>, Eth::Error> {
549 let res = self
550 .eth_api()
551 .trace_block_inspector(
552 block_id,
553 None,
554 OpcodeGasInspector::default,
555 move |tx_info, ctx| {
556 let trace = TransactionOpcodeGas {
557 transaction_hash: tx_info.hash.expect("tx hash is set"),
558 opcode_gas: ctx.inspector.opcode_gas_iter().collect(),
559 };
560 Ok(trace)
561 },
562 )
563 .await?;
564
565 let Some(transactions) = res else { return Ok(None) };
566
567 let Some(block) = self.eth_api().recovered_block(block_id).await? else { return Ok(None) };
568
569 Ok(Some(BlockOpcodeGas {
570 block_hash: block.hash(),
571 block_number: block.number(),
572 transactions,
573 }))
574 }
575
576 pub async fn trace_block_storage_access(
579 &self,
580 block_id: BlockId,
581 ) -> Result<Option<BlockStorageAccess>, Eth::Error> {
582 let res = self
583 .eth_api()
584 .trace_block_inspector(
585 block_id,
586 None,
587 StorageInspector::default,
588 move |tx_info, ctx| {
589 let trace = TransactionStorageAccess {
590 transaction_hash: tx_info.hash.expect("tx hash is set"),
591 storage_access: ctx.inspector.accessed_slots().clone(),
592 unique_loads: ctx.inspector.unique_loads(),
593 warm_loads: ctx.inspector.warm_loads(),
594 };
595 Ok(trace)
596 },
597 )
598 .await?;
599
600 let Some(transactions) = res else { return Ok(None) };
601
602 let Some(block) = self.eth_api().recovered_block(block_id).await? else { return Ok(None) };
603
604 Ok(Some(BlockStorageAccess {
605 block_hash: block.hash(),
606 block_number: block.number(),
607 transactions,
608 }))
609 }
610}
611
612#[async_trait]
613impl<Eth> TraceApiServer<RpcTxReq<Eth::NetworkTypes>> for TraceApi<Eth>
614where
615 Eth: TraceExt + 'static,
616{
617 async fn trace_call(
621 &self,
622 call: RpcTxReq<Eth::NetworkTypes>,
623 trace_types: HashSet<TraceType>,
624 block_id: Option<BlockId>,
625 state_overrides: Option<StateOverride>,
626 block_overrides: Option<Box<BlockOverrides>>,
627 ) -> RpcResult<TraceResults> {
628 let _permit = self.acquire_trace_permit().await;
629 let request =
630 TraceCallRequest { call, trace_types, block_id, state_overrides, block_overrides };
631 Ok(Self::trace_call(self, request).await.map_err(Into::into)?)
632 }
633
634 async fn trace_call_many(
636 &self,
637 calls: Vec<(RpcTxReq<Eth::NetworkTypes>, HashSet<TraceType>)>,
638 block_id: Option<BlockId>,
639 ) -> RpcResult<Vec<TraceResults>> {
640 let _permit = self.acquire_trace_permit().await;
641 Ok(Self::trace_call_many(self, calls, block_id).await.map_err(Into::into)?)
642 }
643
644 async fn trace_raw_transaction(
646 &self,
647 data: Bytes,
648 trace_types: HashSet<TraceType>,
649 block_id: Option<BlockId>,
650 ) -> RpcResult<TraceResults> {
651 let _permit = self.acquire_trace_permit().await;
652 Ok(Self::trace_raw_transaction(self, data, trace_types, block_id)
653 .await
654 .map_err(Into::into)?)
655 }
656
657 async fn replay_block_transactions(
659 &self,
660 block_id: BlockId,
661 trace_types: HashSet<TraceType>,
662 ) -> RpcResult<Option<Vec<TraceResultsWithTransactionHash>>> {
663 let _permit = self.acquire_trace_permit().await;
664 Ok(Self::replay_block_transactions(self, block_id, trace_types)
665 .await
666 .map_err(Into::into)?)
667 }
668
669 async fn replay_transaction(
671 &self,
672 transaction: B256,
673 trace_types: HashSet<TraceType>,
674 ) -> RpcResult<TraceResults> {
675 let _permit = self.acquire_trace_permit().await;
676 Ok(Self::replay_transaction(self, transaction, trace_types).await.map_err(Into::into)?)
677 }
678
679 async fn trace_block(
681 &self,
682 block_id: BlockId,
683 ) -> RpcResult<Option<Vec<LocalizedTransactionTrace>>> {
684 let _permit = self.acquire_trace_permit().await;
685 Ok(Self::trace_block(self, block_id).await.map_err(Into::into)?)
686 }
687
688 async fn trace_filter(&self, filter: TraceFilter) -> RpcResult<Vec<LocalizedTransactionTrace>> {
695 Ok(Self::trace_filter(self, filter).await.map_err(Into::into)?)
696 }
697
698 async fn trace_get(
701 &self,
702 hash: B256,
703 indices: Vec<Index>,
704 ) -> RpcResult<Option<LocalizedTransactionTrace>> {
705 let _permit = self.acquire_trace_permit().await;
706 Ok(Self::trace_get(self, hash, indices.into_iter().map(Into::into).collect())
707 .await
708 .map_err(Into::into)?)
709 }
710
711 async fn trace_transaction(
713 &self,
714 hash: B256,
715 ) -> RpcResult<Option<Vec<LocalizedTransactionTrace>>> {
716 let _permit = self.acquire_trace_permit().await;
717 Ok(Self::trace_transaction(self, hash).await.map_err(Into::into)?)
718 }
719
720 async fn trace_transaction_opcode_gas(
722 &self,
723 tx_hash: B256,
724 ) -> RpcResult<Option<TransactionOpcodeGas>> {
725 let _permit = self.acquire_trace_permit().await;
726 Ok(Self::trace_transaction_opcode_gas(self, tx_hash).await.map_err(Into::into)?)
727 }
728
729 async fn trace_block_opcode_gas(&self, block_id: BlockId) -> RpcResult<Option<BlockOpcodeGas>> {
731 let _permit = self.acquire_trace_permit().await;
732 Ok(Self::trace_block_opcode_gas(self, block_id).await.map_err(Into::into)?)
733 }
734}
735
736impl<Eth> std::fmt::Debug for TraceApi<Eth> {
737 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
738 f.debug_struct("TraceApi").finish_non_exhaustive()
739 }
740}
741impl<Eth> Clone for TraceApi<Eth> {
742 fn clone(&self) -> Self {
743 Self { inner: Arc::clone(&self.inner) }
744 }
745}
746
747struct TraceApiInner<Eth> {
748 eth_api: Eth,
750 blocking_task_guard: BlockingTaskGuard,
752 eth_config: EthConfig,
754}
755
756#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
759#[serde(rename_all = "camelCase")]
760pub struct TransactionStorageAccess {
761 pub transaction_hash: B256,
763 pub storage_access: HashMap<Address, HashMap<B256, u64>>,
765 pub unique_loads: u64,
767 pub warm_loads: u64,
769}
770
771#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
773#[serde(rename_all = "camelCase")]
774pub struct BlockStorageAccess {
775 pub block_hash: BlockHash,
777 pub block_number: u64,
779 pub transactions: Vec<TransactionStorageAccess>,
781}
782
783fn reward_trace<H: BlockHeader>(header: &H, reward: RewardAction) -> LocalizedTransactionTrace {
786 LocalizedTransactionTrace {
787 block_hash: Some(header.hash_slow()),
788 block_number: Some(header.number()),
789 transaction_hash: None,
790 transaction_position: None,
791 trace: TransactionTrace {
792 trace_address: vec![],
793 subtraces: 0,
794 action: Action::Reward(reward),
795 error: None,
796 result: None,
797 },
798 }
799}