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, 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    BlockTraceResult, GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, TraceResult,
14};
15use async_trait::async_trait;
16use futures::Stream;
17use jsonrpsee::core::RpcResult;
18use parking_lot::RwLock;
19use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks};
20use reth_engine_primitives::ConsensusEngineEvent;
21use reth_errors::RethError;
22use reth_evm::{block::BlockExecutor, execute::Executor, ConfigureEvm, EvmEnvFor};
23use reth_primitives_traits::{
24    Block as BlockTrait, BlockBody, BlockTy, ReceiptWithBloom, RecoveredBlock,
25};
26use reth_revm::{db::State, witness::ExecutionWitnessRecord};
27use reth_rpc_api::DebugApiServer;
28use reth_rpc_convert::RpcTxReq;
29use reth_rpc_eth_api::{
30    helpers::{EthTransactions, TraceExt},
31    FromEthApiError, FromEvmError, RpcConvert, RpcNodeCore,
32};
33use reth_rpc_eth_types::{EthApiError, StateCacheDb};
34use reth_rpc_server_types::{result::internal_rpc_err, ToRpcResult};
35use reth_storage_api::{
36    BlockIdReader, BlockReaderIdExt, HashedPostStateProvider, HeaderProvider, ProviderBlock,
37    ReceiptProviderIdExt, StateProviderFactory, StateRootProvider, StorageRootProvider,
38    TransactionVariant,
39};
40use reth_tasks::{pool::BlockingTaskGuard, Runtime};
41use reth_transaction_pool::TransactionPool;
42use reth_trie_common::{
43    updates::TrieUpdates, ExecutionWitnessMode, HashedPostState, HashedStorage,
44};
45use revm::{database::states::bundle_state::BundleRetention, Database, DatabaseCommit};
46use revm_inspectors::tracing::{DebugInspector, TransactionContext};
47use serde::{Deserialize, Serialize};
48use std::{collections::VecDeque, sync::Arc};
49use tokio::sync::{AcquireError, OwnedSemaphorePermit};
50use tokio_stream::StreamExt;
51
52/// `debug` API implementation.
53///
54/// This type provides the functionality for handling `debug` related requests.
55pub struct DebugApi<Eth: RpcNodeCore> {
56    inner: Arc<DebugApiInner<Eth>>,
57}
58
59impl<Eth> DebugApi<Eth>
60where
61    Eth: RpcNodeCore,
62{
63    /// Create a new instance of the [`DebugApi`]
64    pub fn new(
65        eth_api: Eth,
66        blocking_task_guard: BlockingTaskGuard,
67        executor: &Runtime,
68        mut stream: impl Stream<Item = ConsensusEngineEvent<Eth::Primitives>> + Send + Unpin + 'static,
69    ) -> Self {
70        let bad_block_store = BadBlockStore::default();
71        let inner = Arc::new(DebugApiInner {
72            eth_api,
73            blocking_task_guard,
74            bad_block_store: bad_block_store.clone(),
75        });
76
77        // Spawn a task caching bad blocks
78        executor.spawn_task(async move {
79            while let Some(event) = stream.next().await {
80                if let ConsensusEngineEvent::InvalidBlock { block, error } = event &&
81                    let Ok(recovered) = RecoveredBlock::try_recover_sealed(*block)
82                {
83                    bad_block_store.insert(recovered, error);
84                }
85            }
86        });
87
88        Self { inner }
89    }
90
91    /// Access the underlying `Eth` API.
92    pub fn eth_api(&self) -> &Eth {
93        &self.inner.eth_api
94    }
95
96    /// Access the underlying provider.
97    pub fn provider(&self) -> &Eth::Provider {
98        self.inner.eth_api.provider()
99    }
100}
101
102// === impl DebugApi ===
103
104impl<Eth> DebugApi<Eth>
105where
106    Eth: TraceExt,
107{
108    /// Acquires a permit to execute a tracing call.
109    async fn acquire_trace_permit(&self) -> Result<OwnedSemaphorePermit, AcquireError> {
110        self.inner.blocking_task_guard.clone().acquire_owned().await
111    }
112
113    /// Trace the entire block asynchronously
114    async fn trace_block(
115        &self,
116        block: Arc<RecoveredBlock<ProviderBlock<Eth::Provider>>>,
117        evm_env: EvmEnvFor<Eth::Evm>,
118        opts: GethDebugTracingOptions,
119    ) -> Result<Vec<TraceResult>, Eth::Error> {
120        self.eth_api()
121            .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
122                let mut results = Vec::with_capacity(block.body().transactions().len());
123
124                eth_api.apply_pre_execution_changes(&block, &mut db)?;
125
126                let mut transactions = block.transactions_recovered().enumerate().peekable();
127                let mut inspector = DebugInspector::new(opts).map_err(Eth::Error::from_eth_err)?;
128                while let Some((index, tx)) = transactions.next() {
129                    let tx_hash = *tx.tx_hash();
130                    let tx_env = eth_api.evm_config().tx_env(tx);
131
132                    let res = eth_api.inspect(
133                        &mut db,
134                        evm_env.clone(),
135                        tx_env.clone(),
136                        &mut inspector,
137                    )?;
138                    let result = inspector
139                        .get_result(
140                            Some(TransactionContext {
141                                block_hash: Some(block.hash()),
142                                tx_hash: Some(tx_hash),
143                                tx_index: Some(index),
144                            }),
145                            &tx_env,
146                            &evm_env.block_env,
147                            &res,
148                            &mut db,
149                        )
150                        .map_err(Eth::Error::from_eth_err)?;
151
152                    results.push(TraceResult::Success { result, tx_hash: Some(tx_hash) });
153                    if transactions.peek().is_some() {
154                        inspector.fuse().map_err(Eth::Error::from_eth_err)?;
155                        // need to apply the state changes of this transaction before executing the
156                        // next transaction
157                        db.commit(res.state)
158                    }
159                }
160
161                Ok(results)
162            })
163            .await
164    }
165
166    /// Replays the given block and returns the trace of each transaction.
167    ///
168    /// This expects a rlp encoded block
169    ///
170    /// Note, the parent of this block must be present, or it will fail.
171    pub async fn debug_trace_raw_block(
172        &self,
173        rlp_block: Bytes,
174        opts: GethDebugTracingOptions,
175    ) -> Result<Vec<TraceResult>, Eth::Error> {
176        let block: ProviderBlock<Eth::Provider> = Decodable::decode(&mut rlp_block.as_ref())
177            .map_err(BlockError::RlpDecodeRawBlock)
178            .map_err(Eth::Error::from_eth_err)?;
179
180        let evm_env = self
181            .eth_api()
182            .evm_config()
183            .evm_env(block.header())
184            .map_err(RethError::other)
185            .map_err(Eth::Error::from_eth_err)?;
186
187        // Depending on EIP-2 we need to recover the transactions differently
188        let senders =
189            if self.provider().chain_spec().is_homestead_active_at_block(block.header().number()) {
190                block.body().recover_signers()
191            } else {
192                block.body().recover_signers_unchecked()
193            }
194            .map_err(Eth::Error::from_eth_err)?;
195
196        self.trace_block(Arc::new(block.into_recovered_with_signers(senders)), evm_env, opts).await
197    }
198
199    /// Replays a block and returns the trace of each transaction.
200    pub async fn debug_trace_block(
201        &self,
202        block_id: BlockId,
203        opts: GethDebugTracingOptions,
204    ) -> Result<Vec<TraceResult>, Eth::Error> {
205        let block = self
206            .eth_api()
207            .recovered_block(block_id)
208            .await?
209            .ok_or(EthApiError::HeaderNotFound(block_id))?;
210        let evm_env = self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
211
212        self.trace_block(block, evm_env, opts).await
213    }
214
215    /// Trace the transaction according to the provided options.
216    ///
217    /// Ref: <https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers>
218    pub async fn debug_trace_transaction(
219        &self,
220        tx_hash: B256,
221        opts: GethDebugTracingOptions,
222    ) -> Result<GethTrace, Eth::Error> {
223        let (transaction, block) = match self.eth_api().transaction_and_block(tx_hash).await? {
224            None => return Err(EthApiError::TransactionNotFound.into()),
225            Some(res) => res,
226        };
227        let evm_env = self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
228
229        // we need to get the state of the parent block because we're essentially replaying the
230        // block the transaction is included in
231        let state_at: BlockId = block.parent_hash().into();
232        let block_hash = block.hash();
233
234        self.eth_api()
235            .spawn_with_state_at_block(state_at, move |eth_api, mut db| {
236                let block_txs = block.transactions_recovered();
237
238                // configure env for the target transaction
239                let tx = transaction.into_recovered();
240
241                eth_api.apply_pre_execution_changes(&block, &mut db)?;
242
243                // replay all transactions prior to the targeted transaction
244                let index = eth_api.replay_transactions_until(
245                    &mut db,
246                    evm_env.clone(),
247                    block_txs,
248                    *tx.tx_hash(),
249                )?;
250
251                let tx_env = eth_api.evm_config().tx_env(&tx);
252
253                let mut inspector = DebugInspector::new(opts).map_err(Eth::Error::from_eth_err)?;
254                let res =
255                    eth_api.inspect(&mut db, evm_env.clone(), tx_env.clone(), &mut inspector)?;
256                let trace = inspector
257                    .get_result(
258                        Some(TransactionContext {
259                            block_hash: Some(block_hash),
260                            tx_index: Some(index),
261                            tx_hash: Some(*tx.tx_hash()),
262                        }),
263                        &tx_env,
264                        &evm_env.block_env,
265                        &res,
266                        &mut db,
267                    )
268                    .map_err(Eth::Error::from_eth_err)?;
269
270                Ok(trace)
271            })
272            .await
273    }
274
275    /// The `debug_traceCall` method lets you run an `eth_call` within the context of the given
276    /// block execution using the final state of parent block as the base.
277    ///
278    /// If `tx_index` is provided in opts, the call will be traced at the state after executing
279    /// transactions up to the specified index within the block (0-indexed).
280    /// If not provided, then uses the post-state (default behavior).
281    ///
282    /// Differences compare to `eth_call`:
283    ///  - `debug_traceCall` executes with __enabled__ basefee check, `eth_call` does not: <https://github.com/paradigmxyz/reth/issues/6240>
284    pub async fn debug_trace_call(
285        &self,
286        call: RpcTxReq<Eth::NetworkTypes>,
287        block_id: Option<BlockId>,
288        opts: GethDebugTracingCallOptions,
289    ) -> Result<GethTrace, Eth::Error> {
290        let at = block_id.unwrap_or_default();
291        let GethDebugTracingCallOptions {
292            tracing_options,
293            state_overrides,
294            block_overrides,
295            tx_index,
296        } = opts;
297        let overrides = EvmOverrides::new(state_overrides, block_overrides.map(Box::new));
298
299        // Check if we need to replay transactions for a specific tx_index
300        if let Some(tx_idx) = tx_index {
301            return self
302                .debug_trace_call_at_tx_index(call, at, tx_idx as usize, tracing_options, overrides)
303                .await;
304        }
305
306        let this = self.clone();
307        self.eth_api()
308            .spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| {
309                let mut inspector =
310                    DebugInspector::new(tracing_options).map_err(Eth::Error::from_eth_err)?;
311                let res = this.eth_api().inspect(
312                    &mut *db,
313                    evm_env.clone(),
314                    tx_env.clone(),
315                    &mut inspector,
316                )?;
317                let trace = inspector
318                    .get_result(None, &tx_env, &evm_env.block_env, &res, db)
319                    .map_err(Eth::Error::from_eth_err)?;
320                Ok(trace)
321            })
322            .await
323    }
324
325    /// Helper method to execute `debug_trace_call` at a specific transaction index within a block.
326    /// This replays transactions up to the specified index, then executes the trace call in that
327    /// state.
328    async fn debug_trace_call_at_tx_index(
329        &self,
330        call: RpcTxReq<Eth::NetworkTypes>,
331        block_id: BlockId,
332        tx_index: usize,
333        tracing_options: GethDebugTracingOptions,
334        overrides: EvmOverrides,
335    ) -> Result<GethTrace, Eth::Error> {
336        // Get the target block to check transaction count
337        let block = self
338            .eth_api()
339            .recovered_block(block_id)
340            .await?
341            .ok_or(EthApiError::HeaderNotFound(block_id))?;
342
343        if tx_index >= block.transaction_count() {
344            // tx_index out of bounds
345            return Err(EthApiError::InvalidParams(format!(
346                "tx_index {} out of bounds for block with {} transactions",
347                tx_index,
348                block.transaction_count()
349            ))
350            .into())
351        }
352
353        let evm_env = self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
354
355        // execute after the parent block, replaying `tx_index` transactions
356        let state_at = block.parent_hash();
357
358        self.eth_api()
359            .spawn_with_state_at_block(state_at, move |eth_api, mut db| {
360                // 1. apply pre-execution changes
361                eth_api.apply_pre_execution_changes(&block, &mut db)?;
362
363                // 2. replay the required number of transactions
364                eth_api.replay_transactions_until(
365                    &mut db,
366                    evm_env.clone(),
367                    block.transactions_recovered(),
368                    *block.body().transactions()[tx_index].tx_hash(),
369                )?;
370
371                // 3. now execute the trace call on this state
372                let (evm_env, tx_env) =
373                    eth_api.prepare_call_env(evm_env, call, &mut db, overrides)?;
374
375                let mut inspector =
376                    DebugInspector::new(tracing_options).map_err(Eth::Error::from_eth_err)?;
377                let res =
378                    eth_api.inspect(&mut db, evm_env.clone(), tx_env.clone(), &mut inspector)?;
379                let trace = inspector
380                    .get_result(None, &tx_env, &evm_env.block_env, &res, &mut db)
381                    .map_err(Eth::Error::from_eth_err)?;
382
383                Ok(trace)
384            })
385            .await
386    }
387
388    /// The `debug_traceCallMany` method lets you run an `eth_callMany` within the context of the
389    /// given block execution using the first n transactions in the given block as base.
390    /// Each following bundle increments block number by 1 and block timestamp by 12 seconds
391    pub async fn debug_trace_call_many(
392        &self,
393        bundles: Vec<Bundle<RpcTxReq<Eth::NetworkTypes>>>,
394        state_context: Option<StateContext>,
395        opts: Option<GethDebugTracingCallOptions>,
396    ) -> Result<Vec<Vec<GethTrace>>, Eth::Error> {
397        if bundles.is_empty() {
398            return Err(EthApiError::InvalidParams(String::from("bundles are empty.")).into())
399        }
400
401        let StateContext { transaction_index, block_number } = state_context.unwrap_or_default();
402        let transaction_index = transaction_index.unwrap_or_default();
403
404        let target_block = block_number.unwrap_or_default();
405        let block = self
406            .eth_api()
407            .recovered_block(target_block)
408            .await?
409            .ok_or(EthApiError::HeaderNotFound(target_block))?;
410        let mut evm_env =
411            self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
412
413        let opts = opts.unwrap_or_default();
414        let GethDebugTracingCallOptions { tracing_options, mut state_overrides, .. } = opts;
415
416        // we're essentially replaying the transactions in the block here, hence we need the state
417        // that points to the beginning of the block, which is the state at the parent block
418        let mut at = block.parent_hash();
419        let mut replay_block_txs = true;
420
421        // if a transaction index is provided, we need to replay the transactions until the index
422        let num_txs =
423            transaction_index.index().unwrap_or_else(|| block.body().transactions().len());
424        // but if all transactions are to be replayed, we can use the state at the block itself
425        // this works with the exception of the PENDING block, because its state might not exist if
426        // built locally
427        if !target_block.is_pending() && num_txs == block.body().transactions().len() {
428            at = block.hash();
429            replay_block_txs = false;
430        }
431
432        self.eth_api()
433            .spawn_with_state_at_block(at, move |eth_api, mut db| {
434                // the outer vec for the bundles
435                let mut all_bundles = Vec::with_capacity(bundles.len());
436
437                if replay_block_txs {
438                    // only need to replay the transactions in the block if not all transactions are
439                    // to be replayed
440                    eth_api.apply_pre_execution_changes(&block, &mut db)?;
441
442                    let transactions = block.transactions_recovered().take(num_txs);
443
444                    // Execute all transactions until index
445                    for tx in transactions {
446                        let tx_env = eth_api.evm_config().tx_env(tx);
447                        let res = eth_api.transact(&mut db, evm_env.clone(), tx_env)?;
448                        db.commit(res.state);
449                    }
450                }
451
452                // Trace all bundles
453                let mut bundles = bundles.into_iter().peekable();
454                let mut inspector = DebugInspector::new(tracing_options.clone())
455                    .map_err(Eth::Error::from_eth_err)?;
456                while let Some(bundle) = bundles.next() {
457                    let mut results = Vec::with_capacity(bundle.transactions.len());
458                    let Bundle { transactions, block_override } = bundle;
459
460                    let block_overrides = block_override.map(Box::new);
461
462                    let mut transactions = transactions.into_iter().peekable();
463                    while let Some(tx) = transactions.next() {
464                        // apply state overrides only once, before the first transaction
465                        let state_overrides = state_overrides.take();
466                        let overrides = EvmOverrides::new(state_overrides, block_overrides.clone());
467
468                        let (evm_env, tx_env) =
469                            eth_api.prepare_call_env(evm_env.clone(), tx, &mut db, overrides)?;
470
471                        let res = eth_api.inspect(
472                            &mut db,
473                            evm_env.clone(),
474                            tx_env.clone(),
475                            &mut inspector,
476                        )?;
477                        let trace = inspector
478                            .get_result(None, &tx_env, &evm_env.block_env, &res, &mut db)
479                            .map_err(Eth::Error::from_eth_err)?;
480
481                        // If there is more transactions, commit the database
482                        // If there is no transactions, but more bundles, commit to the database too
483                        if transactions.peek().is_some() || bundles.peek().is_some() {
484                            inspector.fuse().map_err(Eth::Error::from_eth_err)?;
485                            db.commit(res.state);
486                        }
487                        results.push(trace);
488                    }
489                    // Increment block_env number and timestamp for the next bundle
490                    evm_env.block_env.inner_mut().number += uint!(1_U256);
491                    evm_env.block_env.inner_mut().timestamp += uint!(12_U256);
492
493                    all_bundles.push(results);
494                }
495                Ok(all_bundles)
496            })
497            .await
498    }
499
500    /// Generates an execution witness for the given block hash. see
501    /// [`Self::debug_execution_witness`] for more info.
502    pub async fn debug_execution_witness_by_block_hash(
503        &self,
504        hash: B256,
505        mode: Option<ExecutionWitnessMode>,
506    ) -> Result<ExecutionWitness, Eth::Error> {
507        let this = self.clone();
508        let block = this
509            .eth_api()
510            .recovered_block(hash.into())
511            .await?
512            .ok_or(EthApiError::HeaderNotFound(hash.into()))?;
513
514        self.debug_execution_witness_for_block(block, mode.unwrap_or_default()).await
515    }
516
517    /// The `debug_executionWitness` method allows for re-execution of a block with the purpose of
518    /// generating an execution witness. The witness comprises of a map of all hashed trie nodes to
519    /// their preimages that were required during the execution of the block, including during state
520    /// root recomputation.
521    pub async fn debug_execution_witness(
522        &self,
523        block_id: BlockNumberOrTag,
524        mode: Option<ExecutionWitnessMode>,
525    ) -> Result<ExecutionWitness, Eth::Error> {
526        let this = self.clone();
527        let block = this
528            .eth_api()
529            .recovered_block(block_id.into())
530            .await?
531            .ok_or(EthApiError::HeaderNotFound(block_id.into()))?;
532
533        self.debug_execution_witness_for_block(block, mode.unwrap_or_default()).await
534    }
535
536    /// Generates an execution witness, using the given recovered block.
537    pub async fn debug_execution_witness_for_block(
538        &self,
539        block: Arc<RecoveredBlock<ProviderBlock<Eth::Provider>>>,
540        mode: ExecutionWitnessMode,
541    ) -> Result<ExecutionWitness, Eth::Error> {
542        let block_number = block.header().number();
543        self.eth_api()
544            .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
545                let block_executor = eth_api.evm_config().executor(&mut db);
546
547                let mut witness_record = ExecutionWitnessRecord::default();
548
549                let _ = block_executor
550                    .execute_with_state_closure(&block, |statedb: &State<_>| {
551                        witness_record.record_executed_state(statedb, mode);
552                    })
553                    .map_err(|err| EthApiError::Internal(err.into()))?;
554
555                Ok(witness_record
556                    .into_execution_witness(&db.database.0, eth_api.provider(), block_number, mode)
557                    .map_err(EthApiError::from)?)
558            })
559            .await
560    }
561
562    /// Returns account information, including the storage root, after replaying the block through
563    /// the transaction at the given index.
564    pub async fn debug_account_at(
565        &self,
566        block_id: BlockId,
567        tx_index: Index,
568        address: Address,
569    ) -> Result<Option<Account>, Eth::Error> {
570        self.replay_block_until(block_id, tx_index, move |db| Self::account(db, address))
571            .await
572            .map(Option::flatten)
573    }
574
575    /// Returns account information after replaying the block through the transaction at the given
576    /// index.
577    pub async fn debug_account_info_at(
578        &self,
579        block_id: BlockId,
580        tx_index: Index,
581        address: Address,
582    ) -> Result<Option<AccountInfo>, Eth::Error> {
583        self.replay_block_until(block_id, tx_index, move |db| Self::account_info(db, address)).await
584    }
585
586    /// Replays a block through the transaction at the given index and calls `f` with the resulting
587    /// state.
588    async fn replay_block_until<F, R>(
589        &self,
590        block_id: BlockId,
591        tx_index: Index,
592        f: F,
593    ) -> Result<Option<R>, Eth::Error>
594    where
595        F: FnOnce(&mut StateCacheDb) -> Result<R, Eth::Error> + Send + 'static,
596        R: Send + 'static,
597    {
598        let block = self
599            .eth_api()
600            .recovered_block(block_id)
601            .await?
602            .ok_or(EthApiError::HeaderNotFound(block_id))?;
603        let tx_index = usize::from(tx_index);
604        let transaction_count = block.transaction_count();
605        if tx_index >= transaction_count {
606            return Err(EthApiError::InvalidParams(format!(
607                "tx_index {tx_index} out of bounds for block with {transaction_count} transactions"
608            ))
609            .into())
610        }
611
612        self.eth_api()
613            .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
614                let mut executor = eth_api
615                    .evm_config()
616                    .executor_for_block(&mut db, block.sealed_block())
617                    .map_err(RethError::other)
618                    .map_err(Eth::Error::from_eth_err)?;
619                executor.apply_pre_execution_changes().map_err(Eth::Error::from_eth_err)?;
620
621                for tx in block.transactions_recovered().take(tx_index + 1) {
622                    executor.execute_transaction(tx).map_err(Eth::Error::from_eth_err)?;
623                }
624                drop(executor);
625
626                f(&mut db)
627            })
628            .await
629            .map(Some)
630    }
631
632    /// Retrieves the account's balance, nonce, code hash, and storage root from the given state.
633    fn account(db: &mut StateCacheDb, address: Address) -> Result<Option<Account>, Eth::Error> {
634        let account = db.basic(address).map_err(Eth::Error::from_eth_err)?;
635        let Some(account) = account else { return Ok(None) };
636
637        let balance = account.balance;
638        let nonce = account.nonce;
639        let code_hash = account.code_hash;
640        let hashed_storage = db
641            .cache
642            .accounts
643            .get(&address)
644            .and_then(|account| {
645                account.account.as_ref().map(|plain_account| {
646                    HashedStorage::from_plain_storage(account.status, plain_account.storage.iter())
647                })
648            })
649            .unwrap_or_default();
650        let storage_root =
651            db.database.storage_root(address, hashed_storage).map_err(Eth::Error::from_eth_err)?;
652
653        Ok(Some(Account { balance, nonce, code_hash, storage_root }))
654    }
655
656    /// Retrieves the account's balance, nonce, and code from the given state.
657    fn account_info<DB>(db: &mut DB, address: Address) -> Result<AccountInfo, Eth::Error>
658    where
659        DB: Database,
660        EthApiError: From<DB::Error>,
661    {
662        let account = db.basic(address).map_err(Eth::Error::from_eth_err)?.unwrap_or_default();
663        let code = if account.code_hash == KECCAK_EMPTY {
664            Default::default()
665        } else if let Some(code) = account.code {
666            code.original_bytes()
667        } else {
668            db.code_by_hash(account.code_hash).map_err(Eth::Error::from_eth_err)?.original_bytes()
669        };
670
671        Ok(AccountInfo { balance: account.balance, nonce: account.nonce, code })
672    }
673
674    /// Returns the code associated with a given hash at the specified block ID. If no code is
675    /// found, it returns None. If no block ID is provided, it defaults to the latest block.
676    pub async fn debug_code_by_hash(
677        &self,
678        hash: B256,
679        block_id: Option<BlockId>,
680    ) -> Result<Option<Bytes>, Eth::Error> {
681        Ok(self
682            .provider()
683            .state_by_block_id(block_id.unwrap_or_default())
684            .map_err(Eth::Error::from_eth_err)?
685            .bytecode_by_hash(&hash)
686            .map_err(Eth::Error::from_eth_err)?
687            .map(|b| b.original_bytes()))
688    }
689
690    /// Returns the state root of the `HashedPostState` on top of the state for the given block with
691    /// trie updates.
692    async fn debug_state_root_with_updates(
693        &self,
694        hashed_state: HashedPostState,
695        block_id: Option<BlockId>,
696    ) -> Result<(B256, TrieUpdates), Eth::Error> {
697        self.inner
698            .eth_api
699            .spawn_blocking_io(move |this| {
700                let state = this
701                    .provider()
702                    .state_by_block_id(block_id.unwrap_or_default())
703                    .map_err(Eth::Error::from_eth_err)?;
704                state.state_root_with_updates(hashed_state).map_err(Eth::Error::from_eth_err)
705            })
706            .await
707    }
708
709    /// Executes a block and returns the state root after each transaction.
710    pub async fn intermediate_roots(&self, block_hash: B256) -> Result<Vec<B256>, Eth::Error> {
711        let block = self
712            .eth_api()
713            .recovered_block(block_hash.into())
714            .await?
715            .ok_or(EthApiError::HeaderNotFound(block_hash.into()))?;
716        let evm_env = self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
717
718        self.eth_api()
719            .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
720                // Enable transition tracking so that merge_transitions works
721                db.transition_state = Some(Default::default());
722
723                eth_api.apply_pre_execution_changes(&block, &mut db)?;
724
725                let mut roots = Vec::with_capacity(block.body().transactions().len());
726                for tx in block.transactions_recovered() {
727                    let tx_env = eth_api.evm_config().tx_env(tx);
728                    {
729                        let mut evm = eth_api.evm_config().evm_with_env(&mut db, evm_env.clone());
730                        evm.transact_commit(tx_env).map_err(Eth::Error::from_evm_err)?;
731                    }
732                    // Merge transitions into cumulative bundle_state
733                    db.merge_transitions(BundleRetention::PlainState);
734                    // Compute state root from the accumulated state changes
735                    let hashed_state = db.database.hashed_post_state(&db.bundle_state);
736                    let root =
737                        db.database.state_root(hashed_state).map_err(Eth::Error::from_eth_err)?;
738                    roots.push(root);
739                }
740
741                Ok(roots)
742            })
743            .await
744    }
745}
746
747#[async_trait]
748impl<Eth> DebugApiServer<RpcTxReq<Eth::NetworkTypes>> for DebugApi<Eth>
749where
750    Eth: EthTransactions + TraceExt,
751{
752    /// Handler for `debug_getRawHeader`
753    async fn raw_header(&self, block_id: BlockId) -> RpcResult<Bytes> {
754        let header = match block_id {
755            BlockId::Hash(hash) => self.provider().header(hash.into()).to_rpc_result()?,
756            BlockId::Number(number_or_tag) => {
757                let number = self
758                    .provider()
759                    .convert_block_number(number_or_tag)
760                    .to_rpc_result()?
761                    .ok_or(EthApiError::HeaderNotFound(block_id))?;
762                self.provider().header_by_number(number).to_rpc_result()?
763            }
764        }
765        .ok_or(EthApiError::HeaderNotFound(block_id))?;
766
767        let mut res = Vec::new();
768        header.encode(&mut res);
769        Ok(res.into())
770    }
771
772    /// Handler for `debug_getRawBlock`
773    async fn raw_block(&self, block_id: BlockId) -> RpcResult<Bytes> {
774        let block = self
775            .provider()
776            .block_by_id(block_id)
777            .to_rpc_result()?
778            .ok_or(EthApiError::HeaderNotFound(block_id))?;
779        let mut res = Vec::new();
780        block.encode(&mut res);
781        Ok(res.into())
782    }
783
784    /// Handler for `debug_getRawBlockAccessList`
785    async fn raw_block_access_list(&self, block_id: BlockId) -> RpcResult<Bytes> {
786        self.eth_api()
787            .get_raw_block_access_list(block_id)
788            .await
789            .map_err(Into::into)?
790            .ok_or_else(|| EthApiError::HeaderNotFound(block_id).into())
791    }
792
793    /// Handler for `debug_getRawTransaction`
794    ///
795    /// If this is a pooled EIP-4844 transaction, the blob sidecar is included.
796    ///
797    /// Returns the bytes of the transaction for the given hash.
798    async fn raw_transaction(&self, hash: B256) -> RpcResult<Option<Bytes>> {
799        self.eth_api().raw_transaction_by_hash(hash).await.map_err(Into::into)
800    }
801
802    /// Handler for `debug_getRawTransactions`
803    /// Returns the bytes of the transaction for the given hash.
804    async fn raw_transactions(&self, block_id: BlockId) -> RpcResult<Vec<Bytes>> {
805        let block: RecoveredBlock<BlockTy<Eth::Primitives>> = self
806            .provider()
807            .block_with_senders_by_id(block_id, TransactionVariant::NoHash)
808            .to_rpc_result()?
809            .unwrap_or_default();
810        Ok(block.into_transactions_recovered().map(|tx| tx.encoded_2718().into()).collect())
811    }
812
813    /// Handler for `debug_getRawReceipts`
814    async fn raw_receipts(&self, block_id: BlockId) -> RpcResult<Vec<Bytes>> {
815        Ok(self
816            .provider()
817            .receipts_by_block_id(block_id)
818            .to_rpc_result()?
819            .ok_or(EthApiError::HeaderNotFound(block_id))?
820            .into_iter()
821            .map(|receipt| ReceiptWithBloom::from(receipt).encoded_2718().into())
822            .collect())
823    }
824
825    /// Handler for `debug_getBadBlocks`
826    async fn bad_blocks(&self) -> RpcResult<Vec<serde_json::Value>> {
827        let entries = self.inner.bad_block_store.all();
828        let mut bad_blocks = Vec::with_capacity(entries.len());
829
830        #[derive(Serialize, Deserialize)]
831        struct BadBlockSerde<T> {
832            block: T,
833            hash: B256,
834            rlp: Bytes,
835            reason: String,
836        }
837
838        for entry in entries {
839            let rlp = alloy_rlp::encode(entry.block.sealed_block()).into();
840            let hash = entry.block.hash();
841
842            let block = entry
843                .block
844                .clone_into_rpc_block(
845                    BlockTransactionsKind::Full,
846                    |tx, tx_info| self.eth_api().converter().fill(tx, tx_info),
847                    |header, size| self.eth_api().converter().convert_header(header, size),
848                )
849                .map_err(|err| Eth::Error::from(err).into())?;
850
851            let bad_block =
852                serde_json::to_value(BadBlockSerde { block, hash, rlp, reason: entry.reason })
853                    .map_err(|err| EthApiError::other(internal_rpc_err(err.to_string())))?;
854
855            bad_blocks.push(bad_block);
856        }
857
858        Ok(bad_blocks)
859    }
860
861    /// Handler for `debug_clearTxpool`
862    async fn debug_clear_txpool(&self) -> RpcResult<()> {
863        let pool = self.eth_api().pool();
864        let all_hashes = pool.all_transaction_hashes();
865        let _ = pool.remove_transactions(all_hashes);
866        Ok(())
867    }
868
869    /// Handler for `debug_traceChain`
870    async fn debug_trace_chain(
871        &self,
872        _start_exclusive: BlockNumberOrTag,
873        _end_inclusive: BlockNumberOrTag,
874    ) -> RpcResult<Vec<BlockTraceResult>> {
875        Err(internal_rpc_err("unimplemented"))
876    }
877
878    /// Handler for `debug_traceBlock`
879    async fn debug_trace_block(
880        &self,
881        rlp_block: Bytes,
882        opts: Option<GethDebugTracingOptions>,
883    ) -> RpcResult<Vec<TraceResult>> {
884        let _permit = self.acquire_trace_permit().await;
885        Self::debug_trace_raw_block(self, rlp_block, opts.unwrap_or_default())
886            .await
887            .map_err(Into::into)
888    }
889
890    /// Handler for `debug_traceBlockByHash`
891    async fn debug_trace_block_by_hash(
892        &self,
893        block: B256,
894        opts: Option<GethDebugTracingOptions>,
895    ) -> RpcResult<Vec<TraceResult>> {
896        let _permit = self.acquire_trace_permit().await;
897        Self::debug_trace_block(self, block.into(), opts.unwrap_or_default())
898            .await
899            .map_err(Into::into)
900    }
901
902    /// Handler for `debug_traceBlockByNumber`
903    async fn debug_trace_block_by_number(
904        &self,
905        block: BlockNumberOrTag,
906        opts: Option<GethDebugTracingOptions>,
907    ) -> RpcResult<Vec<TraceResult>> {
908        let _permit = self.acquire_trace_permit().await;
909        Self::debug_trace_block(self, block.into(), opts.unwrap_or_default())
910            .await
911            .map_err(Into::into)
912    }
913
914    /// Handler for `debug_traceTransaction`
915    async fn debug_trace_transaction(
916        &self,
917        tx_hash: B256,
918        opts: Option<GethDebugTracingOptions>,
919    ) -> RpcResult<GethTrace> {
920        let _permit = self.acquire_trace_permit().await;
921        Self::debug_trace_transaction(self, tx_hash, opts.unwrap_or_default())
922            .await
923            .map_err(Into::into)
924    }
925
926    /// Handler for `debug_traceCall`
927    async fn debug_trace_call(
928        &self,
929        request: RpcTxReq<Eth::NetworkTypes>,
930        block_id: Option<BlockId>,
931        opts: Option<GethDebugTracingCallOptions>,
932    ) -> RpcResult<GethTrace> {
933        let _permit = self.acquire_trace_permit().await;
934        Self::debug_trace_call(self, request, block_id, opts.unwrap_or_default())
935            .await
936            .map_err(Into::into)
937    }
938
939    async fn debug_trace_call_many(
940        &self,
941        bundles: Vec<Bundle<RpcTxReq<Eth::NetworkTypes>>>,
942        state_context: Option<StateContext>,
943        opts: Option<GethDebugTracingCallOptions>,
944    ) -> RpcResult<Vec<Vec<GethTrace>>> {
945        let _permit = self.acquire_trace_permit().await;
946        Self::debug_trace_call_many(self, bundles, state_context, opts).await.map_err(Into::into)
947    }
948
949    /// Handler for `debug_executionWitness`
950    async fn debug_execution_witness(
951        &self,
952        block: BlockNumberOrTag,
953        mode: Option<ExecutionWitnessMode>,
954    ) -> RpcResult<ExecutionWitness> {
955        let _permit = self.acquire_trace_permit().await;
956        Self::debug_execution_witness(self, block, mode).await.map_err(Into::into)
957    }
958
959    /// Handler for `debug_executionWitnessByBlockHash`
960    async fn debug_execution_witness_by_block_hash(
961        &self,
962        hash: B256,
963        mode: Option<ExecutionWitnessMode>,
964    ) -> RpcResult<ExecutionWitness> {
965        let _permit = self.acquire_trace_permit().await;
966        Self::debug_execution_witness_by_block_hash(self, hash, mode).await.map_err(Into::into)
967    }
968
969    /// Handler for `debug_accountAt`
970    async fn debug_account_at(
971        &self,
972        block_id: BlockId,
973        tx_index: Index,
974        address: Address,
975    ) -> RpcResult<Option<Account>> {
976        let _permit = self.acquire_trace_permit().await;
977        Self::debug_account_at(self, block_id, tx_index, address).await.map_err(Into::into)
978    }
979
980    /// Handler for `debug_accountInfoAt`
981    async fn debug_account_info_at(
982        &self,
983        block_id: BlockId,
984        tx_index: Index,
985        address: Address,
986    ) -> RpcResult<Option<AccountInfo>> {
987        let _permit = self.acquire_trace_permit().await;
988        Self::debug_account_info_at(self, block_id, tx_index, address).await.map_err(Into::into)
989    }
990
991    async fn debug_account_range(
992        &self,
993        _block_number: BlockNumberOrTag,
994        _start: Bytes,
995        _max_results: u64,
996        _nocode: bool,
997        _nostorage: bool,
998        _incompletes: bool,
999    ) -> RpcResult<()> {
1000        Ok(())
1001    }
1002
1003    async fn debug_chaindb_compact(&self) -> RpcResult<()> {
1004        Ok(())
1005    }
1006
1007    async fn debug_chain_config(&self) -> RpcResult<ChainConfig> {
1008        Ok(self.provider().chain_spec().genesis().config.clone())
1009    }
1010
1011    async fn debug_chaindb_property(&self, _property: String) -> RpcResult<()> {
1012        Ok(())
1013    }
1014
1015    async fn debug_code_by_hash(
1016        &self,
1017        hash: B256,
1018        block_id: Option<BlockId>,
1019    ) -> RpcResult<Option<Bytes>> {
1020        Self::debug_code_by_hash(self, hash, block_id).await.map_err(Into::into)
1021    }
1022
1023    async fn debug_db_ancient(&self, _kind: String, _number: u64) -> RpcResult<()> {
1024        Ok(())
1025    }
1026
1027    async fn debug_db_ancients(&self) -> RpcResult<()> {
1028        Ok(())
1029    }
1030
1031    /// `debug_db_get` - database key lookup
1032    ///
1033    /// Currently supported:
1034    /// * Contract bytecode associated with a code hash. The key format is: `<0x63><code_hash>`
1035    ///     * Prefix byte: 0x63 (required)
1036    ///     * Code hash: 32 bytes
1037    ///   Must be provided as either:
1038    ///     * Hex string: "0x63..." (66 hex characters after 0x)
1039    ///     * Raw byte string: raw byte string (33 bytes)
1040    ///   See Geth impl: <https://github.com/ethereum/go-ethereum/blob/737ffd1bf0cbee378d0111a5b17ae4724fb2216c/core/rawdb/schema.go#L120>
1041    async fn debug_db_get(&self, key: String) -> RpcResult<Option<Bytes>> {
1042        let key_bytes = if key.starts_with("0x") {
1043            decode(&key).map_err(|_| EthApiError::InvalidParams("Invalid hex key".to_string()))?
1044        } else {
1045            key.into_bytes()
1046        };
1047
1048        if key_bytes.len() != 33 {
1049            return Err(EthApiError::InvalidParams(format!(
1050                "Key must be 33 bytes, got {}",
1051                key_bytes.len()
1052            ))
1053            .into());
1054        }
1055        if key_bytes[0] != 0x63 {
1056            return Err(EthApiError::InvalidParams("Key prefix must be 0x63".to_string()).into());
1057        }
1058
1059        let code_hash = B256::from_slice(&key_bytes[1..33]);
1060
1061        // No block ID is provided, so it defaults to the latest block
1062        self.debug_code_by_hash(code_hash, None).await.map_err(Into::into)
1063    }
1064
1065    async fn debug_dump_block(&self, _number: BlockId) -> RpcResult<()> {
1066        Ok(())
1067    }
1068
1069    async fn debug_free_os_memory(&self) -> RpcResult<()> {
1070        Ok(())
1071    }
1072
1073    async fn debug_gc_stats(&self) -> RpcResult<()> {
1074        Ok(())
1075    }
1076
1077    async fn debug_get_accessible_state(
1078        &self,
1079        _from: BlockNumberOrTag,
1080        _to: BlockNumberOrTag,
1081    ) -> RpcResult<()> {
1082        Ok(())
1083    }
1084
1085    async fn debug_get_modified_accounts_by_hash(
1086        &self,
1087        _start_hash: B256,
1088        _end_hash: B256,
1089    ) -> RpcResult<()> {
1090        Ok(())
1091    }
1092
1093    async fn debug_get_modified_accounts_by_number(
1094        &self,
1095        _start_number: u64,
1096        _end_number: u64,
1097    ) -> RpcResult<()> {
1098        Ok(())
1099    }
1100
1101    async fn debug_intermediate_roots(
1102        &self,
1103        block_hash: B256,
1104        _opts: Option<GethDebugTracingCallOptions>,
1105    ) -> RpcResult<Vec<B256>> {
1106        let _permit = self.acquire_trace_permit().await;
1107        self.intermediate_roots(block_hash).await.map_err(Into::into)
1108    }
1109
1110    async fn debug_mem_stats(&self) -> RpcResult<()> {
1111        Ok(())
1112    }
1113
1114    async fn debug_preimage(&self, _hash: B256) -> RpcResult<()> {
1115        Ok(())
1116    }
1117
1118    async fn debug_print_block(&self, _number: u64) -> RpcResult<()> {
1119        Ok(())
1120    }
1121
1122    async fn debug_seed_hash(&self, _number: u64) -> RpcResult<B256> {
1123        Ok(Default::default())
1124    }
1125
1126    async fn debug_set_gc_percent(&self, _v: i32) -> RpcResult<()> {
1127        Ok(())
1128    }
1129
1130    async fn debug_set_head(&self, _number: U64) -> RpcResult<()> {
1131        Ok(())
1132    }
1133
1134    async fn debug_set_trie_flush_interval(&self, _interval: String) -> RpcResult<()> {
1135        Ok(())
1136    }
1137
1138    async fn debug_standard_trace_bad_block_to_file(
1139        &self,
1140        _block: BlockNumberOrTag,
1141        _opts: Option<GethDebugTracingCallOptions>,
1142    ) -> RpcResult<()> {
1143        Ok(())
1144    }
1145
1146    async fn debug_standard_trace_block_to_file(
1147        &self,
1148        _block: BlockNumberOrTag,
1149        _opts: Option<GethDebugTracingCallOptions>,
1150    ) -> RpcResult<()> {
1151        Ok(())
1152    }
1153
1154    async fn debug_state_root_with_updates(
1155        &self,
1156        hashed_state: HashedPostState,
1157        block_id: Option<BlockId>,
1158    ) -> RpcResult<(B256, TrieUpdates)> {
1159        Self::debug_state_root_with_updates(self, hashed_state, block_id).await.map_err(Into::into)
1160    }
1161
1162    async fn debug_storage_range_at(
1163        &self,
1164        _block_hash: B256,
1165        _tx_idx: usize,
1166        _contract_address: Address,
1167        _key_start: B256,
1168        _max_result: u64,
1169    ) -> RpcResult<()> {
1170        Ok(())
1171    }
1172
1173    async fn debug_trace_bad_block(
1174        &self,
1175        block_hash: B256,
1176        opts: Option<GethDebugTracingCallOptions>,
1177    ) -> RpcResult<Vec<TraceResult>> {
1178        let _permit = self.acquire_trace_permit().await;
1179        let entry = self
1180            .inner
1181            .bad_block_store
1182            .get(block_hash)
1183            .ok_or_else(|| internal_rpc_err("bad block not found in cache"))?;
1184
1185        let evm_env = self
1186            .eth_api()
1187            .evm_config()
1188            .evm_env(entry.block.header())
1189            .map_err(RethError::other)
1190            .to_rpc_result()?;
1191
1192        let opts = opts.map(|o| o.tracing_options).unwrap_or_default();
1193        self.trace_block(entry.block.clone(), evm_env, opts).await.map_err(Into::into)
1194    }
1195}
1196
1197impl<Eth: RpcNodeCore> std::fmt::Debug for DebugApi<Eth> {
1198    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1199        f.debug_struct("DebugApi").finish_non_exhaustive()
1200    }
1201}
1202
1203impl<Eth: RpcNodeCore> Clone for DebugApi<Eth> {
1204    fn clone(&self) -> Self {
1205        Self { inner: Arc::clone(&self.inner) }
1206    }
1207}
1208
1209struct DebugApiInner<Eth: RpcNodeCore> {
1210    /// The implementation of `eth` API
1211    eth_api: Eth,
1212    // restrict the number of concurrent calls to blocking calls
1213    blocking_task_guard: BlockingTaskGuard,
1214    /// Cache for bad blocks.
1215    bad_block_store: BadBlockStore<BlockTy<Eth::Primitives>>,
1216}
1217
1218/// A bounded, deduplicating store of recently observed bad blocks.
1219#[derive(Clone, Debug)]
1220struct BadBlockStore<B: BlockTrait> {
1221    inner: Arc<RwLock<VecDeque<BadBlockEntry<B>>>>,
1222    limit: usize,
1223}
1224
1225/// A cached bad block paired with the reason it was rejected.
1226#[derive(Clone, Debug)]
1227struct BadBlockEntry<B: BlockTrait> {
1228    block: Arc<RecoveredBlock<B>>,
1229    reason: String,
1230}
1231
1232impl<B: BlockTrait> BadBlockStore<B> {
1233    /// Creates a new store with the given capacity.
1234    fn new(limit: usize) -> Self {
1235        Self { inner: Arc::new(RwLock::new(VecDeque::with_capacity(limit))), limit }
1236    }
1237
1238    /// Inserts a recovered block with its rejection reason, keeping only the most recent `limit`
1239    /// entries and deduplicating by block hash.
1240    fn insert(&self, block: RecoveredBlock<B>, reason: String) {
1241        let hash = block.hash();
1242        let mut guard = self.inner.write();
1243
1244        // skip if we already recorded this bad block , and keep original ordering
1245        if guard.iter().any(|entry| entry.block.hash() == hash) {
1246            return;
1247        }
1248        guard.push_back(BadBlockEntry { block: Arc::new(block), reason });
1249
1250        while guard.len() > self.limit {
1251            guard.pop_front();
1252        }
1253    }
1254
1255    /// Returns all cached bad block entries ordered from newest to oldest.
1256    fn all(&self) -> Vec<BadBlockEntry<B>> {
1257        let guard = self.inner.read();
1258        guard.iter().rev().cloned().collect()
1259    }
1260
1261    /// Returns the bad block entry with the given hash, if cached.
1262    fn get(&self, hash: B256) -> Option<BadBlockEntry<B>> {
1263        let guard = self.inner.read();
1264        guard.iter().find(|entry| entry.block.hash() == hash).cloned()
1265    }
1266}
1267
1268impl<B: BlockTrait> Default for BadBlockStore<B> {
1269    fn default() -> Self {
1270        Self::new(64)
1271    }
1272}