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::{helpers::TraceExt, FromEthApiError, RpcNodeCore};
24use reth_rpc_eth_types::{error::EthApiError, utils::recover_raw_transaction, EthConfig};
25use reth_storage_api::{BlockNumReader, BlockReader};
26use reth_tasks::pool::BlockingTaskGuard;
27use reth_transaction_pool::{PoolPooledTx, PoolTransaction, TransactionPool};
28use revm::DatabaseCommit;
29use revm_inspectors::{
30 opcode::OpcodeGasInspector,
31 tracing::{parity::populate_state_diff, TracingInspector, TracingInspectorConfig},
32};
33use std::sync::Arc;
34use tokio::sync::{AcquireError, OwnedSemaphorePermit};
35
36pub struct TraceApi<Eth> {
40 inner: Arc<TraceApiInner<Eth>>,
41}
42
43impl<Eth> TraceApi<Eth> {
46 pub fn new(
48 eth_api: Eth,
49 blocking_task_guard: BlockingTaskGuard,
50 eth_config: EthConfig,
51 ) -> Self {
52 let inner = Arc::new(TraceApiInner { eth_api, blocking_task_guard, eth_config });
53 Self { inner }
54 }
55
56 async fn acquire_trace_permit(
58 &self,
59 ) -> std::result::Result<OwnedSemaphorePermit, AcquireError> {
60 self.inner.blocking_task_guard.clone().acquire_owned().await
61 }
62
63 pub fn eth_api(&self) -> &Eth {
65 &self.inner.eth_api
66 }
67}
68
69impl<Eth: RpcNodeCore> TraceApi<Eth> {
70 pub fn provider(&self) -> &Eth::Provider {
72 self.inner.eth_api.provider()
73 }
74}
75
76impl<Eth> TraceApi<Eth>
79where
80 Eth: TraceExt + 'static,
81{
82 pub async fn trace_call(
84 &self,
85 trace_request: TraceCallRequest,
86 ) -> Result<TraceResults, Eth::Error> {
87 let at = trace_request.block_id.unwrap_or_default();
88 let config = TracingInspectorConfig::from_parity_config(&trace_request.trace_types);
89 let overrides =
90 EvmOverrides::new(trace_request.state_overrides, trace_request.block_overrides);
91 let mut inspector = TracingInspector::new(config);
92 let this = self.clone();
93 self.eth_api()
94 .spawn_with_call_at(trace_request.call, at, overrides, move |db, evm_env, tx_env| {
95 let db = db.0;
98
99 let (res, _) = this.eth_api().inspect(&mut *db, evm_env, tx_env, &mut inspector)?;
100 let trace_res = inspector
101 .into_parity_builder()
102 .into_trace_results_with_state(&res, &trace_request.trace_types, &db)
103 .map_err(Eth::Error::from_eth_err)?;
104 Ok(trace_res)
105 })
106 .await
107 }
108
109 pub async fn trace_raw_transaction(
111 &self,
112 tx: Bytes,
113 trace_types: HashSet<TraceType>,
114 block_id: Option<BlockId>,
115 ) -> Result<TraceResults, Eth::Error> {
116 let tx = recover_raw_transaction::<PoolPooledTx<Eth::Pool>>(&tx)?
117 .map(<Eth::Pool as TransactionPool>::Transaction::pooled_into_consensus);
118
119 let (evm_env, at) = self.eth_api().evm_env_at(block_id.unwrap_or_default()).await?;
120 let tx_env = self.eth_api().evm_config().tx_env(tx);
121
122 let config = TracingInspectorConfig::from_parity_config(&trace_types);
123
124 self.eth_api()
125 .spawn_trace_at_with_state(evm_env, tx_env, config, at, move |inspector, res, db| {
126 inspector
127 .into_parity_builder()
128 .into_trace_results_with_state(&res, &trace_types, &db)
129 .map_err(Eth::Error::from_eth_err)
130 })
131 .await
132 }
133
134 pub async fn trace_call_many(
139 &self,
140 calls: Vec<(TransactionRequest, HashSet<TraceType>)>,
141 block_id: Option<BlockId>,
142 ) -> Result<Vec<TraceResults>, Eth::Error> {
143 let at = block_id.unwrap_or(BlockId::pending());
144 let (evm_env, at) = self.eth_api().evm_env_at(at).await?;
145
146 let this = self.clone();
147 self.eth_api()
149 .spawn_with_state_at_block(at, move |state| {
150 let mut results = Vec::with_capacity(calls.len());
151 let mut db = CacheDB::new(StateProviderDatabase::new(state));
152
153 let mut calls = calls.into_iter().peekable();
154
155 while let Some((call, trace_types)) = calls.next() {
156 let (evm_env, tx_env) = this.eth_api().prepare_call_env(
157 evm_env.clone(),
158 call,
159 &mut db,
160 Default::default(),
161 )?;
162 let config = TracingInspectorConfig::from_parity_config(&trace_types);
163 let mut inspector = TracingInspector::new(config);
164 let (res, _) =
165 this.eth_api().inspect(&mut db, evm_env, tx_env, &mut inspector)?;
166
167 let trace_res = inspector
168 .into_parity_builder()
169 .into_trace_results_with_state(&res, &trace_types, &db)
170 .map_err(Eth::Error::from_eth_err)?;
171
172 results.push(trace_res);
173
174 if calls.peek().is_some() {
177 db.commit(res.state)
180 }
181 }
182
183 Ok(results)
184 })
185 .await
186 }
187
188 pub async fn replay_transaction(
190 &self,
191 hash: B256,
192 trace_types: HashSet<TraceType>,
193 ) -> Result<TraceResults, Eth::Error> {
194 let config = TracingInspectorConfig::from_parity_config(&trace_types);
195 self.eth_api()
196 .spawn_trace_transaction_in_block(hash, config, move |_, inspector, res, db| {
197 let trace_res = inspector
198 .into_parity_builder()
199 .into_trace_results_with_state(&res, &trace_types, &db)
200 .map_err(Eth::Error::from_eth_err)?;
201 Ok(trace_res)
202 })
203 .await
204 .transpose()
205 .ok_or(EthApiError::TransactionNotFound)?
206 }
207
208 pub async fn trace_get(
215 &self,
216 hash: B256,
217 indices: Vec<usize>,
218 ) -> Result<Option<LocalizedTransactionTrace>, Eth::Error> {
219 if indices.len() != 1 {
220 return Ok(None)
222 }
223 self.trace_get_index(hash, indices[0]).await
224 }
225
226 pub async fn trace_get_index(
230 &self,
231 hash: B256,
232 index: usize,
233 ) -> Result<Option<LocalizedTransactionTrace>, Eth::Error> {
234 Ok(self.trace_transaction(hash).await?.and_then(|traces| traces.into_iter().nth(index)))
235 }
236
237 pub async fn trace_filter(
242 &self,
243 filter: TraceFilter,
244 ) -> Result<Vec<LocalizedTransactionTrace>, Eth::Error> {
245 let matcher = Arc::new(filter.matcher());
247 let TraceFilter { from_block, to_block, after, count, .. } = filter;
248 let start = from_block.unwrap_or(0);
249
250 let latest_block = self.provider().best_block_number().map_err(Eth::Error::from_eth_err)?;
251 if start > latest_block {
252 return Err(EthApiError::HeaderNotFound(start.into()).into());
254 }
255 let end = to_block.unwrap_or(latest_block);
256
257 if start > end {
258 return Err(EthApiError::InvalidParams(
259 "invalid parameters: fromBlock cannot be greater than toBlock".to_string(),
260 )
261 .into())
262 }
263
264 let distance = end.saturating_sub(start);
266 if distance > self.inner.eth_config.max_trace_filter_blocks {
267 return Err(EthApiError::InvalidParams(
268 "Block range too large; currently limited to 100 blocks".to_string(),
269 )
270 .into())
271 }
272
273 let blocks = self
275 .provider()
276 .recovered_block_range(start..=end)
277 .map_err(Eth::Error::from_eth_err)?
278 .into_iter()
279 .map(Arc::new)
280 .collect::<Vec<_>>();
281
282 let mut block_traces = Vec::with_capacity(blocks.len());
284 for block in &blocks {
285 let matcher = matcher.clone();
286 let traces = self.eth_api().trace_block_until(
287 block.hash().into(),
288 Some(block.clone()),
289 None,
290 TracingInspectorConfig::default_parity(),
291 move |tx_info, inspector, _, _, _| {
292 let mut traces =
293 inspector.into_parity_builder().into_localized_transaction_traces(tx_info);
294 traces.retain(|trace| matcher.matches(&trace.trace));
295 Ok(Some(traces))
296 },
297 );
298 block_traces.push(traces);
299 }
300
301 let block_traces = futures::future::try_join_all(block_traces).await?;
302 let mut all_traces = block_traces
303 .into_iter()
304 .flatten()
305 .flat_map(|traces| traces.into_iter().flatten().flat_map(|traces| traces.into_iter()))
306 .collect::<Vec<_>>();
307
308 for block in &blocks {
310 if let Some(base_block_reward) = self.calculate_base_block_reward(block.header())? {
311 all_traces.extend(
312 self.extract_reward_traces(
313 block.header(),
314 block.body().ommers(),
315 base_block_reward,
316 )
317 .into_iter()
318 .filter(|trace| matcher.matches(&trace.trace)),
319 );
320 } else {
321 break
324 }
325 }
326
327 if let Some(after) = after.map(|a| a as usize) {
331 if after < all_traces.len() {
332 all_traces.drain(..after);
333 } else {
334 return Ok(vec![])
335 }
336 }
337
338 if let Some(count) = count {
340 let count = count as usize;
341 if count < all_traces.len() {
342 all_traces.truncate(count);
343 }
344 };
345
346 Ok(all_traces)
347 }
348
349 pub async fn trace_transaction(
351 &self,
352 hash: B256,
353 ) -> Result<Option<Vec<LocalizedTransactionTrace>>, Eth::Error> {
354 self.eth_api()
355 .spawn_trace_transaction_in_block(
356 hash,
357 TracingInspectorConfig::default_parity(),
358 move |tx_info, inspector, _, _| {
359 let traces =
360 inspector.into_parity_builder().into_localized_transaction_traces(tx_info);
361 Ok(traces)
362 },
363 )
364 .await
365 }
366
367 pub async fn trace_block(
369 &self,
370 block_id: BlockId,
371 ) -> Result<Option<Vec<LocalizedTransactionTrace>>, Eth::Error> {
372 let traces = self.eth_api().trace_block_with(
373 block_id,
374 None,
375 TracingInspectorConfig::default_parity(),
376 |tx_info, inspector, _, _, _| {
377 let traces =
378 inspector.into_parity_builder().into_localized_transaction_traces(tx_info);
379 Ok(traces)
380 },
381 );
382
383 let block = self.eth_api().recovered_block(block_id);
384 let (maybe_traces, maybe_block) = futures::try_join!(traces, block)?;
385
386 let mut maybe_traces =
387 maybe_traces.map(|traces| traces.into_iter().flatten().collect::<Vec<_>>());
388
389 if let (Some(block), Some(traces)) = (maybe_block, maybe_traces.as_mut()) {
390 if let Some(base_block_reward) = self.calculate_base_block_reward(block.header())? {
391 traces.extend(self.extract_reward_traces(
392 block.header(),
393 block.body().ommers(),
394 base_block_reward,
395 ));
396 }
397 }
398
399 Ok(maybe_traces)
400 }
401
402 pub async fn replay_block_transactions(
404 &self,
405 block_id: BlockId,
406 trace_types: HashSet<TraceType>,
407 ) -> Result<Option<Vec<TraceResultsWithTransactionHash>>, Eth::Error> {
408 self.eth_api()
409 .trace_block_with(
410 block_id,
411 None,
412 TracingInspectorConfig::from_parity_config(&trace_types),
413 move |tx_info, inspector, res, state, db| {
414 let mut full_trace =
415 inspector.into_parity_builder().into_trace_results(&res, &trace_types);
416
417 if let Some(ref mut state_diff) = full_trace.state_diff {
420 populate_state_diff(state_diff, db, state.iter())
421 .map_err(Eth::Error::from_eth_err)?;
422 }
423
424 let trace = TraceResultsWithTransactionHash {
425 transaction_hash: tx_info.hash.expect("tx hash is set"),
426 full_trace,
427 };
428 Ok(trace)
429 },
430 )
431 .await
432 }
433
434 pub async fn trace_transaction_opcode_gas(
437 &self,
438 tx_hash: B256,
439 ) -> Result<Option<TransactionOpcodeGas>, Eth::Error> {
440 self.eth_api()
441 .spawn_trace_transaction_in_block_with_inspector(
442 tx_hash,
443 OpcodeGasInspector::default(),
444 move |_tx_info, inspector, _res, _| {
445 let trace = TransactionOpcodeGas {
446 transaction_hash: tx_hash,
447 opcode_gas: inspector.opcode_gas_iter().collect(),
448 };
449 Ok(trace)
450 },
451 )
452 .await
453 }
454
455 pub async fn trace_block_opcode_gas(
460 &self,
461 block_id: BlockId,
462 ) -> Result<Option<BlockOpcodeGas>, Eth::Error> {
463 let res = self
464 .eth_api()
465 .trace_block_inspector(
466 block_id,
467 None,
468 OpcodeGasInspector::default,
469 move |tx_info, inspector, _res, _, _| {
470 let trace = TransactionOpcodeGas {
471 transaction_hash: tx_info.hash.expect("tx hash is set"),
472 opcode_gas: inspector.opcode_gas_iter().collect(),
473 };
474 Ok(trace)
475 },
476 )
477 .await?;
478
479 let Some(transactions) = res else { return Ok(None) };
480
481 let Some(block) = self.eth_api().recovered_block(block_id).await? else { return Ok(None) };
482
483 Ok(Some(BlockOpcodeGas {
484 block_hash: block.hash(),
485 block_number: block.number(),
486 transactions,
487 }))
488 }
489
490 fn calculate_base_block_reward<H: BlockHeader>(
496 &self,
497 header: &H,
498 ) -> Result<Option<u128>, Eth::Error> {
499 let chain_spec = self.provider().chain_spec();
500 let is_paris_activated = if chain_spec.chain() == MAINNET.chain() {
501 Some(header.number()) >= EthereumHardfork::Paris.mainnet_activation_block()
502 } else if chain_spec.chain() == SEPOLIA.chain() {
503 Some(header.number()) >= EthereumHardfork::Paris.sepolia_activation_block()
504 } else {
505 true
506 };
507
508 if is_paris_activated {
509 return Ok(None)
510 }
511
512 Ok(Some(base_block_reward_pre_merge(&chain_spec, header.number())))
513 }
514
515 fn extract_reward_traces<H: BlockHeader>(
519 &self,
520 header: &H,
521 ommers: Option<&[H]>,
522 base_block_reward: u128,
523 ) -> Vec<LocalizedTransactionTrace> {
524 let ommers_cnt = ommers.map(|o| o.len()).unwrap_or_default();
525 let mut traces = Vec::with_capacity(ommers_cnt + 1);
526
527 let block_reward = block_reward(base_block_reward, ommers_cnt);
528 traces.push(reward_trace(
529 header,
530 RewardAction {
531 author: header.beneficiary(),
532 reward_type: RewardType::Block,
533 value: U256::from(block_reward),
534 },
535 ));
536
537 let Some(ommers) = ommers else { return traces };
538
539 for uncle in ommers {
540 let uncle_reward = ommer_reward(base_block_reward, header.number(), uncle.number());
541 traces.push(reward_trace(
542 header,
543 RewardAction {
544 author: uncle.beneficiary(),
545 reward_type: RewardType::Uncle,
546 value: U256::from(uncle_reward),
547 },
548 ));
549 }
550 traces
551 }
552}
553
554#[async_trait]
555impl<Eth> TraceApiServer for TraceApi<Eth>
556where
557 Eth: TraceExt + 'static,
558{
559 async fn trace_call(
563 &self,
564 call: TransactionRequest,
565 trace_types: HashSet<TraceType>,
566 block_id: Option<BlockId>,
567 state_overrides: Option<StateOverride>,
568 block_overrides: Option<Box<BlockOverrides>>,
569 ) -> RpcResult<TraceResults> {
570 let _permit = self.acquire_trace_permit().await;
571 let request =
572 TraceCallRequest { call, trace_types, block_id, state_overrides, block_overrides };
573 Ok(Self::trace_call(self, request).await.map_err(Into::into)?)
574 }
575
576 async fn trace_call_many(
578 &self,
579 calls: Vec<(TransactionRequest, HashSet<TraceType>)>,
580 block_id: Option<BlockId>,
581 ) -> RpcResult<Vec<TraceResults>> {
582 let _permit = self.acquire_trace_permit().await;
583 Ok(Self::trace_call_many(self, calls, block_id).await.map_err(Into::into)?)
584 }
585
586 async fn trace_raw_transaction(
588 &self,
589 data: Bytes,
590 trace_types: HashSet<TraceType>,
591 block_id: Option<BlockId>,
592 ) -> RpcResult<TraceResults> {
593 let _permit = self.acquire_trace_permit().await;
594 Ok(Self::trace_raw_transaction(self, data, trace_types, block_id)
595 .await
596 .map_err(Into::into)?)
597 }
598
599 async fn replay_block_transactions(
601 &self,
602 block_id: BlockId,
603 trace_types: HashSet<TraceType>,
604 ) -> RpcResult<Option<Vec<TraceResultsWithTransactionHash>>> {
605 let _permit = self.acquire_trace_permit().await;
606 Ok(Self::replay_block_transactions(self, block_id, trace_types)
607 .await
608 .map_err(Into::into)?)
609 }
610
611 async fn replay_transaction(
613 &self,
614 transaction: B256,
615 trace_types: HashSet<TraceType>,
616 ) -> RpcResult<TraceResults> {
617 let _permit = self.acquire_trace_permit().await;
618 Ok(Self::replay_transaction(self, transaction, trace_types).await.map_err(Into::into)?)
619 }
620
621 async fn trace_block(
623 &self,
624 block_id: BlockId,
625 ) -> RpcResult<Option<Vec<LocalizedTransactionTrace>>> {
626 let _permit = self.acquire_trace_permit().await;
627 Ok(Self::trace_block(self, block_id).await.map_err(Into::into)?)
628 }
629
630 async fn trace_filter(&self, filter: TraceFilter) -> RpcResult<Vec<LocalizedTransactionTrace>> {
637 Ok(Self::trace_filter(self, filter).await.map_err(Into::into)?)
638 }
639
640 async fn trace_get(
643 &self,
644 hash: B256,
645 indices: Vec<Index>,
646 ) -> RpcResult<Option<LocalizedTransactionTrace>> {
647 let _permit = self.acquire_trace_permit().await;
648 Ok(Self::trace_get(self, hash, indices.into_iter().map(Into::into).collect())
649 .await
650 .map_err(Into::into)?)
651 }
652
653 async fn trace_transaction(
655 &self,
656 hash: B256,
657 ) -> RpcResult<Option<Vec<LocalizedTransactionTrace>>> {
658 let _permit = self.acquire_trace_permit().await;
659 Ok(Self::trace_transaction(self, hash).await.map_err(Into::into)?)
660 }
661
662 async fn trace_transaction_opcode_gas(
664 &self,
665 tx_hash: B256,
666 ) -> RpcResult<Option<TransactionOpcodeGas>> {
667 let _permit = self.acquire_trace_permit().await;
668 Ok(Self::trace_transaction_opcode_gas(self, tx_hash).await.map_err(Into::into)?)
669 }
670
671 async fn trace_block_opcode_gas(&self, block_id: BlockId) -> RpcResult<Option<BlockOpcodeGas>> {
673 let _permit = self.acquire_trace_permit().await;
674 Ok(Self::trace_block_opcode_gas(self, block_id).await.map_err(Into::into)?)
675 }
676}
677
678impl<Eth> std::fmt::Debug for TraceApi<Eth> {
679 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
680 f.debug_struct("TraceApi").finish_non_exhaustive()
681 }
682}
683impl<Eth> Clone for TraceApi<Eth> {
684 fn clone(&self) -> Self {
685 Self { inner: Arc::clone(&self.inner) }
686 }
687}
688
689struct TraceApiInner<Eth> {
690 eth_api: Eth,
692 blocking_task_guard: BlockingTaskGuard,
694 eth_config: EthConfig,
696}
697
698fn reward_trace<H: BlockHeader>(header: &H, reward: RewardAction) -> LocalizedTransactionTrace {
701 LocalizedTransactionTrace {
702 block_hash: Some(header.hash_slow()),
703 block_number: Some(header.number()),
704 transaction_hash: None,
705 transaction_position: None,
706 trace: TransactionTrace {
707 trace_address: vec![],
708 subtraces: 0,
709 action: Action::Reward(reward),
710 error: None,
711 result: None,
712 },
713 }
714}