Skip to main content

reth_rpc/
debug.rs

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