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