reth_rpc/
trace.rs

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_rpc_api::TraceApiServer;
24use reth_rpc_convert::RpcTxReq;
25use reth_rpc_eth_api::{
26    helpers::{Call, LoadPendingBlock, LoadTransaction, Trace, TraceExt},
27    FromEthApiError, RpcNodeCore,
28};
29use reth_rpc_eth_types::{error::EthApiError, utils::recover_raw_transaction, EthConfig};
30use reth_storage_api::{BlockNumReader, BlockReader};
31use reth_tasks::pool::BlockingTaskGuard;
32use reth_transaction_pool::{PoolPooledTx, PoolTransaction, TransactionPool};
33use revm::DatabaseCommit;
34use revm_inspectors::{
35    opcode::OpcodeGasInspector,
36    storage::StorageInspector,
37    tracing::{parity::populate_state_diff, TracingInspector, TracingInspectorConfig},
38};
39use serde::{Deserialize, Serialize};
40use std::sync::Arc;
41use tokio::sync::{AcquireError, OwnedSemaphorePermit};
42
43/// `trace` API implementation.
44///
45/// This type provides the functionality for handling `trace` related requests.
46pub struct TraceApi<Eth> {
47    inner: Arc<TraceApiInner<Eth>>,
48}
49
50// === impl TraceApi ===
51
52impl<Eth> TraceApi<Eth> {
53    /// Create a new instance of the [`TraceApi`]
54    pub fn new(
55        eth_api: Eth,
56        blocking_task_guard: BlockingTaskGuard,
57        eth_config: EthConfig,
58    ) -> Self {
59        let inner = Arc::new(TraceApiInner { eth_api, blocking_task_guard, eth_config });
60        Self { inner }
61    }
62
63    /// Acquires a permit to execute a tracing call.
64    async fn acquire_trace_permit(
65        &self,
66    ) -> std::result::Result<OwnedSemaphorePermit, AcquireError> {
67        self.inner.blocking_task_guard.clone().acquire_owned().await
68    }
69
70    /// Access the underlying `Eth` API.
71    pub fn eth_api(&self) -> &Eth {
72        &self.inner.eth_api
73    }
74}
75
76impl<Eth: RpcNodeCore> TraceApi<Eth> {
77    /// Access the underlying provider.
78    pub fn provider(&self) -> &Eth::Provider {
79        self.inner.eth_api.provider()
80    }
81}
82
83// === impl TraceApi === //
84
85impl<Eth> TraceApi<Eth>
86where
87    // tracing methods do _not_ read from mempool, hence no `LoadBlock` trait
88    // bound
89    Eth: Trace + Call + LoadPendingBlock + LoadTransaction + 'static,
90{
91    /// Executes the given call and returns a number of possible traces for it.
92    pub async fn trace_call(
93        &self,
94        trace_request: TraceCallRequest<RpcTxReq<Eth::NetworkTypes>>,
95    ) -> Result<TraceResults, Eth::Error> {
96        let at = trace_request.block_id.unwrap_or_default();
97        let config = TracingInspectorConfig::from_parity_config(&trace_request.trace_types);
98        let overrides =
99            EvmOverrides::new(trace_request.state_overrides, trace_request.block_overrides);
100        let mut inspector = TracingInspector::new(config);
101        let this = self.clone();
102        self.eth_api()
103            .spawn_with_call_at(trace_request.call, at, overrides, move |db, evm_env, tx_env| {
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    /// Traces a call to `eth_sendRawTransaction` without making the call, returning the traces.
115    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    /// Performs multiple call traces on top of the same block. i.e. transaction n will be executed
140    /// on top of a pending block with all n-1 transactions applied (traced) first.
141    ///
142    /// Note: Allows tracing dependent transactions, hence all transactions are traced in sequence
143    pub async fn trace_call_many(
144        &self,
145        calls: Vec<(RpcTxReq<Eth::NetworkTypes>, 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        // execute all transactions on top of each other and record the traces
152        self.eth_api()
153            .spawn_with_state_at_block(at, move |eth_api, mut db| {
154                let mut results = Vec::with_capacity(calls.len());
155                let mut calls = calls.into_iter().peekable();
156
157                while let Some((call, trace_types)) = calls.next() {
158                    let (evm_env, tx_env) = eth_api.prepare_call_env(
159                        evm_env.clone(),
160                        call,
161                        &mut db,
162                        Default::default(),
163                    )?;
164                    let config = TracingInspectorConfig::from_parity_config(&trace_types);
165                    let mut inspector = TracingInspector::new(config);
166                    let res = eth_api.inspect(&mut db, evm_env, tx_env, &mut inspector)?;
167
168                    let trace_res = inspector
169                        .into_parity_builder()
170                        .into_trace_results_with_state(&res, &trace_types, &db)
171                        .map_err(Eth::Error::from_eth_err)?;
172
173                    results.push(trace_res);
174
175                    // need to apply the state changes of this call before executing the
176                    // next call
177                    if calls.peek().is_some() {
178                        // need to apply the state changes of this call before executing
179                        // the next call
180                        db.commit(res.state)
181                    }
182                }
183
184                Ok(results)
185            })
186            .await
187    }
188
189    /// Replays a transaction, returning the traces.
190    pub async fn replay_transaction(
191        &self,
192        hash: B256,
193        trace_types: HashSet<TraceType>,
194    ) -> Result<TraceResults, Eth::Error> {
195        let config = TracingInspectorConfig::from_parity_config(&trace_types);
196        self.eth_api()
197            .spawn_trace_transaction_in_block(hash, config, move |_, inspector, res, db| {
198                let trace_res = inspector
199                    .into_parity_builder()
200                    .into_trace_results_with_state(&res, &trace_types, &db)
201                    .map_err(Eth::Error::from_eth_err)?;
202                Ok(trace_res)
203            })
204            .await
205            .transpose()
206            .ok_or(EthApiError::TransactionNotFound)?
207    }
208
209    /// Returns transaction trace objects at the given index
210    ///
211    /// Note: For compatibility reasons this only supports 1 single index, since this method is
212    /// supposed to return a single trace. See also: <https://github.com/ledgerwatch/erigon/blob/862faf054b8a0fa15962a9c73839b619886101eb/turbo/jsonrpc/trace_filtering.go#L114-L133>
213    ///
214    /// This returns `None` if `indices` is empty
215    pub async fn trace_get(
216        &self,
217        hash: B256,
218        indices: Vec<usize>,
219    ) -> Result<Option<LocalizedTransactionTrace>, Eth::Error> {
220        if indices.len() != 1 {
221            // The OG impl failed if it gets more than a single index
222            return Ok(None)
223        }
224        self.trace_get_index(hash, indices[0]).await
225    }
226
227    /// Returns transaction trace object at the given index.
228    ///
229    /// Returns `None` if the trace object at that index does not exist
230    pub async fn trace_get_index(
231        &self,
232        hash: B256,
233        index: usize,
234    ) -> Result<Option<LocalizedTransactionTrace>, Eth::Error> {
235        Ok(self.trace_transaction(hash).await?.and_then(|traces| traces.into_iter().nth(index)))
236    }
237
238    /// Returns all traces for the given transaction hash
239    pub async fn trace_transaction(
240        &self,
241        hash: B256,
242    ) -> Result<Option<Vec<LocalizedTransactionTrace>>, Eth::Error> {
243        self.eth_api()
244            .spawn_trace_transaction_in_block(
245                hash,
246                TracingInspectorConfig::default_parity(),
247                move |tx_info, inspector, _, _| {
248                    let traces =
249                        inspector.into_parity_builder().into_localized_transaction_traces(tx_info);
250                    Ok(traces)
251                },
252            )
253            .await
254    }
255
256    /// Returns all opcodes with their count and combined gas usage for the given transaction in no
257    /// particular order.
258    pub async fn trace_transaction_opcode_gas(
259        &self,
260        tx_hash: B256,
261    ) -> Result<Option<TransactionOpcodeGas>, Eth::Error> {
262        self.eth_api()
263            .spawn_trace_transaction_in_block_with_inspector(
264                tx_hash,
265                OpcodeGasInspector::default(),
266                move |_tx_info, inspector, _res, _| {
267                    let trace = TransactionOpcodeGas {
268                        transaction_hash: tx_hash,
269                        opcode_gas: inspector.opcode_gas_iter().collect(),
270                    };
271                    Ok(trace)
272                },
273            )
274            .await
275    }
276
277    /// Calculates the base block reward for the given block:
278    ///
279    /// - if Paris hardfork is activated, no block rewards are given
280    /// - if Paris hardfork is not activated, calculate block rewards with block number only
281    /// - if Paris hardfork is unknown, calculate block rewards with block number and ttd
282    fn calculate_base_block_reward<H: BlockHeader>(
283        &self,
284        header: &H,
285    ) -> Result<Option<u128>, Eth::Error> {
286        let chain_spec = self.provider().chain_spec();
287        let is_paris_activated = if chain_spec.chain() == MAINNET.chain() {
288            Some(header.number()) >= EthereumHardfork::Paris.mainnet_activation_block()
289        } else if chain_spec.chain() == SEPOLIA.chain() {
290            Some(header.number()) >= EthereumHardfork::Paris.sepolia_activation_block()
291        } else {
292            true
293        };
294
295        if is_paris_activated {
296            return Ok(None)
297        }
298
299        Ok(Some(base_block_reward_pre_merge(&chain_spec, header.number())))
300    }
301
302    /// Extracts the reward traces for the given block:
303    ///  - block reward
304    ///  - uncle rewards
305    fn extract_reward_traces<H: BlockHeader>(
306        &self,
307        header: &H,
308        ommers: Option<&[H]>,
309        base_block_reward: u128,
310    ) -> Vec<LocalizedTransactionTrace> {
311        let ommers_cnt = ommers.map(|o| o.len()).unwrap_or_default();
312        let mut traces = Vec::with_capacity(ommers_cnt + 1);
313
314        let block_reward = block_reward(base_block_reward, ommers_cnt);
315        traces.push(reward_trace(
316            header,
317            RewardAction {
318                author: header.beneficiary(),
319                reward_type: RewardType::Block,
320                value: U256::from(block_reward),
321            },
322        ));
323
324        let Some(ommers) = ommers else { return traces };
325
326        for uncle in ommers {
327            let uncle_reward = ommer_reward(base_block_reward, header.number(), uncle.number());
328            traces.push(reward_trace(
329                header,
330                RewardAction {
331                    author: uncle.beneficiary(),
332                    reward_type: RewardType::Uncle,
333                    value: U256::from(uncle_reward),
334                },
335            ));
336        }
337        traces
338    }
339}
340
341impl<Eth> TraceApi<Eth>
342where
343    // tracing methods read from mempool, hence `LoadBlock` trait bound via
344    // `TraceExt`
345    Eth: TraceExt + 'static,
346{
347    /// Returns all transaction traces that match the given filter.
348    ///
349    /// This is similar to [`Self::trace_block`] but only returns traces for transactions that match
350    /// the filter.
351    pub async fn trace_filter(
352        &self,
353        filter: TraceFilter,
354    ) -> Result<Vec<LocalizedTransactionTrace>, Eth::Error> {
355        // We'll reuse the matcher across multiple blocks that are traced in parallel
356        let matcher = Arc::new(filter.matcher());
357        let TraceFilter { from_block, to_block, mut after, count, .. } = filter;
358        let start = from_block.unwrap_or(0);
359
360        let latest_block = self.provider().best_block_number().map_err(Eth::Error::from_eth_err)?;
361        if start > latest_block {
362            // can't trace that range
363            return Err(EthApiError::HeaderNotFound(start.into()).into());
364        }
365        let end = to_block.unwrap_or(latest_block);
366
367        if start > end {
368            return Err(EthApiError::InvalidParams(
369                "invalid parameters: fromBlock cannot be greater than toBlock".to_string(),
370            )
371            .into())
372        }
373
374        // ensure that the range is not too large, since we need to fetch all blocks in the range
375        let distance = end.saturating_sub(start);
376        if distance > self.inner.eth_config.max_trace_filter_blocks {
377            return Err(EthApiError::InvalidParams(
378                "Block range too large; currently limited to 100 blocks".to_string(),
379            )
380            .into())
381        }
382
383        let mut all_traces = Vec::new();
384        let mut block_traces = Vec::with_capacity(self.inner.eth_config.max_tracing_requests);
385        for chunk_start in (start..end).step_by(self.inner.eth_config.max_tracing_requests) {
386            let chunk_end =
387                std::cmp::min(chunk_start + self.inner.eth_config.max_tracing_requests as u64, end);
388
389            // fetch all blocks in that chunk
390            let blocks = self
391                .eth_api()
392                .spawn_blocking_io(move |this| {
393                    Ok(this
394                        .provider()
395                        .recovered_block_range(chunk_start..=chunk_end)
396                        .map_err(Eth::Error::from_eth_err)?
397                        .into_iter()
398                        .map(Arc::new)
399                        .collect::<Vec<_>>())
400                })
401                .await?;
402
403            // trace all blocks
404            for block in &blocks {
405                let matcher = matcher.clone();
406                let traces = self.eth_api().trace_block_until(
407                    block.hash().into(),
408                    Some(block.clone()),
409                    None,
410                    TracingInspectorConfig::default_parity(),
411                    move |tx_info, mut ctx| {
412                        let mut traces = ctx
413                            .take_inspector()
414                            .into_parity_builder()
415                            .into_localized_transaction_traces(tx_info);
416                        traces.retain(|trace| matcher.matches(&trace.trace));
417                        Ok(Some(traces))
418                    },
419                );
420                block_traces.push(traces);
421            }
422
423            #[allow(clippy::iter_with_drain)]
424            let block_traces = futures::future::try_join_all(block_traces.drain(..)).await?;
425            all_traces.extend(block_traces.into_iter().flatten().flat_map(|traces| {
426                traces.into_iter().flatten().flat_map(|traces| traces.into_iter())
427            }));
428
429            // add reward traces for all blocks
430            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                    // no block reward, means we're past the Paris hardfork and don't expect any
443                    // rewards because the blocks in ascending order
444                    break
445                }
446            }
447
448            // Skips the first `after` number of matching traces.
449            if let Some(cutoff) = after.map(|a| a as usize) &&
450                cutoff < all_traces.len()
451            {
452                all_traces.drain(..cutoff);
453                // we removed the first `after` traces
454                after = None;
455            }
456
457            // Return at most `count` of traces
458            if let Some(count) = count {
459                let count = count as usize;
460                if count < all_traces.len() {
461                    all_traces.truncate(count);
462                    return Ok(all_traces)
463                }
464            };
465        }
466
467        // If `after` is greater than or equal to the number of matched traces, it returns an
468        // empty array.
469        if let Some(cutoff) = after.map(|a| a as usize) &&
470            cutoff >= all_traces.len()
471        {
472            return Ok(vec![])
473        }
474
475        Ok(all_traces)
476    }
477
478    /// Returns traces created at given block.
479    pub async fn trace_block(
480        &self,
481        block_id: BlockId,
482    ) -> Result<Option<Vec<LocalizedTransactionTrace>>, Eth::Error> {
483        let traces = self.eth_api().trace_block_with(
484            block_id,
485            None,
486            TracingInspectorConfig::default_parity(),
487            |tx_info, mut ctx| {
488                let traces = ctx
489                    .take_inspector()
490                    .into_parity_builder()
491                    .into_localized_transaction_traces(tx_info);
492                Ok(traces)
493            },
494        );
495
496        let block = self.eth_api().recovered_block(block_id);
497        let (maybe_traces, maybe_block) = futures::try_join!(traces, block)?;
498
499        let mut maybe_traces =
500            maybe_traces.map(|traces| traces.into_iter().flatten().collect::<Vec<_>>());
501
502        if let (Some(block), Some(traces)) = (maybe_block, maybe_traces.as_mut()) &&
503            let Some(base_block_reward) = self.calculate_base_block_reward(block.header())?
504        {
505            traces.extend(self.extract_reward_traces(
506                block.header(),
507                block.body().ommers(),
508                base_block_reward,
509            ));
510        }
511
512        Ok(maybe_traces)
513    }
514
515    /// Replays all transactions in a block
516    pub async fn replay_block_transactions(
517        &self,
518        block_id: BlockId,
519        trace_types: HashSet<TraceType>,
520    ) -> Result<Option<Vec<TraceResultsWithTransactionHash>>, Eth::Error> {
521        self.eth_api()
522            .trace_block_with(
523                block_id,
524                None,
525                TracingInspectorConfig::from_parity_config(&trace_types),
526                move |tx_info, mut ctx| {
527                    let mut full_trace = ctx
528                        .take_inspector()
529                        .into_parity_builder()
530                        .into_trace_results(&ctx.result, &trace_types);
531
532                    // If statediffs were requested, populate them with the account balance and
533                    // nonce from pre-state
534                    if let Some(ref mut state_diff) = full_trace.state_diff {
535                        populate_state_diff(state_diff, &ctx.db, ctx.state.iter())
536                            .map_err(Eth::Error::from_eth_err)?;
537                    }
538
539                    let trace = TraceResultsWithTransactionHash {
540                        transaction_hash: tx_info.hash.expect("tx hash is set"),
541                        full_trace,
542                    };
543                    Ok(trace)
544                },
545            )
546            .await
547    }
548
549    /// Returns the opcodes of all transactions in the given block.
550    ///
551    /// This is the same as [`Self::trace_transaction_opcode_gas`] but for all transactions in a
552    /// block.
553    pub async fn trace_block_opcode_gas(
554        &self,
555        block_id: BlockId,
556    ) -> Result<Option<BlockOpcodeGas>, Eth::Error> {
557        let res = self
558            .eth_api()
559            .trace_block_inspector(
560                block_id,
561                None,
562                OpcodeGasInspector::default,
563                move |tx_info, ctx| {
564                    let trace = TransactionOpcodeGas {
565                        transaction_hash: tx_info.hash.expect("tx hash is set"),
566                        opcode_gas: ctx.inspector.opcode_gas_iter().collect(),
567                    };
568                    Ok(trace)
569                },
570            )
571            .await?;
572
573        let Some(transactions) = res else { return Ok(None) };
574
575        let Some(block) = self.eth_api().recovered_block(block_id).await? else { return Ok(None) };
576
577        Ok(Some(BlockOpcodeGas {
578            block_hash: block.hash(),
579            block_number: block.number(),
580            transactions,
581        }))
582    }
583
584    /// Returns all storage slots accessed during transaction execution along with their access
585    /// counts.
586    pub async fn trace_block_storage_access(
587        &self,
588        block_id: BlockId,
589    ) -> Result<Option<BlockStorageAccess>, Eth::Error> {
590        let res = self
591            .eth_api()
592            .trace_block_inspector(
593                block_id,
594                None,
595                StorageInspector::default,
596                move |tx_info, ctx| {
597                    let trace = TransactionStorageAccess {
598                        transaction_hash: tx_info.hash.expect("tx hash is set"),
599                        storage_access: ctx.inspector.accessed_slots().clone(),
600                        unique_loads: ctx.inspector.unique_loads(),
601                        warm_loads: ctx.inspector.warm_loads(),
602                    };
603                    Ok(trace)
604                },
605            )
606            .await?;
607
608        let Some(transactions) = res else { return Ok(None) };
609
610        let Some(block) = self.eth_api().recovered_block(block_id).await? else { return Ok(None) };
611
612        Ok(Some(BlockStorageAccess {
613            block_hash: block.hash(),
614            block_number: block.number(),
615            transactions,
616        }))
617    }
618}
619
620#[async_trait]
621impl<Eth> TraceApiServer<RpcTxReq<Eth::NetworkTypes>> for TraceApi<Eth>
622where
623    Eth: TraceExt + 'static,
624{
625    /// Executes the given call and returns a number of possible traces for it.
626    ///
627    /// Handler for `trace_call`
628    async fn trace_call(
629        &self,
630        call: RpcTxReq<Eth::NetworkTypes>,
631        trace_types: HashSet<TraceType>,
632        block_id: Option<BlockId>,
633        state_overrides: Option<StateOverride>,
634        block_overrides: Option<Box<BlockOverrides>>,
635    ) -> RpcResult<TraceResults> {
636        let _permit = self.acquire_trace_permit().await;
637        let request =
638            TraceCallRequest { call, trace_types, block_id, state_overrides, block_overrides };
639        Ok(Self::trace_call(self, request).await.map_err(Into::into)?)
640    }
641
642    /// Handler for `trace_callMany`
643    async fn trace_call_many(
644        &self,
645        calls: Vec<(RpcTxReq<Eth::NetworkTypes>, HashSet<TraceType>)>,
646        block_id: Option<BlockId>,
647    ) -> RpcResult<Vec<TraceResults>> {
648        let _permit = self.acquire_trace_permit().await;
649        Ok(Self::trace_call_many(self, calls, block_id).await.map_err(Into::into)?)
650    }
651
652    /// Handler for `trace_rawTransaction`
653    async fn trace_raw_transaction(
654        &self,
655        data: Bytes,
656        trace_types: HashSet<TraceType>,
657        block_id: Option<BlockId>,
658    ) -> RpcResult<TraceResults> {
659        let _permit = self.acquire_trace_permit().await;
660        Ok(Self::trace_raw_transaction(self, data, trace_types, block_id)
661            .await
662            .map_err(Into::into)?)
663    }
664
665    /// Handler for `trace_replayBlockTransactions`
666    async fn replay_block_transactions(
667        &self,
668        block_id: BlockId,
669        trace_types: HashSet<TraceType>,
670    ) -> RpcResult<Option<Vec<TraceResultsWithTransactionHash>>> {
671        let _permit = self.acquire_trace_permit().await;
672        Ok(Self::replay_block_transactions(self, block_id, trace_types)
673            .await
674            .map_err(Into::into)?)
675    }
676
677    /// Handler for `trace_replayTransaction`
678    async fn replay_transaction(
679        &self,
680        transaction: B256,
681        trace_types: HashSet<TraceType>,
682    ) -> RpcResult<TraceResults> {
683        let _permit = self.acquire_trace_permit().await;
684        Ok(Self::replay_transaction(self, transaction, trace_types).await.map_err(Into::into)?)
685    }
686
687    /// Handler for `trace_block`
688    async fn trace_block(
689        &self,
690        block_id: BlockId,
691    ) -> RpcResult<Option<Vec<LocalizedTransactionTrace>>> {
692        let _permit = self.acquire_trace_permit().await;
693        Ok(Self::trace_block(self, block_id).await.map_err(Into::into)?)
694    }
695
696    /// Handler for `trace_filter`
697    ///
698    /// This is similar to `eth_getLogs` but for traces.
699    ///
700    /// # Limitations
701    /// This currently requires block filter fields, since reth does not have address indices yet.
702    async fn trace_filter(&self, filter: TraceFilter) -> RpcResult<Vec<LocalizedTransactionTrace>> {
703        let _permit = self.inner.blocking_task_guard.clone().acquire_many_owned(2).await;
704        Ok(Self::trace_filter(self, filter).await.map_err(Into::into)?)
705    }
706
707    /// Returns transaction trace at given index.
708    /// Handler for `trace_get`
709    async fn trace_get(
710        &self,
711        hash: B256,
712        indices: Vec<Index>,
713    ) -> RpcResult<Option<LocalizedTransactionTrace>> {
714        let _permit = self.acquire_trace_permit().await;
715        Ok(Self::trace_get(self, hash, indices.into_iter().map(Into::into).collect())
716            .await
717            .map_err(Into::into)?)
718    }
719
720    /// Handler for `trace_transaction`
721    async fn trace_transaction(
722        &self,
723        hash: B256,
724    ) -> RpcResult<Option<Vec<LocalizedTransactionTrace>>> {
725        let _permit = self.acquire_trace_permit().await;
726        Ok(Self::trace_transaction(self, hash).await.map_err(Into::into)?)
727    }
728
729    /// Handler for `trace_transactionOpcodeGas`
730    async fn trace_transaction_opcode_gas(
731        &self,
732        tx_hash: B256,
733    ) -> RpcResult<Option<TransactionOpcodeGas>> {
734        let _permit = self.acquire_trace_permit().await;
735        Ok(Self::trace_transaction_opcode_gas(self, tx_hash).await.map_err(Into::into)?)
736    }
737
738    /// Handler for `trace_blockOpcodeGas`
739    async fn trace_block_opcode_gas(&self, block_id: BlockId) -> RpcResult<Option<BlockOpcodeGas>> {
740        let _permit = self.acquire_trace_permit().await;
741        Ok(Self::trace_block_opcode_gas(self, block_id).await.map_err(Into::into)?)
742    }
743}
744
745impl<Eth> std::fmt::Debug for TraceApi<Eth> {
746    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
747        f.debug_struct("TraceApi").finish_non_exhaustive()
748    }
749}
750impl<Eth> Clone for TraceApi<Eth> {
751    fn clone(&self) -> Self {
752        Self { inner: Arc::clone(&self.inner) }
753    }
754}
755
756struct TraceApiInner<Eth> {
757    /// Access to commonly used code of the `eth` namespace
758    eth_api: Eth,
759    // restrict the number of concurrent calls to `trace_*`
760    blocking_task_guard: BlockingTaskGuard,
761    // eth config settings
762    eth_config: EthConfig,
763}
764
765/// Response type for storage tracing that contains all accessed storage slots
766/// for a transaction.
767#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
768#[serde(rename_all = "camelCase")]
769pub struct TransactionStorageAccess {
770    /// Hash of the transaction
771    pub transaction_hash: B256,
772    /// Tracks storage slots and access counter.
773    pub storage_access: HashMap<Address, HashMap<B256, u64>>,
774    /// Number of unique storage loads
775    pub unique_loads: u64,
776    /// Number of warm storage loads
777    pub warm_loads: u64,
778}
779
780/// Response type for storage tracing that contains all accessed storage slots
781#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
782#[serde(rename_all = "camelCase")]
783pub struct BlockStorageAccess {
784    /// The block hash
785    pub block_hash: BlockHash,
786    /// The block's number
787    pub block_number: u64,
788    /// All executed transactions in the block in the order they were executed
789    pub transactions: Vec<TransactionStorageAccess>,
790}
791
792/// Helper to construct a [`LocalizedTransactionTrace`] that describes a reward to the block
793/// beneficiary.
794fn reward_trace<H: BlockHeader>(header: &H, reward: RewardAction) -> LocalizedTransactionTrace {
795    LocalizedTransactionTrace {
796        block_hash: Some(header.hash_slow()),
797        block_number: Some(header.number()),
798        transaction_hash: None,
799        transaction_position: None,
800        trace: TransactionTrace {
801            trace_address: vec![],
802            subtraces: 0,
803            action: Action::Reward(reward),
804            error: None,
805            result: None,
806        },
807    }
808}