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::{map::HashSet, Bytes, B256, U256};
5use alloy_rpc_types_eth::{
6 state::{EvmOverrides, StateOverride},
7 transaction::TransactionRequest,
8 BlockOverrides, Index,
9};
10use alloy_rpc_types_trace::{
11 filter::TraceFilter,
12 opcode::{BlockOpcodeGas, TransactionOpcodeGas},
13 parity::*,
14 tracerequest::TraceCallRequest,
15};
16use async_trait::async_trait;
17use jsonrpsee::core::RpcResult;
18use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardfork, MAINNET, SEPOLIA};
19use reth_evm::ConfigureEvm;
20use reth_primitives_traits::{BlockBody, BlockHeader};
21use reth_revm::{database::StateProviderDatabase, db::CacheDB};
22use reth_rpc_api::TraceApiServer;
23use reth_rpc_eth_api::{
24 helpers::{Call, LoadPendingBlock, LoadTransaction, Trace, TraceExt},
25 FromEthApiError, RpcNodeCore,
26};
27use reth_rpc_eth_types::{error::EthApiError, utils::recover_raw_transaction, EthConfig};
28use reth_storage_api::{BlockNumReader, BlockReader};
29use reth_tasks::pool::BlockingTaskGuard;
30use reth_transaction_pool::{PoolPooledTx, PoolTransaction, TransactionPool};
31use revm::DatabaseCommit;
32use revm_inspectors::{
33 opcode::OpcodeGasInspector,
34 tracing::{parity::populate_state_diff, TracingInspector, TracingInspectorConfig},
35};
36use std::sync::Arc;
37use tokio::sync::{AcquireError, OwnedSemaphorePermit};
38
39pub struct TraceApi<Eth> {
43 inner: Arc<TraceApiInner<Eth>>,
44}
45
46impl<Eth> TraceApi<Eth> {
49 pub fn new(
51 eth_api: Eth,
52 blocking_task_guard: BlockingTaskGuard,
53 eth_config: EthConfig,
54 ) -> Self {
55 let inner = Arc::new(TraceApiInner { eth_api, blocking_task_guard, eth_config });
56 Self { inner }
57 }
58
59 async fn acquire_trace_permit(
61 &self,
62 ) -> std::result::Result<OwnedSemaphorePermit, AcquireError> {
63 self.inner.blocking_task_guard.clone().acquire_owned().await
64 }
65
66 pub fn eth_api(&self) -> &Eth {
68 &self.inner.eth_api
69 }
70}
71
72impl<Eth: RpcNodeCore> TraceApi<Eth> {
73 pub fn provider(&self) -> &Eth::Provider {
75 self.inner.eth_api.provider()
76 }
77}
78
79impl<Eth> TraceApi<Eth>
82where
83 Eth: Trace + Call + LoadPendingBlock + LoadTransaction + 'static,
86{
87 pub async fn trace_call(
89 &self,
90 trace_request: TraceCallRequest,
91 ) -> Result<TraceResults, Eth::Error> {
92 let at = trace_request.block_id.unwrap_or_default();
93 let config = TracingInspectorConfig::from_parity_config(&trace_request.trace_types);
94 let overrides =
95 EvmOverrides::new(trace_request.state_overrides, trace_request.block_overrides);
96 let mut inspector = TracingInspector::new(config);
97 let this = self.clone();
98 self.eth_api()
99 .spawn_with_call_at(trace_request.call, at, overrides, move |db, evm_env, tx_env| {
100 let db = db.0;
103
104 let (res, _) = this.eth_api().inspect(&mut *db, evm_env, tx_env, &mut inspector)?;
105 let trace_res = inspector
106 .into_parity_builder()
107 .into_trace_results_with_state(&res, &trace_request.trace_types, &db)
108 .map_err(Eth::Error::from_eth_err)?;
109 Ok(trace_res)
110 })
111 .await
112 }
113
114 pub async fn trace_raw_transaction(
116 &self,
117 tx: Bytes,
118 trace_types: HashSet<TraceType>,
119 block_id: Option<BlockId>,
120 ) -> Result<TraceResults, Eth::Error> {
121 let tx = recover_raw_transaction::<PoolPooledTx<Eth::Pool>>(&tx)?
122 .map(<Eth::Pool as TransactionPool>::Transaction::pooled_into_consensus);
123
124 let (evm_env, at) = self.eth_api().evm_env_at(block_id.unwrap_or_default()).await?;
125 let tx_env = self.eth_api().evm_config().tx_env(tx);
126
127 let config = TracingInspectorConfig::from_parity_config(&trace_types);
128
129 self.eth_api()
130 .spawn_trace_at_with_state(evm_env, tx_env, config, at, move |inspector, res, db| {
131 inspector
132 .into_parity_builder()
133 .into_trace_results_with_state(&res, &trace_types, &db)
134 .map_err(Eth::Error::from_eth_err)
135 })
136 .await
137 }
138
139 pub async fn trace_call_many(
144 &self,
145 calls: Vec<(TransactionRequest, HashSet<TraceType>)>,
146 block_id: Option<BlockId>,
147 ) -> Result<Vec<TraceResults>, Eth::Error> {
148 let at = block_id.unwrap_or(BlockId::pending());
149 let (evm_env, at) = self.eth_api().evm_env_at(at).await?;
150
151 let this = self.clone();
152 self.eth_api()
154 .spawn_with_state_at_block(at, move |state| {
155 let mut results = Vec::with_capacity(calls.len());
156 let mut db = CacheDB::new(StateProviderDatabase::new(state));
157
158 let mut calls = calls.into_iter().peekable();
159
160 while let Some((call, trace_types)) = calls.next() {
161 let (evm_env, tx_env) = this.eth_api().prepare_call_env(
162 evm_env.clone(),
163 call,
164 &mut db,
165 Default::default(),
166 )?;
167 let config = TracingInspectorConfig::from_parity_config(&trace_types);
168 let mut inspector = TracingInspector::new(config);
169 let (res, _) =
170 this.eth_api().inspect(&mut db, evm_env, tx_env, &mut inspector)?;
171
172 let trace_res = inspector
173 .into_parity_builder()
174 .into_trace_results_with_state(&res, &trace_types, &db)
175 .map_err(Eth::Error::from_eth_err)?;
176
177 results.push(trace_res);
178
179 if calls.peek().is_some() {
182 db.commit(res.state)
185 }
186 }
187
188 Ok(results)
189 })
190 .await
191 }
192
193 pub async fn replay_transaction(
195 &self,
196 hash: B256,
197 trace_types: HashSet<TraceType>,
198 ) -> Result<TraceResults, Eth::Error> {
199 let config = TracingInspectorConfig::from_parity_config(&trace_types);
200 self.eth_api()
201 .spawn_trace_transaction_in_block(hash, config, move |_, inspector, res, db| {
202 let trace_res = inspector
203 .into_parity_builder()
204 .into_trace_results_with_state(&res, &trace_types, &db)
205 .map_err(Eth::Error::from_eth_err)?;
206 Ok(trace_res)
207 })
208 .await
209 .transpose()
210 .ok_or(EthApiError::TransactionNotFound)?
211 }
212
213 pub async fn trace_get(
220 &self,
221 hash: B256,
222 indices: Vec<usize>,
223 ) -> Result<Option<LocalizedTransactionTrace>, Eth::Error> {
224 if indices.len() != 1 {
225 return Ok(None)
227 }
228 self.trace_get_index(hash, indices[0]).await
229 }
230
231 pub async fn trace_get_index(
235 &self,
236 hash: B256,
237 index: usize,
238 ) -> Result<Option<LocalizedTransactionTrace>, Eth::Error> {
239 Ok(self.trace_transaction(hash).await?.and_then(|traces| traces.into_iter().nth(index)))
240 }
241
242 pub async fn trace_transaction(
244 &self,
245 hash: B256,
246 ) -> Result<Option<Vec<LocalizedTransactionTrace>>, Eth::Error> {
247 self.eth_api()
248 .spawn_trace_transaction_in_block(
249 hash,
250 TracingInspectorConfig::default_parity(),
251 move |tx_info, inspector, _, _| {
252 let traces =
253 inspector.into_parity_builder().into_localized_transaction_traces(tx_info);
254 Ok(traces)
255 },
256 )
257 .await
258 }
259
260 pub async fn trace_transaction_opcode_gas(
263 &self,
264 tx_hash: B256,
265 ) -> Result<Option<TransactionOpcodeGas>, Eth::Error> {
266 self.eth_api()
267 .spawn_trace_transaction_in_block_with_inspector(
268 tx_hash,
269 OpcodeGasInspector::default(),
270 move |_tx_info, inspector, _res, _| {
271 let trace = TransactionOpcodeGas {
272 transaction_hash: tx_hash,
273 opcode_gas: inspector.opcode_gas_iter().collect(),
274 };
275 Ok(trace)
276 },
277 )
278 .await
279 }
280
281 fn calculate_base_block_reward<H: BlockHeader>(
287 &self,
288 header: &H,
289 ) -> Result<Option<u128>, Eth::Error> {
290 let chain_spec = self.provider().chain_spec();
291 let is_paris_activated = if chain_spec.chain() == MAINNET.chain() {
292 Some(header.number()) >= EthereumHardfork::Paris.mainnet_activation_block()
293 } else if chain_spec.chain() == SEPOLIA.chain() {
294 Some(header.number()) >= EthereumHardfork::Paris.sepolia_activation_block()
295 } else {
296 true
297 };
298
299 if is_paris_activated {
300 return Ok(None)
301 }
302
303 Ok(Some(base_block_reward_pre_merge(&chain_spec, header.number())))
304 }
305
306 fn extract_reward_traces<H: BlockHeader>(
310 &self,
311 header: &H,
312 ommers: Option<&[H]>,
313 base_block_reward: u128,
314 ) -> Vec<LocalizedTransactionTrace> {
315 let ommers_cnt = ommers.map(|o| o.len()).unwrap_or_default();
316 let mut traces = Vec::with_capacity(ommers_cnt + 1);
317
318 let block_reward = block_reward(base_block_reward, ommers_cnt);
319 traces.push(reward_trace(
320 header,
321 RewardAction {
322 author: header.beneficiary(),
323 reward_type: RewardType::Block,
324 value: U256::from(block_reward),
325 },
326 ));
327
328 let Some(ommers) = ommers else { return traces };
329
330 for uncle in ommers {
331 let uncle_reward = ommer_reward(base_block_reward, header.number(), uncle.number());
332 traces.push(reward_trace(
333 header,
334 RewardAction {
335 author: uncle.beneficiary(),
336 reward_type: RewardType::Uncle,
337 value: U256::from(uncle_reward),
338 },
339 ));
340 }
341 traces
342 }
343}
344
345impl<Eth> TraceApi<Eth>
346where
347 Eth: TraceExt + 'static,
350{
351 pub async fn trace_filter(
356 &self,
357 filter: TraceFilter,
358 ) -> Result<Vec<LocalizedTransactionTrace>, Eth::Error> {
359 let matcher = Arc::new(filter.matcher());
361 let TraceFilter { from_block, to_block, after, count, .. } = filter;
362 let start = from_block.unwrap_or(0);
363
364 let latest_block = self.provider().best_block_number().map_err(Eth::Error::from_eth_err)?;
365 if start > latest_block {
366 return Err(EthApiError::HeaderNotFound(start.into()).into());
368 }
369 let end = to_block.unwrap_or(latest_block);
370
371 if start > end {
372 return Err(EthApiError::InvalidParams(
373 "invalid parameters: fromBlock cannot be greater than toBlock".to_string(),
374 )
375 .into())
376 }
377
378 let distance = end.saturating_sub(start);
380 if distance > self.inner.eth_config.max_trace_filter_blocks {
381 return Err(EthApiError::InvalidParams(
382 "Block range too large; currently limited to 100 blocks".to_string(),
383 )
384 .into())
385 }
386
387 let blocks = self
389 .provider()
390 .recovered_block_range(start..=end)
391 .map_err(Eth::Error::from_eth_err)?
392 .into_iter()
393 .map(Arc::new)
394 .collect::<Vec<_>>();
395
396 let mut block_traces = Vec::with_capacity(blocks.len());
398 for block in &blocks {
399 let matcher = matcher.clone();
400 let traces = self.eth_api().trace_block_until(
401 block.hash().into(),
402 Some(block.clone()),
403 None,
404 TracingInspectorConfig::default_parity(),
405 move |tx_info, inspector, _, _, _| {
406 let mut traces =
407 inspector.into_parity_builder().into_localized_transaction_traces(tx_info);
408 traces.retain(|trace| matcher.matches(&trace.trace));
409 Ok(Some(traces))
410 },
411 );
412 block_traces.push(traces);
413 }
414
415 let block_traces = futures::future::try_join_all(block_traces).await?;
416 let mut all_traces = block_traces
417 .into_iter()
418 .flatten()
419 .flat_map(|traces| traces.into_iter().flatten().flat_map(|traces| traces.into_iter()))
420 .collect::<Vec<_>>();
421
422 for block in &blocks {
424 if let Some(base_block_reward) = self.calculate_base_block_reward(block.header())? {
425 all_traces.extend(
426 self.extract_reward_traces(
427 block.header(),
428 block.body().ommers(),
429 base_block_reward,
430 )
431 .into_iter()
432 .filter(|trace| matcher.matches(&trace.trace)),
433 );
434 } else {
435 break
438 }
439 }
440
441 if let Some(after) = after.map(|a| a as usize) {
445 if after < all_traces.len() {
446 all_traces.drain(..after);
447 } else {
448 return Ok(vec![])
449 }
450 }
451
452 if let Some(count) = count {
454 let count = count as usize;
455 if count < all_traces.len() {
456 all_traces.truncate(count);
457 }
458 };
459
460 Ok(all_traces)
461 }
462
463 pub async fn trace_block(
465 &self,
466 block_id: BlockId,
467 ) -> Result<Option<Vec<LocalizedTransactionTrace>>, Eth::Error> {
468 let traces = self.eth_api().trace_block_with(
469 block_id,
470 None,
471 TracingInspectorConfig::default_parity(),
472 |tx_info, inspector, _, _, _| {
473 let traces =
474 inspector.into_parity_builder().into_localized_transaction_traces(tx_info);
475 Ok(traces)
476 },
477 );
478
479 let block = self.eth_api().recovered_block(block_id);
480 let (maybe_traces, maybe_block) = futures::try_join!(traces, block)?;
481
482 let mut maybe_traces =
483 maybe_traces.map(|traces| traces.into_iter().flatten().collect::<Vec<_>>());
484
485 if let (Some(block), Some(traces)) = (maybe_block, maybe_traces.as_mut()) {
486 if let Some(base_block_reward) = self.calculate_base_block_reward(block.header())? {
487 traces.extend(self.extract_reward_traces(
488 block.header(),
489 block.body().ommers(),
490 base_block_reward,
491 ));
492 }
493 }
494
495 Ok(maybe_traces)
496 }
497
498 pub async fn replay_block_transactions(
500 &self,
501 block_id: BlockId,
502 trace_types: HashSet<TraceType>,
503 ) -> Result<Option<Vec<TraceResultsWithTransactionHash>>, Eth::Error> {
504 self.eth_api()
505 .trace_block_with(
506 block_id,
507 None,
508 TracingInspectorConfig::from_parity_config(&trace_types),
509 move |tx_info, inspector, res, state, db| {
510 let mut full_trace =
511 inspector.into_parity_builder().into_trace_results(&res, &trace_types);
512
513 if let Some(ref mut state_diff) = full_trace.state_diff {
516 populate_state_diff(state_diff, db, state.iter())
517 .map_err(Eth::Error::from_eth_err)?;
518 }
519
520 let trace = TraceResultsWithTransactionHash {
521 transaction_hash: tx_info.hash.expect("tx hash is set"),
522 full_trace,
523 };
524 Ok(trace)
525 },
526 )
527 .await
528 }
529
530 pub async fn trace_block_opcode_gas(
535 &self,
536 block_id: BlockId,
537 ) -> Result<Option<BlockOpcodeGas>, Eth::Error> {
538 let res = self
539 .eth_api()
540 .trace_block_inspector(
541 block_id,
542 None,
543 OpcodeGasInspector::default,
544 move |tx_info, inspector, _res, _, _| {
545 let trace = TransactionOpcodeGas {
546 transaction_hash: tx_info.hash.expect("tx hash is set"),
547 opcode_gas: inspector.opcode_gas_iter().collect(),
548 };
549 Ok(trace)
550 },
551 )
552 .await?;
553
554 let Some(transactions) = res else { return Ok(None) };
555
556 let Some(block) = self.eth_api().recovered_block(block_id).await? else { return Ok(None) };
557
558 Ok(Some(BlockOpcodeGas {
559 block_hash: block.hash(),
560 block_number: block.number(),
561 transactions,
562 }))
563 }
564}
565
566#[async_trait]
567impl<Eth> TraceApiServer for TraceApi<Eth>
568where
569 Eth: TraceExt + 'static,
570{
571 async fn trace_call(
575 &self,
576 call: TransactionRequest,
577 trace_types: HashSet<TraceType>,
578 block_id: Option<BlockId>,
579 state_overrides: Option<StateOverride>,
580 block_overrides: Option<Box<BlockOverrides>>,
581 ) -> RpcResult<TraceResults> {
582 let _permit = self.acquire_trace_permit().await;
583 let request =
584 TraceCallRequest { call, trace_types, block_id, state_overrides, block_overrides };
585 Ok(Self::trace_call(self, request).await.map_err(Into::into)?)
586 }
587
588 async fn trace_call_many(
590 &self,
591 calls: Vec<(TransactionRequest, HashSet<TraceType>)>,
592 block_id: Option<BlockId>,
593 ) -> RpcResult<Vec<TraceResults>> {
594 let _permit = self.acquire_trace_permit().await;
595 Ok(Self::trace_call_many(self, calls, block_id).await.map_err(Into::into)?)
596 }
597
598 async fn trace_raw_transaction(
600 &self,
601 data: Bytes,
602 trace_types: HashSet<TraceType>,
603 block_id: Option<BlockId>,
604 ) -> RpcResult<TraceResults> {
605 let _permit = self.acquire_trace_permit().await;
606 Ok(Self::trace_raw_transaction(self, data, trace_types, block_id)
607 .await
608 .map_err(Into::into)?)
609 }
610
611 async fn replay_block_transactions(
613 &self,
614 block_id: BlockId,
615 trace_types: HashSet<TraceType>,
616 ) -> RpcResult<Option<Vec<TraceResultsWithTransactionHash>>> {
617 let _permit = self.acquire_trace_permit().await;
618 Ok(Self::replay_block_transactions(self, block_id, trace_types)
619 .await
620 .map_err(Into::into)?)
621 }
622
623 async fn replay_transaction(
625 &self,
626 transaction: B256,
627 trace_types: HashSet<TraceType>,
628 ) -> RpcResult<TraceResults> {
629 let _permit = self.acquire_trace_permit().await;
630 Ok(Self::replay_transaction(self, transaction, trace_types).await.map_err(Into::into)?)
631 }
632
633 async fn trace_block(
635 &self,
636 block_id: BlockId,
637 ) -> RpcResult<Option<Vec<LocalizedTransactionTrace>>> {
638 let _permit = self.acquire_trace_permit().await;
639 Ok(Self::trace_block(self, block_id).await.map_err(Into::into)?)
640 }
641
642 async fn trace_filter(&self, filter: TraceFilter) -> RpcResult<Vec<LocalizedTransactionTrace>> {
649 Ok(Self::trace_filter(self, filter).await.map_err(Into::into)?)
650 }
651
652 async fn trace_get(
655 &self,
656 hash: B256,
657 indices: Vec<Index>,
658 ) -> RpcResult<Option<LocalizedTransactionTrace>> {
659 let _permit = self.acquire_trace_permit().await;
660 Ok(Self::trace_get(self, hash, indices.into_iter().map(Into::into).collect())
661 .await
662 .map_err(Into::into)?)
663 }
664
665 async fn trace_transaction(
667 &self,
668 hash: B256,
669 ) -> RpcResult<Option<Vec<LocalizedTransactionTrace>>> {
670 let _permit = self.acquire_trace_permit().await;
671 Ok(Self::trace_transaction(self, hash).await.map_err(Into::into)?)
672 }
673
674 async fn trace_transaction_opcode_gas(
676 &self,
677 tx_hash: B256,
678 ) -> RpcResult<Option<TransactionOpcodeGas>> {
679 let _permit = self.acquire_trace_permit().await;
680 Ok(Self::trace_transaction_opcode_gas(self, tx_hash).await.map_err(Into::into)?)
681 }
682
683 async fn trace_block_opcode_gas(&self, block_id: BlockId) -> RpcResult<Option<BlockOpcodeGas>> {
685 let _permit = self.acquire_trace_permit().await;
686 Ok(Self::trace_block_opcode_gas(self, block_id).await.map_err(Into::into)?)
687 }
688}
689
690impl<Eth> std::fmt::Debug for TraceApi<Eth> {
691 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
692 f.debug_struct("TraceApi").finish_non_exhaustive()
693 }
694}
695impl<Eth> Clone for TraceApi<Eth> {
696 fn clone(&self) -> Self {
697 Self { inner: Arc::clone(&self.inner) }
698 }
699}
700
701struct TraceApiInner<Eth> {
702 eth_api: Eth,
704 blocking_task_guard: BlockingTaskGuard,
706 eth_config: EthConfig,
708}
709
710fn reward_trace<H: BlockHeader>(header: &H, reward: RewardAction) -> LocalizedTransactionTrace {
713 LocalizedTransactionTrace {
714 block_hash: Some(header.hash_slow()),
715 block_number: Some(header.number()),
716 transaction_hash: None,
717 transaction_position: None,
718 trace: TransactionTrace {
719 trace_address: vec![],
720 subtraces: 0,
721 action: Action::Reward(reward),
722 error: None,
723 result: None,
724 },
725 }
726}