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::{EthChainSpec, EthereumHardfork, MAINNET, SEPOLIA};
19use reth_evm::ConfigureEvm;
20use reth_primitives_traits::{BlockBody, BlockHeader};
21use reth_provider::{BlockNumReader, BlockReader, ChainSpecProvider};
22use reth_revm::{database::StateProviderDatabase, db::CacheDB};
23use reth_rpc_api::TraceApiServer;
24use reth_rpc_eth_api::{helpers::TraceExt, FromEthApiError, RpcNodeCore};
25use reth_rpc_eth_types::{error::EthApiError, utils::recover_raw_transaction, EthConfig};
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 let end = if let Some(to_block) = to_block {
250 to_block
251 } else {
252 self.provider().best_block_number().map_err(Eth::Error::from_eth_err)?
253 };
254
255 if start > end {
256 return Err(EthApiError::InvalidParams(
257 "invalid parameters: fromBlock cannot be greater than toBlock".to_string(),
258 )
259 .into())
260 }
261
262 let distance = end.saturating_sub(start);
264 if distance > self.inner.eth_config.max_trace_filter_blocks {
265 return Err(EthApiError::InvalidParams(
266 "Block range too large; currently limited to 100 blocks".to_string(),
267 )
268 .into())
269 }
270
271 let blocks = self
273 .provider()
274 .recovered_block_range(start..=end)
275 .map_err(Eth::Error::from_eth_err)?
276 .into_iter()
277 .map(Arc::new)
278 .collect::<Vec<_>>();
279
280 let mut block_traces = Vec::with_capacity(blocks.len());
282 for block in &blocks {
283 let matcher = matcher.clone();
284 let traces = self.eth_api().trace_block_until(
285 block.hash().into(),
286 Some(block.clone()),
287 None,
288 TracingInspectorConfig::default_parity(),
289 move |tx_info, inspector, _, _, _| {
290 let mut traces =
291 inspector.into_parity_builder().into_localized_transaction_traces(tx_info);
292 traces.retain(|trace| matcher.matches(&trace.trace));
293 Ok(Some(traces))
294 },
295 );
296 block_traces.push(traces);
297 }
298
299 let block_traces = futures::future::try_join_all(block_traces).await?;
300 let mut all_traces = block_traces
301 .into_iter()
302 .flatten()
303 .flat_map(|traces| traces.into_iter().flatten().flat_map(|traces| traces.into_iter()))
304 .collect::<Vec<_>>();
305
306 for block in &blocks {
308 if let Some(base_block_reward) = self.calculate_base_block_reward(block.header())? {
309 all_traces.extend(
310 self.extract_reward_traces(
311 block.header(),
312 block.body().ommers(),
313 base_block_reward,
314 )
315 .into_iter()
316 .filter(|trace| matcher.matches(&trace.trace)),
317 );
318 } else {
319 break
322 }
323 }
324
325 if let Some(after) = after.map(|a| a as usize) {
329 if after < all_traces.len() {
330 all_traces.drain(..after);
331 } else {
332 return Ok(vec![])
333 }
334 }
335
336 if let Some(count) = count {
338 let count = count as usize;
339 if count < all_traces.len() {
340 all_traces.truncate(count);
341 }
342 };
343
344 Ok(all_traces)
345 }
346
347 pub async fn trace_transaction(
349 &self,
350 hash: B256,
351 ) -> Result<Option<Vec<LocalizedTransactionTrace>>, Eth::Error> {
352 self.eth_api()
353 .spawn_trace_transaction_in_block(
354 hash,
355 TracingInspectorConfig::default_parity(),
356 move |tx_info, inspector, _, _| {
357 let traces =
358 inspector.into_parity_builder().into_localized_transaction_traces(tx_info);
359 Ok(traces)
360 },
361 )
362 .await
363 }
364
365 pub async fn trace_block(
367 &self,
368 block_id: BlockId,
369 ) -> Result<Option<Vec<LocalizedTransactionTrace>>, Eth::Error> {
370 let traces = self.eth_api().trace_block_with(
371 block_id,
372 None,
373 TracingInspectorConfig::default_parity(),
374 |tx_info, inspector, _, _, _| {
375 let traces =
376 inspector.into_parity_builder().into_localized_transaction_traces(tx_info);
377 Ok(traces)
378 },
379 );
380
381 let block = self.eth_api().recovered_block(block_id);
382 let (maybe_traces, maybe_block) = futures::try_join!(traces, block)?;
383
384 let mut maybe_traces =
385 maybe_traces.map(|traces| traces.into_iter().flatten().collect::<Vec<_>>());
386
387 if let (Some(block), Some(traces)) = (maybe_block, maybe_traces.as_mut()) {
388 if let Some(base_block_reward) = self.calculate_base_block_reward(block.header())? {
389 traces.extend(self.extract_reward_traces(
390 block.header(),
391 block.body().ommers(),
392 base_block_reward,
393 ));
394 }
395 }
396
397 Ok(maybe_traces)
398 }
399
400 pub async fn replay_block_transactions(
402 &self,
403 block_id: BlockId,
404 trace_types: HashSet<TraceType>,
405 ) -> Result<Option<Vec<TraceResultsWithTransactionHash>>, Eth::Error> {
406 self.eth_api()
407 .trace_block_with(
408 block_id,
409 None,
410 TracingInspectorConfig::from_parity_config(&trace_types),
411 move |tx_info, inspector, res, state, db| {
412 let mut full_trace =
413 inspector.into_parity_builder().into_trace_results(&res, &trace_types);
414
415 if let Some(ref mut state_diff) = full_trace.state_diff {
418 populate_state_diff(state_diff, db, state.iter())
419 .map_err(Eth::Error::from_eth_err)?;
420 }
421
422 let trace = TraceResultsWithTransactionHash {
423 transaction_hash: tx_info.hash.expect("tx hash is set"),
424 full_trace,
425 };
426 Ok(trace)
427 },
428 )
429 .await
430 }
431
432 pub async fn trace_transaction_opcode_gas(
435 &self,
436 tx_hash: B256,
437 ) -> Result<Option<TransactionOpcodeGas>, Eth::Error> {
438 self.eth_api()
439 .spawn_trace_transaction_in_block_with_inspector(
440 tx_hash,
441 OpcodeGasInspector::default(),
442 move |_tx_info, inspector, _res, _| {
443 let trace = TransactionOpcodeGas {
444 transaction_hash: tx_hash,
445 opcode_gas: inspector.opcode_gas_iter().collect(),
446 };
447 Ok(trace)
448 },
449 )
450 .await
451 }
452
453 pub async fn trace_block_opcode_gas(
458 &self,
459 block_id: BlockId,
460 ) -> Result<Option<BlockOpcodeGas>, Eth::Error> {
461 let res = self
462 .eth_api()
463 .trace_block_inspector(
464 block_id,
465 None,
466 OpcodeGasInspector::default,
467 move |tx_info, inspector, _res, _, _| {
468 let trace = TransactionOpcodeGas {
469 transaction_hash: tx_info.hash.expect("tx hash is set"),
470 opcode_gas: inspector.opcode_gas_iter().collect(),
471 };
472 Ok(trace)
473 },
474 )
475 .await?;
476
477 let Some(transactions) = res else { return Ok(None) };
478
479 let Some(block) = self.eth_api().recovered_block(block_id).await? else { return Ok(None) };
480
481 Ok(Some(BlockOpcodeGas {
482 block_hash: block.hash(),
483 block_number: block.number(),
484 transactions,
485 }))
486 }
487
488 fn calculate_base_block_reward<H: BlockHeader>(
494 &self,
495 header: &H,
496 ) -> Result<Option<u128>, Eth::Error> {
497 let chain_spec = self.provider().chain_spec();
498 let is_paris_activated = if chain_spec.chain() == MAINNET.chain() {
499 Some(header.number()) >= EthereumHardfork::Paris.mainnet_activation_block()
500 } else if chain_spec.chain() == SEPOLIA.chain() {
501 Some(header.number()) >= EthereumHardfork::Paris.sepolia_activation_block()
502 } else {
503 true
504 };
505
506 if is_paris_activated {
507 return Ok(None)
508 }
509
510 Ok(Some(base_block_reward_pre_merge(&chain_spec, header.number())))
511 }
512
513 fn extract_reward_traces<H: BlockHeader>(
517 &self,
518 header: &H,
519 ommers: Option<&[H]>,
520 base_block_reward: u128,
521 ) -> Vec<LocalizedTransactionTrace> {
522 let ommers_cnt = ommers.map(|o| o.len()).unwrap_or_default();
523 let mut traces = Vec::with_capacity(ommers_cnt + 1);
524
525 let block_reward = block_reward(base_block_reward, ommers_cnt);
526 traces.push(reward_trace(
527 header,
528 RewardAction {
529 author: header.beneficiary(),
530 reward_type: RewardType::Block,
531 value: U256::from(block_reward),
532 },
533 ));
534
535 let Some(ommers) = ommers else { return traces };
536
537 for uncle in ommers {
538 let uncle_reward = ommer_reward(base_block_reward, header.number(), uncle.number());
539 traces.push(reward_trace(
540 header,
541 RewardAction {
542 author: uncle.beneficiary(),
543 reward_type: RewardType::Uncle,
544 value: U256::from(uncle_reward),
545 },
546 ));
547 }
548 traces
549 }
550}
551
552#[async_trait]
553impl<Eth> TraceApiServer for TraceApi<Eth>
554where
555 Eth: TraceExt + 'static,
556{
557 async fn trace_call(
561 &self,
562 call: TransactionRequest,
563 trace_types: HashSet<TraceType>,
564 block_id: Option<BlockId>,
565 state_overrides: Option<StateOverride>,
566 block_overrides: Option<Box<BlockOverrides>>,
567 ) -> RpcResult<TraceResults> {
568 let _permit = self.acquire_trace_permit().await;
569 let request =
570 TraceCallRequest { call, trace_types, block_id, state_overrides, block_overrides };
571 Ok(Self::trace_call(self, request).await.map_err(Into::into)?)
572 }
573
574 async fn trace_call_many(
576 &self,
577 calls: Vec<(TransactionRequest, HashSet<TraceType>)>,
578 block_id: Option<BlockId>,
579 ) -> RpcResult<Vec<TraceResults>> {
580 let _permit = self.acquire_trace_permit().await;
581 Ok(Self::trace_call_many(self, calls, block_id).await.map_err(Into::into)?)
582 }
583
584 async fn trace_raw_transaction(
586 &self,
587 data: Bytes,
588 trace_types: HashSet<TraceType>,
589 block_id: Option<BlockId>,
590 ) -> RpcResult<TraceResults> {
591 let _permit = self.acquire_trace_permit().await;
592 Ok(Self::trace_raw_transaction(self, data, trace_types, block_id)
593 .await
594 .map_err(Into::into)?)
595 }
596
597 async fn replay_block_transactions(
599 &self,
600 block_id: BlockId,
601 trace_types: HashSet<TraceType>,
602 ) -> RpcResult<Option<Vec<TraceResultsWithTransactionHash>>> {
603 let _permit = self.acquire_trace_permit().await;
604 Ok(Self::replay_block_transactions(self, block_id, trace_types)
605 .await
606 .map_err(Into::into)?)
607 }
608
609 async fn replay_transaction(
611 &self,
612 transaction: B256,
613 trace_types: HashSet<TraceType>,
614 ) -> RpcResult<TraceResults> {
615 let _permit = self.acquire_trace_permit().await;
616 Ok(Self::replay_transaction(self, transaction, trace_types).await.map_err(Into::into)?)
617 }
618
619 async fn trace_block(
621 &self,
622 block_id: BlockId,
623 ) -> RpcResult<Option<Vec<LocalizedTransactionTrace>>> {
624 let _permit = self.acquire_trace_permit().await;
625 Ok(Self::trace_block(self, block_id).await.map_err(Into::into)?)
626 }
627
628 async fn trace_filter(&self, filter: TraceFilter) -> RpcResult<Vec<LocalizedTransactionTrace>> {
635 Ok(Self::trace_filter(self, filter).await.map_err(Into::into)?)
636 }
637
638 async fn trace_get(
641 &self,
642 hash: B256,
643 indices: Vec<Index>,
644 ) -> RpcResult<Option<LocalizedTransactionTrace>> {
645 let _permit = self.acquire_trace_permit().await;
646 Ok(Self::trace_get(self, hash, indices.into_iter().map(Into::into).collect())
647 .await
648 .map_err(Into::into)?)
649 }
650
651 async fn trace_transaction(
653 &self,
654 hash: B256,
655 ) -> RpcResult<Option<Vec<LocalizedTransactionTrace>>> {
656 let _permit = self.acquire_trace_permit().await;
657 Ok(Self::trace_transaction(self, hash).await.map_err(Into::into)?)
658 }
659
660 async fn trace_transaction_opcode_gas(
662 &self,
663 tx_hash: B256,
664 ) -> RpcResult<Option<TransactionOpcodeGas>> {
665 let _permit = self.acquire_trace_permit().await;
666 Ok(Self::trace_transaction_opcode_gas(self, tx_hash).await.map_err(Into::into)?)
667 }
668
669 async fn trace_block_opcode_gas(&self, block_id: BlockId) -> RpcResult<Option<BlockOpcodeGas>> {
671 let _permit = self.acquire_trace_permit().await;
672 Ok(Self::trace_block_opcode_gas(self, block_id).await.map_err(Into::into)?)
673 }
674}
675
676impl<Eth> std::fmt::Debug for TraceApi<Eth> {
677 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
678 f.debug_struct("TraceApi").finish_non_exhaustive()
679 }
680}
681impl<Eth> Clone for TraceApi<Eth> {
682 fn clone(&self) -> Self {
683 Self { inner: Arc::clone(&self.inner) }
684 }
685}
686
687struct TraceApiInner<Eth> {
688 eth_api: Eth,
690 blocking_task_guard: BlockingTaskGuard,
692 eth_config: EthConfig,
694}
695
696fn reward_trace<H: BlockHeader>(header: &H, reward: RewardAction) -> LocalizedTransactionTrace {
699 LocalizedTransactionTrace {
700 block_hash: Some(header.hash_slow()),
701 block_number: Some(header.number()),
702 transaction_hash: None,
703 transaction_position: None,
704 trace: TransactionTrace {
705 trace_address: vec![],
706 subtraces: 0,
707 action: Action::Reward(reward),
708 error: None,
709 result: None,
710 },
711 }
712}