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