Skip to main content

reth_rpc/
debug.rs

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