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