Skip to main content

reth_rpc/
debug.rs

1use alloy_consensus::{constants::KECCAK_EMPTY, transaction::TxHashRef, BlockHeader};
2use alloy_eips::{eip2718::Encodable2718, BlockId, BlockNumberOrTag};
3use alloy_evm::{env::BlockEnvironment, Evm};
4use alloy_genesis::ChainConfig;
5use alloy_primitives::{hex::decode, uint, Address, Bytes, B256, U256, U64};
6use alloy_rlp::{Decodable, Encodable};
7use alloy_rpc_types::BlockTransactionsKind;
8use alloy_rpc_types_debug::ExecutionWitness;
9use alloy_rpc_types_eth::{
10    state::EvmOverrides, Account, AccountInfo, BlockError, Bundle, Index, StateContext,
11};
12use alloy_rpc_types_trace::geth::{
13    ChainBlockTraceResult, GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace,
14    TraceResult,
15};
16use async_trait::async_trait;
17use futures::Stream;
18use jsonrpsee::{core::RpcResult, PendingSubscriptionSink, SubscriptionMessage};
19use parking_lot::RwLock;
20use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks};
21use reth_engine_primitives::ConsensusEngineEvent;
22use reth_errors::RethError;
23use reth_evm::{block::BlockExecutor, execute::Executor, ConfigureEvm, EvmEnvFor};
24use reth_primitives_traits::{
25    Block as BlockTrait, BlockBody, BlockTy, ReceiptWithBloom, RecoveredBlock,
26};
27use reth_revm::{db::State, witness::ExecutionWitnessRecord};
28use reth_rpc_api::DebugApiServer;
29use reth_rpc_convert::RpcTxReq;
30use reth_rpc_eth_api::{
31    helpers::{EthTransactions, TraceExt},
32    AsEthApiError, FromEthApiError, FromEvmError, RpcConvert, RpcNodeCore,
33};
34use reth_rpc_eth_types::{EthApiError, StateCacheDb};
35use reth_rpc_server_types::{result::internal_rpc_err, ToRpcResult};
36use reth_storage_api::{
37    BlockIdReader, BlockReaderIdExt, HashedPostStateProvider, HeaderProvider, ProviderBlock,
38    ReceiptProviderIdExt, StateProviderFactory, StateRootProvider, StorageRootProvider,
39    TransactionVariant,
40};
41use reth_tasks::{pool::BlockingTaskGuard, Runtime};
42use reth_transaction_pool::TransactionPool;
43use reth_trie_common::{
44    root::storage_root_unsorted, updates::TrieUpdates, ExecutionWitnessMode, HashedPostState,
45    HashedStorage,
46};
47use revm::{database::states::bundle_state::BundleRetention, Database, DatabaseCommit};
48use revm_inspectors::tracing::{DebugInspector, TransactionContext};
49use serde::{Deserialize, Serialize};
50use std::{collections::VecDeque, sync::Arc};
51use tokio::sync::{AcquireError, OwnedSemaphorePermit};
52use tokio_stream::StreamExt;
53
54/// `debug` API implementation.
55///
56/// This type provides the functionality for handling `debug` related requests.
57pub struct DebugApi<Eth: RpcNodeCore> {
58    inner: Arc<DebugApiInner<Eth>>,
59}
60
61impl<Eth> DebugApi<Eth>
62where
63    Eth: RpcNodeCore,
64{
65    /// Create a new instance of the [`DebugApi`]
66    pub fn new(
67        eth_api: Eth,
68        blocking_task_guard: BlockingTaskGuard,
69        executor: &Runtime,
70        mut stream: impl Stream<Item = ConsensusEngineEvent<Eth::Primitives>> + Send + Unpin + 'static,
71    ) -> Self {
72        let bad_block_store = BadBlockStore::default();
73        let inner = Arc::new(DebugApiInner {
74            eth_api,
75            blocking_task_guard,
76            task_spawner: executor.clone(),
77            bad_block_store: bad_block_store.clone(),
78        });
79
80        // Spawn a task caching bad blocks
81        executor.spawn_task(async move {
82            while let Some(event) = stream.next().await {
83                if let ConsensusEngineEvent::InvalidBlock { block, error } = event &&
84                    let Ok(recovered) = RecoveredBlock::try_recover_sealed(*block)
85                {
86                    bad_block_store.insert(recovered, error);
87                }
88            }
89        });
90
91        Self { inner }
92    }
93
94    /// Access the underlying `Eth` API.
95    pub fn eth_api(&self) -> &Eth {
96        &self.inner.eth_api
97    }
98
99    /// Access the underlying provider.
100    pub fn provider(&self) -> &Eth::Provider {
101        self.inner.eth_api.provider()
102    }
103}
104
105// === impl DebugApi ===
106
107impl<Eth> DebugApi<Eth>
108where
109    Eth: TraceExt,
110{
111    /// Acquires a permit to execute a tracing call.
112    async fn acquire_trace_permit(&self) -> Result<OwnedSemaphorePermit, AcquireError> {
113        self.inner.blocking_task_guard.clone().acquire_owned().await
114    }
115
116    /// Trace the entire block asynchronously
117    async fn trace_block(
118        &self,
119        block: Arc<RecoveredBlock<ProviderBlock<Eth::Provider>>>,
120        evm_env: EvmEnvFor<Eth::Evm>,
121        opts: GethDebugTracingOptions,
122    ) -> Result<Vec<TraceResult>, Eth::Error> {
123        self.eth_api()
124            .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
125                let mut results = Vec::with_capacity(block.body().transactions().len());
126
127                eth_api.apply_pre_execution_changes(&block, &mut db)?;
128
129                let block_env = evm_env.block_env.clone();
130
131                let mut transactions = block.transactions_recovered().enumerate().peekable();
132                let inspector = DebugInspector::new(opts).map_err(Eth::Error::from_eth_err)?;
133                let mut evm =
134                    eth_api.evm_config().evm_with_env_and_inspector(&mut db, evm_env, inspector);
135                while let Some((index, tx)) = transactions.next() {
136                    let tx_env = eth_api.evm_config().tx_env(tx);
137
138                    let res = evm.transact(tx_env.clone()).map_err(Eth::Error::from_evm_err)?;
139
140                    let (db, inspector, _) = evm.components_mut();
141                    let result = inspector
142                        .get_result(
143                            Some(TransactionContext {
144                                block_hash: Some(block.hash()),
145                                tx_hash: Some(*tx.tx_hash()),
146                                tx_index: Some(index),
147                            }),
148                            &tx_env,
149                            &block_env,
150                            &res,
151                            db,
152                        )
153                        .map_err(Eth::Error::from_eth_err)?;
154
155                    results.push(TraceResult::Success { result, tx_hash: Some(*tx.tx_hash()) });
156                    if transactions.peek().is_some() {
157                        inspector.fuse().map_err(Eth::Error::from_eth_err)?;
158                        // need to apply the state changes of this transaction before executing the
159                        // next transaction
160                        db.commit(res.state)
161                    }
162                }
163
164                Ok(results)
165            })
166            .await
167    }
168
169    /// Traces a block for `traceChain`, preserving transaction-level tracing failures in the
170    /// subscription result instead of failing the entire range.
171    async fn trace_chain_block(
172        &self,
173        block: Arc<RecoveredBlock<ProviderBlock<Eth::Provider>>>,
174        opts: GethDebugTracingOptions,
175    ) -> Result<Vec<Option<TraceResult>>, Eth::Error> {
176        let evm_env = self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
177
178        self.eth_api()
179            .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
180                let tx_count = block.body().transactions().len();
181                let mut results = Vec::with_capacity(tx_count);
182
183                eth_api.apply_pre_execution_changes(&block, &mut db)?;
184
185                let block_env = evm_env.block_env.clone();
186                let mut transactions = block.transactions_recovered().enumerate().peekable();
187                let inspector = match DebugInspector::new(opts) {
188                    Ok(inspector) => inspector,
189                    Err(err) => {
190                        if let Some((_, tx)) = transactions.peek() {
191                            results.push(Some(TraceResult::Error {
192                                error: err.to_string(),
193                                tx_hash: Some(*tx.tx_hash()),
194                            }));
195                        }
196                        results.resize(tx_count, None);
197                        return Ok(results)
198                    }
199                };
200                let mut evm =
201                    eth_api.evm_config().evm_with_env_and_inspector(&mut db, evm_env, inspector);
202
203                while let Some((index, tx)) = transactions.next() {
204                    let tx_hash = *tx.tx_hash();
205                    let tx_env = eth_api.evm_config().tx_env(tx);
206                    let res = match evm.transact(tx_env.clone()) {
207                        Ok(res) => res,
208                        Err(err) => {
209                            results.push(Some(TraceResult::Error {
210                                error: err.to_string(),
211                                tx_hash: Some(tx_hash),
212                            }));
213                            break
214                        }
215                    };
216
217                    let (db, inspector, _) = evm.components_mut();
218                    let result = match inspector.get_result(
219                        Some(TransactionContext {
220                            block_hash: Some(block.hash()),
221                            tx_hash: Some(tx_hash),
222                            tx_index: Some(index),
223                        }),
224                        &tx_env,
225                        &block_env,
226                        &res,
227                        db,
228                    ) {
229                        Ok(result) => result,
230                        Err(err) => {
231                            results.push(Some(TraceResult::Error {
232                                error: err.to_string(),
233                                tx_hash: Some(tx_hash),
234                            }));
235                            break
236                        }
237                    };
238
239                    results.push(Some(TraceResult::Success { result, tx_hash: Some(tx_hash) }));
240                    if let Some((_, next_tx)) = transactions.peek() {
241                        if let Err(err) = inspector.fuse() {
242                            results.push(Some(TraceResult::Error {
243                                error: err.to_string(),
244                                tx_hash: Some(*next_tx.tx_hash()),
245                            }));
246                            break
247                        }
248                        db.commit(res.state);
249                    }
250                }
251
252                results.resize(tx_count, None);
253                Ok(results)
254            })
255            .await
256    }
257
258    /// Replays the given block and returns the trace of each transaction.
259    ///
260    /// This expects a rlp encoded block
261    ///
262    /// Note, the parent of this block must be present, or it will fail.
263    pub async fn debug_trace_raw_block(
264        &self,
265        rlp_block: Bytes,
266        opts: GethDebugTracingOptions,
267    ) -> Result<Vec<TraceResult>, Eth::Error> {
268        let block: ProviderBlock<Eth::Provider> = Decodable::decode(&mut rlp_block.as_ref())
269            .map_err(BlockError::RlpDecodeRawBlock)
270            .map_err(Eth::Error::from_eth_err)?;
271
272        let evm_env = self
273            .eth_api()
274            .evm_config()
275            .evm_env(block.header())
276            .map_err(RethError::other)
277            .map_err(Eth::Error::from_eth_err)?;
278
279        // Depending on EIP-2 we need to recover the transactions differently
280        let senders =
281            if self.provider().chain_spec().is_homestead_active_at_block(block.header().number()) {
282                block.body().recover_signers()
283            } else {
284                block.body().recover_signers_unchecked()
285            }
286            .map_err(Eth::Error::from_eth_err)?;
287
288        self.trace_block(Arc::new(block.into_recovered_with_signers(senders)), evm_env, opts).await
289    }
290
291    /// Replays a block and returns the trace of each transaction.
292    pub async fn debug_trace_block(
293        &self,
294        block_id: BlockId,
295        opts: GethDebugTracingOptions,
296    ) -> Result<Vec<TraceResult>, Eth::Error> {
297        let block = self
298            .eth_api()
299            .recovered_block(block_id)
300            .await?
301            .ok_or(EthApiError::TracingBlockNotFound(block_id))?;
302        // Tracing requires the parent state, which does not exist for the genesis block.
303        if block.number() == 0 {
304            return Err(EthApiError::GenesisNotTraceable.into())
305        }
306        let evm_env = self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
307
308        self.trace_block(block, evm_env, opts).await
309    }
310
311    /// Trace the transaction according to the provided options.
312    ///
313    /// Ref: <https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers>
314    pub async fn debug_trace_transaction(
315        &self,
316        tx_hash: B256,
317        opts: GethDebugTracingOptions,
318    ) -> Result<GethTrace, Eth::Error> {
319        let (transaction, block, bal) =
320            match self.eth_api().transaction_and_block_and_maybe_bal(tx_hash).await? {
321                None => return Err(EthApiError::TracingTransactionNotFound.into()),
322                Some(res) => res,
323            };
324
325        self.eth_api()
326            .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
327                // configure env for the target transaction
328                let (tx, tx_info) = transaction.split();
329
330                // index should always be available because `transaction_and_block` only
331                // returns transactions included in a block
332                let index =
333                    tx_info.index.expect("transaction_and_block only returns block transactions")
334                        as usize;
335
336                let mut inspector = DebugInspector::new(opts).map_err(Eth::Error::from_eth_err)?;
337                let tx_env = eth_api.evm_config().tx_env(&tx);
338                let (res, evm_env) = eth_api.inspect_transaction_in_block(
339                    &block,
340                    &mut db,
341                    &mut inspector,
342                    index,
343                    tx_env.clone(),
344                    bal.as_deref(),
345                )?;
346
347                let trace = inspector
348                    .get_result(
349                        Some(TransactionContext {
350                            block_hash: Some(block.hash()),
351                            tx_index: Some(index),
352                            tx_hash: Some(*tx.tx_hash()),
353                        }),
354                        &tx_env,
355                        &evm_env.block_env,
356                        &res,
357                        &mut db,
358                    )
359                    .map_err(Eth::Error::from_eth_err)?;
360
361                Ok(trace)
362            })
363            .await
364    }
365
366    /// The `debug_traceCall` method lets you run an `eth_call` within the context of the given
367    /// block execution using the final state of parent block as the base.
368    ///
369    /// If `tx_index` is provided in opts, the call will be traced at the state after executing
370    /// transactions up to the specified index within the block (0-indexed).
371    /// If not provided, then uses the post-state (default behavior).
372    ///
373    /// Differences compare to `eth_call`:
374    ///  - `debug_traceCall` executes with __enabled__ basefee check, `eth_call` does not: <https://github.com/paradigmxyz/reth/issues/6240>
375    pub async fn debug_trace_call(
376        &self,
377        call: RpcTxReq<Eth::NetworkTypes>,
378        block_id: Option<BlockId>,
379        opts: GethDebugTracingCallOptions,
380    ) -> Result<GethTrace, Eth::Error> {
381        let at = block_id.unwrap_or_default();
382        let GethDebugTracingCallOptions {
383            tracing_options,
384            state_overrides,
385            block_overrides,
386            tx_index,
387        } = opts;
388        let overrides = EvmOverrides::new(state_overrides, block_overrides.map(Box::new));
389
390        // Check if we need to replay transactions for a specific tx_index
391        if let Some(tx_idx) = tx_index {
392            return self
393                .debug_trace_call_at_tx_index(call, at, tx_idx as usize, tracing_options, overrides)
394                .await;
395        }
396
397        let this = self.clone();
398        self.eth_api()
399            .spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| {
400                let mut inspector =
401                    DebugInspector::new(tracing_options).map_err(Eth::Error::from_eth_err)?;
402                let res = this.eth_api().inspect(
403                    &mut *db,
404                    evm_env.clone(),
405                    tx_env.clone(),
406                    &mut inspector,
407                )?;
408                let trace = inspector
409                    .get_result(None, &tx_env, &evm_env.block_env, &res, db)
410                    .map_err(Eth::Error::from_eth_err)?;
411                Ok(trace)
412            })
413            .await
414            .map_err(|err| match err.as_err() {
415                Some(EthApiError::HeaderNotFound(id)) if *id == at => {
416                    // Unknown blocks use -32000: https://github.com/ethereum/execution-apis/pull/855
417                    EthApiError::TracingBlockNotFound(at).into()
418                }
419                _ => err,
420            })
421    }
422
423    /// Helper method to execute `debug_trace_call` at a specific transaction index within a block.
424    /// This replays transactions up to the specified index, then executes the trace call in that
425    /// state.
426    async fn debug_trace_call_at_tx_index(
427        &self,
428        call: RpcTxReq<Eth::NetworkTypes>,
429        block_id: BlockId,
430        tx_index: usize,
431        tracing_options: GethDebugTracingOptions,
432        overrides: EvmOverrides,
433    ) -> Result<GethTrace, Eth::Error> {
434        // Get the target block to check transaction count
435        let (block, bal) = self
436            .eth_api()
437            .recovered_block_and_maybe_bal(block_id)
438            .await?
439            .ok_or(EthApiError::TracingBlockNotFound(block_id))?;
440
441        if tx_index >= block.transaction_count() {
442            // tx_index out of bounds
443            return Err(EthApiError::InvalidParams(format!(
444                "tx_index {} out of bounds for block with {} transactions",
445                tx_index,
446                block.transaction_count()
447            ))
448            .into())
449        }
450
451        // state overrides commit changes to the database, which an attached BAL would take read
452        // precedence over, so only position via BAL if there are none
453        let bal = bal.filter(|_| !overrides.has_state());
454
455        let evm_env = self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
456
457        self.eth_api()
458            .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
459                // 1. position the state before the transaction at the index
460                eth_api.replay_block_until(&mut db, &block, tx_index, bal.as_deref())?;
461
462                // 2. now execute the trace call on this state
463                let (evm_env, tx_env) =
464                    eth_api.prepare_call_env(evm_env, call, &mut db, overrides)?;
465
466                let mut inspector =
467                    DebugInspector::new(tracing_options).map_err(Eth::Error::from_eth_err)?;
468                let res =
469                    eth_api.inspect(&mut db, evm_env.clone(), tx_env.clone(), &mut inspector)?;
470                let trace = inspector
471                    .get_result(None, &tx_env, &evm_env.block_env, &res, &mut db)
472                    .map_err(Eth::Error::from_eth_err)?;
473
474                Ok(trace)
475            })
476            .await
477    }
478
479    /// The `debug_traceCallMany` method lets you run an `eth_callMany` within the context of the
480    /// given block execution using the first n transactions in the given block as base.
481    /// Each following bundle increments block number by 1 and block timestamp by 12 seconds
482    pub async fn debug_trace_call_many(
483        &self,
484        bundles: Vec<Bundle<RpcTxReq<Eth::NetworkTypes>>>,
485        state_context: Option<StateContext>,
486        opts: Option<GethDebugTracingCallOptions>,
487    ) -> Result<Vec<Vec<GethTrace>>, Eth::Error> {
488        if bundles.is_empty() {
489            return Err(EthApiError::InvalidParams(String::from("bundles are empty.")).into())
490        }
491
492        let StateContext { transaction_index, block_number } = state_context.unwrap_or_default();
493        let transaction_index = transaction_index.unwrap_or_default();
494
495        let target_block = block_number.unwrap_or_default();
496        let block = self
497            .eth_api()
498            .recovered_block(target_block)
499            .await?
500            .ok_or(EthApiError::HeaderNotFound(target_block))?;
501        let mut evm_env =
502            self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
503
504        let opts = opts.unwrap_or_default();
505        let GethDebugTracingCallOptions { tracing_options, mut state_overrides, .. } = opts;
506
507        // we're essentially replaying the transactions in the block here, hence we need the state
508        // that points to the beginning of the block, which is the state at the parent block
509        let mut at = block.parent_hash();
510        let mut replay_block_txs = true;
511
512        // if a transaction index is provided, we need to replay the transactions until the index
513        let num_txs =
514            transaction_index.index().unwrap_or_else(|| block.body().transactions().len());
515        // but if all transactions are to be replayed, we can use the state at the block itself
516        // this works with the exception of the PENDING block, because its state might not exist if
517        // built locally
518        if !target_block.is_pending() && num_txs == block.body().transactions().len() {
519            at = block.hash();
520            replay_block_txs = false;
521        }
522
523        self.eth_api()
524            .spawn_with_state_at_block(at, move |eth_api, mut db| {
525                // the outer vec for the bundles
526                let mut all_bundles = Vec::with_capacity(bundles.len());
527
528                if replay_block_txs {
529                    // only need to replay the transactions in the block if not all transactions are
530                    // to be replayed
531                    // Execute all transactions until index. No BAL positioning here: bundle
532                    // transactions commit state on top, and an attached BAL would take read
533                    // precedence over the committed changes
534                    eth_api.replay_block_until(&mut db, &block, num_txs, None)?;
535                }
536
537                // Trace all bundles
538                let mut bundles = bundles.into_iter().peekable();
539                let mut inspector = DebugInspector::new(tracing_options.clone())
540                    .map_err(Eth::Error::from_eth_err)?;
541                while let Some(bundle) = bundles.next() {
542                    let mut results = Vec::with_capacity(bundle.transactions.len());
543                    let Bundle { transactions, block_override } = bundle;
544
545                    let block_overrides = block_override.map(Box::new);
546
547                    let mut transactions = transactions.into_iter().peekable();
548                    while let Some(tx) = transactions.next() {
549                        // apply state overrides only once, before the first transaction
550                        let state_overrides = state_overrides.take();
551                        let overrides = EvmOverrides::new(state_overrides, block_overrides.clone());
552
553                        let (evm_env, tx_env) =
554                            eth_api.prepare_call_env(evm_env.clone(), tx, &mut db, overrides)?;
555
556                        let res = eth_api.inspect(
557                            &mut db,
558                            evm_env.clone(),
559                            tx_env.clone(),
560                            &mut inspector,
561                        )?;
562                        let trace = inspector
563                            .get_result(None, &tx_env, &evm_env.block_env, &res, &mut db)
564                            .map_err(Eth::Error::from_eth_err)?;
565
566                        // If there is more transactions, commit the database
567                        // If there is no transactions, but more bundles, commit to the database too
568                        if transactions.peek().is_some() || bundles.peek().is_some() {
569                            inspector.fuse().map_err(Eth::Error::from_eth_err)?;
570                            db.commit(res.state);
571                        }
572                        results.push(trace);
573                    }
574                    // Increment block_env number and timestamp for the next bundle
575                    evm_env.block_env.inner_mut().number += uint!(1_U256);
576                    evm_env.block_env.inner_mut().timestamp += uint!(12_U256);
577
578                    all_bundles.push(results);
579                }
580                Ok(all_bundles)
581            })
582            .await
583    }
584
585    /// Generates an execution witness for the given block hash. see
586    /// [`Self::debug_execution_witness`] for more info.
587    pub async fn debug_execution_witness_by_block_hash(
588        &self,
589        hash: B256,
590        mode: Option<ExecutionWitnessMode>,
591    ) -> Result<ExecutionWitness, Eth::Error> {
592        let this = self.clone();
593        let block = this
594            .eth_api()
595            .recovered_block(hash.into())
596            .await?
597            .ok_or(EthApiError::HeaderNotFound(hash.into()))?;
598
599        self.debug_execution_witness_for_block(block, mode.unwrap_or_default()).await
600    }
601
602    /// The `debug_executionWitness` method allows for re-execution of a block with the purpose of
603    /// generating an execution witness. The witness comprises of a map of all hashed trie nodes to
604    /// their preimages that were required during the execution of the block, including during state
605    /// root recomputation.
606    pub async fn debug_execution_witness(
607        &self,
608        block_id: BlockId,
609        mode: Option<ExecutionWitnessMode>,
610    ) -> Result<ExecutionWitness, Eth::Error> {
611        let this = self.clone();
612        let block = this
613            .eth_api()
614            .recovered_block(block_id)
615            .await?
616            .ok_or(EthApiError::HeaderNotFound(block_id))?;
617
618        self.debug_execution_witness_for_block(block, mode.unwrap_or_default()).await
619    }
620
621    /// Generates an execution witness, using the given recovered block.
622    pub async fn debug_execution_witness_for_block(
623        &self,
624        block: Arc<RecoveredBlock<ProviderBlock<Eth::Provider>>>,
625        mode: ExecutionWitnessMode,
626    ) -> Result<ExecutionWitness, Eth::Error> {
627        let block_number = block.header().number();
628        self.eth_api()
629            .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
630                let block_executor = eth_api.evm_config().executor(&mut db);
631
632                let mut witness = None;
633                let _ = block_executor
634                    .execute_with_state_closure(&block, |statedb: &State<_>| {
635                        witness =
636                            Some(ExecutionWitnessRecord::new(statedb).into_execution_witness(
637                                &statedb.database.database.0,
638                                eth_api.provider(),
639                                block_number,
640                                mode,
641                            ));
642                    })
643                    .map_err(|err| EthApiError::Internal(err.into()))?;
644
645                Ok(witness
646                    .expect("state closure is called after successful execution")
647                    .map_err(EthApiError::from)?)
648            })
649            .await
650    }
651
652    /// Returns account information, including the storage root, after replaying the block through
653    /// the transaction at the given index.
654    pub async fn debug_account_at(
655        &self,
656        block_id: BlockId,
657        tx_index: Index,
658        address: Address,
659    ) -> Result<Option<Account>, Eth::Error> {
660        self.replay_block_until(block_id, tx_index, move |db| Self::account(db, address))
661            .await
662            .map(Option::flatten)
663    }
664
665    /// Returns account information after replaying the block through the transaction at the given
666    /// index.
667    pub async fn debug_account_info_at(
668        &self,
669        block_id: BlockId,
670        tx_index: Index,
671        address: Address,
672    ) -> Result<Option<AccountInfo>, Eth::Error> {
673        self.replay_block_until(block_id, tx_index, move |db| Self::account_info(db, address)).await
674    }
675
676    /// Replays a block through the transaction at the given index and calls `f` with the resulting
677    /// state.
678    async fn replay_block_until<F, R>(
679        &self,
680        block_id: BlockId,
681        tx_index: Index,
682        f: F,
683    ) -> Result<Option<R>, Eth::Error>
684    where
685        F: FnOnce(&mut StateCacheDb) -> Result<R, Eth::Error> + Send + 'static,
686        R: Send + 'static,
687    {
688        let block = self
689            .eth_api()
690            .recovered_block(block_id)
691            .await?
692            .ok_or(EthApiError::HeaderNotFound(block_id))?;
693        let tx_index = usize::from(tx_index);
694        let transaction_count = block.transaction_count();
695        if tx_index >= transaction_count {
696            return Err(EthApiError::InvalidParams(format!(
697                "tx_index {tx_index} out of bounds for block with {transaction_count} transactions"
698            ))
699            .into())
700        }
701
702        self.eth_api()
703            .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
704                let mut executor = eth_api
705                    .evm_config()
706                    .executor_for_block(&mut db, block.sealed_block())
707                    .map_err(RethError::other)
708                    .map_err(Eth::Error::from_eth_err)?;
709                executor.apply_pre_execution_changes().map_err(Eth::Error::from_eth_err)?;
710
711                for tx in block.transactions_recovered().take(tx_index + 1) {
712                    executor.execute_transaction(tx).map_err(Eth::Error::from_eth_err)?;
713                }
714                drop(executor);
715
716                f(&mut db)
717            })
718            .await
719            .map(Some)
720    }
721
722    /// Retrieves the account's balance, nonce, code hash, and storage root from the given state.
723    fn account(db: &mut StateCacheDb, address: Address) -> Result<Option<Account>, Eth::Error> {
724        let account = db.basic(address).map_err(Eth::Error::from_eth_err)?;
725        let Some(account) = account else { return Ok(None) };
726
727        let balance = account.balance;
728        let nonce = account.nonce;
729        let code_hash = account.code_hash;
730        let (hashed_storage, status) = db
731            .cache
732            .accounts
733            .get(&address)
734            .and_then(|account| {
735                account.account.as_ref().map(|plain_account| {
736                    (HashedStorage::from_plain_storage(&plain_account.storage), account.status)
737                })
738            })
739            .unwrap_or_default();
740        let storage_root = if status.was_destroyed() {
741            // Destruction makes every slot not present in the cache zero, so the cache contains the
742            // complete storage trie for the account's new incarnation.
743            storage_root_unsorted(
744                hashed_storage.storage.into_iter().filter(|(_, value)| !value.is_zero()),
745            )
746        } else {
747            db.database.storage_root(address, hashed_storage).map_err(Eth::Error::from_eth_err)?
748        };
749
750        Ok(Some(Account { balance, nonce, code_hash, storage_root }))
751    }
752
753    /// Retrieves the account's balance, nonce, and code from the given state.
754    fn account_info<DB>(db: &mut DB, address: Address) -> Result<AccountInfo, Eth::Error>
755    where
756        DB: Database,
757        EthApiError: From<DB::Error>,
758    {
759        let account = db.basic(address).map_err(Eth::Error::from_eth_err)?.unwrap_or_default();
760        let code = if account.code_hash == KECCAK_EMPTY {
761            Default::default()
762        } else if let Some(code) = account.code {
763            code.original_bytes()
764        } else {
765            db.code_by_hash(account.code_hash).map_err(Eth::Error::from_eth_err)?.original_bytes()
766        };
767
768        Ok(AccountInfo { balance: account.balance, nonce: account.nonce, code })
769    }
770
771    /// Returns the code associated with a given hash at the specified block ID. If no code is
772    /// found, it returns None. If no block ID is provided, it defaults to the latest block.
773    pub async fn debug_code_by_hash(
774        &self,
775        hash: B256,
776        block_id: Option<BlockId>,
777    ) -> Result<Option<Bytes>, Eth::Error> {
778        Ok(self
779            .provider()
780            .state_by_block_id(block_id.unwrap_or_default())
781            .map_err(Eth::Error::from_eth_err)?
782            .bytecode_by_hash(&hash)
783            .map_err(Eth::Error::from_eth_err)?
784            .map(|b| b.original_bytes()))
785    }
786
787    /// Returns the state root of the `HashedPostState` on top of the state for the given block with
788    /// trie updates.
789    async fn debug_state_root_with_updates(
790        &self,
791        hashed_state: HashedPostState,
792        block_id: Option<BlockId>,
793    ) -> Result<(B256, TrieUpdates), Eth::Error> {
794        self.inner
795            .eth_api
796            .spawn_blocking_io(move |this| {
797                let state = this
798                    .provider()
799                    .state_by_block_id(block_id.unwrap_or_default())
800                    .map_err(Eth::Error::from_eth_err)?;
801                state.state_root_with_updates(hashed_state).map_err(Eth::Error::from_eth_err)
802            })
803            .await
804    }
805
806    /// Executes a block and returns the state root after each transaction.
807    pub async fn intermediate_roots(&self, block_hash: B256) -> Result<Vec<B256>, Eth::Error> {
808        let block = self
809            .eth_api()
810            .recovered_block(block_hash.into())
811            .await?
812            .ok_or(EthApiError::HeaderNotFound(block_hash.into()))?;
813        let evm_env = self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
814
815        self.eth_api()
816            .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
817                // Enable transition tracking so that merge_transitions works
818                db.transition_state = Some(Default::default());
819
820                eth_api.apply_pre_execution_changes(&block, &mut db)?;
821
822                let mut roots = Vec::with_capacity(block.body().transactions().len());
823                let mut evm = eth_api.evm_config().evm_with_env(&mut db, evm_env);
824                for tx in block.transactions_recovered() {
825                    let tx_env = eth_api.evm_config().tx_env(tx);
826                    evm.transact_commit(tx_env).map_err(Eth::Error::from_evm_err)?;
827
828                    let state = evm.db_mut();
829                    // Merge transitions into cumulative bundle_state
830                    state.merge_transitions(BundleRetention::PlainState);
831                    // Compute state root from the accumulated state changes
832                    let hashed_state = state
833                        .database
834                        .hashed_post_state(&state.bundle_state)
835                        .map_err(Eth::Error::from_eth_err)?;
836                    let root = state
837                        .database
838                        .state_root(hashed_state)
839                        .map_err(Eth::Error::from_eth_err)?;
840                    roots.push(root);
841                }
842
843                Ok(roots)
844            })
845            .await
846    }
847}
848
849#[async_trait]
850impl<Eth> DebugApiServer<RpcTxReq<Eth::NetworkTypes>> for DebugApi<Eth>
851where
852    Eth: EthTransactions + TraceExt,
853{
854    /// Handler for `debug_getRawHeader`
855    async fn raw_header(&self, block_id: BlockId) -> RpcResult<Bytes> {
856        let header = match block_id {
857            BlockId::Hash(hash) => self.provider().header(hash.into()).to_rpc_result()?,
858            BlockId::Number(number_or_tag) => {
859                let number = self
860                    .provider()
861                    .convert_block_number(number_or_tag)
862                    .to_rpc_result()?
863                    .ok_or(EthApiError::HeaderNotFound(block_id))?;
864                self.provider().header_by_number(number).to_rpc_result()?
865            }
866        }
867        .ok_or(EthApiError::HeaderNotFound(block_id))?;
868
869        let mut res = Vec::new();
870        header.encode(&mut res);
871        Ok(res.into())
872    }
873
874    /// Handler for `debug_getRawBlock`
875    async fn raw_block(&self, block_id: BlockId) -> RpcResult<Bytes> {
876        let block = self
877            .provider()
878            .block_by_id(block_id)
879            .to_rpc_result()?
880            .ok_or(EthApiError::HeaderNotFound(block_id))?;
881        let mut res = Vec::new();
882        block.encode(&mut res);
883        Ok(res.into())
884    }
885
886    /// Handler for `debug_getRawBlockAccessList`
887    async fn raw_block_access_list(&self, block_id: BlockId) -> RpcResult<Bytes> {
888        self.eth_api()
889            .get_raw_block_access_list(block_id)
890            .await
891            .map_err(Into::into)?
892            .ok_or_else(|| EthApiError::HeaderNotFound(block_id).into())
893    }
894
895    /// Handler for `debug_getRawTransaction`
896    ///
897    /// If this is a pooled EIP-4844 transaction, the blob sidecar is included.
898    ///
899    /// Returns the bytes of the transaction for the given hash.
900    async fn raw_transaction(&self, hash: B256) -> RpcResult<Option<Bytes>> {
901        self.eth_api().raw_transaction_by_hash(hash).await.map_err(Into::into)
902    }
903
904    /// Handler for `debug_getRawTransactions`
905    /// Returns the bytes of the transaction for the given hash.
906    async fn raw_transactions(&self, block_id: BlockId) -> RpcResult<Vec<Bytes>> {
907        let block: RecoveredBlock<BlockTy<Eth::Primitives>> = self
908            .provider()
909            .block_with_senders_by_id(block_id, TransactionVariant::NoHash)
910            .to_rpc_result()?
911            .unwrap_or_default();
912        Ok(block.into_transactions_recovered().map(|tx| tx.encoded_2718().into()).collect())
913    }
914
915    /// Handler for `debug_getRawReceipts`
916    async fn raw_receipts(&self, block_id: BlockId) -> RpcResult<Vec<Bytes>> {
917        Ok(self
918            .provider()
919            .receipts_by_block_id(block_id)
920            .to_rpc_result()?
921            .ok_or(EthApiError::HeaderNotFound(block_id))?
922            .into_iter()
923            .map(|receipt| ReceiptWithBloom::from(receipt).encoded_2718().into())
924            .collect())
925    }
926
927    /// Handler for `debug_getBadBlocks`
928    async fn bad_blocks(&self) -> RpcResult<Vec<serde_json::Value>> {
929        let entries = self.inner.bad_block_store.all();
930        let mut bad_blocks = Vec::with_capacity(entries.len());
931
932        #[derive(Serialize, Deserialize)]
933        struct BadBlockSerde<T> {
934            block: T,
935            hash: B256,
936            rlp: Bytes,
937            reason: String,
938        }
939
940        for entry in entries {
941            let rlp = alloy_rlp::encode(entry.block.sealed_block()).into();
942            let hash = entry.block.hash();
943
944            let block = entry
945                .block
946                .clone_into_rpc_block(
947                    BlockTransactionsKind::Full,
948                    |tx, tx_info| self.eth_api().converter().fill(tx, tx_info),
949                    |header, size| self.eth_api().converter().convert_header(header, size),
950                )
951                .map_err(|err| Eth::Error::from(err).into())?;
952
953            let bad_block =
954                serde_json::to_value(BadBlockSerde { block, hash, rlp, reason: entry.reason })
955                    .map_err(|err| EthApiError::other(internal_rpc_err(err.to_string())))?;
956
957            bad_blocks.push(bad_block);
958        }
959
960        Ok(bad_blocks)
961    }
962
963    /// Handler for `debug_clearTxpool`
964    async fn debug_clear_txpool(&self) -> RpcResult<()> {
965        let pool = self.eth_api().pool();
966        let all_hashes = pool.all_transaction_hashes();
967        let _ = pool.remove_transactions(all_hashes);
968        Ok(())
969    }
970
971    /// Handler for `debug_subscribe("traceChain", ...)`.
972    async fn debug_subscribe(
973        &self,
974        pending: PendingSubscriptionSink,
975        subscription: String,
976        start_exclusive: BlockNumberOrTag,
977        end_inclusive: BlockNumberOrTag,
978        opts: Option<GethDebugTracingOptions>,
979    ) -> jsonrpsee::core::SubscriptionResult {
980        if subscription != "traceChain" {
981            pending
982                .reject(EthApiError::InvalidParams(format!(
983                    "unsupported debug subscription: {subscription}"
984                )))
985                .await;
986            return Ok(())
987        }
988
989        let start_id = BlockId::Number(start_exclusive);
990        let start = match self.eth_api().recovered_block(start_id).await {
991            Ok(Some(block)) => block,
992            Ok(None) => {
993                pending.reject(EthApiError::TracingBlockNotFound(start_id)).await;
994                return Ok(())
995            }
996            Err(err) => {
997                pending.reject(err).await;
998                return Ok(())
999            }
1000        };
1001        let end_id = BlockId::Number(end_inclusive);
1002        let end = match self.eth_api().recovered_block(end_id).await {
1003            Ok(Some(block)) => block,
1004            Ok(None) => {
1005                pending.reject(EthApiError::TracingBlockNotFound(end_id)).await;
1006                return Ok(())
1007            }
1008            Err(err) => {
1009                pending.reject(err).await;
1010                return Ok(())
1011            }
1012        };
1013
1014        if start.number() >= end.number() {
1015            pending
1016                .reject(EthApiError::InvalidParams(format!(
1017                    "end block (#{}) needs to come after start block (#{})",
1018                    end.number(),
1019                    start.number()
1020                )))
1021                .await;
1022            return Ok(())
1023        }
1024
1025        let sink = pending.accept().await?;
1026        let this = self.clone();
1027        let task_spawner = self.inner.task_spawner.clone();
1028        task_spawner.spawn_task(async move {
1029            let end_number = end.number();
1030            let opts = opts.unwrap_or_default();
1031
1032            for number in (start.number() + 1)..=end_number {
1033                if sink.is_closed() {
1034                    break
1035                }
1036
1037                let block_id = BlockId::Number(number.into());
1038                let block = match this.eth_api().recovered_block(block_id).await {
1039                    Ok(Some(block)) => block,
1040                    Ok(None) => {
1041                        tracing::warn!(target: "rpc::debug", %number, "Chain tracing block not found");
1042                        break
1043                    }
1044                    Err(err) => {
1045                        tracing::warn!(target: "rpc::debug", %number, %err, "Failed to load chain tracing block");
1046                        break
1047                    }
1048                };
1049                let permit = tokio::select! {
1050                    _ = sink.closed() => break,
1051                    permit = this.acquire_trace_permit() => match permit {
1052                        Ok(permit) => permit,
1053                        Err(err) => {
1054                            tracing::debug!(target: "rpc::debug", %err, "Failed to acquire trace permit");
1055                            break
1056                        }
1057                    }
1058                };
1059                if sink.is_closed() {
1060                    break
1061                }
1062                let traces = match this.trace_chain_block(block.clone(), opts.clone()).await {
1063                    Ok(traces) => traces,
1064                    Err(err) => {
1065                        tracing::warn!(target: "rpc::debug", %number, %err, "Failed to trace chain block");
1066                        break
1067                    }
1068                };
1069                drop(permit);
1070
1071                if traces.is_empty() && number != end_number {
1072                    continue
1073                }
1074                let result = ChainBlockTraceResult {
1075                    block: U256::from(number),
1076                    hash: block.hash(),
1077                    traces,
1078                };
1079                let message = match SubscriptionMessage::new(
1080                    sink.method_name(),
1081                    sink.subscription_id(),
1082                    &result,
1083                ) {
1084                    Ok(message) => message,
1085                    Err(err) => {
1086                        tracing::warn!(target: "rpc::debug", %number, %err, "Failed to serialize chain trace");
1087                        break
1088                    }
1089                };
1090                if sink.send(message).await.is_err() {
1091                    break
1092                }
1093            }
1094
1095            // Dropping jsonrpsee's final sink unregisters the subscription without notifying the
1096            // client that this finite stream completed. Retain it so the subscription remains
1097            // explicitly unsubscribable until the client unsubscribes or disconnects.
1098            sink.closed().await;
1099        });
1100
1101        Ok(())
1102    }
1103
1104    /// Handler for `debug_traceBlock`
1105    async fn debug_trace_block(
1106        &self,
1107        rlp_block: Bytes,
1108        opts: Option<GethDebugTracingOptions>,
1109    ) -> RpcResult<Vec<TraceResult>> {
1110        let _permit = self.acquire_trace_permit().await;
1111        Self::debug_trace_raw_block(self, rlp_block, opts.unwrap_or_default())
1112            .await
1113            .map_err(Into::into)
1114    }
1115
1116    /// Handler for `debug_traceBlockByHash`
1117    async fn debug_trace_block_by_hash(
1118        &self,
1119        block: B256,
1120        opts: Option<GethDebugTracingOptions>,
1121    ) -> RpcResult<Vec<TraceResult>> {
1122        let _permit = self.acquire_trace_permit().await;
1123        Self::debug_trace_block(self, block.into(), opts.unwrap_or_default())
1124            .await
1125            .map_err(Into::into)
1126    }
1127
1128    /// Handler for `debug_traceBlockByNumber`
1129    async fn debug_trace_block_by_number(
1130        &self,
1131        block: BlockNumberOrTag,
1132        opts: Option<GethDebugTracingOptions>,
1133    ) -> RpcResult<Vec<TraceResult>> {
1134        let _permit = self.acquire_trace_permit().await;
1135        Self::debug_trace_block(self, block.into(), opts.unwrap_or_default())
1136            .await
1137            .map_err(Into::into)
1138    }
1139
1140    /// Handler for `debug_traceTransaction`
1141    async fn debug_trace_transaction(
1142        &self,
1143        tx_hash: B256,
1144        opts: Option<GethDebugTracingOptions>,
1145    ) -> RpcResult<GethTrace> {
1146        let _permit = self.acquire_trace_permit().await;
1147        Self::debug_trace_transaction(self, tx_hash, opts.unwrap_or_default())
1148            .await
1149            .map_err(Into::into)
1150    }
1151
1152    /// Handler for `debug_traceCall`
1153    async fn debug_trace_call(
1154        &self,
1155        request: RpcTxReq<Eth::NetworkTypes>,
1156        block_id: Option<BlockId>,
1157        opts: Option<GethDebugTracingCallOptions>,
1158    ) -> RpcResult<GethTrace> {
1159        let _permit = self.acquire_trace_permit().await;
1160        Self::debug_trace_call(self, request, block_id, opts.unwrap_or_default())
1161            .await
1162            .map_err(Into::into)
1163    }
1164
1165    async fn debug_trace_call_many(
1166        &self,
1167        bundles: Vec<Bundle<RpcTxReq<Eth::NetworkTypes>>>,
1168        state_context: Option<StateContext>,
1169        opts: Option<GethDebugTracingCallOptions>,
1170    ) -> RpcResult<Vec<Vec<GethTrace>>> {
1171        let _permit = self.acquire_trace_permit().await;
1172        Self::debug_trace_call_many(self, bundles, state_context, opts).await.map_err(Into::into)
1173    }
1174
1175    /// Handler for `debug_executionWitness`
1176    async fn debug_execution_witness(
1177        &self,
1178        block: BlockId,
1179        mode: Option<ExecutionWitnessMode>,
1180    ) -> RpcResult<ExecutionWitness> {
1181        let _permit = self.acquire_trace_permit().await;
1182        Self::debug_execution_witness(self, block, mode).await.map_err(Into::into)
1183    }
1184
1185    /// Handler for `debug_executionWitnessByBlockHash`
1186    async fn debug_execution_witness_by_block_hash(
1187        &self,
1188        hash: B256,
1189        mode: Option<ExecutionWitnessMode>,
1190    ) -> RpcResult<ExecutionWitness> {
1191        let _permit = self.acquire_trace_permit().await;
1192        Self::debug_execution_witness_by_block_hash(self, hash, mode).await.map_err(Into::into)
1193    }
1194
1195    /// Handler for `debug_accountAt`
1196    async fn debug_account_at(
1197        &self,
1198        block_id: BlockId,
1199        tx_index: Index,
1200        address: Address,
1201    ) -> RpcResult<Option<Account>> {
1202        let _permit = self.acquire_trace_permit().await;
1203        Self::debug_account_at(self, block_id, tx_index, address).await.map_err(Into::into)
1204    }
1205
1206    /// Handler for `debug_accountInfoAt`
1207    async fn debug_account_info_at(
1208        &self,
1209        block_id: BlockId,
1210        tx_index: Index,
1211        address: Address,
1212    ) -> RpcResult<Option<AccountInfo>> {
1213        let _permit = self.acquire_trace_permit().await;
1214        Self::debug_account_info_at(self, block_id, tx_index, address).await.map_err(Into::into)
1215    }
1216
1217    async fn debug_account_range(
1218        &self,
1219        _block_number: BlockNumberOrTag,
1220        _start: Bytes,
1221        _max_results: u64,
1222        _nocode: bool,
1223        _nostorage: bool,
1224        _incompletes: bool,
1225    ) -> RpcResult<()> {
1226        Ok(())
1227    }
1228
1229    async fn debug_chaindb_compact(&self) -> RpcResult<()> {
1230        Ok(())
1231    }
1232
1233    async fn debug_chain_config(&self) -> RpcResult<ChainConfig> {
1234        Ok(self.provider().chain_spec().genesis().config.clone())
1235    }
1236
1237    async fn debug_chaindb_property(&self, _property: String) -> RpcResult<()> {
1238        Ok(())
1239    }
1240
1241    async fn debug_code_by_hash(
1242        &self,
1243        hash: B256,
1244        block_id: Option<BlockId>,
1245    ) -> RpcResult<Option<Bytes>> {
1246        Self::debug_code_by_hash(self, hash, block_id).await.map_err(Into::into)
1247    }
1248
1249    async fn debug_db_ancient(&self, _kind: String, _number: u64) -> RpcResult<()> {
1250        Ok(())
1251    }
1252
1253    async fn debug_db_ancients(&self) -> RpcResult<()> {
1254        Ok(())
1255    }
1256
1257    /// `debug_db_get` - database key lookup
1258    ///
1259    /// Currently supported:
1260    /// * Contract bytecode associated with a code hash. The key format is: `<0x63><code_hash>`
1261    ///     * Prefix byte: 0x63 (required)
1262    ///     * Code hash: 32 bytes
1263    ///   Must be provided as either:
1264    ///     * Hex string: "0x63..." (66 hex characters after 0x)
1265    ///     * Raw byte string: raw byte string (33 bytes)
1266    ///   See Geth impl: <https://github.com/ethereum/go-ethereum/blob/737ffd1bf0cbee378d0111a5b17ae4724fb2216c/core/rawdb/schema.go#L120>
1267    async fn debug_db_get(&self, key: String) -> RpcResult<Option<Bytes>> {
1268        let key_bytes = if key.starts_with("0x") {
1269            decode(&key).map_err(|_| EthApiError::InvalidParams("Invalid hex key".to_string()))?
1270        } else {
1271            key.into_bytes()
1272        };
1273
1274        if key_bytes.len() != 33 {
1275            return Err(EthApiError::InvalidParams(format!(
1276                "Key must be 33 bytes, got {}",
1277                key_bytes.len()
1278            ))
1279            .into());
1280        }
1281        if key_bytes[0] != 0x63 {
1282            return Err(EthApiError::InvalidParams("Key prefix must be 0x63".to_string()).into());
1283        }
1284
1285        let code_hash = B256::from_slice(&key_bytes[1..33]);
1286
1287        // No block ID is provided, so it defaults to the latest block
1288        self.debug_code_by_hash(code_hash, None).await.map_err(Into::into)
1289    }
1290
1291    async fn debug_dump_block(&self, _number: BlockId) -> RpcResult<()> {
1292        Ok(())
1293    }
1294
1295    async fn debug_free_os_memory(&self) -> RpcResult<()> {
1296        Ok(())
1297    }
1298
1299    async fn debug_gc_stats(&self) -> RpcResult<()> {
1300        Ok(())
1301    }
1302
1303    async fn debug_get_accessible_state(
1304        &self,
1305        _from: BlockNumberOrTag,
1306        _to: BlockNumberOrTag,
1307    ) -> RpcResult<()> {
1308        Ok(())
1309    }
1310
1311    async fn debug_get_modified_accounts_by_hash(
1312        &self,
1313        _start_hash: B256,
1314        _end_hash: B256,
1315    ) -> RpcResult<()> {
1316        Ok(())
1317    }
1318
1319    async fn debug_get_modified_accounts_by_number(
1320        &self,
1321        _start_number: u64,
1322        _end_number: u64,
1323    ) -> RpcResult<()> {
1324        Ok(())
1325    }
1326
1327    async fn debug_intermediate_roots(
1328        &self,
1329        block_hash: B256,
1330        _opts: Option<GethDebugTracingCallOptions>,
1331    ) -> RpcResult<Vec<B256>> {
1332        let _permit = self.acquire_trace_permit().await;
1333        self.intermediate_roots(block_hash).await.map_err(Into::into)
1334    }
1335
1336    async fn debug_mem_stats(&self) -> RpcResult<()> {
1337        Ok(())
1338    }
1339
1340    async fn debug_preimage(&self, _hash: B256) -> RpcResult<()> {
1341        Ok(())
1342    }
1343
1344    async fn debug_print_block(&self, _number: u64) -> RpcResult<()> {
1345        Ok(())
1346    }
1347
1348    async fn debug_seed_hash(&self, _number: u64) -> RpcResult<B256> {
1349        Ok(Default::default())
1350    }
1351
1352    async fn debug_set_gc_percent(&self, _v: i32) -> RpcResult<()> {
1353        Ok(())
1354    }
1355
1356    async fn debug_set_head(&self, _number: U64) -> RpcResult<()> {
1357        Ok(())
1358    }
1359
1360    async fn debug_set_trie_flush_interval(&self, _interval: String) -> RpcResult<()> {
1361        Ok(())
1362    }
1363
1364    async fn debug_standard_trace_bad_block_to_file(
1365        &self,
1366        _block: BlockNumberOrTag,
1367        _opts: Option<GethDebugTracingCallOptions>,
1368    ) -> RpcResult<()> {
1369        Ok(())
1370    }
1371
1372    async fn debug_standard_trace_block_to_file(
1373        &self,
1374        _block: BlockNumberOrTag,
1375        _opts: Option<GethDebugTracingCallOptions>,
1376    ) -> RpcResult<()> {
1377        Ok(())
1378    }
1379
1380    async fn debug_state_root_with_updates(
1381        &self,
1382        hashed_state: HashedPostState,
1383        block_id: Option<BlockId>,
1384    ) -> RpcResult<(B256, TrieUpdates)> {
1385        Self::debug_state_root_with_updates(self, hashed_state, block_id).await.map_err(Into::into)
1386    }
1387
1388    async fn debug_storage_range_at(
1389        &self,
1390        _block_hash: B256,
1391        _tx_idx: usize,
1392        _contract_address: Address,
1393        _key_start: B256,
1394        _max_result: u64,
1395    ) -> RpcResult<()> {
1396        Ok(())
1397    }
1398
1399    async fn debug_trace_bad_block(
1400        &self,
1401        block_hash: B256,
1402        opts: Option<GethDebugTracingCallOptions>,
1403    ) -> RpcResult<Vec<TraceResult>> {
1404        let _permit = self.acquire_trace_permit().await;
1405        let entry = self
1406            .inner
1407            .bad_block_store
1408            .get(block_hash)
1409            .ok_or_else(|| internal_rpc_err("bad block not found in cache"))?;
1410
1411        let evm_env = self
1412            .eth_api()
1413            .evm_config()
1414            .evm_env(entry.block.header())
1415            .map_err(RethError::other)
1416            .to_rpc_result()?;
1417
1418        let opts = opts.map(|o| o.tracing_options).unwrap_or_default();
1419        self.trace_block(entry.block.clone(), evm_env, opts).await.map_err(Into::into)
1420    }
1421}
1422
1423impl<Eth: RpcNodeCore> std::fmt::Debug for DebugApi<Eth> {
1424    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1425        f.debug_struct("DebugApi").finish_non_exhaustive()
1426    }
1427}
1428
1429impl<Eth: RpcNodeCore> Clone for DebugApi<Eth> {
1430    fn clone(&self) -> Self {
1431        Self { inner: Arc::clone(&self.inner) }
1432    }
1433}
1434
1435struct DebugApiInner<Eth: RpcNodeCore> {
1436    /// The implementation of `eth` API
1437    eth_api: Eth,
1438    // restrict the number of concurrent calls to blocking calls
1439    blocking_task_guard: BlockingTaskGuard,
1440    /// Spawns long-running subscription tasks.
1441    task_spawner: Runtime,
1442    /// Cache for bad blocks.
1443    bad_block_store: BadBlockStore<BlockTy<Eth::Primitives>>,
1444}
1445
1446/// A bounded, deduplicating store of recently observed bad blocks.
1447#[derive(Clone, Debug)]
1448struct BadBlockStore<B: BlockTrait> {
1449    inner: Arc<RwLock<VecDeque<BadBlockEntry<B>>>>,
1450    limit: usize,
1451}
1452
1453/// A cached bad block paired with the reason it was rejected.
1454#[derive(Clone, Debug)]
1455struct BadBlockEntry<B: BlockTrait> {
1456    block: Arc<RecoveredBlock<B>>,
1457    reason: String,
1458}
1459
1460impl<B: BlockTrait> BadBlockStore<B> {
1461    /// Creates a new store with the given capacity.
1462    fn new(limit: usize) -> Self {
1463        Self { inner: Arc::new(RwLock::new(VecDeque::with_capacity(limit))), limit }
1464    }
1465
1466    /// Inserts a recovered block with its rejection reason, keeping only the most recent `limit`
1467    /// entries and deduplicating by block hash.
1468    fn insert(&self, block: RecoveredBlock<B>, reason: String) {
1469        let hash = block.hash();
1470        let mut guard = self.inner.write();
1471
1472        // skip if we already recorded this bad block , and keep original ordering
1473        if guard.iter().any(|entry| entry.block.hash() == hash) {
1474            return;
1475        }
1476        guard.push_back(BadBlockEntry { block: Arc::new(block), reason });
1477
1478        while guard.len() > self.limit {
1479            guard.pop_front();
1480        }
1481    }
1482
1483    /// Returns all cached bad block entries ordered from newest to oldest.
1484    fn all(&self) -> Vec<BadBlockEntry<B>> {
1485        let guard = self.inner.read();
1486        guard.iter().rev().cloned().collect()
1487    }
1488
1489    /// Returns the bad block entry with the given hash, if cached.
1490    fn get(&self, hash: B256) -> Option<BadBlockEntry<B>> {
1491        let guard = self.inner.read();
1492        guard.iter().find(|entry| entry.block.hash() == hash).cloned()
1493    }
1494}
1495
1496impl<B: BlockTrait> Default for BadBlockStore<B> {
1497    fn default() -> Self {
1498        Self::new(64)
1499    }
1500}
1501
1502#[cfg(test)]
1503mod tests {
1504    use super::*;
1505    use crate::{eth::helpers::types::EthRpcConverter, EthApi};
1506    use alloy_primitives::{keccak256, U256};
1507    use reth_chainspec::ChainSpec;
1508    use reth_db_api::{tables, transaction::DbTxMut};
1509    use reth_evm_ethereum::EthEvmConfig;
1510    use reth_network_api::noop::NoopNetwork;
1511    use reth_primitives_traits::StorageEntry;
1512    use reth_provider::test_utils::{create_test_provider_factory, NoopProvider};
1513    use reth_rpc_eth_api::EthApiServer;
1514    use reth_transaction_pool::test_utils::testing_pool;
1515    use revm::{
1516        database::{states::StorageSlot, AccountStatus, BundleAccount, BundleState},
1517        state::AccountInfo as RevmAccountInfo,
1518    };
1519
1520    #[tokio::test]
1521    async fn trace_call_out_of_range_block_error() {
1522        let eth_api = EthApi::<_, EthRpcConverter<ChainSpec>>::builder(
1523            NoopProvider::default(),
1524            testing_pool(),
1525            NoopNetwork::default(),
1526            EthEvmConfig::mainnet(),
1527        )
1528        .build();
1529        let debug_api = DebugApi::new(
1530            eth_api.clone(),
1531            BlockingTaskGuard::new(1),
1532            &Runtime::test(),
1533            futures::stream::empty(),
1534        );
1535        let block_id = BlockId::number(0xfffffffff);
1536        for tx_index in [None, Some(0)] {
1537            let mut opts: GethDebugTracingCallOptions =
1538                serde_json::from_value(serde_json::json!({ "tracer": "callTracer" })).unwrap();
1539            opts.tx_index = tx_index;
1540            let err = DebugApiServer::debug_trace_call(
1541                &debug_api,
1542                Default::default(),
1543                Some(block_id),
1544                Some(opts),
1545            )
1546            .await
1547            .unwrap_err();
1548            assert_eq!(err.code(), -32000);
1549            assert!(err.message().contains("not found"));
1550        }
1551
1552        let err = EthApiServer::call(&eth_api, Default::default(), Some(block_id), None, None)
1553            .await
1554            .unwrap_err();
1555        assert_eq!(err.code(), -32001);
1556    }
1557
1558    #[test]
1559    fn hashed_post_state_zeroes_destroyed_account_parent_storage() {
1560        let factory = create_test_provider_factory();
1561        let address = Address::with_last_byte(1);
1562        let old_slot = U256::from(1);
1563        let new_slot = U256::from(2);
1564        let old_value = U256::from(10);
1565        let new_value = U256::from(20);
1566        let hashed_address = keccak256(address);
1567        let hashed_old_slot = keccak256(B256::from(old_slot));
1568        let hashed_new_slot = keccak256(B256::from(new_slot));
1569
1570        let provider_rw = factory.provider_rw().unwrap();
1571        provider_rw
1572            .tx_ref()
1573            .put::<tables::HashedStorages>(
1574                hashed_address,
1575                StorageEntry { key: hashed_old_slot, value: old_value },
1576            )
1577            .unwrap();
1578        provider_rw.commit().unwrap();
1579
1580        let mut bundle_state = BundleState::default();
1581        bundle_state.state.insert(
1582            address,
1583            BundleAccount::new(
1584                Some(RevmAccountInfo::default()),
1585                Some(RevmAccountInfo::default()),
1586                std::iter::once((new_slot, StorageSlot::new_changed(U256::ZERO, new_value)))
1587                    .collect(),
1588                AccountStatus::DestroyedChanged,
1589            ),
1590        );
1591
1592        let provider = factory.latest().unwrap();
1593        let hashed_state = provider.hashed_post_state(&bundle_state).unwrap();
1594        let storage = &hashed_state.storages[&hashed_address];
1595
1596        assert_eq!(storage.storage[&hashed_old_slot], U256::ZERO);
1597        assert_eq!(storage.storage[&hashed_new_slot], new_value);
1598    }
1599}