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    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) = match self.eth_api().transaction_and_block(tx_hash).await? {
320            None => return Err(EthApiError::TracingTransactionNotFound.into()),
321            Some(res) => res,
322        };
323
324        self.eth_api()
325            .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
326                // configure env for the target transaction
327                let (tx, tx_info) = transaction.split();
328
329                // index should always be available because `transaction_and_block` only
330                // returns transactions included in a block
331                let index =
332                    tx_info.index.expect("transaction_and_block only returns block transactions")
333                        as usize;
334
335                let mut inspector = DebugInspector::new(opts).map_err(Eth::Error::from_eth_err)?;
336                let tx_env = eth_api.evm_config().tx_env(&tx);
337                let (res, evm_env) = eth_api.inspect_transaction_in_block(
338                    &block,
339                    &mut db,
340                    &mut inspector,
341                    index,
342                    tx_env.clone(),
343                )?;
344
345                let trace = inspector
346                    .get_result(
347                        Some(TransactionContext {
348                            block_hash: Some(block.hash()),
349                            tx_index: Some(index),
350                            tx_hash: Some(*tx.tx_hash()),
351                        }),
352                        &tx_env,
353                        &evm_env.block_env,
354                        &res,
355                        &mut db,
356                    )
357                    .map_err(Eth::Error::from_eth_err)?;
358
359                Ok(trace)
360            })
361            .await
362    }
363
364    /// The `debug_traceCall` method lets you run an `eth_call` within the context of the given
365    /// block execution using the final state of parent block as the base.
366    ///
367    /// If `tx_index` is provided in opts, the call will be traced at the state after executing
368    /// transactions up to the specified index within the block (0-indexed).
369    /// If not provided, then uses the post-state (default behavior).
370    ///
371    /// Differences compare to `eth_call`:
372    ///  - `debug_traceCall` executes with __enabled__ basefee check, `eth_call` does not: <https://github.com/paradigmxyz/reth/issues/6240>
373    pub async fn debug_trace_call(
374        &self,
375        call: RpcTxReq<Eth::NetworkTypes>,
376        block_id: Option<BlockId>,
377        opts: GethDebugTracingCallOptions,
378    ) -> Result<GethTrace, Eth::Error> {
379        let at = block_id.unwrap_or_default();
380        let GethDebugTracingCallOptions {
381            tracing_options,
382            state_overrides,
383            block_overrides,
384            tx_index,
385        } = opts;
386        let overrides = EvmOverrides::new(state_overrides, block_overrides.map(Box::new));
387
388        // Check if we need to replay transactions for a specific tx_index
389        if let Some(tx_idx) = tx_index {
390            return self
391                .debug_trace_call_at_tx_index(call, at, tx_idx as usize, tracing_options, overrides)
392                .await;
393        }
394
395        let this = self.clone();
396        self.eth_api()
397            .spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| {
398                let mut inspector =
399                    DebugInspector::new(tracing_options).map_err(Eth::Error::from_eth_err)?;
400                let res = this.eth_api().inspect(
401                    &mut *db,
402                    evm_env.clone(),
403                    tx_env.clone(),
404                    &mut inspector,
405                )?;
406                let trace = inspector
407                    .get_result(None, &tx_env, &evm_env.block_env, &res, db)
408                    .map_err(Eth::Error::from_eth_err)?;
409                Ok(trace)
410            })
411            .await
412    }
413
414    /// Helper method to execute `debug_trace_call` at a specific transaction index within a block.
415    /// This replays transactions up to the specified index, then executes the trace call in that
416    /// state.
417    async fn debug_trace_call_at_tx_index(
418        &self,
419        call: RpcTxReq<Eth::NetworkTypes>,
420        block_id: BlockId,
421        tx_index: usize,
422        tracing_options: GethDebugTracingOptions,
423        overrides: EvmOverrides,
424    ) -> Result<GethTrace, Eth::Error> {
425        // Get the target block to check transaction count
426        let block = self
427            .eth_api()
428            .recovered_block(block_id)
429            .await?
430            .ok_or(EthApiError::HeaderNotFound(block_id))?;
431
432        if tx_index >= block.transaction_count() {
433            // tx_index out of bounds
434            return Err(EthApiError::InvalidParams(format!(
435                "tx_index {} out of bounds for block with {} transactions",
436                tx_index,
437                block.transaction_count()
438            ))
439            .into())
440        }
441
442        let evm_env = self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
443
444        self.eth_api()
445            .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
446                // 1. replay the required number of transactions
447                eth_api.replay_block_until(&mut db, &block, tx_index)?;
448
449                // 2. now execute the trace call on this state
450                let (evm_env, tx_env) =
451                    eth_api.prepare_call_env(evm_env, call, &mut db, overrides)?;
452
453                let mut inspector =
454                    DebugInspector::new(tracing_options).map_err(Eth::Error::from_eth_err)?;
455                let res =
456                    eth_api.inspect(&mut db, evm_env.clone(), tx_env.clone(), &mut inspector)?;
457                let trace = inspector
458                    .get_result(None, &tx_env, &evm_env.block_env, &res, &mut db)
459                    .map_err(Eth::Error::from_eth_err)?;
460
461                Ok(trace)
462            })
463            .await
464    }
465
466    /// The `debug_traceCallMany` method lets you run an `eth_callMany` within the context of the
467    /// given block execution using the first n transactions in the given block as base.
468    /// Each following bundle increments block number by 1 and block timestamp by 12 seconds
469    pub async fn debug_trace_call_many(
470        &self,
471        bundles: Vec<Bundle<RpcTxReq<Eth::NetworkTypes>>>,
472        state_context: Option<StateContext>,
473        opts: Option<GethDebugTracingCallOptions>,
474    ) -> Result<Vec<Vec<GethTrace>>, Eth::Error> {
475        if bundles.is_empty() {
476            return Err(EthApiError::InvalidParams(String::from("bundles are empty.")).into())
477        }
478
479        let StateContext { transaction_index, block_number } = state_context.unwrap_or_default();
480        let transaction_index = transaction_index.unwrap_or_default();
481
482        let target_block = block_number.unwrap_or_default();
483        let block = self
484            .eth_api()
485            .recovered_block(target_block)
486            .await?
487            .ok_or(EthApiError::HeaderNotFound(target_block))?;
488        let mut evm_env =
489            self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
490
491        let opts = opts.unwrap_or_default();
492        let GethDebugTracingCallOptions { tracing_options, mut state_overrides, .. } = opts;
493
494        // we're essentially replaying the transactions in the block here, hence we need the state
495        // that points to the beginning of the block, which is the state at the parent block
496        let mut at = block.parent_hash();
497        let mut replay_block_txs = true;
498
499        // if a transaction index is provided, we need to replay the transactions until the index
500        let num_txs =
501            transaction_index.index().unwrap_or_else(|| block.body().transactions().len());
502        // but if all transactions are to be replayed, we can use the state at the block itself
503        // this works with the exception of the PENDING block, because its state might not exist if
504        // built locally
505        if !target_block.is_pending() && num_txs == block.body().transactions().len() {
506            at = block.hash();
507            replay_block_txs = false;
508        }
509
510        self.eth_api()
511            .spawn_with_state_at_block(at, move |eth_api, mut db| {
512                // the outer vec for the bundles
513                let mut all_bundles = Vec::with_capacity(bundles.len());
514
515                if replay_block_txs {
516                    // only need to replay the transactions in the block if not all transactions are
517                    // to be replayed
518                    // Execute all transactions until index
519                    eth_api.replay_block_until(&mut db, &block, num_txs)?;
520                }
521
522                // Trace all bundles
523                let mut bundles = bundles.into_iter().peekable();
524                let mut inspector = DebugInspector::new(tracing_options.clone())
525                    .map_err(Eth::Error::from_eth_err)?;
526                while let Some(bundle) = bundles.next() {
527                    let mut results = Vec::with_capacity(bundle.transactions.len());
528                    let Bundle { transactions, block_override } = bundle;
529
530                    let block_overrides = block_override.map(Box::new);
531
532                    let mut transactions = transactions.into_iter().peekable();
533                    while let Some(tx) = transactions.next() {
534                        // apply state overrides only once, before the first transaction
535                        let state_overrides = state_overrides.take();
536                        let overrides = EvmOverrides::new(state_overrides, block_overrides.clone());
537
538                        let (evm_env, tx_env) =
539                            eth_api.prepare_call_env(evm_env.clone(), tx, &mut db, overrides)?;
540
541                        let res = eth_api.inspect(
542                            &mut db,
543                            evm_env.clone(),
544                            tx_env.clone(),
545                            &mut inspector,
546                        )?;
547                        let trace = inspector
548                            .get_result(None, &tx_env, &evm_env.block_env, &res, &mut db)
549                            .map_err(Eth::Error::from_eth_err)?;
550
551                        // If there is more transactions, commit the database
552                        // If there is no transactions, but more bundles, commit to the database too
553                        if transactions.peek().is_some() || bundles.peek().is_some() {
554                            inspector.fuse().map_err(Eth::Error::from_eth_err)?;
555                            db.commit(res.state);
556                        }
557                        results.push(trace);
558                    }
559                    // Increment block_env number and timestamp for the next bundle
560                    evm_env.block_env.inner_mut().number += uint!(1_U256);
561                    evm_env.block_env.inner_mut().timestamp += uint!(12_U256);
562
563                    all_bundles.push(results);
564                }
565                Ok(all_bundles)
566            })
567            .await
568    }
569
570    /// Generates an execution witness for the given block hash. see
571    /// [`Self::debug_execution_witness`] for more info.
572    pub async fn debug_execution_witness_by_block_hash(
573        &self,
574        hash: B256,
575        mode: Option<ExecutionWitnessMode>,
576    ) -> Result<ExecutionWitness, Eth::Error> {
577        let this = self.clone();
578        let block = this
579            .eth_api()
580            .recovered_block(hash.into())
581            .await?
582            .ok_or(EthApiError::HeaderNotFound(hash.into()))?;
583
584        self.debug_execution_witness_for_block(block, mode.unwrap_or_default()).await
585    }
586
587    /// The `debug_executionWitness` method allows for re-execution of a block with the purpose of
588    /// generating an execution witness. The witness comprises of a map of all hashed trie nodes to
589    /// their preimages that were required during the execution of the block, including during state
590    /// root recomputation.
591    pub async fn debug_execution_witness(
592        &self,
593        block_id: BlockId,
594        mode: Option<ExecutionWitnessMode>,
595    ) -> Result<ExecutionWitness, Eth::Error> {
596        let this = self.clone();
597        let block = this
598            .eth_api()
599            .recovered_block(block_id)
600            .await?
601            .ok_or(EthApiError::HeaderNotFound(block_id))?;
602
603        self.debug_execution_witness_for_block(block, mode.unwrap_or_default()).await
604    }
605
606    /// Generates an execution witness, using the given recovered block.
607    pub async fn debug_execution_witness_for_block(
608        &self,
609        block: Arc<RecoveredBlock<ProviderBlock<Eth::Provider>>>,
610        mode: ExecutionWitnessMode,
611    ) -> Result<ExecutionWitness, Eth::Error> {
612        let block_number = block.header().number();
613        self.eth_api()
614            .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
615                let block_executor = eth_api.evm_config().executor(&mut db);
616
617                let mut witness = None;
618                let _ = block_executor
619                    .execute_with_state_closure(&block, |statedb: &State<_>| {
620                        witness =
621                            Some(ExecutionWitnessRecord::new(statedb).into_execution_witness(
622                                &statedb.database.database.0,
623                                eth_api.provider(),
624                                block_number,
625                                mode,
626                            ));
627                    })
628                    .map_err(|err| EthApiError::Internal(err.into()))?;
629
630                Ok(witness
631                    .expect("state closure is called after successful execution")
632                    .map_err(EthApiError::from)?)
633            })
634            .await
635    }
636
637    /// Returns account information, including the storage root, after replaying the block through
638    /// the transaction at the given index.
639    pub async fn debug_account_at(
640        &self,
641        block_id: BlockId,
642        tx_index: Index,
643        address: Address,
644    ) -> Result<Option<Account>, Eth::Error> {
645        self.replay_block_until(block_id, tx_index, move |db| Self::account(db, address))
646            .await
647            .map(Option::flatten)
648    }
649
650    /// Returns account information after replaying the block through the transaction at the given
651    /// index.
652    pub async fn debug_account_info_at(
653        &self,
654        block_id: BlockId,
655        tx_index: Index,
656        address: Address,
657    ) -> Result<Option<AccountInfo>, Eth::Error> {
658        self.replay_block_until(block_id, tx_index, move |db| Self::account_info(db, address)).await
659    }
660
661    /// Replays a block through the transaction at the given index and calls `f` with the resulting
662    /// state.
663    async fn replay_block_until<F, R>(
664        &self,
665        block_id: BlockId,
666        tx_index: Index,
667        f: F,
668    ) -> Result<Option<R>, Eth::Error>
669    where
670        F: FnOnce(&mut StateCacheDb) -> Result<R, Eth::Error> + Send + 'static,
671        R: Send + 'static,
672    {
673        let block = self
674            .eth_api()
675            .recovered_block(block_id)
676            .await?
677            .ok_or(EthApiError::HeaderNotFound(block_id))?;
678        let tx_index = usize::from(tx_index);
679        let transaction_count = block.transaction_count();
680        if tx_index >= transaction_count {
681            return Err(EthApiError::InvalidParams(format!(
682                "tx_index {tx_index} out of bounds for block with {transaction_count} transactions"
683            ))
684            .into())
685        }
686
687        self.eth_api()
688            .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
689                let mut executor = eth_api
690                    .evm_config()
691                    .executor_for_block(&mut db, block.sealed_block())
692                    .map_err(RethError::other)
693                    .map_err(Eth::Error::from_eth_err)?;
694                executor.apply_pre_execution_changes().map_err(Eth::Error::from_eth_err)?;
695
696                for tx in block.transactions_recovered().take(tx_index + 1) {
697                    executor.execute_transaction(tx).map_err(Eth::Error::from_eth_err)?;
698                }
699                drop(executor);
700
701                f(&mut db)
702            })
703            .await
704            .map(Some)
705    }
706
707    /// Retrieves the account's balance, nonce, code hash, and storage root from the given state.
708    fn account(db: &mut StateCacheDb, address: Address) -> Result<Option<Account>, Eth::Error> {
709        let account = db.basic(address).map_err(Eth::Error::from_eth_err)?;
710        let Some(account) = account else { return Ok(None) };
711
712        let balance = account.balance;
713        let nonce = account.nonce;
714        let code_hash = account.code_hash;
715        let (hashed_storage, status) = db
716            .cache
717            .accounts
718            .get(&address)
719            .and_then(|account| {
720                account.account.as_ref().map(|plain_account| {
721                    (HashedStorage::from_plain_storage(&plain_account.storage), account.status)
722                })
723            })
724            .unwrap_or_default();
725        let storage_root = if status.was_destroyed() {
726            // Destruction makes every slot not present in the cache zero, so the cache contains the
727            // complete storage trie for the account's new incarnation.
728            storage_root_unsorted(
729                hashed_storage.storage.into_iter().filter(|(_, value)| !value.is_zero()),
730            )
731        } else {
732            db.database.storage_root(address, hashed_storage).map_err(Eth::Error::from_eth_err)?
733        };
734
735        Ok(Some(Account { balance, nonce, code_hash, storage_root }))
736    }
737
738    /// Retrieves the account's balance, nonce, and code from the given state.
739    fn account_info<DB>(db: &mut DB, address: Address) -> Result<AccountInfo, Eth::Error>
740    where
741        DB: Database,
742        EthApiError: From<DB::Error>,
743    {
744        let account = db.basic(address).map_err(Eth::Error::from_eth_err)?.unwrap_or_default();
745        let code = if account.code_hash == KECCAK_EMPTY {
746            Default::default()
747        } else if let Some(code) = account.code {
748            code.original_bytes()
749        } else {
750            db.code_by_hash(account.code_hash).map_err(Eth::Error::from_eth_err)?.original_bytes()
751        };
752
753        Ok(AccountInfo { balance: account.balance, nonce: account.nonce, code })
754    }
755
756    /// Returns the code associated with a given hash at the specified block ID. If no code is
757    /// found, it returns None. If no block ID is provided, it defaults to the latest block.
758    pub async fn debug_code_by_hash(
759        &self,
760        hash: B256,
761        block_id: Option<BlockId>,
762    ) -> Result<Option<Bytes>, Eth::Error> {
763        Ok(self
764            .provider()
765            .state_by_block_id(block_id.unwrap_or_default())
766            .map_err(Eth::Error::from_eth_err)?
767            .bytecode_by_hash(&hash)
768            .map_err(Eth::Error::from_eth_err)?
769            .map(|b| b.original_bytes()))
770    }
771
772    /// Returns the state root of the `HashedPostState` on top of the state for the given block with
773    /// trie updates.
774    async fn debug_state_root_with_updates(
775        &self,
776        hashed_state: HashedPostState,
777        block_id: Option<BlockId>,
778    ) -> Result<(B256, TrieUpdates), Eth::Error> {
779        self.inner
780            .eth_api
781            .spawn_blocking_io(move |this| {
782                let state = this
783                    .provider()
784                    .state_by_block_id(block_id.unwrap_or_default())
785                    .map_err(Eth::Error::from_eth_err)?;
786                state.state_root_with_updates(hashed_state).map_err(Eth::Error::from_eth_err)
787            })
788            .await
789    }
790
791    /// Executes a block and returns the state root after each transaction.
792    pub async fn intermediate_roots(&self, block_hash: B256) -> Result<Vec<B256>, Eth::Error> {
793        let block = self
794            .eth_api()
795            .recovered_block(block_hash.into())
796            .await?
797            .ok_or(EthApiError::HeaderNotFound(block_hash.into()))?;
798        let evm_env = self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
799
800        self.eth_api()
801            .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
802                // Enable transition tracking so that merge_transitions works
803                db.transition_state = Some(Default::default());
804
805                eth_api.apply_pre_execution_changes(&block, &mut db)?;
806
807                let mut roots = Vec::with_capacity(block.body().transactions().len());
808                let mut evm = eth_api.evm_config().evm_with_env(&mut db, evm_env);
809                for tx in block.transactions_recovered() {
810                    let tx_env = eth_api.evm_config().tx_env(tx);
811                    evm.transact_commit(tx_env).map_err(Eth::Error::from_evm_err)?;
812
813                    let state = evm.db_mut();
814                    // Merge transitions into cumulative bundle_state
815                    state.merge_transitions(BundleRetention::PlainState);
816                    // Compute state root from the accumulated state changes
817                    let hashed_state = state
818                        .database
819                        .hashed_post_state(&state.bundle_state)
820                        .map_err(Eth::Error::from_eth_err)?;
821                    let root = state
822                        .database
823                        .state_root(hashed_state)
824                        .map_err(Eth::Error::from_eth_err)?;
825                    roots.push(root);
826                }
827
828                Ok(roots)
829            })
830            .await
831    }
832}
833
834#[async_trait]
835impl<Eth> DebugApiServer<RpcTxReq<Eth::NetworkTypes>> for DebugApi<Eth>
836where
837    Eth: EthTransactions + TraceExt,
838{
839    /// Handler for `debug_getRawHeader`
840    async fn raw_header(&self, block_id: BlockId) -> RpcResult<Bytes> {
841        let header = match block_id {
842            BlockId::Hash(hash) => self.provider().header(hash.into()).to_rpc_result()?,
843            BlockId::Number(number_or_tag) => {
844                let number = self
845                    .provider()
846                    .convert_block_number(number_or_tag)
847                    .to_rpc_result()?
848                    .ok_or(EthApiError::HeaderNotFound(block_id))?;
849                self.provider().header_by_number(number).to_rpc_result()?
850            }
851        }
852        .ok_or(EthApiError::HeaderNotFound(block_id))?;
853
854        let mut res = Vec::new();
855        header.encode(&mut res);
856        Ok(res.into())
857    }
858
859    /// Handler for `debug_getRawBlock`
860    async fn raw_block(&self, block_id: BlockId) -> RpcResult<Bytes> {
861        let block = self
862            .provider()
863            .block_by_id(block_id)
864            .to_rpc_result()?
865            .ok_or(EthApiError::HeaderNotFound(block_id))?;
866        let mut res = Vec::new();
867        block.encode(&mut res);
868        Ok(res.into())
869    }
870
871    /// Handler for `debug_getRawBlockAccessList`
872    async fn raw_block_access_list(&self, block_id: BlockId) -> RpcResult<Bytes> {
873        self.eth_api()
874            .get_raw_block_access_list(block_id)
875            .await
876            .map_err(Into::into)?
877            .ok_or_else(|| EthApiError::HeaderNotFound(block_id).into())
878    }
879
880    /// Handler for `debug_getRawTransaction`
881    ///
882    /// If this is a pooled EIP-4844 transaction, the blob sidecar is included.
883    ///
884    /// Returns the bytes of the transaction for the given hash.
885    async fn raw_transaction(&self, hash: B256) -> RpcResult<Option<Bytes>> {
886        self.eth_api().raw_transaction_by_hash(hash).await.map_err(Into::into)
887    }
888
889    /// Handler for `debug_getRawTransactions`
890    /// Returns the bytes of the transaction for the given hash.
891    async fn raw_transactions(&self, block_id: BlockId) -> RpcResult<Vec<Bytes>> {
892        let block: RecoveredBlock<BlockTy<Eth::Primitives>> = self
893            .provider()
894            .block_with_senders_by_id(block_id, TransactionVariant::NoHash)
895            .to_rpc_result()?
896            .unwrap_or_default();
897        Ok(block.into_transactions_recovered().map(|tx| tx.encoded_2718().into()).collect())
898    }
899
900    /// Handler for `debug_getRawReceipts`
901    async fn raw_receipts(&self, block_id: BlockId) -> RpcResult<Vec<Bytes>> {
902        Ok(self
903            .provider()
904            .receipts_by_block_id(block_id)
905            .to_rpc_result()?
906            .ok_or(EthApiError::HeaderNotFound(block_id))?
907            .into_iter()
908            .map(|receipt| ReceiptWithBloom::from(receipt).encoded_2718().into())
909            .collect())
910    }
911
912    /// Handler for `debug_getBadBlocks`
913    async fn bad_blocks(&self) -> RpcResult<Vec<serde_json::Value>> {
914        let entries = self.inner.bad_block_store.all();
915        let mut bad_blocks = Vec::with_capacity(entries.len());
916
917        #[derive(Serialize, Deserialize)]
918        struct BadBlockSerde<T> {
919            block: T,
920            hash: B256,
921            rlp: Bytes,
922            reason: String,
923        }
924
925        for entry in entries {
926            let rlp = alloy_rlp::encode(entry.block.sealed_block()).into();
927            let hash = entry.block.hash();
928
929            let block = entry
930                .block
931                .clone_into_rpc_block(
932                    BlockTransactionsKind::Full,
933                    |tx, tx_info| self.eth_api().converter().fill(tx, tx_info),
934                    |header, size| self.eth_api().converter().convert_header(header, size),
935                )
936                .map_err(|err| Eth::Error::from(err).into())?;
937
938            let bad_block =
939                serde_json::to_value(BadBlockSerde { block, hash, rlp, reason: entry.reason })
940                    .map_err(|err| EthApiError::other(internal_rpc_err(err.to_string())))?;
941
942            bad_blocks.push(bad_block);
943        }
944
945        Ok(bad_blocks)
946    }
947
948    /// Handler for `debug_clearTxpool`
949    async fn debug_clear_txpool(&self) -> RpcResult<()> {
950        let pool = self.eth_api().pool();
951        let all_hashes = pool.all_transaction_hashes();
952        let _ = pool.remove_transactions(all_hashes);
953        Ok(())
954    }
955
956    /// Handler for `debug_subscribe("traceChain", ...)`.
957    async fn debug_subscribe(
958        &self,
959        pending: PendingSubscriptionSink,
960        subscription: String,
961        start_exclusive: BlockNumberOrTag,
962        end_inclusive: BlockNumberOrTag,
963        opts: Option<GethDebugTracingOptions>,
964    ) -> jsonrpsee::core::SubscriptionResult {
965        if subscription != "traceChain" {
966            pending
967                .reject(EthApiError::InvalidParams(format!(
968                    "unsupported debug subscription: {subscription}"
969                )))
970                .await;
971            return Ok(())
972        }
973
974        let start_id = BlockId::Number(start_exclusive);
975        let start = match self.eth_api().recovered_block(start_id).await {
976            Ok(Some(block)) => block,
977            Ok(None) => {
978                pending.reject(EthApiError::TracingBlockNotFound(start_id)).await;
979                return Ok(())
980            }
981            Err(err) => {
982                pending.reject(err).await;
983                return Ok(())
984            }
985        };
986        let end_id = BlockId::Number(end_inclusive);
987        let end = match self.eth_api().recovered_block(end_id).await {
988            Ok(Some(block)) => block,
989            Ok(None) => {
990                pending.reject(EthApiError::TracingBlockNotFound(end_id)).await;
991                return Ok(())
992            }
993            Err(err) => {
994                pending.reject(err).await;
995                return Ok(())
996            }
997        };
998
999        if start.number() >= end.number() {
1000            pending
1001                .reject(EthApiError::InvalidParams(format!(
1002                    "end block (#{}) needs to come after start block (#{})",
1003                    end.number(),
1004                    start.number()
1005                )))
1006                .await;
1007            return Ok(())
1008        }
1009
1010        let sink = pending.accept().await?;
1011        let this = self.clone();
1012        let task_spawner = self.inner.task_spawner.clone();
1013        task_spawner.spawn_task(async move {
1014            let end_number = end.number();
1015            let opts = opts.unwrap_or_default();
1016
1017            for number in (start.number() + 1)..=end_number {
1018                if sink.is_closed() {
1019                    break
1020                }
1021
1022                let block_id = BlockId::Number(number.into());
1023                let block = match this.eth_api().recovered_block(block_id).await {
1024                    Ok(Some(block)) => block,
1025                    Ok(None) => {
1026                        tracing::warn!(target: "rpc::debug", %number, "Chain tracing block not found");
1027                        break
1028                    }
1029                    Err(err) => {
1030                        tracing::warn!(target: "rpc::debug", %number, %err, "Failed to load chain tracing block");
1031                        break
1032                    }
1033                };
1034                let permit = tokio::select! {
1035                    _ = sink.closed() => break,
1036                    permit = this.acquire_trace_permit() => match permit {
1037                        Ok(permit) => permit,
1038                        Err(err) => {
1039                            tracing::debug!(target: "rpc::debug", %err, "Failed to acquire trace permit");
1040                            break
1041                        }
1042                    }
1043                };
1044                if sink.is_closed() {
1045                    break
1046                }
1047                let traces = match this.trace_chain_block(block.clone(), opts.clone()).await {
1048                    Ok(traces) => traces,
1049                    Err(err) => {
1050                        tracing::warn!(target: "rpc::debug", %number, %err, "Failed to trace chain block");
1051                        break
1052                    }
1053                };
1054                drop(permit);
1055
1056                if traces.is_empty() && number != end_number {
1057                    continue
1058                }
1059                let result = ChainBlockTraceResult {
1060                    block: U256::from(number),
1061                    hash: block.hash(),
1062                    traces,
1063                };
1064                let message = match SubscriptionMessage::new(
1065                    sink.method_name(),
1066                    sink.subscription_id(),
1067                    &result,
1068                ) {
1069                    Ok(message) => message,
1070                    Err(err) => {
1071                        tracing::warn!(target: "rpc::debug", %number, %err, "Failed to serialize chain trace");
1072                        break
1073                    }
1074                };
1075                if sink.send(message).await.is_err() {
1076                    break
1077                }
1078            }
1079
1080            // Dropping jsonrpsee's final sink unregisters the subscription without notifying the
1081            // client that this finite stream completed. Retain it so the subscription remains
1082            // explicitly unsubscribable until the client unsubscribes or disconnects.
1083            sink.closed().await;
1084        });
1085
1086        Ok(())
1087    }
1088
1089    /// Handler for `debug_traceBlock`
1090    async fn debug_trace_block(
1091        &self,
1092        rlp_block: Bytes,
1093        opts: Option<GethDebugTracingOptions>,
1094    ) -> RpcResult<Vec<TraceResult>> {
1095        let _permit = self.acquire_trace_permit().await;
1096        Self::debug_trace_raw_block(self, rlp_block, opts.unwrap_or_default())
1097            .await
1098            .map_err(Into::into)
1099    }
1100
1101    /// Handler for `debug_traceBlockByHash`
1102    async fn debug_trace_block_by_hash(
1103        &self,
1104        block: B256,
1105        opts: Option<GethDebugTracingOptions>,
1106    ) -> RpcResult<Vec<TraceResult>> {
1107        let _permit = self.acquire_trace_permit().await;
1108        Self::debug_trace_block(self, block.into(), opts.unwrap_or_default())
1109            .await
1110            .map_err(Into::into)
1111    }
1112
1113    /// Handler for `debug_traceBlockByNumber`
1114    async fn debug_trace_block_by_number(
1115        &self,
1116        block: BlockNumberOrTag,
1117        opts: Option<GethDebugTracingOptions>,
1118    ) -> RpcResult<Vec<TraceResult>> {
1119        let _permit = self.acquire_trace_permit().await;
1120        Self::debug_trace_block(self, block.into(), opts.unwrap_or_default())
1121            .await
1122            .map_err(Into::into)
1123    }
1124
1125    /// Handler for `debug_traceTransaction`
1126    async fn debug_trace_transaction(
1127        &self,
1128        tx_hash: B256,
1129        opts: Option<GethDebugTracingOptions>,
1130    ) -> RpcResult<GethTrace> {
1131        let _permit = self.acquire_trace_permit().await;
1132        Self::debug_trace_transaction(self, tx_hash, opts.unwrap_or_default())
1133            .await
1134            .map_err(Into::into)
1135    }
1136
1137    /// Handler for `debug_traceCall`
1138    async fn debug_trace_call(
1139        &self,
1140        request: RpcTxReq<Eth::NetworkTypes>,
1141        block_id: Option<BlockId>,
1142        opts: Option<GethDebugTracingCallOptions>,
1143    ) -> RpcResult<GethTrace> {
1144        let _permit = self.acquire_trace_permit().await;
1145        Self::debug_trace_call(self, request, block_id, opts.unwrap_or_default())
1146            .await
1147            .map_err(Into::into)
1148    }
1149
1150    async fn debug_trace_call_many(
1151        &self,
1152        bundles: Vec<Bundle<RpcTxReq<Eth::NetworkTypes>>>,
1153        state_context: Option<StateContext>,
1154        opts: Option<GethDebugTracingCallOptions>,
1155    ) -> RpcResult<Vec<Vec<GethTrace>>> {
1156        let _permit = self.acquire_trace_permit().await;
1157        Self::debug_trace_call_many(self, bundles, state_context, opts).await.map_err(Into::into)
1158    }
1159
1160    /// Handler for `debug_executionWitness`
1161    async fn debug_execution_witness(
1162        &self,
1163        block: BlockId,
1164        mode: Option<ExecutionWitnessMode>,
1165    ) -> RpcResult<ExecutionWitness> {
1166        let _permit = self.acquire_trace_permit().await;
1167        Self::debug_execution_witness(self, block, mode).await.map_err(Into::into)
1168    }
1169
1170    /// Handler for `debug_executionWitnessByBlockHash`
1171    async fn debug_execution_witness_by_block_hash(
1172        &self,
1173        hash: B256,
1174        mode: Option<ExecutionWitnessMode>,
1175    ) -> RpcResult<ExecutionWitness> {
1176        let _permit = self.acquire_trace_permit().await;
1177        Self::debug_execution_witness_by_block_hash(self, hash, mode).await.map_err(Into::into)
1178    }
1179
1180    /// Handler for `debug_accountAt`
1181    async fn debug_account_at(
1182        &self,
1183        block_id: BlockId,
1184        tx_index: Index,
1185        address: Address,
1186    ) -> RpcResult<Option<Account>> {
1187        let _permit = self.acquire_trace_permit().await;
1188        Self::debug_account_at(self, block_id, tx_index, address).await.map_err(Into::into)
1189    }
1190
1191    /// Handler for `debug_accountInfoAt`
1192    async fn debug_account_info_at(
1193        &self,
1194        block_id: BlockId,
1195        tx_index: Index,
1196        address: Address,
1197    ) -> RpcResult<Option<AccountInfo>> {
1198        let _permit = self.acquire_trace_permit().await;
1199        Self::debug_account_info_at(self, block_id, tx_index, address).await.map_err(Into::into)
1200    }
1201
1202    async fn debug_account_range(
1203        &self,
1204        _block_number: BlockNumberOrTag,
1205        _start: Bytes,
1206        _max_results: u64,
1207        _nocode: bool,
1208        _nostorage: bool,
1209        _incompletes: bool,
1210    ) -> RpcResult<()> {
1211        Ok(())
1212    }
1213
1214    async fn debug_chaindb_compact(&self) -> RpcResult<()> {
1215        Ok(())
1216    }
1217
1218    async fn debug_chain_config(&self) -> RpcResult<ChainConfig> {
1219        Ok(self.provider().chain_spec().genesis().config.clone())
1220    }
1221
1222    async fn debug_chaindb_property(&self, _property: String) -> RpcResult<()> {
1223        Ok(())
1224    }
1225
1226    async fn debug_code_by_hash(
1227        &self,
1228        hash: B256,
1229        block_id: Option<BlockId>,
1230    ) -> RpcResult<Option<Bytes>> {
1231        Self::debug_code_by_hash(self, hash, block_id).await.map_err(Into::into)
1232    }
1233
1234    async fn debug_db_ancient(&self, _kind: String, _number: u64) -> RpcResult<()> {
1235        Ok(())
1236    }
1237
1238    async fn debug_db_ancients(&self) -> RpcResult<()> {
1239        Ok(())
1240    }
1241
1242    /// `debug_db_get` - database key lookup
1243    ///
1244    /// Currently supported:
1245    /// * Contract bytecode associated with a code hash. The key format is: `<0x63><code_hash>`
1246    ///     * Prefix byte: 0x63 (required)
1247    ///     * Code hash: 32 bytes
1248    ///   Must be provided as either:
1249    ///     * Hex string: "0x63..." (66 hex characters after 0x)
1250    ///     * Raw byte string: raw byte string (33 bytes)
1251    ///   See Geth impl: <https://github.com/ethereum/go-ethereum/blob/737ffd1bf0cbee378d0111a5b17ae4724fb2216c/core/rawdb/schema.go#L120>
1252    async fn debug_db_get(&self, key: String) -> RpcResult<Option<Bytes>> {
1253        let key_bytes = if key.starts_with("0x") {
1254            decode(&key).map_err(|_| EthApiError::InvalidParams("Invalid hex key".to_string()))?
1255        } else {
1256            key.into_bytes()
1257        };
1258
1259        if key_bytes.len() != 33 {
1260            return Err(EthApiError::InvalidParams(format!(
1261                "Key must be 33 bytes, got {}",
1262                key_bytes.len()
1263            ))
1264            .into());
1265        }
1266        if key_bytes[0] != 0x63 {
1267            return Err(EthApiError::InvalidParams("Key prefix must be 0x63".to_string()).into());
1268        }
1269
1270        let code_hash = B256::from_slice(&key_bytes[1..33]);
1271
1272        // No block ID is provided, so it defaults to the latest block
1273        self.debug_code_by_hash(code_hash, None).await.map_err(Into::into)
1274    }
1275
1276    async fn debug_dump_block(&self, _number: BlockId) -> RpcResult<()> {
1277        Ok(())
1278    }
1279
1280    async fn debug_free_os_memory(&self) -> RpcResult<()> {
1281        Ok(())
1282    }
1283
1284    async fn debug_gc_stats(&self) -> RpcResult<()> {
1285        Ok(())
1286    }
1287
1288    async fn debug_get_accessible_state(
1289        &self,
1290        _from: BlockNumberOrTag,
1291        _to: BlockNumberOrTag,
1292    ) -> RpcResult<()> {
1293        Ok(())
1294    }
1295
1296    async fn debug_get_modified_accounts_by_hash(
1297        &self,
1298        _start_hash: B256,
1299        _end_hash: B256,
1300    ) -> RpcResult<()> {
1301        Ok(())
1302    }
1303
1304    async fn debug_get_modified_accounts_by_number(
1305        &self,
1306        _start_number: u64,
1307        _end_number: u64,
1308    ) -> RpcResult<()> {
1309        Ok(())
1310    }
1311
1312    async fn debug_intermediate_roots(
1313        &self,
1314        block_hash: B256,
1315        _opts: Option<GethDebugTracingCallOptions>,
1316    ) -> RpcResult<Vec<B256>> {
1317        let _permit = self.acquire_trace_permit().await;
1318        self.intermediate_roots(block_hash).await.map_err(Into::into)
1319    }
1320
1321    async fn debug_mem_stats(&self) -> RpcResult<()> {
1322        Ok(())
1323    }
1324
1325    async fn debug_preimage(&self, _hash: B256) -> RpcResult<()> {
1326        Ok(())
1327    }
1328
1329    async fn debug_print_block(&self, _number: u64) -> RpcResult<()> {
1330        Ok(())
1331    }
1332
1333    async fn debug_seed_hash(&self, _number: u64) -> RpcResult<B256> {
1334        Ok(Default::default())
1335    }
1336
1337    async fn debug_set_gc_percent(&self, _v: i32) -> RpcResult<()> {
1338        Ok(())
1339    }
1340
1341    async fn debug_set_head(&self, _number: U64) -> RpcResult<()> {
1342        Ok(())
1343    }
1344
1345    async fn debug_set_trie_flush_interval(&self, _interval: String) -> RpcResult<()> {
1346        Ok(())
1347    }
1348
1349    async fn debug_standard_trace_bad_block_to_file(
1350        &self,
1351        _block: BlockNumberOrTag,
1352        _opts: Option<GethDebugTracingCallOptions>,
1353    ) -> RpcResult<()> {
1354        Ok(())
1355    }
1356
1357    async fn debug_standard_trace_block_to_file(
1358        &self,
1359        _block: BlockNumberOrTag,
1360        _opts: Option<GethDebugTracingCallOptions>,
1361    ) -> RpcResult<()> {
1362        Ok(())
1363    }
1364
1365    async fn debug_state_root_with_updates(
1366        &self,
1367        hashed_state: HashedPostState,
1368        block_id: Option<BlockId>,
1369    ) -> RpcResult<(B256, TrieUpdates)> {
1370        Self::debug_state_root_with_updates(self, hashed_state, block_id).await.map_err(Into::into)
1371    }
1372
1373    async fn debug_storage_range_at(
1374        &self,
1375        _block_hash: B256,
1376        _tx_idx: usize,
1377        _contract_address: Address,
1378        _key_start: B256,
1379        _max_result: u64,
1380    ) -> RpcResult<()> {
1381        Ok(())
1382    }
1383
1384    async fn debug_trace_bad_block(
1385        &self,
1386        block_hash: B256,
1387        opts: Option<GethDebugTracingCallOptions>,
1388    ) -> RpcResult<Vec<TraceResult>> {
1389        let _permit = self.acquire_trace_permit().await;
1390        let entry = self
1391            .inner
1392            .bad_block_store
1393            .get(block_hash)
1394            .ok_or_else(|| internal_rpc_err("bad block not found in cache"))?;
1395
1396        let evm_env = self
1397            .eth_api()
1398            .evm_config()
1399            .evm_env(entry.block.header())
1400            .map_err(RethError::other)
1401            .to_rpc_result()?;
1402
1403        let opts = opts.map(|o| o.tracing_options).unwrap_or_default();
1404        self.trace_block(entry.block.clone(), evm_env, opts).await.map_err(Into::into)
1405    }
1406}
1407
1408impl<Eth: RpcNodeCore> std::fmt::Debug for DebugApi<Eth> {
1409    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1410        f.debug_struct("DebugApi").finish_non_exhaustive()
1411    }
1412}
1413
1414impl<Eth: RpcNodeCore> Clone for DebugApi<Eth> {
1415    fn clone(&self) -> Self {
1416        Self { inner: Arc::clone(&self.inner) }
1417    }
1418}
1419
1420struct DebugApiInner<Eth: RpcNodeCore> {
1421    /// The implementation of `eth` API
1422    eth_api: Eth,
1423    // restrict the number of concurrent calls to blocking calls
1424    blocking_task_guard: BlockingTaskGuard,
1425    /// Spawns long-running subscription tasks.
1426    task_spawner: Runtime,
1427    /// Cache for bad blocks.
1428    bad_block_store: BadBlockStore<BlockTy<Eth::Primitives>>,
1429}
1430
1431/// A bounded, deduplicating store of recently observed bad blocks.
1432#[derive(Clone, Debug)]
1433struct BadBlockStore<B: BlockTrait> {
1434    inner: Arc<RwLock<VecDeque<BadBlockEntry<B>>>>,
1435    limit: usize,
1436}
1437
1438/// A cached bad block paired with the reason it was rejected.
1439#[derive(Clone, Debug)]
1440struct BadBlockEntry<B: BlockTrait> {
1441    block: Arc<RecoveredBlock<B>>,
1442    reason: String,
1443}
1444
1445impl<B: BlockTrait> BadBlockStore<B> {
1446    /// Creates a new store with the given capacity.
1447    fn new(limit: usize) -> Self {
1448        Self { inner: Arc::new(RwLock::new(VecDeque::with_capacity(limit))), limit }
1449    }
1450
1451    /// Inserts a recovered block with its rejection reason, keeping only the most recent `limit`
1452    /// entries and deduplicating by block hash.
1453    fn insert(&self, block: RecoveredBlock<B>, reason: String) {
1454        let hash = block.hash();
1455        let mut guard = self.inner.write();
1456
1457        // skip if we already recorded this bad block , and keep original ordering
1458        if guard.iter().any(|entry| entry.block.hash() == hash) {
1459            return;
1460        }
1461        guard.push_back(BadBlockEntry { block: Arc::new(block), reason });
1462
1463        while guard.len() > self.limit {
1464            guard.pop_front();
1465        }
1466    }
1467
1468    /// Returns all cached bad block entries ordered from newest to oldest.
1469    fn all(&self) -> Vec<BadBlockEntry<B>> {
1470        let guard = self.inner.read();
1471        guard.iter().rev().cloned().collect()
1472    }
1473
1474    /// Returns the bad block entry with the given hash, if cached.
1475    fn get(&self, hash: B256) -> Option<BadBlockEntry<B>> {
1476        let guard = self.inner.read();
1477        guard.iter().find(|entry| entry.block.hash() == hash).cloned()
1478    }
1479}
1480
1481impl<B: BlockTrait> Default for BadBlockStore<B> {
1482    fn default() -> Self {
1483        Self::new(64)
1484    }
1485}
1486
1487#[cfg(test)]
1488mod tests {
1489    use super::*;
1490    use alloy_primitives::{keccak256, U256};
1491    use reth_db_api::{tables, transaction::DbTxMut};
1492    use reth_primitives_traits::StorageEntry;
1493    use reth_provider::test_utils::create_test_provider_factory;
1494    use revm::{
1495        database::{states::StorageSlot, AccountStatus, BundleAccount, BundleState},
1496        state::AccountInfo as RevmAccountInfo,
1497    };
1498
1499    #[test]
1500    fn hashed_post_state_zeroes_destroyed_account_parent_storage() {
1501        let factory = create_test_provider_factory();
1502        let address = Address::with_last_byte(1);
1503        let old_slot = U256::from(1);
1504        let new_slot = U256::from(2);
1505        let old_value = U256::from(10);
1506        let new_value = U256::from(20);
1507        let hashed_address = keccak256(address);
1508        let hashed_old_slot = keccak256(B256::from(old_slot));
1509        let hashed_new_slot = keccak256(B256::from(new_slot));
1510
1511        let provider_rw = factory.provider_rw().unwrap();
1512        provider_rw
1513            .tx_ref()
1514            .put::<tables::HashedStorages>(
1515                hashed_address,
1516                StorageEntry { key: hashed_old_slot, value: old_value },
1517            )
1518            .unwrap();
1519        provider_rw.commit().unwrap();
1520
1521        let mut bundle_state = BundleState::default();
1522        bundle_state.state.insert(
1523            address,
1524            BundleAccount::new(
1525                Some(RevmAccountInfo::default()),
1526                Some(RevmAccountInfo::default()),
1527                std::iter::once((new_slot, StorageSlot::new_changed(U256::ZERO, new_value)))
1528                    .collect(),
1529                AccountStatus::DestroyedChanged,
1530            ),
1531        );
1532
1533        let provider = factory.latest().unwrap();
1534        let hashed_state = provider.hashed_post_state(&bundle_state).unwrap();
1535        let storage = &hashed_state.storages[&hashed_address];
1536
1537        assert!(!storage.wiped);
1538        assert_eq!(storage.storage[&hashed_old_slot], U256::ZERO);
1539        assert_eq!(storage.storage[&hashed_new_slot], new_value);
1540    }
1541}