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