1use alloy_consensus::{constants::KECCAK_EMPTY, transaction::TxHashRef, BlockHeader};
2use alloy_eips::{eip2718::Encodable2718, BlockId, BlockNumberOrTag};
3use alloy_evm::{env::BlockEnvironment, Evm};
4use alloy_genesis::ChainConfig;
5use alloy_primitives::{hex::decode, uint, Address, Bytes, B256, U256, U64};
6use alloy_rlp::{Decodable, Encodable};
7use alloy_rpc_types::BlockTransactionsKind;
8use alloy_rpc_types_debug::ExecutionWitness;
9use alloy_rpc_types_eth::{
10 state::EvmOverrides, Account, AccountInfo, BlockError, Bundle, Index, StateContext,
11};
12use alloy_rpc_types_trace::geth::{
13 ChainBlockTraceResult, GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace,
14 TraceResult,
15};
16use async_trait::async_trait;
17use futures::Stream;
18use jsonrpsee::{core::RpcResult, PendingSubscriptionSink, SubscriptionMessage};
19use parking_lot::RwLock;
20use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks};
21use reth_engine_primitives::ConsensusEngineEvent;
22use reth_errors::RethError;
23use reth_evm::{block::BlockExecutor, execute::Executor, ConfigureEvm, EvmEnvFor};
24use reth_primitives_traits::{
25 Block as BlockTrait, BlockBody, BlockTy, ReceiptWithBloom, RecoveredBlock,
26};
27use reth_revm::{db::State, witness::ExecutionWitnessRecord};
28use reth_rpc_api::DebugApiServer;
29use reth_rpc_convert::RpcTxReq;
30use reth_rpc_eth_api::{
31 helpers::{EthTransactions, TraceExt},
32 FromEthApiError, FromEvmError, RpcConvert, RpcNodeCore,
33};
34use reth_rpc_eth_types::{EthApiError, StateCacheDb};
35use reth_rpc_server_types::{result::internal_rpc_err, ToRpcResult};
36use reth_storage_api::{
37 BlockIdReader, BlockReaderIdExt, HashedPostStateProvider, HeaderProvider, ProviderBlock,
38 ReceiptProviderIdExt, StateProviderFactory, StateRootProvider, StorageRootProvider,
39 TransactionVariant,
40};
41use reth_tasks::{pool::BlockingTaskGuard, Runtime};
42use reth_transaction_pool::TransactionPool;
43use reth_trie_common::{
44 root::storage_root_unsorted, updates::TrieUpdates, ExecutionWitnessMode, HashedPostState,
45 HashedStorage,
46};
47use revm::{database::states::bundle_state::BundleRetention, Database, DatabaseCommit};
48use revm_inspectors::tracing::{DebugInspector, TransactionContext};
49use serde::{Deserialize, Serialize};
50use std::{collections::VecDeque, sync::Arc};
51use tokio::sync::{AcquireError, OwnedSemaphorePermit};
52use tokio_stream::StreamExt;
53
54pub struct DebugApi<Eth: RpcNodeCore> {
58 inner: Arc<DebugApiInner<Eth>>,
59}
60
61impl<Eth> DebugApi<Eth>
62where
63 Eth: RpcNodeCore,
64{
65 pub fn new(
67 eth_api: Eth,
68 blocking_task_guard: BlockingTaskGuard,
69 executor: &Runtime,
70 mut stream: impl Stream<Item = ConsensusEngineEvent<Eth::Primitives>> + Send + Unpin + 'static,
71 ) -> Self {
72 let bad_block_store = BadBlockStore::default();
73 let inner = Arc::new(DebugApiInner {
74 eth_api,
75 blocking_task_guard,
76 task_spawner: executor.clone(),
77 bad_block_store: bad_block_store.clone(),
78 });
79
80 executor.spawn_task(async move {
82 while let Some(event) = stream.next().await {
83 if let ConsensusEngineEvent::InvalidBlock { block, error } = event &&
84 let Ok(recovered) = RecoveredBlock::try_recover_sealed(*block)
85 {
86 bad_block_store.insert(recovered, error);
87 }
88 }
89 });
90
91 Self { inner }
92 }
93
94 pub fn eth_api(&self) -> &Eth {
96 &self.inner.eth_api
97 }
98
99 pub fn provider(&self) -> &Eth::Provider {
101 self.inner.eth_api.provider()
102 }
103}
104
105impl<Eth> DebugApi<Eth>
108where
109 Eth: TraceExt,
110{
111 async fn acquire_trace_permit(&self) -> Result<OwnedSemaphorePermit, AcquireError> {
113 self.inner.blocking_task_guard.clone().acquire_owned().await
114 }
115
116 async fn trace_block(
118 &self,
119 block: Arc<RecoveredBlock<ProviderBlock<Eth::Provider>>>,
120 evm_env: EvmEnvFor<Eth::Evm>,
121 opts: GethDebugTracingOptions,
122 ) -> Result<Vec<TraceResult>, Eth::Error> {
123 self.eth_api()
124 .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
125 let mut results = Vec::with_capacity(block.body().transactions().len());
126
127 eth_api.apply_pre_execution_changes(&block, &mut db)?;
128
129 let block_env = evm_env.block_env.clone();
130
131 let mut transactions = block.transactions_recovered().enumerate().peekable();
132 let inspector = DebugInspector::new(opts).map_err(Eth::Error::from_eth_err)?;
133 let mut evm =
134 eth_api.evm_config().evm_with_env_and_inspector(&mut db, evm_env, inspector);
135 while let Some((index, tx)) = transactions.next() {
136 let tx_env = eth_api.evm_config().tx_env(tx);
137
138 let res = evm.transact(tx_env.clone()).map_err(Eth::Error::from_evm_err)?;
139
140 let (db, inspector, _) = evm.components_mut();
141 let result = inspector
142 .get_result(
143 Some(TransactionContext {
144 block_hash: Some(block.hash()),
145 tx_hash: Some(*tx.tx_hash()),
146 tx_index: Some(index),
147 }),
148 &tx_env,
149 &block_env,
150 &res,
151 db,
152 )
153 .map_err(Eth::Error::from_eth_err)?;
154
155 results.push(TraceResult::Success { result, tx_hash: Some(*tx.tx_hash()) });
156 if transactions.peek().is_some() {
157 inspector.fuse().map_err(Eth::Error::from_eth_err)?;
158 db.commit(res.state)
161 }
162 }
163
164 Ok(results)
165 })
166 .await
167 }
168
169 async fn trace_chain_block(
172 &self,
173 block: Arc<RecoveredBlock<ProviderBlock<Eth::Provider>>>,
174 opts: GethDebugTracingOptions,
175 ) -> Result<Vec<Option<TraceResult>>, Eth::Error> {
176 let evm_env = self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
177
178 self.eth_api()
179 .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
180 let tx_count = block.body().transactions().len();
181 let mut results = Vec::with_capacity(tx_count);
182
183 eth_api.apply_pre_execution_changes(&block, &mut db)?;
184
185 let block_env = evm_env.block_env.clone();
186 let mut transactions = block.transactions_recovered().enumerate().peekable();
187 let inspector = match DebugInspector::new(opts) {
188 Ok(inspector) => inspector,
189 Err(err) => {
190 if let Some((_, tx)) = transactions.peek() {
191 results.push(Some(TraceResult::Error {
192 error: err.to_string(),
193 tx_hash: Some(*tx.tx_hash()),
194 }));
195 }
196 results.resize(tx_count, None);
197 return Ok(results)
198 }
199 };
200 let mut evm =
201 eth_api.evm_config().evm_with_env_and_inspector(&mut db, evm_env, inspector);
202
203 while let Some((index, tx)) = transactions.next() {
204 let tx_hash = *tx.tx_hash();
205 let tx_env = eth_api.evm_config().tx_env(tx);
206 let res = match evm.transact(tx_env.clone()) {
207 Ok(res) => res,
208 Err(err) => {
209 results.push(Some(TraceResult::Error {
210 error: err.to_string(),
211 tx_hash: Some(tx_hash),
212 }));
213 break
214 }
215 };
216
217 let (db, inspector, _) = evm.components_mut();
218 let result = match inspector.get_result(
219 Some(TransactionContext {
220 block_hash: Some(block.hash()),
221 tx_hash: Some(tx_hash),
222 tx_index: Some(index),
223 }),
224 &tx_env,
225 &block_env,
226 &res,
227 db,
228 ) {
229 Ok(result) => result,
230 Err(err) => {
231 results.push(Some(TraceResult::Error {
232 error: err.to_string(),
233 tx_hash: Some(tx_hash),
234 }));
235 break
236 }
237 };
238
239 results.push(Some(TraceResult::Success { result, tx_hash: Some(tx_hash) }));
240 if let Some((_, next_tx)) = transactions.peek() {
241 if let Err(err) = inspector.fuse() {
242 results.push(Some(TraceResult::Error {
243 error: err.to_string(),
244 tx_hash: Some(*next_tx.tx_hash()),
245 }));
246 break
247 }
248 db.commit(res.state);
249 }
250 }
251
252 results.resize(tx_count, None);
253 Ok(results)
254 })
255 .await
256 }
257
258 pub async fn debug_trace_raw_block(
264 &self,
265 rlp_block: Bytes,
266 opts: GethDebugTracingOptions,
267 ) -> Result<Vec<TraceResult>, Eth::Error> {
268 let block: ProviderBlock<Eth::Provider> = Decodable::decode(&mut rlp_block.as_ref())
269 .map_err(BlockError::RlpDecodeRawBlock)
270 .map_err(Eth::Error::from_eth_err)?;
271
272 let evm_env = self
273 .eth_api()
274 .evm_config()
275 .evm_env(block.header())
276 .map_err(RethError::other)
277 .map_err(Eth::Error::from_eth_err)?;
278
279 let senders =
281 if self.provider().chain_spec().is_homestead_active_at_block(block.header().number()) {
282 block.body().recover_signers()
283 } else {
284 block.body().recover_signers_unchecked()
285 }
286 .map_err(Eth::Error::from_eth_err)?;
287
288 self.trace_block(Arc::new(block.into_recovered_with_signers(senders)), evm_env, opts).await
289 }
290
291 pub async fn debug_trace_block(
293 &self,
294 block_id: BlockId,
295 opts: GethDebugTracingOptions,
296 ) -> Result<Vec<TraceResult>, Eth::Error> {
297 let block = self
298 .eth_api()
299 .recovered_block(block_id)
300 .await?
301 .ok_or(EthApiError::TracingBlockNotFound(block_id))?;
302 if block.number() == 0 {
304 return Err(EthApiError::GenesisNotTraceable.into())
305 }
306 let evm_env = self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
307
308 self.trace_block(block, evm_env, opts).await
309 }
310
311 pub async fn debug_trace_transaction(
315 &self,
316 tx_hash: B256,
317 opts: GethDebugTracingOptions,
318 ) -> Result<GethTrace, Eth::Error> {
319 let (transaction, block) = match self.eth_api().transaction_and_block(tx_hash).await? {
320 None => return Err(EthApiError::TracingTransactionNotFound.into()),
321 Some(res) => res,
322 };
323
324 self.eth_api()
325 .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
326 let (tx, tx_info) = transaction.split();
328
329 let index =
332 tx_info.index.expect("transaction_and_block only returns block transactions")
333 as usize;
334
335 let mut inspector = DebugInspector::new(opts).map_err(Eth::Error::from_eth_err)?;
336 let tx_env = eth_api.evm_config().tx_env(&tx);
337 let (res, evm_env) = eth_api.inspect_transaction_in_block(
338 &block,
339 &mut db,
340 &mut inspector,
341 index,
342 tx_env.clone(),
343 )?;
344
345 let trace = inspector
346 .get_result(
347 Some(TransactionContext {
348 block_hash: Some(block.hash()),
349 tx_index: Some(index),
350 tx_hash: Some(*tx.tx_hash()),
351 }),
352 &tx_env,
353 &evm_env.block_env,
354 &res,
355 &mut db,
356 )
357 .map_err(Eth::Error::from_eth_err)?;
358
359 Ok(trace)
360 })
361 .await
362 }
363
364 pub async fn debug_trace_call(
374 &self,
375 call: RpcTxReq<Eth::NetworkTypes>,
376 block_id: Option<BlockId>,
377 opts: GethDebugTracingCallOptions,
378 ) -> Result<GethTrace, Eth::Error> {
379 let at = block_id.unwrap_or_default();
380 let GethDebugTracingCallOptions {
381 tracing_options,
382 state_overrides,
383 block_overrides,
384 tx_index,
385 } = opts;
386 let overrides = EvmOverrides::new(state_overrides, block_overrides.map(Box::new));
387
388 if let Some(tx_idx) = tx_index {
390 return self
391 .debug_trace_call_at_tx_index(call, at, tx_idx as usize, tracing_options, overrides)
392 .await;
393 }
394
395 let this = self.clone();
396 self.eth_api()
397 .spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| {
398 let mut inspector =
399 DebugInspector::new(tracing_options).map_err(Eth::Error::from_eth_err)?;
400 let res = this.eth_api().inspect(
401 &mut *db,
402 evm_env.clone(),
403 tx_env.clone(),
404 &mut inspector,
405 )?;
406 let trace = inspector
407 .get_result(None, &tx_env, &evm_env.block_env, &res, db)
408 .map_err(Eth::Error::from_eth_err)?;
409 Ok(trace)
410 })
411 .await
412 }
413
414 async fn debug_trace_call_at_tx_index(
418 &self,
419 call: RpcTxReq<Eth::NetworkTypes>,
420 block_id: BlockId,
421 tx_index: usize,
422 tracing_options: GethDebugTracingOptions,
423 overrides: EvmOverrides,
424 ) -> Result<GethTrace, Eth::Error> {
425 let block = self
427 .eth_api()
428 .recovered_block(block_id)
429 .await?
430 .ok_or(EthApiError::HeaderNotFound(block_id))?;
431
432 if tx_index >= block.transaction_count() {
433 return Err(EthApiError::InvalidParams(format!(
435 "tx_index {} out of bounds for block with {} transactions",
436 tx_index,
437 block.transaction_count()
438 ))
439 .into())
440 }
441
442 let evm_env = self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
443
444 self.eth_api()
445 .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
446 eth_api.replay_block_until(&mut db, &block, tx_index)?;
448
449 let (evm_env, tx_env) =
451 eth_api.prepare_call_env(evm_env, call, &mut db, overrides)?;
452
453 let mut inspector =
454 DebugInspector::new(tracing_options).map_err(Eth::Error::from_eth_err)?;
455 let res =
456 eth_api.inspect(&mut db, evm_env.clone(), tx_env.clone(), &mut inspector)?;
457 let trace = inspector
458 .get_result(None, &tx_env, &evm_env.block_env, &res, &mut db)
459 .map_err(Eth::Error::from_eth_err)?;
460
461 Ok(trace)
462 })
463 .await
464 }
465
466 pub async fn debug_trace_call_many(
470 &self,
471 bundles: Vec<Bundle<RpcTxReq<Eth::NetworkTypes>>>,
472 state_context: Option<StateContext>,
473 opts: Option<GethDebugTracingCallOptions>,
474 ) -> Result<Vec<Vec<GethTrace>>, Eth::Error> {
475 if bundles.is_empty() {
476 return Err(EthApiError::InvalidParams(String::from("bundles are empty.")).into())
477 }
478
479 let StateContext { transaction_index, block_number } = state_context.unwrap_or_default();
480 let transaction_index = transaction_index.unwrap_or_default();
481
482 let target_block = block_number.unwrap_or_default();
483 let block = self
484 .eth_api()
485 .recovered_block(target_block)
486 .await?
487 .ok_or(EthApiError::HeaderNotFound(target_block))?;
488 let mut evm_env =
489 self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
490
491 let opts = opts.unwrap_or_default();
492 let GethDebugTracingCallOptions { tracing_options, mut state_overrides, .. } = opts;
493
494 let mut at = block.parent_hash();
497 let mut replay_block_txs = true;
498
499 let num_txs =
501 transaction_index.index().unwrap_or_else(|| block.body().transactions().len());
502 if !target_block.is_pending() && num_txs == block.body().transactions().len() {
506 at = block.hash();
507 replay_block_txs = false;
508 }
509
510 self.eth_api()
511 .spawn_with_state_at_block(at, move |eth_api, mut db| {
512 let mut all_bundles = Vec::with_capacity(bundles.len());
514
515 if replay_block_txs {
516 eth_api.replay_block_until(&mut db, &block, num_txs)?;
520 }
521
522 let mut bundles = bundles.into_iter().peekable();
524 let mut inspector = DebugInspector::new(tracing_options.clone())
525 .map_err(Eth::Error::from_eth_err)?;
526 while let Some(bundle) = bundles.next() {
527 let mut results = Vec::with_capacity(bundle.transactions.len());
528 let Bundle { transactions, block_override } = bundle;
529
530 let block_overrides = block_override.map(Box::new);
531
532 let mut transactions = transactions.into_iter().peekable();
533 while let Some(tx) = transactions.next() {
534 let state_overrides = state_overrides.take();
536 let overrides = EvmOverrides::new(state_overrides, block_overrides.clone());
537
538 let (evm_env, tx_env) =
539 eth_api.prepare_call_env(evm_env.clone(), tx, &mut db, overrides)?;
540
541 let res = eth_api.inspect(
542 &mut db,
543 evm_env.clone(),
544 tx_env.clone(),
545 &mut inspector,
546 )?;
547 let trace = inspector
548 .get_result(None, &tx_env, &evm_env.block_env, &res, &mut db)
549 .map_err(Eth::Error::from_eth_err)?;
550
551 if transactions.peek().is_some() || bundles.peek().is_some() {
554 inspector.fuse().map_err(Eth::Error::from_eth_err)?;
555 db.commit(res.state);
556 }
557 results.push(trace);
558 }
559 evm_env.block_env.inner_mut().number += uint!(1_U256);
561 evm_env.block_env.inner_mut().timestamp += uint!(12_U256);
562
563 all_bundles.push(results);
564 }
565 Ok(all_bundles)
566 })
567 .await
568 }
569
570 pub async fn debug_execution_witness_by_block_hash(
573 &self,
574 hash: B256,
575 mode: Option<ExecutionWitnessMode>,
576 ) -> Result<ExecutionWitness, Eth::Error> {
577 let this = self.clone();
578 let block = this
579 .eth_api()
580 .recovered_block(hash.into())
581 .await?
582 .ok_or(EthApiError::HeaderNotFound(hash.into()))?;
583
584 self.debug_execution_witness_for_block(block, mode.unwrap_or_default()).await
585 }
586
587 pub async fn debug_execution_witness(
592 &self,
593 block_id: BlockId,
594 mode: Option<ExecutionWitnessMode>,
595 ) -> Result<ExecutionWitness, Eth::Error> {
596 let this = self.clone();
597 let block = this
598 .eth_api()
599 .recovered_block(block_id)
600 .await?
601 .ok_or(EthApiError::HeaderNotFound(block_id))?;
602
603 self.debug_execution_witness_for_block(block, mode.unwrap_or_default()).await
604 }
605
606 pub async fn debug_execution_witness_for_block(
608 &self,
609 block: Arc<RecoveredBlock<ProviderBlock<Eth::Provider>>>,
610 mode: ExecutionWitnessMode,
611 ) -> Result<ExecutionWitness, Eth::Error> {
612 let block_number = block.header().number();
613 self.eth_api()
614 .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
615 let block_executor = eth_api.evm_config().executor(&mut db);
616
617 let mut witness = None;
618 let _ = block_executor
619 .execute_with_state_closure(&block, |statedb: &State<_>| {
620 witness =
621 Some(ExecutionWitnessRecord::new(statedb).into_execution_witness(
622 &statedb.database.database.0,
623 eth_api.provider(),
624 block_number,
625 mode,
626 ));
627 })
628 .map_err(|err| EthApiError::Internal(err.into()))?;
629
630 Ok(witness
631 .expect("state closure is called after successful execution")
632 .map_err(EthApiError::from)?)
633 })
634 .await
635 }
636
637 pub async fn debug_account_at(
640 &self,
641 block_id: BlockId,
642 tx_index: Index,
643 address: Address,
644 ) -> Result<Option<Account>, Eth::Error> {
645 self.replay_block_until(block_id, tx_index, move |db| Self::account(db, address))
646 .await
647 .map(Option::flatten)
648 }
649
650 pub async fn debug_account_info_at(
653 &self,
654 block_id: BlockId,
655 tx_index: Index,
656 address: Address,
657 ) -> Result<Option<AccountInfo>, Eth::Error> {
658 self.replay_block_until(block_id, tx_index, move |db| Self::account_info(db, address)).await
659 }
660
661 async fn replay_block_until<F, R>(
664 &self,
665 block_id: BlockId,
666 tx_index: Index,
667 f: F,
668 ) -> Result<Option<R>, Eth::Error>
669 where
670 F: FnOnce(&mut StateCacheDb) -> Result<R, Eth::Error> + Send + 'static,
671 R: Send + 'static,
672 {
673 let block = self
674 .eth_api()
675 .recovered_block(block_id)
676 .await?
677 .ok_or(EthApiError::HeaderNotFound(block_id))?;
678 let tx_index = usize::from(tx_index);
679 let transaction_count = block.transaction_count();
680 if tx_index >= transaction_count {
681 return Err(EthApiError::InvalidParams(format!(
682 "tx_index {tx_index} out of bounds for block with {transaction_count} transactions"
683 ))
684 .into())
685 }
686
687 self.eth_api()
688 .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
689 let mut executor = eth_api
690 .evm_config()
691 .executor_for_block(&mut db, block.sealed_block())
692 .map_err(RethError::other)
693 .map_err(Eth::Error::from_eth_err)?;
694 executor.apply_pre_execution_changes().map_err(Eth::Error::from_eth_err)?;
695
696 for tx in block.transactions_recovered().take(tx_index + 1) {
697 executor.execute_transaction(tx).map_err(Eth::Error::from_eth_err)?;
698 }
699 drop(executor);
700
701 f(&mut db)
702 })
703 .await
704 .map(Some)
705 }
706
707 fn account(db: &mut StateCacheDb, address: Address) -> Result<Option<Account>, Eth::Error> {
709 let account = db.basic(address).map_err(Eth::Error::from_eth_err)?;
710 let Some(account) = account else { return Ok(None) };
711
712 let balance = account.balance;
713 let nonce = account.nonce;
714 let code_hash = account.code_hash;
715 let (hashed_storage, status) = db
716 .cache
717 .accounts
718 .get(&address)
719 .and_then(|account| {
720 account.account.as_ref().map(|plain_account| {
721 (HashedStorage::from_plain_storage(&plain_account.storage), account.status)
722 })
723 })
724 .unwrap_or_default();
725 let storage_root = if status.was_destroyed() {
726 storage_root_unsorted(
729 hashed_storage.storage.into_iter().filter(|(_, value)| !value.is_zero()),
730 )
731 } else {
732 db.database.storage_root(address, hashed_storage).map_err(Eth::Error::from_eth_err)?
733 };
734
735 Ok(Some(Account { balance, nonce, code_hash, storage_root }))
736 }
737
738 fn account_info<DB>(db: &mut DB, address: Address) -> Result<AccountInfo, Eth::Error>
740 where
741 DB: Database,
742 EthApiError: From<DB::Error>,
743 {
744 let account = db.basic(address).map_err(Eth::Error::from_eth_err)?.unwrap_or_default();
745 let code = if account.code_hash == KECCAK_EMPTY {
746 Default::default()
747 } else if let Some(code) = account.code {
748 code.original_bytes()
749 } else {
750 db.code_by_hash(account.code_hash).map_err(Eth::Error::from_eth_err)?.original_bytes()
751 };
752
753 Ok(AccountInfo { balance: account.balance, nonce: account.nonce, code })
754 }
755
756 pub async fn debug_code_by_hash(
759 &self,
760 hash: B256,
761 block_id: Option<BlockId>,
762 ) -> Result<Option<Bytes>, Eth::Error> {
763 Ok(self
764 .provider()
765 .state_by_block_id(block_id.unwrap_or_default())
766 .map_err(Eth::Error::from_eth_err)?
767 .bytecode_by_hash(&hash)
768 .map_err(Eth::Error::from_eth_err)?
769 .map(|b| b.original_bytes()))
770 }
771
772 async fn debug_state_root_with_updates(
775 &self,
776 hashed_state: HashedPostState,
777 block_id: Option<BlockId>,
778 ) -> Result<(B256, TrieUpdates), Eth::Error> {
779 self.inner
780 .eth_api
781 .spawn_blocking_io(move |this| {
782 let state = this
783 .provider()
784 .state_by_block_id(block_id.unwrap_or_default())
785 .map_err(Eth::Error::from_eth_err)?;
786 state.state_root_with_updates(hashed_state).map_err(Eth::Error::from_eth_err)
787 })
788 .await
789 }
790
791 pub async fn intermediate_roots(&self, block_hash: B256) -> Result<Vec<B256>, Eth::Error> {
793 let block = self
794 .eth_api()
795 .recovered_block(block_hash.into())
796 .await?
797 .ok_or(EthApiError::HeaderNotFound(block_hash.into()))?;
798 let evm_env = self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
799
800 self.eth_api()
801 .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
802 db.transition_state = Some(Default::default());
804
805 eth_api.apply_pre_execution_changes(&block, &mut db)?;
806
807 let mut roots = Vec::with_capacity(block.body().transactions().len());
808 let mut evm = eth_api.evm_config().evm_with_env(&mut db, evm_env);
809 for tx in block.transactions_recovered() {
810 let tx_env = eth_api.evm_config().tx_env(tx);
811 evm.transact_commit(tx_env).map_err(Eth::Error::from_evm_err)?;
812
813 let state = evm.db_mut();
814 state.merge_transitions(BundleRetention::PlainState);
816 let hashed_state = state
818 .database
819 .hashed_post_state(&state.bundle_state)
820 .map_err(Eth::Error::from_eth_err)?;
821 let root = state
822 .database
823 .state_root(hashed_state)
824 .map_err(Eth::Error::from_eth_err)?;
825 roots.push(root);
826 }
827
828 Ok(roots)
829 })
830 .await
831 }
832}
833
834#[async_trait]
835impl<Eth> DebugApiServer<RpcTxReq<Eth::NetworkTypes>> for DebugApi<Eth>
836where
837 Eth: EthTransactions + TraceExt,
838{
839 async fn raw_header(&self, block_id: BlockId) -> RpcResult<Bytes> {
841 let header = match block_id {
842 BlockId::Hash(hash) => self.provider().header(hash.into()).to_rpc_result()?,
843 BlockId::Number(number_or_tag) => {
844 let number = self
845 .provider()
846 .convert_block_number(number_or_tag)
847 .to_rpc_result()?
848 .ok_or(EthApiError::HeaderNotFound(block_id))?;
849 self.provider().header_by_number(number).to_rpc_result()?
850 }
851 }
852 .ok_or(EthApiError::HeaderNotFound(block_id))?;
853
854 let mut res = Vec::new();
855 header.encode(&mut res);
856 Ok(res.into())
857 }
858
859 async fn raw_block(&self, block_id: BlockId) -> RpcResult<Bytes> {
861 let block = self
862 .provider()
863 .block_by_id(block_id)
864 .to_rpc_result()?
865 .ok_or(EthApiError::HeaderNotFound(block_id))?;
866 let mut res = Vec::new();
867 block.encode(&mut res);
868 Ok(res.into())
869 }
870
871 async fn raw_block_access_list(&self, block_id: BlockId) -> RpcResult<Bytes> {
873 self.eth_api()
874 .get_raw_block_access_list(block_id)
875 .await
876 .map_err(Into::into)?
877 .ok_or_else(|| EthApiError::HeaderNotFound(block_id).into())
878 }
879
880 async fn raw_transaction(&self, hash: B256) -> RpcResult<Option<Bytes>> {
886 self.eth_api().raw_transaction_by_hash(hash).await.map_err(Into::into)
887 }
888
889 async fn raw_transactions(&self, block_id: BlockId) -> RpcResult<Vec<Bytes>> {
892 let block: RecoveredBlock<BlockTy<Eth::Primitives>> = self
893 .provider()
894 .block_with_senders_by_id(block_id, TransactionVariant::NoHash)
895 .to_rpc_result()?
896 .unwrap_or_default();
897 Ok(block.into_transactions_recovered().map(|tx| tx.encoded_2718().into()).collect())
898 }
899
900 async fn raw_receipts(&self, block_id: BlockId) -> RpcResult<Vec<Bytes>> {
902 Ok(self
903 .provider()
904 .receipts_by_block_id(block_id)
905 .to_rpc_result()?
906 .ok_or(EthApiError::HeaderNotFound(block_id))?
907 .into_iter()
908 .map(|receipt| ReceiptWithBloom::from(receipt).encoded_2718().into())
909 .collect())
910 }
911
912 async fn bad_blocks(&self) -> RpcResult<Vec<serde_json::Value>> {
914 let entries = self.inner.bad_block_store.all();
915 let mut bad_blocks = Vec::with_capacity(entries.len());
916
917 #[derive(Serialize, Deserialize)]
918 struct BadBlockSerde<T> {
919 block: T,
920 hash: B256,
921 rlp: Bytes,
922 reason: String,
923 }
924
925 for entry in entries {
926 let rlp = alloy_rlp::encode(entry.block.sealed_block()).into();
927 let hash = entry.block.hash();
928
929 let block = entry
930 .block
931 .clone_into_rpc_block(
932 BlockTransactionsKind::Full,
933 |tx, tx_info| self.eth_api().converter().fill(tx, tx_info),
934 |header, size| self.eth_api().converter().convert_header(header, size),
935 )
936 .map_err(|err| Eth::Error::from(err).into())?;
937
938 let bad_block =
939 serde_json::to_value(BadBlockSerde { block, hash, rlp, reason: entry.reason })
940 .map_err(|err| EthApiError::other(internal_rpc_err(err.to_string())))?;
941
942 bad_blocks.push(bad_block);
943 }
944
945 Ok(bad_blocks)
946 }
947
948 async fn debug_clear_txpool(&self) -> RpcResult<()> {
950 let pool = self.eth_api().pool();
951 let all_hashes = pool.all_transaction_hashes();
952 let _ = pool.remove_transactions(all_hashes);
953 Ok(())
954 }
955
956 async fn debug_subscribe(
958 &self,
959 pending: PendingSubscriptionSink,
960 subscription: String,
961 start_exclusive: BlockNumberOrTag,
962 end_inclusive: BlockNumberOrTag,
963 opts: Option<GethDebugTracingOptions>,
964 ) -> jsonrpsee::core::SubscriptionResult {
965 if subscription != "traceChain" {
966 pending
967 .reject(EthApiError::InvalidParams(format!(
968 "unsupported debug subscription: {subscription}"
969 )))
970 .await;
971 return Ok(())
972 }
973
974 let start_id = BlockId::Number(start_exclusive);
975 let start = match self.eth_api().recovered_block(start_id).await {
976 Ok(Some(block)) => block,
977 Ok(None) => {
978 pending.reject(EthApiError::TracingBlockNotFound(start_id)).await;
979 return Ok(())
980 }
981 Err(err) => {
982 pending.reject(err).await;
983 return Ok(())
984 }
985 };
986 let end_id = BlockId::Number(end_inclusive);
987 let end = match self.eth_api().recovered_block(end_id).await {
988 Ok(Some(block)) => block,
989 Ok(None) => {
990 pending.reject(EthApiError::TracingBlockNotFound(end_id)).await;
991 return Ok(())
992 }
993 Err(err) => {
994 pending.reject(err).await;
995 return Ok(())
996 }
997 };
998
999 if start.number() >= end.number() {
1000 pending
1001 .reject(EthApiError::InvalidParams(format!(
1002 "end block (#{}) needs to come after start block (#{})",
1003 end.number(),
1004 start.number()
1005 )))
1006 .await;
1007 return Ok(())
1008 }
1009
1010 let sink = pending.accept().await?;
1011 let this = self.clone();
1012 let task_spawner = self.inner.task_spawner.clone();
1013 task_spawner.spawn_task(async move {
1014 let end_number = end.number();
1015 let opts = opts.unwrap_or_default();
1016
1017 for number in (start.number() + 1)..=end_number {
1018 if sink.is_closed() {
1019 break
1020 }
1021
1022 let block_id = BlockId::Number(number.into());
1023 let block = match this.eth_api().recovered_block(block_id).await {
1024 Ok(Some(block)) => block,
1025 Ok(None) => {
1026 tracing::warn!(target: "rpc::debug", %number, "Chain tracing block not found");
1027 break
1028 }
1029 Err(err) => {
1030 tracing::warn!(target: "rpc::debug", %number, %err, "Failed to load chain tracing block");
1031 break
1032 }
1033 };
1034 let permit = tokio::select! {
1035 _ = sink.closed() => break,
1036 permit = this.acquire_trace_permit() => match permit {
1037 Ok(permit) => permit,
1038 Err(err) => {
1039 tracing::debug!(target: "rpc::debug", %err, "Failed to acquire trace permit");
1040 break
1041 }
1042 }
1043 };
1044 if sink.is_closed() {
1045 break
1046 }
1047 let traces = match this.trace_chain_block(block.clone(), opts.clone()).await {
1048 Ok(traces) => traces,
1049 Err(err) => {
1050 tracing::warn!(target: "rpc::debug", %number, %err, "Failed to trace chain block");
1051 break
1052 }
1053 };
1054 drop(permit);
1055
1056 if traces.is_empty() && number != end_number {
1057 continue
1058 }
1059 let result = ChainBlockTraceResult {
1060 block: U256::from(number),
1061 hash: block.hash(),
1062 traces,
1063 };
1064 let message = match SubscriptionMessage::new(
1065 sink.method_name(),
1066 sink.subscription_id(),
1067 &result,
1068 ) {
1069 Ok(message) => message,
1070 Err(err) => {
1071 tracing::warn!(target: "rpc::debug", %number, %err, "Failed to serialize chain trace");
1072 break
1073 }
1074 };
1075 if sink.send(message).await.is_err() {
1076 break
1077 }
1078 }
1079
1080 sink.closed().await;
1084 });
1085
1086 Ok(())
1087 }
1088
1089 async fn debug_trace_block(
1091 &self,
1092 rlp_block: Bytes,
1093 opts: Option<GethDebugTracingOptions>,
1094 ) -> RpcResult<Vec<TraceResult>> {
1095 let _permit = self.acquire_trace_permit().await;
1096 Self::debug_trace_raw_block(self, rlp_block, opts.unwrap_or_default())
1097 .await
1098 .map_err(Into::into)
1099 }
1100
1101 async fn debug_trace_block_by_hash(
1103 &self,
1104 block: B256,
1105 opts: Option<GethDebugTracingOptions>,
1106 ) -> RpcResult<Vec<TraceResult>> {
1107 let _permit = self.acquire_trace_permit().await;
1108 Self::debug_trace_block(self, block.into(), opts.unwrap_or_default())
1109 .await
1110 .map_err(Into::into)
1111 }
1112
1113 async fn debug_trace_block_by_number(
1115 &self,
1116 block: BlockNumberOrTag,
1117 opts: Option<GethDebugTracingOptions>,
1118 ) -> RpcResult<Vec<TraceResult>> {
1119 let _permit = self.acquire_trace_permit().await;
1120 Self::debug_trace_block(self, block.into(), opts.unwrap_or_default())
1121 .await
1122 .map_err(Into::into)
1123 }
1124
1125 async fn debug_trace_transaction(
1127 &self,
1128 tx_hash: B256,
1129 opts: Option<GethDebugTracingOptions>,
1130 ) -> RpcResult<GethTrace> {
1131 let _permit = self.acquire_trace_permit().await;
1132 Self::debug_trace_transaction(self, tx_hash, opts.unwrap_or_default())
1133 .await
1134 .map_err(Into::into)
1135 }
1136
1137 async fn debug_trace_call(
1139 &self,
1140 request: RpcTxReq<Eth::NetworkTypes>,
1141 block_id: Option<BlockId>,
1142 opts: Option<GethDebugTracingCallOptions>,
1143 ) -> RpcResult<GethTrace> {
1144 let _permit = self.acquire_trace_permit().await;
1145 Self::debug_trace_call(self, request, block_id, opts.unwrap_or_default())
1146 .await
1147 .map_err(Into::into)
1148 }
1149
1150 async fn debug_trace_call_many(
1151 &self,
1152 bundles: Vec<Bundle<RpcTxReq<Eth::NetworkTypes>>>,
1153 state_context: Option<StateContext>,
1154 opts: Option<GethDebugTracingCallOptions>,
1155 ) -> RpcResult<Vec<Vec<GethTrace>>> {
1156 let _permit = self.acquire_trace_permit().await;
1157 Self::debug_trace_call_many(self, bundles, state_context, opts).await.map_err(Into::into)
1158 }
1159
1160 async fn debug_execution_witness(
1162 &self,
1163 block: BlockId,
1164 mode: Option<ExecutionWitnessMode>,
1165 ) -> RpcResult<ExecutionWitness> {
1166 let _permit = self.acquire_trace_permit().await;
1167 Self::debug_execution_witness(self, block, mode).await.map_err(Into::into)
1168 }
1169
1170 async fn debug_execution_witness_by_block_hash(
1172 &self,
1173 hash: B256,
1174 mode: Option<ExecutionWitnessMode>,
1175 ) -> RpcResult<ExecutionWitness> {
1176 let _permit = self.acquire_trace_permit().await;
1177 Self::debug_execution_witness_by_block_hash(self, hash, mode).await.map_err(Into::into)
1178 }
1179
1180 async fn debug_account_at(
1182 &self,
1183 block_id: BlockId,
1184 tx_index: Index,
1185 address: Address,
1186 ) -> RpcResult<Option<Account>> {
1187 let _permit = self.acquire_trace_permit().await;
1188 Self::debug_account_at(self, block_id, tx_index, address).await.map_err(Into::into)
1189 }
1190
1191 async fn debug_account_info_at(
1193 &self,
1194 block_id: BlockId,
1195 tx_index: Index,
1196 address: Address,
1197 ) -> RpcResult<Option<AccountInfo>> {
1198 let _permit = self.acquire_trace_permit().await;
1199 Self::debug_account_info_at(self, block_id, tx_index, address).await.map_err(Into::into)
1200 }
1201
1202 async fn debug_account_range(
1203 &self,
1204 _block_number: BlockNumberOrTag,
1205 _start: Bytes,
1206 _max_results: u64,
1207 _nocode: bool,
1208 _nostorage: bool,
1209 _incompletes: bool,
1210 ) -> RpcResult<()> {
1211 Ok(())
1212 }
1213
1214 async fn debug_chaindb_compact(&self) -> RpcResult<()> {
1215 Ok(())
1216 }
1217
1218 async fn debug_chain_config(&self) -> RpcResult<ChainConfig> {
1219 Ok(self.provider().chain_spec().genesis().config.clone())
1220 }
1221
1222 async fn debug_chaindb_property(&self, _property: String) -> RpcResult<()> {
1223 Ok(())
1224 }
1225
1226 async fn debug_code_by_hash(
1227 &self,
1228 hash: B256,
1229 block_id: Option<BlockId>,
1230 ) -> RpcResult<Option<Bytes>> {
1231 Self::debug_code_by_hash(self, hash, block_id).await.map_err(Into::into)
1232 }
1233
1234 async fn debug_db_ancient(&self, _kind: String, _number: u64) -> RpcResult<()> {
1235 Ok(())
1236 }
1237
1238 async fn debug_db_ancients(&self) -> RpcResult<()> {
1239 Ok(())
1240 }
1241
1242 async fn debug_db_get(&self, key: String) -> RpcResult<Option<Bytes>> {
1253 let key_bytes = if key.starts_with("0x") {
1254 decode(&key).map_err(|_| EthApiError::InvalidParams("Invalid hex key".to_string()))?
1255 } else {
1256 key.into_bytes()
1257 };
1258
1259 if key_bytes.len() != 33 {
1260 return Err(EthApiError::InvalidParams(format!(
1261 "Key must be 33 bytes, got {}",
1262 key_bytes.len()
1263 ))
1264 .into());
1265 }
1266 if key_bytes[0] != 0x63 {
1267 return Err(EthApiError::InvalidParams("Key prefix must be 0x63".to_string()).into());
1268 }
1269
1270 let code_hash = B256::from_slice(&key_bytes[1..33]);
1271
1272 self.debug_code_by_hash(code_hash, None).await.map_err(Into::into)
1274 }
1275
1276 async fn debug_dump_block(&self, _number: BlockId) -> RpcResult<()> {
1277 Ok(())
1278 }
1279
1280 async fn debug_free_os_memory(&self) -> RpcResult<()> {
1281 Ok(())
1282 }
1283
1284 async fn debug_gc_stats(&self) -> RpcResult<()> {
1285 Ok(())
1286 }
1287
1288 async fn debug_get_accessible_state(
1289 &self,
1290 _from: BlockNumberOrTag,
1291 _to: BlockNumberOrTag,
1292 ) -> RpcResult<()> {
1293 Ok(())
1294 }
1295
1296 async fn debug_get_modified_accounts_by_hash(
1297 &self,
1298 _start_hash: B256,
1299 _end_hash: B256,
1300 ) -> RpcResult<()> {
1301 Ok(())
1302 }
1303
1304 async fn debug_get_modified_accounts_by_number(
1305 &self,
1306 _start_number: u64,
1307 _end_number: u64,
1308 ) -> RpcResult<()> {
1309 Ok(())
1310 }
1311
1312 async fn debug_intermediate_roots(
1313 &self,
1314 block_hash: B256,
1315 _opts: Option<GethDebugTracingCallOptions>,
1316 ) -> RpcResult<Vec<B256>> {
1317 let _permit = self.acquire_trace_permit().await;
1318 self.intermediate_roots(block_hash).await.map_err(Into::into)
1319 }
1320
1321 async fn debug_mem_stats(&self) -> RpcResult<()> {
1322 Ok(())
1323 }
1324
1325 async fn debug_preimage(&self, _hash: B256) -> RpcResult<()> {
1326 Ok(())
1327 }
1328
1329 async fn debug_print_block(&self, _number: u64) -> RpcResult<()> {
1330 Ok(())
1331 }
1332
1333 async fn debug_seed_hash(&self, _number: u64) -> RpcResult<B256> {
1334 Ok(Default::default())
1335 }
1336
1337 async fn debug_set_gc_percent(&self, _v: i32) -> RpcResult<()> {
1338 Ok(())
1339 }
1340
1341 async fn debug_set_head(&self, _number: U64) -> RpcResult<()> {
1342 Ok(())
1343 }
1344
1345 async fn debug_set_trie_flush_interval(&self, _interval: String) -> RpcResult<()> {
1346 Ok(())
1347 }
1348
1349 async fn debug_standard_trace_bad_block_to_file(
1350 &self,
1351 _block: BlockNumberOrTag,
1352 _opts: Option<GethDebugTracingCallOptions>,
1353 ) -> RpcResult<()> {
1354 Ok(())
1355 }
1356
1357 async fn debug_standard_trace_block_to_file(
1358 &self,
1359 _block: BlockNumberOrTag,
1360 _opts: Option<GethDebugTracingCallOptions>,
1361 ) -> RpcResult<()> {
1362 Ok(())
1363 }
1364
1365 async fn debug_state_root_with_updates(
1366 &self,
1367 hashed_state: HashedPostState,
1368 block_id: Option<BlockId>,
1369 ) -> RpcResult<(B256, TrieUpdates)> {
1370 Self::debug_state_root_with_updates(self, hashed_state, block_id).await.map_err(Into::into)
1371 }
1372
1373 async fn debug_storage_range_at(
1374 &self,
1375 _block_hash: B256,
1376 _tx_idx: usize,
1377 _contract_address: Address,
1378 _key_start: B256,
1379 _max_result: u64,
1380 ) -> RpcResult<()> {
1381 Ok(())
1382 }
1383
1384 async fn debug_trace_bad_block(
1385 &self,
1386 block_hash: B256,
1387 opts: Option<GethDebugTracingCallOptions>,
1388 ) -> RpcResult<Vec<TraceResult>> {
1389 let _permit = self.acquire_trace_permit().await;
1390 let entry = self
1391 .inner
1392 .bad_block_store
1393 .get(block_hash)
1394 .ok_or_else(|| internal_rpc_err("bad block not found in cache"))?;
1395
1396 let evm_env = self
1397 .eth_api()
1398 .evm_config()
1399 .evm_env(entry.block.header())
1400 .map_err(RethError::other)
1401 .to_rpc_result()?;
1402
1403 let opts = opts.map(|o| o.tracing_options).unwrap_or_default();
1404 self.trace_block(entry.block.clone(), evm_env, opts).await.map_err(Into::into)
1405 }
1406}
1407
1408impl<Eth: RpcNodeCore> std::fmt::Debug for DebugApi<Eth> {
1409 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1410 f.debug_struct("DebugApi").finish_non_exhaustive()
1411 }
1412}
1413
1414impl<Eth: RpcNodeCore> Clone for DebugApi<Eth> {
1415 fn clone(&self) -> Self {
1416 Self { inner: Arc::clone(&self.inner) }
1417 }
1418}
1419
1420struct DebugApiInner<Eth: RpcNodeCore> {
1421 eth_api: Eth,
1423 blocking_task_guard: BlockingTaskGuard,
1425 task_spawner: Runtime,
1427 bad_block_store: BadBlockStore<BlockTy<Eth::Primitives>>,
1429}
1430
1431#[derive(Clone, Debug)]
1433struct BadBlockStore<B: BlockTrait> {
1434 inner: Arc<RwLock<VecDeque<BadBlockEntry<B>>>>,
1435 limit: usize,
1436}
1437
1438#[derive(Clone, Debug)]
1440struct BadBlockEntry<B: BlockTrait> {
1441 block: Arc<RecoveredBlock<B>>,
1442 reason: String,
1443}
1444
1445impl<B: BlockTrait> BadBlockStore<B> {
1446 fn new(limit: usize) -> Self {
1448 Self { inner: Arc::new(RwLock::new(VecDeque::with_capacity(limit))), limit }
1449 }
1450
1451 fn insert(&self, block: RecoveredBlock<B>, reason: String) {
1454 let hash = block.hash();
1455 let mut guard = self.inner.write();
1456
1457 if guard.iter().any(|entry| entry.block.hash() == hash) {
1459 return;
1460 }
1461 guard.push_back(BadBlockEntry { block: Arc::new(block), reason });
1462
1463 while guard.len() > self.limit {
1464 guard.pop_front();
1465 }
1466 }
1467
1468 fn all(&self) -> Vec<BadBlockEntry<B>> {
1470 let guard = self.inner.read();
1471 guard.iter().rev().cloned().collect()
1472 }
1473
1474 fn get(&self, hash: B256) -> Option<BadBlockEntry<B>> {
1476 let guard = self.inner.read();
1477 guard.iter().find(|entry| entry.block.hash() == hash).cloned()
1478 }
1479}
1480
1481impl<B: BlockTrait> Default for BadBlockStore<B> {
1482 fn default() -> Self {
1483 Self::new(64)
1484 }
1485}
1486
1487#[cfg(test)]
1488mod tests {
1489 use super::*;
1490 use alloy_primitives::{keccak256, U256};
1491 use reth_db_api::{tables, transaction::DbTxMut};
1492 use reth_primitives_traits::StorageEntry;
1493 use reth_provider::test_utils::create_test_provider_factory;
1494 use revm::{
1495 database::{states::StorageSlot, AccountStatus, BundleAccount, BundleState},
1496 state::AccountInfo as RevmAccountInfo,
1497 };
1498
1499 #[test]
1500 fn hashed_post_state_zeroes_destroyed_account_parent_storage() {
1501 let factory = create_test_provider_factory();
1502 let address = Address::with_last_byte(1);
1503 let old_slot = U256::from(1);
1504 let new_slot = U256::from(2);
1505 let old_value = U256::from(10);
1506 let new_value = U256::from(20);
1507 let hashed_address = keccak256(address);
1508 let hashed_old_slot = keccak256(B256::from(old_slot));
1509 let hashed_new_slot = keccak256(B256::from(new_slot));
1510
1511 let provider_rw = factory.provider_rw().unwrap();
1512 provider_rw
1513 .tx_ref()
1514 .put::<tables::HashedStorages>(
1515 hashed_address,
1516 StorageEntry { key: hashed_old_slot, value: old_value },
1517 )
1518 .unwrap();
1519 provider_rw.commit().unwrap();
1520
1521 let mut bundle_state = BundleState::default();
1522 bundle_state.state.insert(
1523 address,
1524 BundleAccount::new(
1525 Some(RevmAccountInfo::default()),
1526 Some(RevmAccountInfo::default()),
1527 std::iter::once((new_slot, StorageSlot::new_changed(U256::ZERO, new_value)))
1528 .collect(),
1529 AccountStatus::DestroyedChanged,
1530 ),
1531 );
1532
1533 let provider = factory.latest().unwrap();
1534 let hashed_state = provider.hashed_post_state(&bundle_state).unwrap();
1535 let storage = &hashed_state.storages[&hashed_address];
1536
1537 assert!(!storage.wiped);
1538 assert_eq!(storage.storage[&hashed_old_slot], U256::ZERO);
1539 assert_eq!(storage.storage[&hashed_new_slot], new_value);
1540 }
1541}