Skip to main content

reth_rpc/
debug.rs

1use alloy_consensus::{transaction::TxHashRef, BlockHeader};
2use alloy_eips::{eip2718::Encodable2718, BlockId, BlockNumberOrTag};
3use alloy_genesis::ChainConfig;
4use alloy_primitives::{hex::decode, uint, Address, Bytes, B256, U256, U64};
5use alloy_rlp::{Decodable, Encodable};
6use alloy_rpc_types::BlockTransactionsKind;
7use alloy_rpc_types_debug::ExecutionWitness;
8use alloy_rpc_types_eth::{
9    state::EvmOverrides, Account, AccountInfo, BlockError, Bundle, Index, StateContext,
10};
11use alloy_rpc_types_trace::geth::{
12    ChainBlockTraceResult, GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace,
13    TraceResult,
14};
15use evm2::evm::StateChangeSource;
16use evm2_inspectors::tracing::{DebugInspector, TransactionContext};
17use futures::Stream;
18use jsonrpsee::{core::RpcResult, PendingSubscriptionSink, SubscriptionMessage};
19use parking_lot::RwLock;
20use reth_chainspec::{ChainSpecProvider, EthereumHardforks};
21use reth_engine_primitives::ConsensusEngineEvent;
22use reth_errors::RethError;
23use reth_evm::{
24    execute::{BlockExecutor, BlockTransactionResult, Executor},
25    witness::ExecutionWitnessRecord,
26    BlockExecutionError, ConfigureEvm, DynDatabase, Evm, EvmEnv, EvmEnvFor,
27};
28use reth_primitives_traits::{
29    Block as BlockTrait, BlockBody, BlockTy, ReceiptWithBloom, RecoveredBlock,
30};
31use reth_rpc_api::DebugApiServer;
32use reth_rpc_convert::RpcTxReq;
33use reth_rpc_eth_api::{
34    helpers::{EthTransactions, TraceExt},
35    AsEthApiError, FromEthApiError, FromEvmError, RpcConvert, RpcNodeCore,
36};
37use reth_rpc_eth_types::{utils::CallManyBudget, EthApiError, StateCacheDb};
38use reth_rpc_server_types::{result::internal_rpc_err, ToRpcResult};
39use reth_storage_api::{
40    BlockIdReader, BlockReaderIdExt, HashedPostStateProvider, HeaderProvider, ProviderBlock,
41    ReceiptProviderIdExt, StateProviderFactory, StateRootProvider, StorageRootProvider,
42    TransactionVariant,
43};
44use reth_tasks::{cancel::is_cancelled, pool::BlockingTaskGuard, Runtime};
45use reth_transaction_pool::TransactionPool;
46use reth_trie_common::{
47    root::storage_root_unsorted, updates::TrieUpdates, ExecutionWitnessMode, HashedPostState,
48    HashedStorage,
49};
50use serde::{Deserialize, Serialize};
51use std::{collections::VecDeque, sync::Arc, time::Duration};
52use tokio::sync::{AcquireError, OwnedSemaphorePermit};
53use tokio_stream::StreamExt;
54
55#[cfg(feature = "js-tracer")]
56use alloy_rpc_types_trace::geth::{
57    GethDebugBuiltInTracerType, GethDebugTracerConfig, GethDebugTracerType,
58};
59#[cfg(feature = "js-tracer")]
60use evm2_inspectors::tracing::js::JsInspectorInterrupt;
61
62/// `debug` API implementation.
63///
64/// This type provides the functionality for handling `debug` related requests.
65pub struct DebugApi<Eth: RpcNodeCore> {
66    inner: Arc<DebugApiInner<Eth>>,
67}
68
69impl<Eth> DebugApi<Eth>
70where
71    Eth: RpcNodeCore,
72{
73    /// Create a new instance of the [`DebugApi`]
74    pub fn new(
75        eth_api: Eth,
76        blocking_task_guard: BlockingTaskGuard,
77        executor: &Runtime,
78        mut stream: impl Stream<Item = ConsensusEngineEvent<Eth::Primitives>> + Send + Unpin + 'static,
79    ) -> Self {
80        let bad_block_store = BadBlockStore::default();
81        let inner = Arc::new(DebugApiInner {
82            eth_api,
83            blocking_task_guard,
84            task_spawner: executor.clone(),
85            bad_block_store: bad_block_store.clone(),
86        });
87
88        // Spawn a task caching bad blocks
89        executor.spawn_task(async move {
90            while let Some(event) = stream.next().await {
91                if let ConsensusEngineEvent::InvalidBlock { block, error } = event &&
92                    let Ok(recovered) = RecoveredBlock::try_recover_sealed(*block)
93                {
94                    bad_block_store.insert(recovered, error);
95                }
96            }
97        });
98
99        Self { inner }
100    }
101
102    /// Access the underlying `Eth` API.
103    pub fn eth_api(&self) -> &Eth {
104        &self.inner.eth_api
105    }
106
107    /// Access the underlying provider.
108    pub fn provider(&self) -> &Eth::Provider {
109        self.inner.eth_api.provider()
110    }
111}
112
113// === impl DebugApi ===
114
115impl<Eth> DebugApi<Eth>
116where
117    Eth: TraceExt,
118{
119    /// Acquires a permit to execute a tracing call.
120    async fn acquire_trace_permit(&self) -> Result<OwnedSemaphorePermit, AcquireError> {
121        self.inner.blocking_task_guard.clone().acquire_owned().await
122    }
123
124    /// Traces every transaction of a block through the block executor, see
125    /// [`Call::block_executor`](reth_rpc_eth_api::helpers::Call::block_executor).
126    ///
127    /// A supplied permit stays with the worker if the waiting request times out or is dropped.
128    async fn trace_block(
129        &self,
130        block: Arc<RecoveredBlock<ProviderBlock<Eth::Provider>>>,
131        evm_env: EvmEnvFor<Eth::Evm>,
132        opts: GethDebugTracingOptions,
133        permit: Option<OwnedSemaphorePermit>,
134    ) -> Result<Vec<TraceResult>, Eth::Error> {
135        self.eth_api()
136            .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
137                let _permit = permit;
138                let mut results = Vec::with_capacity(block.body().transactions().len());
139
140                eth_api.apply_pre_execution_changes(&block, &mut db)?;
141
142                let block_env = *evm_env.block_env();
143
144                let mut transactions = block.transactions_recovered().enumerate().peekable();
145                let mut inspector = DebugInspector::new(opts).map_err(Eth::Error::from_eth_err)?;
146                let mut executor = eth_api.block_executor(&block, &mut db, evm_env)?;
147                while let Some((index, tx)) = transactions.next() {
148                    if is_cancelled() {
149                        return Err(EthApiError::InternalEthError.into())
150                    }
151                    let tx_hash = *tx.tx_hash();
152                    let tx_env = eth_api.evm_config().tx_env(tx.cloned());
153
154                    let (next_inspector, output) = executor
155                        .inspect_transaction_without_commit(tx, inspector)
156                        .map_err(Eth::Error::from_evm_err)?;
157                    inspector = next_inspector;
158                    let result = inspector
159                        .get_result(
160                            Some(TransactionContext {
161                                block_hash: Some(block.hash()),
162                                tx_hash: Some(tx_hash),
163                                tx_index: Some(index),
164                            }),
165                            &tx_env,
166                            &block_env,
167                            output.result(),
168                            executor.evm_mut().state_db_mut(),
169                        )
170                        .map_err(Eth::Error::from_eth_err)?;
171
172                    results.push(TraceResult::Success { result, tx_hash: Some(tx_hash) });
173                    if transactions.peek().is_some() {
174                        inspector.fuse().map_err(Eth::Error::from_eth_err)?;
175                        // need to apply the state changes of this transaction before executing the
176                        // next transaction
177                        executor.commit_transaction(output).map_err(Eth::Error::from_evm_err)?;
178                    }
179                }
180
181                Ok(results)
182            })
183            .await
184    }
185
186    /// Traces a block for `traceChain`, preserving transaction-level tracing failures in the
187    /// subscription result instead of failing the entire range.
188    async fn trace_chain_block(
189        &self,
190        block: Arc<RecoveredBlock<ProviderBlock<Eth::Provider>>>,
191        opts: GethDebugTracingOptions,
192    ) -> Result<Vec<Option<TraceResult>>, Eth::Error> {
193        let evm_env = self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
194
195        self.eth_api()
196            .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
197                let tx_count = block.body().transactions().len();
198                let mut results = Vec::with_capacity(tx_count);
199
200                eth_api.apply_pre_execution_changes(&block, &mut db)?;
201
202                let block_env = *evm_env.block_env();
203                let mut transactions = block.transactions_recovered().enumerate().peekable();
204                let mut inspector = match DebugInspector::new(opts) {
205                    Ok(inspector) => inspector,
206                    Err(err) => {
207                        if let Some((_, tx)) = transactions.peek() {
208                            results.push(Some(TraceResult::Error {
209                                error: err.to_string(),
210                                tx_hash: Some(*tx.tx_hash()),
211                            }));
212                        }
213                        results.resize(tx_count, None);
214                        return Ok(results)
215                    }
216                };
217                let mut executor = eth_api.block_executor(&block, &mut db, evm_env)?;
218
219                while let Some((index, tx)) = transactions.next() {
220                    let tx_hash = *tx.tx_hash();
221                    let tx_env = eth_api.evm_config().tx_env(tx.cloned());
222                    let output = match executor.inspect_transaction_without_commit(tx, inspector) {
223                        Ok((next_inspector, output)) => {
224                            inspector = next_inspector;
225                            output
226                        }
227                        Err(err) => {
228                            results.push(Some(TraceResult::Error {
229                                error: err.to_string(),
230                                tx_hash: Some(tx_hash),
231                            }));
232                            break
233                        }
234                    };
235
236                    let result = match inspector.get_result(
237                        Some(TransactionContext {
238                            block_hash: Some(block.hash()),
239                            tx_hash: Some(tx_hash),
240                            tx_index: Some(index),
241                        }),
242                        &tx_env,
243                        &block_env,
244                        output.result(),
245                        executor.evm_mut().state_db_mut(),
246                    ) {
247                        Ok(result) => result,
248                        Err(err) => {
249                            results.push(Some(TraceResult::Error {
250                                error: err.to_string(),
251                                tx_hash: Some(tx_hash),
252                            }));
253                            break
254                        }
255                    };
256
257                    results.push(Some(TraceResult::Success { result, tx_hash: Some(tx_hash) }));
258                    if let Some((_, next_tx)) = transactions.peek() {
259                        if let Err(err) = inspector.fuse() {
260                            results.push(Some(TraceResult::Error {
261                                error: err.to_string(),
262                                tx_hash: Some(*next_tx.tx_hash()),
263                            }));
264                            break
265                        }
266                        if let Err(err) = executor.commit_transaction(output) {
267                            results.push(Some(TraceResult::Error {
268                                error: err.to_string(),
269                                tx_hash: Some(*next_tx.tx_hash()),
270                            }));
271                            break
272                        }
273                    }
274                }
275
276                results.resize(tx_count, None);
277                Ok(results)
278            })
279            .await
280    }
281
282    /// Replays the given block and returns the trace of each transaction.
283    ///
284    /// This expects a rlp encoded block
285    ///
286    /// Note, the parent of this block must be present, or it will fail.
287    ///
288    /// The block is supplied by the caller, so its replay is bounded by a timeout of 30 seconds: no
289    /// transaction starts after it expires, the request then fails with
290    /// [`EthApiError::ExecutionTimedOut`].
291    /// The replay retains its tracing permit until the running transaction finishes.
292    pub async fn debug_trace_raw_block(
293        &self,
294        rlp_block: Bytes,
295        opts: GethDebugTracingOptions,
296    ) -> Result<Vec<TraceResult>, Eth::Error> {
297        let permit =
298            self.acquire_trace_permit().await.map_err(|_| EthApiError::InternalEthError)?;
299        let block: ProviderBlock<Eth::Provider> = Decodable::decode(&mut rlp_block.as_ref())
300            .map_err(BlockError::RlpDecodeRawBlock)
301            .map_err(Eth::Error::from_eth_err)?;
302
303        let evm_env = self
304            .eth_api()
305            .evm_config()
306            .evm_env(block.header())
307            .map_err(RethError::other)
308            .map_err(Eth::Error::from_eth_err)?;
309
310        // Depending on EIP-2 we need to recover the transactions differently
311        let senders =
312            if self.provider().chain_spec().is_homestead_active_at_block(block.header().number()) {
313                block.body().recover_signers()
314            } else {
315                block.body().recover_signers_unchecked()
316            }
317            .map_err(Eth::Error::from_eth_err)?;
318
319        // Dropping the replay cancels it before its next transaction.
320        tokio::time::timeout(
321            RAW_BLOCK_TRACE_TIMEOUT,
322            self.trace_block(
323                Arc::new(block.into_recovered_with_signers(senders)),
324                evm_env,
325                opts,
326                Some(permit),
327            ),
328        )
329        .await
330        .map_err(|_| {
331            Eth::Error::from_eth_err(EthApiError::ExecutionTimedOut(RAW_BLOCK_TRACE_TIMEOUT))
332        })?
333    }
334
335    /// Replays a block and returns the trace of each transaction.
336    pub async fn debug_trace_block(
337        &self,
338        block_id: BlockId,
339        opts: GethDebugTracingOptions,
340    ) -> Result<Vec<TraceResult>, Eth::Error> {
341        let block = self
342            .eth_api()
343            .recovered_block(block_id)
344            .await?
345            .ok_or(EthApiError::TracingBlockNotFound(block_id))?;
346        // Tracing requires the parent state, which does not exist for the genesis block.
347        if block.number() == 0 {
348            return Err(EthApiError::GenesisNotTraceable.into())
349        }
350        let evm_env = self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
351
352        self.trace_block(block, evm_env, opts, None).await
353    }
354
355    /// Trace the transaction according to the provided options.
356    ///
357    /// Ref: <https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers>
358    pub async fn debug_trace_transaction(
359        &self,
360        tx_hash: B256,
361        opts: GethDebugTracingOptions,
362    ) -> Result<GethTrace, Eth::Error> {
363        let (transaction, block, bal) =
364            match self.eth_api().transaction_and_block_and_maybe_bal(tx_hash).await? {
365                None => return Err(EthApiError::TracingTransactionNotFound.into()),
366                Some(res) => res,
367            };
368
369        let interrupt = JsTraceInterrupt::new(&opts)?;
370        let tracer_interrupt = interrupt.clone();
371        let trace = self.eth_api().spawn_with_state_at_block(
372            block.parent_hash(),
373            move |eth_api, mut db| {
374                // configure env for the target transaction
375                let (tx, tx_info) = transaction.split();
376                let tx_hash = *tx.tx_hash();
377
378                // index should always be available because `transaction_and_block` only
379                // returns transactions included in a block
380                let index =
381                    tx_info.index.expect("transaction_and_block only returns block transactions")
382                        as usize;
383
384                let inspector = tracer_interrupt
385                    .attach(DebugInspector::new(opts).map_err(Eth::Error::from_eth_err)?);
386                let tx_env = eth_api.evm_config().tx_env(tx);
387                let (mut inspector, res, evm_env) = eth_api.inspect_transaction_in_block(
388                    &block,
389                    &mut db,
390                    inspector,
391                    index,
392                    bal.as_deref(),
393                )?;
394                let trace = inspector
395                    .get_result(
396                        Some(TransactionContext {
397                            block_hash: Some(block.hash()),
398                            tx_index: Some(index),
399                            tx_hash: Some(tx_hash),
400                        }),
401                        &tx_env,
402                        evm_env.block_env(),
403                        &res,
404                        &mut db,
405                    )
406                    .map_err(Eth::Error::from_eth_err)?;
407
408                Ok(trace)
409            },
410        );
411        interrupt.run(trace).await
412    }
413
414    /// The `debug_traceCall` method lets you run an `eth_call` within the context of the given
415    /// block execution using the final state of parent block as the base.
416    ///
417    /// If `tx_index` is provided in opts, the call will be traced at the state after executing
418    /// transactions up to the specified index within the block (0-indexed).
419    /// If not provided, then uses the post-state (default behavior).
420    ///
421    /// Differences compare to `eth_call`:
422    ///  - `debug_traceCall` executes with __enabled__ basefee check, `eth_call` does not: <https://github.com/paradigmxyz/reth/issues/6240>
423    pub async fn debug_trace_call(
424        &self,
425        call: RpcTxReq<Eth::NetworkTypes>,
426        block_id: Option<BlockId>,
427        opts: GethDebugTracingCallOptions,
428    ) -> Result<GethTrace, Eth::Error> {
429        let at = block_id.unwrap_or_default();
430        let GethDebugTracingCallOptions {
431            tracing_options,
432            state_overrides,
433            block_overrides,
434            tx_index,
435        } = opts;
436        let overrides = EvmOverrides::new(state_overrides, block_overrides.map(Box::new));
437
438        // Check if we need to replay transactions for a specific tx_index
439        if let Some(tx_idx) = tx_index {
440            return self
441                .debug_trace_call_at_tx_index(call, at, tx_idx as usize, tracing_options, overrides)
442                .await;
443        }
444
445        let interrupt = JsTraceInterrupt::new(&tracing_options)?;
446        let tracer_interrupt = interrupt.clone();
447        let this = self.clone();
448        let trace =
449            self.eth_api().spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| {
450                let inspector = tracer_interrupt.attach(
451                    DebugInspector::new(tracing_options).map_err(Eth::Error::from_eth_err)?,
452                );
453                let (mut inspector, res) =
454                    this.eth_api().inspect(&mut *db, evm_env.clone(), &tx_env, inspector)?;
455                let trace = inspector
456                    .get_result(None, &tx_env, evm_env.block_env(), &res, db)
457                    .map_err(Eth::Error::from_eth_err)?;
458                Ok(trace)
459            });
460        interrupt.run(trace).await.map_err(|err| match err.as_err() {
461            Some(EthApiError::HeaderNotFound(id)) if *id == at => {
462                // Unknown blocks use -32000: https://github.com/ethereum/execution-apis/pull/855
463                EthApiError::TracingBlockNotFound(at).into()
464            }
465            _ => err,
466        })
467    }
468
469    /// Helper method to execute `debug_trace_call` at a specific transaction index within a block.
470    /// This replays transactions up to the specified index, then executes the trace call in that
471    /// state.
472    async fn debug_trace_call_at_tx_index(
473        &self,
474        call: RpcTxReq<Eth::NetworkTypes>,
475        block_id: BlockId,
476        tx_index: usize,
477        tracing_options: GethDebugTracingOptions,
478        overrides: EvmOverrides,
479    ) -> Result<GethTrace, Eth::Error> {
480        // Get the target block to check transaction count
481        let (block, bal) = self
482            .eth_api()
483            .recovered_block_and_maybe_bal(block_id)
484            .await?
485            .ok_or(EthApiError::TracingBlockNotFound(block_id))?;
486
487        if tx_index >= block.transaction_count() {
488            // tx_index out of bounds
489            return Err(EthApiError::InvalidParams(format!(
490                "tx_index {} out of bounds for block with {} transactions",
491                tx_index,
492                block.transaction_count()
493            ))
494            .into())
495        }
496
497        // state overrides commit changes to the database, which an attached BAL would take read
498        // precedence over, so only position via BAL if there are none
499        let bal = bal.filter(|_| !overrides.has_state());
500
501        let evm_env = self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
502
503        let interrupt = JsTraceInterrupt::new(&tracing_options)?;
504        let tracer_interrupt = interrupt.clone();
505        let trace = self.eth_api().spawn_with_state_at_block(
506            block.parent_hash(),
507            move |eth_api, mut db| {
508                // 1. position the state before the transaction at the index
509                eth_api.replay_block_until(&mut db, &block, tx_index, bal.as_deref())?;
510
511                // 2. now execute the trace call on this state
512                let (evm_env, tx_env) =
513                    eth_api.prepare_call_env(evm_env, call, &mut db, overrides)?;
514
515                let inspector = tracer_interrupt.attach(
516                    DebugInspector::new(tracing_options).map_err(Eth::Error::from_eth_err)?,
517                );
518                let (mut inspector, res) =
519                    eth_api.inspect(&mut db, evm_env.clone(), &tx_env, inspector)?;
520                let trace = inspector
521                    .get_result(None, &tx_env, evm_env.block_env(), &res, &mut db)
522                    .map_err(Eth::Error::from_eth_err)?;
523
524                Ok(trace)
525            },
526        );
527        interrupt.run(trace).await
528    }
529
530    /// The `debug_traceCallMany` method lets you run an `eth_callMany` within the context of the
531    /// given block execution using the first n transactions in the given block as base.
532    /// Each following bundle increments block number by 1 and block timestamp by 12 seconds
533    pub async fn debug_trace_call_many(
534        &self,
535        bundles: Vec<Bundle<RpcTxReq<Eth::NetworkTypes>>>,
536        state_context: Option<StateContext>,
537        opts: Option<GethDebugTracingCallOptions>,
538    ) -> Result<Vec<Vec<GethTrace>>, Eth::Error> {
539        if bundles.is_empty() {
540            return Err(EthApiError::InvalidParams(String::from("bundles are empty.")).into())
541        }
542        let mut budget = CallManyBudget::for_bundles(&bundles, self.eth_api().call_gas_limit())?;
543
544        let StateContext { transaction_index, block_number } = state_context.unwrap_or_default();
545        let transaction_index = transaction_index.unwrap_or_default();
546
547        let target_block = block_number.unwrap_or_default();
548        let block = self
549            .eth_api()
550            .recovered_block(target_block)
551            .await?
552            .ok_or(EthApiError::HeaderNotFound(target_block))?;
553        let mut evm_env =
554            self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
555
556        let opts = opts.unwrap_or_default();
557        let GethDebugTracingCallOptions { tracing_options, mut state_overrides, .. } = opts;
558
559        // we're essentially replaying the transactions in the block here, hence we need the state
560        // that points to the beginning of the block, which is the state at the parent block
561        let mut at = block.parent_hash();
562        let mut replay_block_txs = true;
563
564        // if a transaction index is provided, we need to replay the transactions until the index
565        let num_txs =
566            transaction_index.index().unwrap_or_else(|| block.body().transactions().len());
567        // but if all transactions are to be replayed, we can use the state at the block itself
568        // this works with the exception of the PENDING block, because its state might not exist if
569        // built locally
570        if !target_block.is_pending() && num_txs == block.body().transactions().len() {
571            at = block.hash();
572            replay_block_txs = false;
573        }
574
575        self.eth_api()
576            .spawn_with_state_at_block(at, move |eth_api, mut db| {
577                // the outer vec for the bundles
578                let mut all_bundles = Vec::with_capacity(bundles.len());
579
580                if replay_block_txs {
581                    // only need to replay the transactions in the block if not all transactions are
582                    // to be replayed
583                    // Execute all transactions until index. No BAL positioning here: bundle
584                    // transactions commit state on top, and an attached BAL would take read
585                    // precedence over the committed changes
586                    eth_api.replay_block_until(&mut db, &block, num_txs, None)?;
587                }
588
589                // Trace all bundles
590                let mut bundles = bundles.into_iter().peekable();
591                let mut inspector = DebugInspector::new(tracing_options.clone())
592                    .map_err(Eth::Error::from_eth_err)?;
593                while let Some(bundle) = bundles.next() {
594                    let mut results = Vec::with_capacity(bundle.transactions.len());
595                    let Bundle { transactions, block_override } = bundle;
596
597                    let block_overrides = block_override.map(Box::new);
598
599                    let mut transactions = transactions.into_iter().peekable();
600                    while let Some(tx) = transactions.next() {
601                        if is_cancelled() {
602                            return Err(EthApiError::InternalEthError.into())
603                        }
604                        // apply state overrides only once, before the first transaction
605                        let state_overrides = state_overrides.take();
606                        let overrides = EvmOverrides::new(state_overrides, block_overrides.clone());
607
608                        let (evm_env, mut tx_env) =
609                            eth_api.prepare_call_env(evm_env.clone(), tx, &mut db, overrides)?;
610                        budget.admit(&mut tx_env)?;
611
612                        let (next_inspector, res) = eth_api
613                            .inspect(&mut db, evm_env.clone(), &tx_env, inspector)
614                            .map_err(|err| budget.map_call_err(err))?;
615                        budget.charge(&res.result)?;
616                        inspector = next_inspector;
617                        let trace = inspector
618                            .get_result(None, &tx_env, evm_env.block_env(), &res, &mut db)
619                            .map_err(Eth::Error::from_eth_err)?;
620
621                        // If there is more transactions, commit the database
622                        // If there is no transactions, but more bundles, commit to the database too
623                        if transactions.peek().is_some() || bundles.peek().is_some() {
624                            inspector.fuse().map_err(Eth::Error::from_eth_err)?;
625                            db.commit_source(&res.pending_state);
626                        }
627                        results.push(trace);
628                    }
629                    // Increment block_env number and timestamp for the next bundle
630                    evm_env.block_env_mut().number += uint!(1_U256);
631                    evm_env.block_env_mut().timestamp += uint!(12_U256);
632
633                    all_bundles.push(results);
634                }
635                Ok(all_bundles)
636            })
637            .await
638    }
639
640    /// Generates an execution witness for the given block hash. see
641    /// [`Self::debug_execution_witness`] for more info.
642    pub async fn debug_execution_witness_by_block_hash(
643        &self,
644        hash: B256,
645        mode: Option<ExecutionWitnessMode>,
646    ) -> Result<ExecutionWitness, Eth::Error> {
647        let this = self.clone();
648        let block = this
649            .eth_api()
650            .recovered_block(hash.into())
651            .await?
652            .ok_or(EthApiError::HeaderNotFound(hash.into()))?;
653
654        self.debug_execution_witness_for_block(block, mode.unwrap_or_default()).await
655    }
656
657    /// The `debug_executionWitness` method allows for re-execution of a block with the purpose of
658    /// generating an execution witness. The witness comprises of a map of all hashed trie nodes to
659    /// their preimages that were required during the execution of the block, including during state
660    /// root recomputation.
661    pub async fn debug_execution_witness(
662        &self,
663        block_id: BlockId,
664        mode: Option<ExecutionWitnessMode>,
665    ) -> Result<ExecutionWitness, Eth::Error> {
666        let this = self.clone();
667        let block = this
668            .eth_api()
669            .recovered_block(block_id)
670            .await?
671            .ok_or(EthApiError::HeaderNotFound(block_id))?;
672
673        self.debug_execution_witness_for_block(block, mode.unwrap_or_default()).await
674    }
675
676    /// Generates an execution witness, using the given recovered block.
677    pub async fn debug_execution_witness_for_block(
678        &self,
679        block: Arc<RecoveredBlock<ProviderBlock<Eth::Provider>>>,
680        mode: ExecutionWitnessMode,
681    ) -> Result<ExecutionWitness, Eth::Error> {
682        let block_number = block.header().number();
683        self.eth_api()
684            .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
685                let output = eth_api
686                    .evm_config()
687                    .executor(&mut db)
688                    .execute(&block)
689                    .map_err(|err| EthApiError::Internal(err.into()))?;
690                let codes = ExecutionWitnessRecord::commit_execution(&mut db, &output.state);
691
692                Ok(ExecutionWitnessRecord::new(&db)
693                    .with_canonical_codes(codes)
694                    .into_execution_witness(
695                        &db.db.inner().0 .0,
696                        eth_api.provider(),
697                        block_number,
698                        mode,
699                    )
700                    .map_err(EthApiError::from)?)
701            })
702            .await
703    }
704
705    /// Returns account information, including the storage root, after replaying the block through
706    /// the transaction at the given index.
707    pub async fn debug_account_at(
708        &self,
709        block_id: BlockId,
710        tx_index: Index,
711        address: Address,
712    ) -> Result<Option<Account>, Eth::Error> {
713        self.replay_block_until(block_id, tx_index, move |db| Self::account(db, address))
714            .await
715            .map(Option::flatten)
716    }
717
718    /// Returns account information after replaying the block through the transaction at the given
719    /// index.
720    pub async fn debug_account_info_at(
721        &self,
722        block_id: BlockId,
723        tx_index: Index,
724        address: Address,
725    ) -> Result<Option<AccountInfo>, Eth::Error> {
726        self.replay_block_until(block_id, tx_index, move |db| Self::account_info(db, address)).await
727    }
728
729    /// Replays a block through the transaction at the given index and calls `f` with the resulting
730    /// state.
731    async fn replay_block_until<F, R>(
732        &self,
733        block_id: BlockId,
734        tx_index: Index,
735        f: F,
736    ) -> Result<Option<R>, Eth::Error>
737    where
738        F: FnOnce(&mut StateCacheDb) -> Result<R, Eth::Error> + Send + 'static,
739        R: Send + 'static,
740    {
741        let block = self
742            .eth_api()
743            .recovered_block(block_id)
744            .await?
745            .ok_or(EthApiError::HeaderNotFound(block_id))?;
746        let tx_index = usize::from(tx_index);
747        let transaction_count = block.transaction_count();
748        if tx_index >= transaction_count {
749            return Err(EthApiError::InvalidParams(format!(
750                "tx_index {tx_index} out of bounds for block with {transaction_count} transactions"
751            ))
752            .into())
753        }
754
755        self.eth_api()
756            .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
757                eth_api.replay_block_until(&mut db, &block, tx_index + 1, None)?;
758
759                f(&mut db)
760            })
761            .await
762            .map(Some)
763    }
764
765    /// Retrieves the account's balance, nonce, code hash, and storage root from the given state.
766    fn account(db: &mut StateCacheDb, address: Address) -> Result<Option<Account>, Eth::Error> {
767        let account = db.get_account(&address).map_err(Eth::Error::from_eth_err)?;
768        let Some(account) = account else { return Ok(None) };
769
770        let storage = db.cache.storage.get(&address);
771        let hashed_storage = storage.map_or_else(HashedStorage::default, |s| {
772            HashedStorage::from_plain_storage(s.slots.iter())
773        });
774        let storage_root = if storage.is_some_and(|s| s.wiped) {
775            storage_root_unsorted(
776                hashed_storage.storage.into_iter().filter(|(_, value)| !value.is_zero()),
777            )
778        } else {
779            db.db.inner().storage_root(address, hashed_storage).map_err(Eth::Error::from_eth_err)?
780        };
781
782        Ok(Some(reth_primitives_traits::Account::from(&account).into_trie_account(storage_root)))
783    }
784
785    /// Retrieves the account's balance, nonce, and code from the given state.
786    #[allow(clippy::needless_update)]
787    fn account_info(db: &mut StateCacheDb, address: Address) -> Result<AccountInfo, Eth::Error> {
788        let account =
789            db.get_account(&address).map_err(Eth::Error::from_eth_err)?.unwrap_or_default();
790        let code = if account.is_empty_code_hash() {
791            Default::default()
792        } else if let Some(code) = account.code {
793            code.original_bytes()
794        } else {
795            db.get_code_by_hash(&account.code_hash)
796                .map_err(Eth::Error::from_eth_err)?
797                .original_bytes()
798        };
799
800        Ok(AccountInfo {
801            balance: account.balance,
802            nonce: account.nonce,
803            code,
804            #[cfg(feature = "account-ext")]
805            extension: reth_primitives_traits::AccountExtension::from_shared(
806                account.extension.into_shared(),
807            ),
808            ..Default::default()
809        })
810    }
811
812    /// Returns the code associated with a given hash at the specified block ID. If no code is
813    /// found, it returns None. If no block ID is provided, it defaults to the latest block.
814    pub async fn debug_code_by_hash(
815        &self,
816        hash: B256,
817        block_id: Option<BlockId>,
818    ) -> Result<Option<Bytes>, Eth::Error> {
819        Ok(self
820            .provider()
821            .state_by_block_id(block_id.unwrap_or_default())
822            .map_err(Eth::Error::from_eth_err)?
823            .bytecode_by_hash(&hash)
824            .map_err(Eth::Error::from_eth_err)?
825            .map(|b| b.original_bytes()))
826    }
827
828    /// Returns the state root of the `HashedPostState` on top of the state for the given block with
829    /// trie updates.
830    async fn debug_state_root_with_updates(
831        &self,
832        hashed_state: HashedPostState,
833        block_id: Option<BlockId>,
834    ) -> Result<(B256, TrieUpdates), Eth::Error> {
835        self.inner
836            .eth_api
837            .spawn_blocking_io(move |this| {
838                let state = this
839                    .provider()
840                    .state_by_block_id(block_id.unwrap_or_default())
841                    .map_err(Eth::Error::from_eth_err)?;
842                state.state_root_with_updates(hashed_state).map_err(Eth::Error::from_eth_err)
843            })
844            .await
845    }
846
847    /// Executes a block and returns the state root after each transaction.
848    ///
849    /// The block is either canonical or a bad block the engine rejected. Execution stops at the
850    /// first transaction that is invalid on top of its predecessors, so the result for a bad block
851    /// holds the roots before that transaction.
852    pub async fn intermediate_roots(&self, block_hash: B256) -> Result<Vec<B256>, Eth::Error> {
853        let block = match self.eth_api().recovered_block(block_hash.into()).await? {
854            Some(block) => block,
855            None => {
856                self.inner
857                    .bad_block_store
858                    .get(block_hash)
859                    .ok_or(EthApiError::HeaderNotFound(block_hash.into()))?
860                    .block
861            }
862        };
863        let evm_env = self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
864
865        self.eth_api()
866            .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
867                let tx_count = block.body().transactions().len();
868                let mut state = eth_api.apply_pre_execution_changes(&block, &mut db)?;
869
870                // the executor borrows the database, so collect each transaction's changes first
871                // and compute the roots once it is dropped
872                let mut transaction_states = Vec::with_capacity(tx_count);
873                let mut executor = eth_api.block_executor(&block, &mut db, evm_env)?;
874                for tx in block.transactions_recovered() {
875                    if is_cancelled() {
876                        return Err(EthApiError::InternalEthError.into())
877                    }
878                    let output = match executor.execute_transaction_without_commit(tx) {
879                        Ok(output) => output,
880                        Err(BlockExecutionError::Validation(_)) => break,
881                        Err(err) => return Err(Eth::Error::from_evm_err(err)),
882                    };
883                    let mut changes = reth_execution_types::TransactionChanges::default();
884                    let Ok(()) = output.result().pending_state.visit(&mut changes);
885                    let mut transaction_state = reth_execution_types::BlockState::default();
886                    transaction_state.commit(&changes);
887                    transaction_states.push(transaction_state.into_bundle());
888                    executor.commit_transaction(output).map_err(Eth::Error::from_evm_err)?;
889                }
890                drop(executor);
891
892                let mut roots = Vec::with_capacity(tx_count);
893                for transaction_state in transaction_states {
894                    if is_cancelled() {
895                        return Err(EthApiError::InternalEthError.into())
896                    }
897                    state.extend(transaction_state);
898                    let hashed_state = db
899                        .db
900                        .inner()
901                        .hashed_post_state(&state)
902                        .map_err(Eth::Error::from_eth_err)?;
903                    let root =
904                        db.db.inner().state_root(hashed_state).map_err(Eth::Error::from_eth_err)?;
905                    roots.push(root);
906                }
907
908                Ok(roots)
909            })
910            .await
911    }
912}
913
914impl<Eth> DebugApiServer<RpcTxReq<Eth::NetworkTypes>> for DebugApi<Eth>
915where
916    Eth: EthTransactions + TraceExt,
917{
918    /// Handler for `debug_getRawHeader`
919    async fn raw_header(&self, block_id: BlockId) -> RpcResult<Bytes> {
920        let header = match block_id {
921            BlockId::Hash(hash) => self.provider().header(hash.into()).to_rpc_result()?,
922            BlockId::Number(number_or_tag) => {
923                let number = self
924                    .provider()
925                    .convert_block_number(number_or_tag)
926                    .to_rpc_result()?
927                    .ok_or(EthApiError::HeaderNotFound(block_id))?;
928                self.provider().header_by_number(number).to_rpc_result()?
929            }
930        }
931        .ok_or(EthApiError::HeaderNotFound(block_id))?;
932
933        let mut res = Vec::new();
934        header.encode(&mut res);
935        Ok(res.into())
936    }
937
938    /// Handler for `debug_getRawBlock`
939    async fn raw_block(&self, block_id: BlockId) -> RpcResult<Bytes> {
940        let block = self
941            .provider()
942            .block_by_id(block_id)
943            .to_rpc_result()?
944            .ok_or(EthApiError::HeaderNotFound(block_id))?;
945        let mut res = Vec::new();
946        block.encode(&mut res);
947        Ok(res.into())
948    }
949
950    /// Handler for `debug_getRawBlockAccessList`
951    async fn raw_block_access_list(&self, block_id: BlockId) -> RpcResult<Bytes> {
952        self.eth_api()
953            .get_raw_block_access_list(block_id)
954            .await
955            .map_err(Into::into)?
956            .ok_or_else(|| EthApiError::HeaderNotFound(block_id).into())
957    }
958
959    /// Handler for `debug_getRawTransaction`
960    ///
961    /// If this is a pooled EIP-4844 transaction, the blob sidecar is included.
962    ///
963    /// Returns the bytes of the transaction for the given hash.
964    async fn raw_transaction(&self, hash: B256) -> RpcResult<Option<Bytes>> {
965        self.eth_api().raw_transaction_by_hash(hash).await.map_err(Into::into)
966    }
967
968    /// Handler for `debug_getRawTransactions`
969    /// Returns the bytes of the transaction for the given hash.
970    async fn raw_transactions(&self, block_id: BlockId) -> RpcResult<Vec<Bytes>> {
971        let block: RecoveredBlock<BlockTy<Eth::Primitives>> = self
972            .provider()
973            .block_with_senders_by_id(block_id, TransactionVariant::NoHash)
974            .to_rpc_result()?
975            .unwrap_or_default();
976        Ok(block.into_transactions_recovered().map(|tx| tx.encoded_2718().into()).collect())
977    }
978
979    /// Handler for `debug_getRawReceipts`
980    async fn raw_receipts(&self, block_id: BlockId) -> RpcResult<Vec<Bytes>> {
981        Ok(self
982            .provider()
983            .receipts_by_block_id(block_id)
984            .to_rpc_result()?
985            .ok_or(EthApiError::HeaderNotFound(block_id))?
986            .into_iter()
987            .map(|receipt| ReceiptWithBloom::from(receipt).encoded_2718().into())
988            .collect())
989    }
990
991    /// Handler for `debug_getBadBlocks`
992    async fn bad_blocks(&self) -> RpcResult<Vec<serde_json::Value>> {
993        let entries = self.inner.bad_block_store.all();
994        let mut bad_blocks = Vec::with_capacity(entries.len());
995
996        #[derive(Serialize, Deserialize)]
997        struct BadBlockSerde<T> {
998            block: T,
999            hash: B256,
1000            rlp: Bytes,
1001            reason: String,
1002        }
1003
1004        for entry in entries {
1005            let rlp = Bytes::from(alloy_rlp::encode(entry.block.sealed_block()));
1006            let hash = entry.block.hash();
1007
1008            let block = entry
1009                .block
1010                .clone_into_rpc_block(
1011                    BlockTransactionsKind::Full,
1012                    |tx, tx_info| self.eth_api().converter().fill(tx, tx_info),
1013                    |header, block_size| {
1014                        self.eth_api().converter().convert_header(header, Some(block_size))
1015                    },
1016                )
1017                .map_err(|err| Eth::Error::from(err).into())?;
1018
1019            let bad_block =
1020                serde_json::to_value(BadBlockSerde { block, hash, rlp, reason: entry.reason })
1021                    .map_err(|err| EthApiError::other(internal_rpc_err(err.to_string())))?;
1022
1023            bad_blocks.push(bad_block);
1024        }
1025
1026        Ok(bad_blocks)
1027    }
1028
1029    /// Handler for `debug_clearTxpool`
1030    async fn debug_clear_txpool(&self) -> RpcResult<()> {
1031        let pool = self.eth_api().pool();
1032        let all_hashes = pool.all_transaction_hashes();
1033        let _ = pool.remove_transactions(all_hashes);
1034        Ok(())
1035    }
1036
1037    /// Handler for `debug_subscribe("traceChain", ...)`.
1038    async fn debug_subscribe(
1039        &self,
1040        pending: PendingSubscriptionSink,
1041        subscription: String,
1042        start_exclusive: BlockNumberOrTag,
1043        end_inclusive: BlockNumberOrTag,
1044        opts: Option<GethDebugTracingOptions>,
1045    ) -> jsonrpsee::core::SubscriptionResult {
1046        if subscription != "traceChain" {
1047            pending
1048                .reject(EthApiError::InvalidParams(format!(
1049                    "unsupported debug subscription: {subscription}"
1050                )))
1051                .await;
1052            return Ok(())
1053        }
1054
1055        let start_id = BlockId::Number(start_exclusive);
1056        let start = match self.eth_api().recovered_block(start_id).await {
1057            Ok(Some(block)) => block,
1058            Ok(None) => {
1059                pending.reject(EthApiError::TracingBlockNotFound(start_id)).await;
1060                return Ok(())
1061            }
1062            Err(err) => {
1063                pending.reject(err).await;
1064                return Ok(())
1065            }
1066        };
1067        let end_id = BlockId::Number(end_inclusive);
1068        let end = match self.eth_api().recovered_block(end_id).await {
1069            Ok(Some(block)) => block,
1070            Ok(None) => {
1071                pending.reject(EthApiError::TracingBlockNotFound(end_id)).await;
1072                return Ok(())
1073            }
1074            Err(err) => {
1075                pending.reject(err).await;
1076                return Ok(())
1077            }
1078        };
1079
1080        if start.number() >= end.number() {
1081            pending
1082                .reject(EthApiError::InvalidParams(format!(
1083                    "end block (#{}) needs to come after start block (#{})",
1084                    end.number(),
1085                    start.number()
1086                )))
1087                .await;
1088            return Ok(())
1089        }
1090
1091        let sink = pending.accept().await?;
1092        let this = self.clone();
1093        let task_spawner = self.inner.task_spawner.clone();
1094        task_spawner.spawn_task(async move {
1095            let end_number = end.number();
1096            let opts = opts.unwrap_or_default();
1097
1098            for number in (start.number() + 1)..=end_number {
1099                if sink.is_closed() {
1100                    break
1101                }
1102
1103                let block_id = BlockId::number(number);
1104                let block = match this.eth_api().recovered_block(block_id).await {
1105                    Ok(Some(block)) => block,
1106                    Ok(None) => {
1107                        tracing::warn!(target: "rpc::debug", %number, "Chain tracing block not found");
1108                        break
1109                    }
1110                    Err(err) => {
1111                        tracing::warn!(target: "rpc::debug", %number, %err, "Failed to load chain tracing block");
1112                        break
1113                    }
1114                };
1115                let permit = tokio::select! {
1116                    _ = sink.closed() => break,
1117                    permit = this.acquire_trace_permit() => match permit {
1118                        Ok(permit) => permit,
1119                        Err(err) => {
1120                            tracing::debug!(target: "rpc::debug", %err, "Failed to acquire trace permit");
1121                            break
1122                        }
1123                    }
1124                };
1125                if sink.is_closed() {
1126                    break
1127                }
1128                let traces = match this.trace_chain_block(block.clone(), opts.clone()).await {
1129                    Ok(traces) => traces,
1130                    Err(err) => {
1131                        tracing::warn!(target: "rpc::debug", %number, %err, "Failed to trace chain block");
1132                        break
1133                    }
1134                };
1135                drop(permit);
1136
1137                if traces.is_empty() && number != end_number {
1138                    continue
1139                }
1140                let result = ChainBlockTraceResult {
1141                    block: U256::from(number),
1142                    hash: block.hash(),
1143                    traces,
1144                };
1145                let message = match SubscriptionMessage::new(
1146                    sink.method_name(),
1147                    sink.subscription_id(),
1148                    &result,
1149                ) {
1150                    Ok(message) => message,
1151                    Err(err) => {
1152                        tracing::warn!(target: "rpc::debug", %number, %err, "Failed to serialize chain trace");
1153                        break
1154                    }
1155                };
1156                if sink.send(message).await.is_err() {
1157                    break
1158                }
1159            }
1160
1161            // Dropping jsonrpsee's final sink unregisters the subscription without notifying the
1162            // client that this finite stream completed. Retain it so the subscription remains
1163            // explicitly unsubscribable until the client unsubscribes or disconnects.
1164            sink.closed().await;
1165        });
1166
1167        Ok(())
1168    }
1169
1170    /// Handler for `debug_traceBlock`
1171    async fn debug_trace_block(
1172        &self,
1173        rlp_block: Bytes,
1174        opts: Option<GethDebugTracingOptions>,
1175    ) -> RpcResult<Vec<TraceResult>> {
1176        Self::debug_trace_raw_block(self, rlp_block, opts.unwrap_or_default())
1177            .await
1178            .map_err(Into::into)
1179    }
1180
1181    /// Handler for `debug_traceBlockByHash`
1182    async fn debug_trace_block_by_hash(
1183        &self,
1184        block: B256,
1185        opts: Option<GethDebugTracingOptions>,
1186    ) -> RpcResult<Vec<TraceResult>> {
1187        let _permit = self.acquire_trace_permit().await;
1188        Self::debug_trace_block(self, block.into(), opts.unwrap_or_default())
1189            .await
1190            .map_err(Into::into)
1191    }
1192
1193    /// Handler for `debug_traceBlockByNumber`
1194    async fn debug_trace_block_by_number(
1195        &self,
1196        block: BlockNumberOrTag,
1197        opts: Option<GethDebugTracingOptions>,
1198    ) -> RpcResult<Vec<TraceResult>> {
1199        let _permit = self.acquire_trace_permit().await;
1200        Self::debug_trace_block(self, block.into(), opts.unwrap_or_default())
1201            .await
1202            .map_err(Into::into)
1203    }
1204
1205    /// Handler for `debug_traceTransaction`
1206    async fn debug_trace_transaction(
1207        &self,
1208        tx_hash: B256,
1209        opts: Option<GethDebugTracingOptions>,
1210    ) -> RpcResult<GethTrace> {
1211        let _permit = self.acquire_trace_permit().await;
1212        Self::debug_trace_transaction(self, tx_hash, opts.unwrap_or_default())
1213            .await
1214            .map_err(Into::into)
1215    }
1216
1217    /// Handler for `debug_traceCall`
1218    async fn debug_trace_call(
1219        &self,
1220        request: RpcTxReq<Eth::NetworkTypes>,
1221        block_id: Option<BlockId>,
1222        opts: Option<GethDebugTracingCallOptions>,
1223    ) -> RpcResult<GethTrace> {
1224        let _permit = self.acquire_trace_permit().await;
1225        Self::debug_trace_call(self, request, block_id, opts.unwrap_or_default())
1226            .await
1227            .map_err(Into::into)
1228    }
1229
1230    async fn debug_trace_call_many(
1231        &self,
1232        bundles: Vec<Bundle<RpcTxReq<Eth::NetworkTypes>>>,
1233        state_context: Option<StateContext>,
1234        opts: Option<GethDebugTracingCallOptions>,
1235    ) -> RpcResult<Vec<Vec<GethTrace>>> {
1236        let _permit = self.acquire_trace_permit().await;
1237        Self::debug_trace_call_many(self, bundles, state_context, opts).await.map_err(Into::into)
1238    }
1239
1240    /// Handler for `debug_executionWitness`
1241    async fn debug_execution_witness(
1242        &self,
1243        block: BlockId,
1244        mode: Option<ExecutionWitnessMode>,
1245    ) -> RpcResult<ExecutionWitness> {
1246        let _permit = self.acquire_trace_permit().await;
1247        Self::debug_execution_witness(self, block, mode).await.map_err(Into::into)
1248    }
1249
1250    /// Handler for `debug_executionWitnessByBlockHash`
1251    async fn debug_execution_witness_by_block_hash(
1252        &self,
1253        hash: B256,
1254        mode: Option<ExecutionWitnessMode>,
1255    ) -> RpcResult<ExecutionWitness> {
1256        let _permit = self.acquire_trace_permit().await;
1257        Self::debug_execution_witness_by_block_hash(self, hash, mode).await.map_err(Into::into)
1258    }
1259
1260    /// Handler for `debug_accountAt`
1261    async fn debug_account_at(
1262        &self,
1263        block_id: BlockId,
1264        tx_index: Index,
1265        address: Address,
1266    ) -> RpcResult<Option<Account>> {
1267        let _permit = self.acquire_trace_permit().await;
1268        Self::debug_account_at(self, block_id, tx_index, address).await.map_err(Into::into)
1269    }
1270
1271    /// Handler for `debug_accountInfoAt`
1272    async fn debug_account_info_at(
1273        &self,
1274        block_id: BlockId,
1275        tx_index: Index,
1276        address: Address,
1277    ) -> RpcResult<Option<AccountInfo>> {
1278        let _permit = self.acquire_trace_permit().await;
1279        Self::debug_account_info_at(self, block_id, tx_index, address).await.map_err(Into::into)
1280    }
1281
1282    async fn debug_account_range(
1283        &self,
1284        _block_number: BlockNumberOrTag,
1285        _start: Bytes,
1286        _max_results: u64,
1287        _nocode: bool,
1288        _nostorage: bool,
1289        _incompletes: bool,
1290    ) -> RpcResult<()> {
1291        Ok(())
1292    }
1293
1294    async fn debug_chaindb_compact(&self) -> RpcResult<()> {
1295        Ok(())
1296    }
1297
1298    async fn debug_chain_config(&self) -> RpcResult<ChainConfig> {
1299        Ok(crate::chain_config::chain_config(&*self.provider().chain_spec()))
1300    }
1301
1302    async fn debug_chaindb_property(&self, _property: String) -> RpcResult<()> {
1303        Ok(())
1304    }
1305
1306    async fn debug_code_by_hash(
1307        &self,
1308        hash: B256,
1309        block_id: Option<BlockId>,
1310    ) -> RpcResult<Option<Bytes>> {
1311        Self::debug_code_by_hash(self, hash, block_id).await.map_err(Into::into)
1312    }
1313
1314    async fn debug_db_ancient(&self, _kind: String, _number: u64) -> RpcResult<()> {
1315        Ok(())
1316    }
1317
1318    async fn debug_db_ancients(&self) -> RpcResult<()> {
1319        Ok(())
1320    }
1321
1322    /// `debug_db_get` - database key lookup
1323    ///
1324    /// Currently supported:
1325    /// * Contract bytecode associated with a code hash. The key format is: `<0x63><code_hash>`
1326    ///     * Prefix byte: 0x63 (required)
1327    ///     * Code hash: 32 bytes
1328    ///
1329    ///   Must be provided as either:
1330    ///     * Hex string: "0x63..." (66 hex characters after 0x)
1331    ///     * Raw byte string: raw byte string (33 bytes)
1332    ///
1333    ///   See Geth impl: <https://github.com/ethereum/go-ethereum/blob/737ffd1bf0cbee378d0111a5b17ae4724fb2216c/core/rawdb/schema.go#L120>
1334    async fn debug_db_get(&self, key: String) -> RpcResult<Option<Bytes>> {
1335        let key_bytes = if key.starts_with("0x") {
1336            decode(&key).map_err(|_| EthApiError::InvalidParams("Invalid hex key".to_string()))?
1337        } else {
1338            key.into_bytes()
1339        };
1340
1341        if key_bytes.len() != 33 {
1342            return Err(EthApiError::InvalidParams(format!(
1343                "Key must be 33 bytes, got {}",
1344                key_bytes.len()
1345            ))
1346            .into());
1347        }
1348        if key_bytes[0] != 0x63 {
1349            return Err(EthApiError::InvalidParams("Key prefix must be 0x63".to_string()).into());
1350        }
1351
1352        let code_hash = B256::from_slice(&key_bytes[1..33]);
1353
1354        // No block ID is provided, so it defaults to the latest block
1355        self.debug_code_by_hash(code_hash, None).await.map_err(Into::into)
1356    }
1357
1358    async fn debug_dump_block(&self, _number: BlockId) -> RpcResult<()> {
1359        Ok(())
1360    }
1361
1362    async fn debug_free_os_memory(&self) -> RpcResult<()> {
1363        Ok(())
1364    }
1365
1366    async fn debug_gc_stats(&self) -> RpcResult<()> {
1367        Ok(())
1368    }
1369
1370    async fn debug_get_accessible_state(
1371        &self,
1372        _from: BlockNumberOrTag,
1373        _to: BlockNumberOrTag,
1374    ) -> RpcResult<()> {
1375        Ok(())
1376    }
1377
1378    async fn debug_get_modified_accounts_by_hash(
1379        &self,
1380        _start_hash: B256,
1381        _end_hash: B256,
1382    ) -> RpcResult<()> {
1383        Ok(())
1384    }
1385
1386    async fn debug_get_modified_accounts_by_number(
1387        &self,
1388        _start_number: u64,
1389        _end_number: u64,
1390    ) -> RpcResult<()> {
1391        Ok(())
1392    }
1393
1394    async fn debug_intermediate_roots(
1395        &self,
1396        block_hash: B256,
1397        _opts: Option<GethDebugTracingCallOptions>,
1398    ) -> RpcResult<Vec<B256>> {
1399        let _permit = self.acquire_trace_permit().await;
1400        self.intermediate_roots(block_hash).await.map_err(Into::into)
1401    }
1402
1403    async fn debug_mem_stats(&self) -> RpcResult<()> {
1404        Ok(())
1405    }
1406
1407    async fn debug_preimage(&self, _hash: B256) -> RpcResult<()> {
1408        Ok(())
1409    }
1410
1411    async fn debug_print_block(&self, _number: u64) -> RpcResult<()> {
1412        Ok(())
1413    }
1414
1415    async fn debug_seed_hash(&self, _number: u64) -> RpcResult<B256> {
1416        Ok(Default::default())
1417    }
1418
1419    async fn debug_set_gc_percent(&self, _v: i32) -> RpcResult<()> {
1420        Ok(())
1421    }
1422
1423    async fn debug_set_head(&self, _number: U64) -> RpcResult<()> {
1424        Ok(())
1425    }
1426
1427    async fn debug_set_trie_flush_interval(&self, _interval: String) -> RpcResult<()> {
1428        Ok(())
1429    }
1430
1431    async fn debug_standard_trace_bad_block_to_file(
1432        &self,
1433        _block: BlockNumberOrTag,
1434        _opts: Option<GethDebugTracingCallOptions>,
1435    ) -> RpcResult<()> {
1436        Ok(())
1437    }
1438
1439    async fn debug_standard_trace_block_to_file(
1440        &self,
1441        _block: BlockNumberOrTag,
1442        _opts: Option<GethDebugTracingCallOptions>,
1443    ) -> RpcResult<()> {
1444        Ok(())
1445    }
1446
1447    async fn debug_state_root_with_updates(
1448        &self,
1449        hashed_state: HashedPostState,
1450        block_id: Option<BlockId>,
1451    ) -> RpcResult<(B256, TrieUpdates)> {
1452        Self::debug_state_root_with_updates(self, hashed_state, block_id).await.map_err(Into::into)
1453    }
1454
1455    async fn debug_storage_range_at(
1456        &self,
1457        _block_hash: B256,
1458        _tx_idx: usize,
1459        _contract_address: Address,
1460        _key_start: B256,
1461        _max_result: u64,
1462    ) -> RpcResult<()> {
1463        Ok(())
1464    }
1465
1466    async fn debug_trace_bad_block(
1467        &self,
1468        block_hash: B256,
1469        opts: Option<GethDebugTracingCallOptions>,
1470    ) -> RpcResult<Vec<TraceResult>> {
1471        let _permit = self.acquire_trace_permit().await;
1472        let entry = self
1473            .inner
1474            .bad_block_store
1475            .get(block_hash)
1476            .ok_or_else(|| internal_rpc_err("bad block not found in cache"))?;
1477
1478        let evm_env = self
1479            .eth_api()
1480            .evm_config()
1481            .evm_env(entry.block.header())
1482            .map_err(RethError::other)
1483            .to_rpc_result()?;
1484
1485        let opts = opts.map(|o| o.tracing_options).unwrap_or_default();
1486        self.trace_block(entry.block.clone(), evm_env, opts, None).await.map_err(Into::into)
1487    }
1488}
1489
1490impl<Eth: RpcNodeCore> std::fmt::Debug for DebugApi<Eth> {
1491    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1492        f.debug_struct("DebugApi").finish_non_exhaustive()
1493    }
1494}
1495
1496impl<Eth: RpcNodeCore> Clone for DebugApi<Eth> {
1497    fn clone(&self) -> Self {
1498        Self { inner: Arc::clone(&self.inner) }
1499    }
1500}
1501
1502struct DebugApiInner<Eth: RpcNodeCore> {
1503    /// The implementation of `eth` API
1504    eth_api: Eth,
1505    // restrict the number of concurrent calls to blocking calls
1506    blocking_task_guard: BlockingTaskGuard,
1507    /// Spawns long-running subscription tasks.
1508    task_spawner: Runtime,
1509    /// Cache for bad blocks.
1510    bad_block_store: BadBlockStore<BlockTy<Eth::Primitives>>,
1511}
1512
1513/// A bounded, deduplicating store of recently observed bad blocks.
1514#[derive(Clone, Debug)]
1515struct BadBlockStore<B: BlockTrait> {
1516    inner: Arc<RwLock<VecDeque<BadBlockEntry<B>>>>,
1517    limit: usize,
1518}
1519
1520/// A cached bad block paired with the reason it was rejected.
1521#[derive(Clone, Debug)]
1522struct BadBlockEntry<B: BlockTrait> {
1523    block: Arc<RecoveredBlock<B>>,
1524    reason: String,
1525}
1526
1527impl<B: BlockTrait> BadBlockStore<B> {
1528    /// Creates a new store with the given capacity.
1529    fn new(limit: usize) -> Self {
1530        Self { inner: Arc::new(RwLock::new(VecDeque::with_capacity(limit))), limit }
1531    }
1532
1533    /// Inserts a recovered block with its rejection reason, keeping only the most recent `limit`
1534    /// entries and deduplicating by block hash.
1535    fn insert(&self, block: RecoveredBlock<B>, reason: String) {
1536        let hash = block.hash();
1537        let mut guard = self.inner.write();
1538
1539        // skip if we already recorded this bad block , and keep original ordering
1540        if guard.iter().any(|entry| entry.block.hash() == hash) {
1541            return;
1542        }
1543        guard.push_back(BadBlockEntry { block: Arc::new(block), reason });
1544
1545        while guard.len() > self.limit {
1546            guard.pop_front();
1547        }
1548    }
1549
1550    /// Returns all cached bad block entries ordered from newest to oldest.
1551    fn all(&self) -> Vec<BadBlockEntry<B>> {
1552        let guard = self.inner.read();
1553        guard.iter().rev().cloned().collect()
1554    }
1555
1556    /// Returns the bad block entry with the given hash, if cached.
1557    fn get(&self, hash: B256) -> Option<BadBlockEntry<B>> {
1558        let guard = self.inner.read();
1559        guard.iter().find(|entry| entry.block.hash() == hash).cloned()
1560    }
1561}
1562
1563impl<B: BlockTrait> Default for BadBlockStore<B> {
1564    fn default() -> Self {
1565        Self::new(64)
1566    }
1567}
1568
1569/// Timeout of tracing a block supplied by the caller, whose work no consensus rule bounds.
1570const RAW_BLOCK_TRACE_TIMEOUT: Duration = Duration::from_secs(30);
1571
1572/// Default timeout of a JS tracer, matching geth.
1573#[cfg(feature = "js-tracer")]
1574const DEFAULT_JS_TRACER_TIMEOUT: Duration = Duration::from_secs(5);
1575
1576/// Returns whether `tracer` is a JS tracer or a mux tracer that holds one, at any depth.
1577#[cfg(feature = "js-tracer")]
1578fn runs_js_tracer(tracer: &GethDebugTracerType, config: &GethDebugTracerConfig) -> bool {
1579    match tracer {
1580        GethDebugTracerType::JsTracer(_) => true,
1581        GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::MuxTracer) => {
1582            config.clone().into_mux_config().is_ok_and(|mux| {
1583                mux.0.iter().any(|(tracer, config)| {
1584                    runs_js_tracer(tracer, &config.clone().unwrap_or_default())
1585                })
1586            })
1587        }
1588        GethDebugTracerType::BuiltInTracer(_) => false,
1589    }
1590}
1591
1592/// Interrupts a JS tracer once its request is dropped or its timeout expires.
1593///
1594/// Built-in tracers can't be interrupted, so for them this does nothing.
1595#[derive(Clone, Debug, Default)]
1596struct JsTraceInterrupt {
1597    #[cfg(feature = "js-tracer")]
1598    js: Option<(JsInspectorInterrupt, Duration)>,
1599}
1600
1601impl JsTraceInterrupt {
1602    /// Creates an interrupt if `opts` selects a JS tracer, using its `timeout` or geth's default.
1603    #[cfg_attr(
1604        not(feature = "js-tracer"),
1605        expect(unused_variables, reason = "only JS tracers can be interrupted")
1606    )]
1607    fn new(opts: &GethDebugTracingOptions) -> Result<Self, EthApiError> {
1608        #[cfg(feature = "js-tracer")]
1609        if let Some(tracer) = &opts.tracer &&
1610            runs_js_tracer(tracer, &opts.tracer_config)
1611        {
1612            let timeout = match &opts.timeout {
1613                Some(timeout) => humantime::parse_duration(timeout).map_err(|err| {
1614                    EthApiError::InvalidParams(format!("invalid tracer timeout {timeout:?}: {err}"))
1615                })?,
1616                None => DEFAULT_JS_TRACER_TIMEOUT,
1617            };
1618            return Ok(Self { js: Some((JsInspectorInterrupt::new(), timeout)) })
1619        }
1620        Ok(Self::default())
1621    }
1622
1623    /// Lets this interrupt stop the JS tracers of `inspector`, including those in a mux tracer.
1624    #[cfg_attr(
1625        not(feature = "js-tracer"),
1626        expect(clippy::missing_const_for_fn, reason = "only JS tracers can be interrupted")
1627    )]
1628    fn attach(&self, mut inspector: DebugInspector) -> DebugInspector {
1629        #[cfg(feature = "js-tracer")]
1630        if let Some((interrupt, _)) = &self.js {
1631            inspector.set_js_interrupt(interrupt);
1632        }
1633        inspector
1634    }
1635
1636    /// Awaits `trace`, interrupting the tracer if the returned future is dropped or the timeout
1637    /// expires.
1638    ///
1639    /// The interrupt is armed before the future is first polled, so dropping it unpolled still
1640    /// stops a trace that is already running on a blocking task.
1641    #[cfg_attr(
1642        not(feature = "js-tracer"),
1643        expect(
1644            clippy::manual_async_fn,
1645            clippy::redundant_async_block,
1646            reason = "only JS tracers can be interrupted"
1647        )
1648    )]
1649    fn run<T, E: FromEthApiError>(
1650        &self,
1651        trace: impl Future<Output = Result<T, E>>,
1652    ) -> impl Future<Output = Result<T, E>> {
1653        #[cfg(feature = "js-tracer")]
1654        let js = self.js.as_ref().map(|(interrupt, timeout)| (interrupt.drop_guard(), *timeout));
1655        async move {
1656            #[cfg(feature = "js-tracer")]
1657            if let Some((_interrupt_on_drop, timeout)) = js {
1658                return tokio::time::timeout(timeout, trace)
1659                    .await
1660                    .map_err(|_| E::from_eth_err(EthApiError::ExecutionTimedOut(timeout)))?
1661            }
1662            trace.await
1663        }
1664    }
1665}
1666
1667#[cfg(test)]
1668mod tests {
1669    use super::*;
1670    use crate::{eth::helpers::types::EthRpcConverter, EthApi};
1671    use alloy_genesis::{Genesis, GenesisAccount};
1672    use alloy_primitives::{keccak256, U256};
1673    use evm2::{bytecode::Bytecode, evm::AccountInfo};
1674    use reth_chainspec::{ChainSpec, ChainSpecBuilder};
1675    use reth_db_api::{tables, transaction::DbTxMut};
1676    use reth_db_common::init::init_genesis;
1677    use reth_evm_ethereum::EthEvmConfig;
1678    use reth_network_api::noop::NoopNetwork;
1679    use reth_primitives_traits::StorageEntry;
1680    use reth_provider::test_utils::{
1681        create_test_provider_factory, create_test_provider_factory_with_chain_spec, NoopProvider,
1682    };
1683    use reth_rpc_eth_api::EthApiServer;
1684    use reth_transaction_pool::test_utils::testing_pool;
1685    use reth_trie_common::bundle_state::{AccountStatus, BundleAccount, BundleState, StorageSlot};
1686
1687    #[tokio::test]
1688    async fn trace_call_out_of_range_block_error() {
1689        let eth_api = EthApi::<_, EthRpcConverter<ChainSpec>>::builder(
1690            NoopProvider::default(),
1691            testing_pool(),
1692            NoopNetwork::default(),
1693            EthEvmConfig::mainnet(),
1694        )
1695        .build();
1696        let debug_api = DebugApi::new(
1697            eth_api.clone(),
1698            BlockingTaskGuard::new(1),
1699            &Runtime::test(),
1700            futures::stream::empty(),
1701        );
1702        let block_id = BlockId::number(0xfffffffff);
1703        for tx_index in [None, Some(0)] {
1704            let mut opts: GethDebugTracingCallOptions =
1705                serde_json::from_value(serde_json::json!({ "tracer": "callTracer" })).unwrap();
1706            opts.tx_index = tx_index;
1707            let err = DebugApiServer::debug_trace_call(
1708                &debug_api,
1709                Default::default(),
1710                Some(block_id),
1711                Some(opts),
1712            )
1713            .await
1714            .unwrap_err();
1715            assert_eq!(err.code(), -32000);
1716            assert!(err.message().contains("not found"));
1717        }
1718
1719        let err = EthApiServer::call(&eth_api, Default::default(), Some(block_id), None, None)
1720            .await
1721            .unwrap_err();
1722        assert_eq!(err.code(), -32001);
1723    }
1724
1725    #[cfg(feature = "js-tracer")]
1726    fn js_tracer_opts(timeout: Option<&str>) -> GethDebugTracingOptions {
1727        GethDebugTracingOptions {
1728            tracer: Some(GethDebugTracerType::JsTracer(
1729                "{ fault: function() {}, result: function() { return null; } }".into(),
1730            )),
1731            timeout: timeout.map(Into::into),
1732            ..Default::default()
1733        }
1734    }
1735
1736    #[cfg(feature = "js-tracer")]
1737    #[test]
1738    fn js_trace_interrupt_stops_attached_js_tracer() {
1739        let opts = js_tracer_opts(None);
1740        let interrupt = JsTraceInterrupt::new(&opts).unwrap();
1741        let inspector = interrupt.attach(DebugInspector::new(opts).unwrap());
1742        interrupt.js.as_ref().unwrap().0.interrupt();
1743
1744        let config = EthEvmConfig::mainnet();
1745        let mut evm = config.evm_with_env(
1746            evm2::evm::CacheDB::<evm2::evm::EmptyDB>::default(),
1747            reth_evm_ethereum::EthEvmEnv::new(evm2::SpecId::CANCUN, Default::default(), 1),
1748        );
1749        let tx = alloy_consensus::transaction::Recovered::new_unchecked(
1750            evm2::ethereum::TxEnvelope::Legacy(alloy_consensus::TxLegacy {
1751                gas_limit: 1_000_000,
1752                to: alloy_primitives::TxKind::Call(Address::with_last_byte(1)),
1753                ..Default::default()
1754            }),
1755            Address::ZERO,
1756        );
1757        let err = Evm::transact_with_inspector(&mut evm, &tx, inspector).unwrap_err();
1758        assert_eq!(err.to_string(), "fatal error: JavaScript tracing interrupted");
1759    }
1760
1761    #[cfg(feature = "js-tracer")]
1762    #[test]
1763    fn js_trace_interrupt_covers_mux_tracers_with_js_children() {
1764        let mux = |config: serde_json::Value| GethDebugTracingOptions {
1765            tracer: Some(GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::MuxTracer)),
1766            tracer_config: serde_json::from_value(config).unwrap(),
1767            ..Default::default()
1768        };
1769        let js = "{ fault: function() {}, result: function() { return null; } }";
1770        let nested = mux(serde_json::json!({ "muxTracer": { (js): {} } }));
1771        let nested = serde_json::json!({ "muxTracer": nested.tracer_config });
1772        for (config, interrupted) in [
1773            (serde_json::json!({ (js): {} }), true),
1774            (nested, true),
1775            (serde_json::json!({ "callTracer": {} }), false),
1776        ] {
1777            let opts = mux(config);
1778            let interrupt = JsTraceInterrupt::new(&opts).unwrap();
1779            assert_eq!(interrupt.js.is_some(), interrupted);
1780        }
1781    }
1782
1783    #[cfg(feature = "js-tracer")]
1784    #[tokio::test]
1785    async fn js_trace_interrupt_times_out_and_interrupts_tracer() {
1786        let interrupt = JsTraceInterrupt::new(&js_tracer_opts(Some("10ms"))).unwrap();
1787        let err =
1788            interrupt.run(std::future::pending::<Result<(), EthApiError>>()).await.unwrap_err();
1789        assert!(
1790            matches!(err, EthApiError::ExecutionTimedOut(timeout) if timeout == Duration::from_millis(10))
1791        );
1792        assert!(interrupt.js.unwrap().0.is_interrupted());
1793    }
1794
1795    #[cfg(feature = "js-tracer")]
1796    #[test]
1797    fn js_trace_interrupt_interrupts_tracer_when_dropped_unpolled() {
1798        let interrupt = JsTraceInterrupt::new(&js_tracer_opts(None)).unwrap();
1799        let (tracer_interrupt, _) = interrupt.js.clone().unwrap();
1800        let trace = interrupt.run(std::future::pending::<Result<(), EthApiError>>());
1801        assert!(!tracer_interrupt.is_interrupted());
1802        drop(trace);
1803        assert!(tracer_interrupt.is_interrupted());
1804    }
1805
1806    #[test]
1807    fn witness_preserves_parent_storage_and_code_reads() {
1808        let address = Address::with_last_byte(1);
1809        let slots = [B256::with_last_byte(1), B256::with_last_byte(2)];
1810        let genesis = Genesis::default().extend_accounts([(
1811            address,
1812            GenesisAccount::default().with_nonce(Some(1)).with_storage(Some(
1813                slots.into_iter().map(|slot| (slot, B256::with_last_byte(10))).collect(),
1814            )),
1815        )]);
1816        let factory = create_test_provider_factory_with_chain_spec(Arc::new(
1817            ChainSpecBuilder::mainnet().genesis(genesis).build(),
1818        ));
1819        init_genesis(&factory).unwrap();
1820        let provider = factory.latest().unwrap();
1821        let proof = provider.proof(Default::default(), address, &slots).unwrap();
1822        let mut state = evm2::evm::CacheDB::<evm2::evm::EmptyDB>::default();
1823        state.cache.accounts.insert(address, None);
1824        state.cache.storage.entry(address).or_default().wipe();
1825
1826        let read_code = Bytecode::new_raw(alloy_primitives::bytes!("600100"));
1827        let created_code = Bytecode::new_raw(alloy_primitives::bytes!("600200"));
1828        state.cache.contracts.insert(read_code.hash_slow(), read_code.clone());
1829        let mut output = BundleState::default();
1830        // Recreating code already read from parent state must not remove it from the witness.
1831        output.contracts.insert(read_code.hash_slow(), read_code.clone());
1832        output.contracts.insert(created_code.hash_slow(), created_code.clone());
1833        let codes = ExecutionWitnessRecord::commit_execution(&mut state, &output);
1834
1835        for mode in [ExecutionWitnessMode::Legacy, ExecutionWitnessMode::Canonical] {
1836            let witness = ExecutionWitnessRecord::new(&state)
1837                .with_canonical_codes(codes.clone())
1838                .into_execution_witness_without_headers(&provider, mode)
1839                .unwrap();
1840            // Canonical mode needs only parent code; legacy also includes execution output.
1841            let mut expected_codes = vec![read_code.original_bytes()];
1842            if !mode.is_canonical() {
1843                expected_codes.push(created_code.original_bytes());
1844                expected_codes.push(Bytes::new());
1845                expected_codes.sort_unstable();
1846            }
1847            let mut actual_codes = witness.codes;
1848            actual_codes.sort_unstable();
1849            assert_eq!(actual_codes, expected_codes);
1850            for storage in &proof.storage_proofs {
1851                assert!(!storage.proof.is_empty());
1852                assert!(storage.proof.iter().all(|node| witness.state.contains(node)));
1853            }
1854        }
1855    }
1856
1857    #[test]
1858    fn hashed_post_state_zeroes_destroyed_account_parent_storage() {
1859        let factory = create_test_provider_factory();
1860        let address = Address::with_last_byte(1);
1861        let old_slot = U256::ONE;
1862        let new_slot = U256::from(2);
1863        let old_value = U256::from(10);
1864        let new_value = U256::from(20);
1865        let hashed_address = keccak256(address);
1866        let hashed_old_slot = keccak256(B256::from(old_slot));
1867        let hashed_new_slot = keccak256(B256::from(new_slot));
1868
1869        let provider_rw = factory.provider_rw().unwrap();
1870        provider_rw
1871            .tx_ref()
1872            .put::<tables::HashedStorages>(
1873                hashed_address,
1874                StorageEntry { key: hashed_old_slot, value: old_value },
1875            )
1876            .unwrap();
1877        provider_rw.commit().unwrap();
1878
1879        let mut bundle_state = BundleState::default();
1880        bundle_state.state.insert(
1881            address,
1882            BundleAccount::new(
1883                Some(AccountInfo::default()),
1884                Some(AccountInfo::default()),
1885                std::iter::once((new_slot, StorageSlot::new_changed(U256::ZERO, new_value)))
1886                    .collect(),
1887                AccountStatus::DestroyedChanged,
1888            ),
1889        );
1890
1891        let provider = factory.latest().unwrap();
1892        let hashed_state = provider.hashed_post_state(&bundle_state).unwrap();
1893        let storage = &hashed_state.storages[&hashed_address];
1894
1895        assert_eq!(storage.storage[&hashed_old_slot], U256::ZERO);
1896        assert_eq!(storage.storage[&hashed_new_slot], new_value);
1897    }
1898}