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