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, db::CacheDB};
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 = CacheDB::new(StateProviderDatabase::new(state));
162
163 let mut calls = calls.into_iter().peekable();
164
165 while let Some((call, trace_types)) = calls.next() {
166 let (evm_env, tx_env) = this.eth_api().prepare_call_env(
167 evm_env.clone(),
168 call,
169 &mut db,
170 Default::default(),
171 )?;
172 let config = TracingInspectorConfig::from_parity_config(&trace_types);
173 let mut inspector = TracingInspector::new(config);
174 let res = this.eth_api().inspect(&mut db, evm_env, tx_env, &mut inspector)?;
175
176 let trace_res = inspector
177 .into_parity_builder()
178 .into_trace_results_with_state(&res, &trace_types, &db)
179 .map_err(Eth::Error::from_eth_err)?;
180
181 results.push(trace_res);
182
183 if calls.peek().is_some() {
186 db.commit(res.state)
189 }
190 }
191
192 Ok(results)
193 })
194 .await
195 }
196
197 pub async fn replay_transaction(
199 &self,
200 hash: B256,
201 trace_types: HashSet<TraceType>,
202 ) -> Result<TraceResults, Eth::Error> {
203 let config = TracingInspectorConfig::from_parity_config(&trace_types);
204 self.eth_api()
205 .spawn_trace_transaction_in_block(hash, config, move |_, inspector, res, db| {
206 let trace_res = inspector
207 .into_parity_builder()
208 .into_trace_results_with_state(&res, &trace_types, &db)
209 .map_err(Eth::Error::from_eth_err)?;
210 Ok(trace_res)
211 })
212 .await
213 .transpose()
214 .ok_or(EthApiError::TransactionNotFound)?
215 }
216
217 pub async fn trace_get(
224 &self,
225 hash: B256,
226 indices: Vec<usize>,
227 ) -> Result<Option<LocalizedTransactionTrace>, Eth::Error> {
228 if indices.len() != 1 {
229 return Ok(None)
231 }
232 self.trace_get_index(hash, indices[0]).await
233 }
234
235 pub async fn trace_get_index(
239 &self,
240 hash: B256,
241 index: usize,
242 ) -> Result<Option<LocalizedTransactionTrace>, Eth::Error> {
243 Ok(self.trace_transaction(hash).await?.and_then(|traces| traces.into_iter().nth(index)))
244 }
245
246 pub async fn trace_transaction(
248 &self,
249 hash: B256,
250 ) -> Result<Option<Vec<LocalizedTransactionTrace>>, Eth::Error> {
251 self.eth_api()
252 .spawn_trace_transaction_in_block(
253 hash,
254 TracingInspectorConfig::default_parity(),
255 move |tx_info, inspector, _, _| {
256 let traces =
257 inspector.into_parity_builder().into_localized_transaction_traces(tx_info);
258 Ok(traces)
259 },
260 )
261 .await
262 }
263
264 pub async fn trace_transaction_opcode_gas(
267 &self,
268 tx_hash: B256,
269 ) -> Result<Option<TransactionOpcodeGas>, Eth::Error> {
270 self.eth_api()
271 .spawn_trace_transaction_in_block_with_inspector(
272 tx_hash,
273 OpcodeGasInspector::default(),
274 move |_tx_info, inspector, _res, _| {
275 let trace = TransactionOpcodeGas {
276 transaction_hash: tx_hash,
277 opcode_gas: inspector.opcode_gas_iter().collect(),
278 };
279 Ok(trace)
280 },
281 )
282 .await
283 }
284
285 fn calculate_base_block_reward<H: BlockHeader>(
291 &self,
292 header: &H,
293 ) -> Result<Option<u128>, Eth::Error> {
294 let chain_spec = self.provider().chain_spec();
295 let is_paris_activated = if chain_spec.chain() == MAINNET.chain() {
296 Some(header.number()) >= EthereumHardfork::Paris.mainnet_activation_block()
297 } else if chain_spec.chain() == SEPOLIA.chain() {
298 Some(header.number()) >= EthereumHardfork::Paris.sepolia_activation_block()
299 } else {
300 true
301 };
302
303 if is_paris_activated {
304 return Ok(None)
305 }
306
307 Ok(Some(base_block_reward_pre_merge(&chain_spec, header.number())))
308 }
309
310 fn extract_reward_traces<H: BlockHeader>(
314 &self,
315 header: &H,
316 ommers: Option<&[H]>,
317 base_block_reward: u128,
318 ) -> Vec<LocalizedTransactionTrace> {
319 let ommers_cnt = ommers.map(|o| o.len()).unwrap_or_default();
320 let mut traces = Vec::with_capacity(ommers_cnt + 1);
321
322 let block_reward = block_reward(base_block_reward, ommers_cnt);
323 traces.push(reward_trace(
324 header,
325 RewardAction {
326 author: header.beneficiary(),
327 reward_type: RewardType::Block,
328 value: U256::from(block_reward),
329 },
330 ));
331
332 let Some(ommers) = ommers else { return traces };
333
334 for uncle in ommers {
335 let uncle_reward = ommer_reward(base_block_reward, header.number(), uncle.number());
336 traces.push(reward_trace(
337 header,
338 RewardAction {
339 author: uncle.beneficiary(),
340 reward_type: RewardType::Uncle,
341 value: U256::from(uncle_reward),
342 },
343 ));
344 }
345 traces
346 }
347}
348
349impl<Eth> TraceApi<Eth>
350where
351 Eth: TraceExt + 'static,
354{
355 pub async fn trace_filter(
360 &self,
361 filter: TraceFilter,
362 ) -> Result<Vec<LocalizedTransactionTrace>, Eth::Error> {
363 let matcher = Arc::new(filter.matcher());
365 let TraceFilter { from_block, to_block, after, count, .. } = filter;
366 let start = from_block.unwrap_or(0);
367
368 let latest_block = self.provider().best_block_number().map_err(Eth::Error::from_eth_err)?;
369 if start > latest_block {
370 return Err(EthApiError::HeaderNotFound(start.into()).into());
372 }
373 let end = to_block.unwrap_or(latest_block);
374
375 if start > end {
376 return Err(EthApiError::InvalidParams(
377 "invalid parameters: fromBlock cannot be greater than toBlock".to_string(),
378 )
379 .into())
380 }
381
382 let distance = end.saturating_sub(start);
384 if distance > self.inner.eth_config.max_trace_filter_blocks {
385 return Err(EthApiError::InvalidParams(
386 "Block range too large; currently limited to 100 blocks".to_string(),
387 )
388 .into())
389 }
390
391 let blocks = self
393 .provider()
394 .recovered_block_range(start..=end)
395 .map_err(Eth::Error::from_eth_err)?
396 .into_iter()
397 .map(Arc::new)
398 .collect::<Vec<_>>();
399
400 let mut block_traces = Vec::with_capacity(blocks.len());
402 for block in &blocks {
403 let matcher = matcher.clone();
404 let traces = self.eth_api().trace_block_until(
405 block.hash().into(),
406 Some(block.clone()),
407 None,
408 TracingInspectorConfig::default_parity(),
409 move |tx_info, mut ctx| {
410 let mut traces = ctx
411 .take_inspector()
412 .into_parity_builder()
413 .into_localized_transaction_traces(tx_info);
414 traces.retain(|trace| matcher.matches(&trace.trace));
415 Ok(Some(traces))
416 },
417 );
418 block_traces.push(traces);
419 }
420
421 let block_traces = futures::future::try_join_all(block_traces).await?;
422 let mut all_traces = block_traces
423 .into_iter()
424 .flatten()
425 .flat_map(|traces| traces.into_iter().flatten().flat_map(|traces| traces.into_iter()))
426 .collect::<Vec<_>>();
427
428 for block in &blocks {
430 if let Some(base_block_reward) = self.calculate_base_block_reward(block.header())? {
431 all_traces.extend(
432 self.extract_reward_traces(
433 block.header(),
434 block.body().ommers(),
435 base_block_reward,
436 )
437 .into_iter()
438 .filter(|trace| matcher.matches(&trace.trace)),
439 );
440 } else {
441 break
444 }
445 }
446
447 if let Some(after) = after.map(|a| a as usize) {
451 if after < all_traces.len() {
452 all_traces.drain(..after);
453 } else {
454 return Ok(vec![])
455 }
456 }
457
458 if let Some(count) = count {
460 let count = count as usize;
461 if count < all_traces.len() {
462 all_traces.truncate(count);
463 }
464 };
465
466 Ok(all_traces)
467 }
468
469 pub async fn trace_block(
471 &self,
472 block_id: BlockId,
473 ) -> Result<Option<Vec<LocalizedTransactionTrace>>, Eth::Error> {
474 let traces = self.eth_api().trace_block_with(
475 block_id,
476 None,
477 TracingInspectorConfig::default_parity(),
478 |tx_info, mut ctx| {
479 let traces = ctx
480 .take_inspector()
481 .into_parity_builder()
482 .into_localized_transaction_traces(tx_info);
483 Ok(traces)
484 },
485 );
486
487 let block = self.eth_api().recovered_block(block_id);
488 let (maybe_traces, maybe_block) = futures::try_join!(traces, block)?;
489
490 let mut maybe_traces =
491 maybe_traces.map(|traces| traces.into_iter().flatten().collect::<Vec<_>>());
492
493 if let (Some(block), Some(traces)) = (maybe_block, maybe_traces.as_mut()) {
494 if let Some(base_block_reward) = self.calculate_base_block_reward(block.header())? {
495 traces.extend(self.extract_reward_traces(
496 block.header(),
497 block.body().ommers(),
498 base_block_reward,
499 ));
500 }
501 }
502
503 Ok(maybe_traces)
504 }
505
506 pub async fn replay_block_transactions(
508 &self,
509 block_id: BlockId,
510 trace_types: HashSet<TraceType>,
511 ) -> Result<Option<Vec<TraceResultsWithTransactionHash>>, Eth::Error> {
512 self.eth_api()
513 .trace_block_with(
514 block_id,
515 None,
516 TracingInspectorConfig::from_parity_config(&trace_types),
517 move |tx_info, mut ctx| {
518 let mut full_trace = ctx
519 .take_inspector()
520 .into_parity_builder()
521 .into_trace_results(&ctx.result, &trace_types);
522
523 if let Some(ref mut state_diff) = full_trace.state_diff {
526 populate_state_diff(state_diff, &ctx.db, ctx.state.iter())
527 .map_err(Eth::Error::from_eth_err)?;
528 }
529
530 let trace = TraceResultsWithTransactionHash {
531 transaction_hash: tx_info.hash.expect("tx hash is set"),
532 full_trace,
533 };
534 Ok(trace)
535 },
536 )
537 .await
538 }
539
540 pub async fn trace_block_opcode_gas(
545 &self,
546 block_id: BlockId,
547 ) -> Result<Option<BlockOpcodeGas>, Eth::Error> {
548 let res = self
549 .eth_api()
550 .trace_block_inspector(
551 block_id,
552 None,
553 OpcodeGasInspector::default,
554 move |tx_info, ctx| {
555 let trace = TransactionOpcodeGas {
556 transaction_hash: tx_info.hash.expect("tx hash is set"),
557 opcode_gas: ctx.inspector.opcode_gas_iter().collect(),
558 };
559 Ok(trace)
560 },
561 )
562 .await?;
563
564 let Some(transactions) = res else { return Ok(None) };
565
566 let Some(block) = self.eth_api().recovered_block(block_id).await? else { return Ok(None) };
567
568 Ok(Some(BlockOpcodeGas {
569 block_hash: block.hash(),
570 block_number: block.number(),
571 transactions,
572 }))
573 }
574
575 pub async fn trace_block_storage_access(
578 &self,
579 block_id: BlockId,
580 ) -> Result<Option<BlockStorageAccess>, Eth::Error> {
581 let res = self
582 .eth_api()
583 .trace_block_inspector(
584 block_id,
585 None,
586 StorageInspector::default,
587 move |tx_info, ctx| {
588 let trace = TransactionStorageAccess {
589 transaction_hash: tx_info.hash.expect("tx hash is set"),
590 storage_access: ctx.inspector.accessed_slots().clone(),
591 unique_loads: ctx.inspector.unique_loads(),
592 warm_loads: ctx.inspector.warm_loads(),
593 };
594 Ok(trace)
595 },
596 )
597 .await?;
598
599 let Some(transactions) = res else { return Ok(None) };
600
601 let Some(block) = self.eth_api().recovered_block(block_id).await? else { return Ok(None) };
602
603 Ok(Some(BlockStorageAccess {
604 block_hash: block.hash(),
605 block_number: block.number(),
606 transactions,
607 }))
608 }
609}
610
611#[async_trait]
612impl<Eth> TraceApiServer<RpcTxReq<Eth::NetworkTypes>> for TraceApi<Eth>
613where
614 Eth: TraceExt + 'static,
615{
616 async fn trace_call(
620 &self,
621 call: RpcTxReq<Eth::NetworkTypes>,
622 trace_types: HashSet<TraceType>,
623 block_id: Option<BlockId>,
624 state_overrides: Option<StateOverride>,
625 block_overrides: Option<Box<BlockOverrides>>,
626 ) -> RpcResult<TraceResults> {
627 let _permit = self.acquire_trace_permit().await;
628 let request =
629 TraceCallRequest { call, trace_types, block_id, state_overrides, block_overrides };
630 Ok(Self::trace_call(self, request).await.map_err(Into::into)?)
631 }
632
633 async fn trace_call_many(
635 &self,
636 calls: Vec<(RpcTxReq<Eth::NetworkTypes>, HashSet<TraceType>)>,
637 block_id: Option<BlockId>,
638 ) -> RpcResult<Vec<TraceResults>> {
639 let _permit = self.acquire_trace_permit().await;
640 Ok(Self::trace_call_many(self, calls, block_id).await.map_err(Into::into)?)
641 }
642
643 async fn trace_raw_transaction(
645 &self,
646 data: Bytes,
647 trace_types: HashSet<TraceType>,
648 block_id: Option<BlockId>,
649 ) -> RpcResult<TraceResults> {
650 let _permit = self.acquire_trace_permit().await;
651 Ok(Self::trace_raw_transaction(self, data, trace_types, block_id)
652 .await
653 .map_err(Into::into)?)
654 }
655
656 async fn replay_block_transactions(
658 &self,
659 block_id: BlockId,
660 trace_types: HashSet<TraceType>,
661 ) -> RpcResult<Option<Vec<TraceResultsWithTransactionHash>>> {
662 let _permit = self.acquire_trace_permit().await;
663 Ok(Self::replay_block_transactions(self, block_id, trace_types)
664 .await
665 .map_err(Into::into)?)
666 }
667
668 async fn replay_transaction(
670 &self,
671 transaction: B256,
672 trace_types: HashSet<TraceType>,
673 ) -> RpcResult<TraceResults> {
674 let _permit = self.acquire_trace_permit().await;
675 Ok(Self::replay_transaction(self, transaction, trace_types).await.map_err(Into::into)?)
676 }
677
678 async fn trace_block(
680 &self,
681 block_id: BlockId,
682 ) -> RpcResult<Option<Vec<LocalizedTransactionTrace>>> {
683 let _permit = self.acquire_trace_permit().await;
684 Ok(Self::trace_block(self, block_id).await.map_err(Into::into)?)
685 }
686
687 async fn trace_filter(&self, filter: TraceFilter) -> RpcResult<Vec<LocalizedTransactionTrace>> {
694 Ok(Self::trace_filter(self, filter).await.map_err(Into::into)?)
695 }
696
697 async fn trace_get(
700 &self,
701 hash: B256,
702 indices: Vec<Index>,
703 ) -> RpcResult<Option<LocalizedTransactionTrace>> {
704 let _permit = self.acquire_trace_permit().await;
705 Ok(Self::trace_get(self, hash, indices.into_iter().map(Into::into).collect())
706 .await
707 .map_err(Into::into)?)
708 }
709
710 async fn trace_transaction(
712 &self,
713 hash: B256,
714 ) -> RpcResult<Option<Vec<LocalizedTransactionTrace>>> {
715 let _permit = self.acquire_trace_permit().await;
716 Ok(Self::trace_transaction(self, hash).await.map_err(Into::into)?)
717 }
718
719 async fn trace_transaction_opcode_gas(
721 &self,
722 tx_hash: B256,
723 ) -> RpcResult<Option<TransactionOpcodeGas>> {
724 let _permit = self.acquire_trace_permit().await;
725 Ok(Self::trace_transaction_opcode_gas(self, tx_hash).await.map_err(Into::into)?)
726 }
727
728 async fn trace_block_opcode_gas(&self, block_id: BlockId) -> RpcResult<Option<BlockOpcodeGas>> {
730 let _permit = self.acquire_trace_permit().await;
731 Ok(Self::trace_block_opcode_gas(self, block_id).await.map_err(Into::into)?)
732 }
733}
734
735impl<Eth> std::fmt::Debug for TraceApi<Eth> {
736 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
737 f.debug_struct("TraceApi").finish_non_exhaustive()
738 }
739}
740impl<Eth> Clone for TraceApi<Eth> {
741 fn clone(&self) -> Self {
742 Self { inner: Arc::clone(&self.inner) }
743 }
744}
745
746struct TraceApiInner<Eth> {
747 eth_api: Eth,
749 blocking_task_guard: BlockingTaskGuard,
751 eth_config: EthConfig,
753}
754
755#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
758#[serde(rename_all = "camelCase")]
759pub struct TransactionStorageAccess {
760 pub transaction_hash: B256,
762 pub storage_access: HashMap<Address, HashMap<B256, u64>>,
764 pub unique_loads: u64,
766 pub warm_loads: u64,
768}
769
770#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
772#[serde(rename_all = "camelCase")]
773pub struct BlockStorageAccess {
774 pub block_hash: BlockHash,
776 pub block_number: u64,
778 pub transactions: Vec<TransactionStorageAccess>,
780}
781
782fn reward_trace<H: BlockHeader>(header: &H, reward: RewardAction) -> LocalizedTransactionTrace {
785 LocalizedTransactionTrace {
786 block_hash: Some(header.hash_slow()),
787 block_number: Some(header.number()),
788 transaction_hash: None,
789 transaction_position: None,
790 trace: TransactionTrace {
791 trace_address: vec![],
792 subtraces: 0,
793 action: Action::Reward(reward),
794 error: None,
795 result: None,
796 },
797 }
798}