1use alloy_consensus::{transaction::TxHashRef, BlockHeader};
2use alloy_eips::{eip2718::Encodable2718, BlockId, BlockNumberOrTag};
3use alloy_evm::{env::BlockEnvironment, Evm};
4use alloy_genesis::ChainConfig;
5use alloy_primitives::{hex::decode, uint, Address, Bytes, B256, 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, 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 AsEthApiError, 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, StateProviderBox, StateProviderFactory, StateRootProvider,
39 StorageRootProvider, TransactionVariant,
40};
41use reth_tasks::{cancel::is_cancelled, 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#[cfg(feature = "js-tracer")]
54use {
55 alloy_rpc_types_trace::geth::GethDebugTracerType,
56 revm_inspectors::tracing::js::JsInspectorInterrupt, std::time::Duration,
57};
58
59pub struct DebugApi<Eth: RpcNodeCore> {
63 inner: Arc<DebugApiInner<Eth>>,
64}
65
66impl<Eth> DebugApi<Eth>
67where
68 Eth: RpcNodeCore,
69{
70 pub fn new(
72 eth_api: Eth,
73 blocking_task_guard: BlockingTaskGuard,
74 executor: &Runtime,
75 mut stream: impl Stream<Item = ConsensusEngineEvent<Eth::Primitives>> + Send + Unpin + 'static,
76 ) -> Self {
77 let bad_block_store = BadBlockStore::default();
78 let inner = Arc::new(DebugApiInner {
79 eth_api,
80 blocking_task_guard,
81 task_spawner: executor.clone(),
82 bad_block_store: bad_block_store.clone(),
83 });
84
85 executor.spawn_task(async move {
87 while let Some(event) = stream.next().await {
88 if let ConsensusEngineEvent::InvalidBlock { block, error } = event &&
89 let Ok(recovered) = RecoveredBlock::try_recover_sealed(*block)
90 {
91 bad_block_store.insert(recovered, error);
92 }
93 }
94 });
95
96 Self { inner }
97 }
98
99 pub fn eth_api(&self) -> &Eth {
101 &self.inner.eth_api
102 }
103
104 pub fn provider(&self) -> &Eth::Provider {
106 self.inner.eth_api.provider()
107 }
108}
109
110impl<Eth> DebugApi<Eth>
113where
114 Eth: TraceExt,
115{
116 async fn acquire_trace_permit(&self) -> Result<OwnedSemaphorePermit, AcquireError> {
118 self.inner.blocking_task_guard.clone().acquire_owned().await
119 }
120
121 async fn trace_block(
123 &self,
124 block: Arc<RecoveredBlock<ProviderBlock<Eth::Provider>>>,
125 evm_env: EvmEnvFor<Eth::Evm>,
126 opts: GethDebugTracingOptions,
127 ) -> Result<Vec<TraceResult>, Eth::Error> {
128 self.eth_api()
129 .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
130 let mut results = Vec::with_capacity(block.body().transactions().len());
131
132 eth_api.apply_pre_execution_changes(&block, &mut db)?;
133
134 let block_env = evm_env.block_env.clone();
135
136 let mut transactions = block.transactions_recovered().enumerate().peekable();
137 let inspector = DebugInspector::new(opts).map_err(Eth::Error::from_eth_err)?;
138 let mut evm =
139 eth_api.evm_config().evm_with_env_and_inspector(&mut db, evm_env, inspector);
140 while let Some((index, tx)) = transactions.next() {
141 if is_cancelled() {
142 return Err(EthApiError::InternalEthError.into())
143 }
144 let tx_env = eth_api.evm_config().tx_env(tx);
145
146 let res = evm.transact(tx_env.clone()).map_err(Eth::Error::from_evm_err)?;
147
148 let (db, inspector, _) = evm.components_mut();
149 let result = inspector
150 .get_result(
151 Some(TransactionContext {
152 block_hash: Some(block.hash()),
153 tx_hash: Some(*tx.tx_hash()),
154 tx_index: Some(index),
155 }),
156 &tx_env,
157 &block_env,
158 &res,
159 db,
160 )
161 .map_err(Eth::Error::from_eth_err)?;
162
163 results.push(TraceResult::Success { result, tx_hash: Some(*tx.tx_hash()) });
164 if transactions.peek().is_some() {
165 inspector.fuse().map_err(Eth::Error::from_eth_err)?;
166 db.commit(res.state)
169 }
170 }
171
172 Ok(results)
173 })
174 .await
175 }
176
177 async fn trace_chain_block(
180 &self,
181 block: Arc<RecoveredBlock<ProviderBlock<Eth::Provider>>>,
182 opts: GethDebugTracingOptions,
183 ) -> Result<Vec<Option<TraceResult>>, Eth::Error> {
184 let evm_env = self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
185
186 self.eth_api()
187 .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
188 let tx_count = block.body().transactions().len();
189 let mut results = Vec::with_capacity(tx_count);
190
191 eth_api.apply_pre_execution_changes(&block, &mut db)?;
192
193 let block_env = evm_env.block_env.clone();
194 let mut transactions = block.transactions_recovered().enumerate().peekable();
195 let inspector = match DebugInspector::new(opts) {
196 Ok(inspector) => inspector,
197 Err(err) => {
198 if let Some((_, tx)) = transactions.peek() {
199 results.push(Some(TraceResult::Error {
200 error: err.to_string(),
201 tx_hash: Some(*tx.tx_hash()),
202 }));
203 }
204 results.resize(tx_count, None);
205 return Ok(results)
206 }
207 };
208 let mut evm =
209 eth_api.evm_config().evm_with_env_and_inspector(&mut db, evm_env, inspector);
210
211 while let Some((index, tx)) = transactions.next() {
212 let tx_hash = *tx.tx_hash();
213 let tx_env = eth_api.evm_config().tx_env(tx);
214 let res = match evm.transact(tx_env.clone()) {
215 Ok(res) => res,
216 Err(err) => {
217 results.push(Some(TraceResult::Error {
218 error: err.to_string(),
219 tx_hash: Some(tx_hash),
220 }));
221 break
222 }
223 };
224
225 let (db, inspector, _) = evm.components_mut();
226 let result = match inspector.get_result(
227 Some(TransactionContext {
228 block_hash: Some(block.hash()),
229 tx_hash: Some(tx_hash),
230 tx_index: Some(index),
231 }),
232 &tx_env,
233 &block_env,
234 &res,
235 db,
236 ) {
237 Ok(result) => result,
238 Err(err) => {
239 results.push(Some(TraceResult::Error {
240 error: err.to_string(),
241 tx_hash: Some(tx_hash),
242 }));
243 break
244 }
245 };
246
247 results.push(Some(TraceResult::Success { result, tx_hash: Some(tx_hash) }));
248 if let Some((_, next_tx)) = transactions.peek() {
249 if let Err(err) = inspector.fuse() {
250 results.push(Some(TraceResult::Error {
251 error: err.to_string(),
252 tx_hash: Some(*next_tx.tx_hash()),
253 }));
254 break
255 }
256 db.commit(res.state);
257 }
258 }
259
260 results.resize(tx_count, None);
261 Ok(results)
262 })
263 .await
264 }
265
266 pub async fn debug_trace_raw_block(
272 &self,
273 rlp_block: Bytes,
274 opts: GethDebugTracingOptions,
275 ) -> Result<Vec<TraceResult>, Eth::Error> {
276 let block: ProviderBlock<Eth::Provider> = Decodable::decode(&mut rlp_block.as_ref())
277 .map_err(BlockError::RlpDecodeRawBlock)
278 .map_err(Eth::Error::from_eth_err)?;
279
280 let evm_env = self
281 .eth_api()
282 .evm_config()
283 .evm_env(block.header())
284 .map_err(RethError::other)
285 .map_err(Eth::Error::from_eth_err)?;
286
287 let senders =
289 if self.provider().chain_spec().is_homestead_active_at_block(block.header().number()) {
290 block.body().recover_signers()
291 } else {
292 block.body().recover_signers_unchecked()
293 }
294 .map_err(Eth::Error::from_eth_err)?;
295
296 self.trace_block(Arc::new(block.into_recovered_with_signers(senders)), evm_env, opts).await
297 }
298
299 pub async fn debug_trace_block(
301 &self,
302 block_id: BlockId,
303 opts: GethDebugTracingOptions,
304 ) -> Result<Vec<TraceResult>, Eth::Error> {
305 let block = self
306 .eth_api()
307 .recovered_block(block_id)
308 .await?
309 .ok_or(EthApiError::TracingBlockNotFound(block_id))?;
310 if block.number() == 0 {
312 return Err(EthApiError::GenesisNotTraceable.into())
313 }
314 let evm_env = self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
315
316 self.trace_block(block, evm_env, opts).await
317 }
318
319 pub async fn debug_trace_transaction(
323 &self,
324 tx_hash: B256,
325 opts: GethDebugTracingOptions,
326 ) -> Result<GethTrace, Eth::Error> {
327 let (transaction, block, bal) =
328 match self.eth_api().transaction_and_block_and_maybe_bal(tx_hash).await? {
329 None => return Err(EthApiError::TracingTransactionNotFound.into()),
330 Some(res) => res,
331 };
332
333 let interrupt = JsTraceInterrupt::new(&opts)?;
334 let tracer_interrupt = interrupt.clone();
335 let trace = self.eth_api().spawn_with_state_at_block(
336 block.parent_hash(),
337 move |eth_api, mut db| {
338 let (tx, tx_info) = transaction.split();
340
341 let index =
344 tx_info.index.expect("transaction_and_block only returns block transactions")
345 as usize;
346
347 let mut inspector = tracer_interrupt
348 .attach(DebugInspector::new(opts).map_err(Eth::Error::from_eth_err)?);
349 let tx_env = eth_api.evm_config().tx_env(&tx);
350 let (res, evm_env) = eth_api.inspect_transaction_in_block(
351 &block,
352 &mut db,
353 &mut inspector,
354 index,
355 tx_env.clone(),
356 bal.as_deref(),
357 )?;
358
359 let trace = inspector
360 .get_result(
361 Some(TransactionContext {
362 block_hash: Some(block.hash()),
363 tx_index: Some(index),
364 tx_hash: Some(*tx.tx_hash()),
365 }),
366 &tx_env,
367 &evm_env.block_env,
368 &res,
369 &mut db,
370 )
371 .map_err(Eth::Error::from_eth_err)?;
372
373 Ok(trace)
374 },
375 );
376 interrupt.run(trace).await
377 }
378
379 pub async fn debug_trace_call(
389 &self,
390 call: RpcTxReq<Eth::NetworkTypes>,
391 block_id: Option<BlockId>,
392 opts: GethDebugTracingCallOptions,
393 ) -> Result<GethTrace, Eth::Error> {
394 let at = block_id.unwrap_or_default();
395 let GethDebugTracingCallOptions {
396 tracing_options,
397 state_overrides,
398 block_overrides,
399 tx_index,
400 } = opts;
401 let overrides = EvmOverrides::new(state_overrides, block_overrides.map(Box::new));
402
403 if let Some(tx_idx) = tx_index {
405 return self
406 .debug_trace_call_at_tx_index(call, at, tx_idx as usize, tracing_options, overrides)
407 .await;
408 }
409
410 let interrupt = JsTraceInterrupt::new(&tracing_options)?;
411 let tracer_interrupt = interrupt.clone();
412 let this = self.clone();
413 let trace =
414 self.eth_api().spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| {
415 let mut inspector = tracer_interrupt.attach(
416 DebugInspector::new(tracing_options).map_err(Eth::Error::from_eth_err)?,
417 );
418 let res = this.eth_api().inspect(
419 &mut *db,
420 evm_env.clone(),
421 tx_env.clone(),
422 &mut inspector,
423 )?;
424 let trace = inspector
425 .get_result(None, &tx_env, &evm_env.block_env, &res, db)
426 .map_err(Eth::Error::from_eth_err)?;
427 Ok(trace)
428 });
429 interrupt.run(trace).await.map_err(|err| match err.as_err() {
430 Some(EthApiError::HeaderNotFound(id)) if *id == at => {
431 EthApiError::TracingBlockNotFound(at).into()
433 }
434 _ => err,
435 })
436 }
437
438 async fn debug_trace_call_at_tx_index(
442 &self,
443 call: RpcTxReq<Eth::NetworkTypes>,
444 block_id: BlockId,
445 tx_index: usize,
446 tracing_options: GethDebugTracingOptions,
447 overrides: EvmOverrides,
448 ) -> Result<GethTrace, Eth::Error> {
449 let (block, bal) = self
451 .eth_api()
452 .recovered_block_and_maybe_bal(block_id)
453 .await?
454 .ok_or(EthApiError::TracingBlockNotFound(block_id))?;
455
456 if tx_index >= block.transaction_count() {
457 return Err(EthApiError::InvalidParams(format!(
459 "tx_index {} out of bounds for block with {} transactions",
460 tx_index,
461 block.transaction_count()
462 ))
463 .into())
464 }
465
466 let bal = bal.filter(|_| !overrides.has_state());
469
470 let evm_env = self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
471
472 let interrupt = JsTraceInterrupt::new(&tracing_options)?;
473 let tracer_interrupt = interrupt.clone();
474 let trace = self.eth_api().spawn_with_state_at_block(
475 block.parent_hash(),
476 move |eth_api, mut db| {
477 eth_api.replay_block_until(&mut db, &block, tx_index, bal.as_deref())?;
479
480 let (evm_env, tx_env) =
482 eth_api.prepare_call_env(evm_env, call, &mut db, overrides)?;
483
484 let mut inspector = tracer_interrupt.attach(
485 DebugInspector::new(tracing_options).map_err(Eth::Error::from_eth_err)?,
486 );
487 let res =
488 eth_api.inspect(&mut db, evm_env.clone(), tx_env.clone(), &mut inspector)?;
489 let trace = inspector
490 .get_result(None, &tx_env, &evm_env.block_env, &res, &mut db)
491 .map_err(Eth::Error::from_eth_err)?;
492
493 Ok(trace)
494 },
495 );
496 interrupt.run(trace).await
497 }
498
499 pub async fn debug_trace_call_many(
503 &self,
504 bundles: Vec<Bundle<RpcTxReq<Eth::NetworkTypes>>>,
505 state_context: Option<StateContext>,
506 opts: Option<GethDebugTracingCallOptions>,
507 ) -> Result<Vec<Vec<GethTrace>>, Eth::Error> {
508 if bundles.is_empty() {
509 return Err(EthApiError::InvalidParams(String::from("bundles are empty.")).into())
510 }
511
512 let StateContext { transaction_index, block_number } = state_context.unwrap_or_default();
513 let transaction_index = transaction_index.unwrap_or_default();
514
515 let target_block = block_number.unwrap_or_default();
516 let block = self
517 .eth_api()
518 .recovered_block(target_block)
519 .await?
520 .ok_or(EthApiError::HeaderNotFound(target_block))?;
521 let mut evm_env =
522 self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
523
524 let opts = opts.unwrap_or_default();
525 let GethDebugTracingCallOptions { tracing_options, mut state_overrides, .. } = opts;
526
527 let mut at = block.parent_hash();
530 let mut replay_block_txs = true;
531
532 let num_txs =
534 transaction_index.index().unwrap_or_else(|| block.body().transactions().len());
535 if !target_block.is_pending() && num_txs == block.body().transactions().len() {
539 at = block.hash();
540 replay_block_txs = false;
541 }
542
543 self.eth_api()
544 .spawn_with_state_at_block(at, move |eth_api, mut db| {
545 let mut all_bundles = Vec::with_capacity(bundles.len());
547
548 if replay_block_txs {
549 eth_api.replay_block_until(&mut db, &block, num_txs, None)?;
555 }
556
557 let mut bundles = bundles.into_iter().peekable();
559 let mut inspector = DebugInspector::new(tracing_options.clone())
560 .map_err(Eth::Error::from_eth_err)?;
561 while let Some(bundle) = bundles.next() {
562 let mut results = Vec::with_capacity(bundle.transactions.len());
563 let Bundle { transactions, block_override } = bundle;
564
565 let block_overrides = block_override.map(Box::new);
566
567 let mut transactions = transactions.into_iter().peekable();
568 while let Some(tx) = transactions.next() {
569 if is_cancelled() {
570 return Err(EthApiError::InternalEthError.into())
571 }
572 let state_overrides = state_overrides.take();
574 let overrides = EvmOverrides::new(state_overrides, block_overrides.clone());
575
576 let (evm_env, tx_env) =
577 eth_api.prepare_call_env(evm_env.clone(), tx, &mut db, overrides)?;
578
579 let res = eth_api.inspect(
580 &mut db,
581 evm_env.clone(),
582 tx_env.clone(),
583 &mut inspector,
584 )?;
585 let trace = inspector
586 .get_result(None, &tx_env, &evm_env.block_env, &res, &mut db)
587 .map_err(Eth::Error::from_eth_err)?;
588
589 if transactions.peek().is_some() || bundles.peek().is_some() {
592 inspector.fuse().map_err(Eth::Error::from_eth_err)?;
593 db.commit(res.state);
594 }
595 results.push(trace);
596 }
597 evm_env.block_env.inner_mut().number += uint!(1_U256);
599 evm_env.block_env.inner_mut().timestamp += uint!(12_U256);
600
601 all_bundles.push(results);
602 }
603 Ok(all_bundles)
604 })
605 .await
606 }
607
608 pub async fn debug_execution_witness_by_block_hash(
611 &self,
612 hash: B256,
613 mode: Option<ExecutionWitnessMode>,
614 ) -> Result<ExecutionWitness, Eth::Error> {
615 let this = self.clone();
616 let block = this
617 .eth_api()
618 .recovered_block(hash.into())
619 .await?
620 .ok_or(EthApiError::HeaderNotFound(hash.into()))?;
621
622 self.debug_execution_witness_for_block(block, mode.unwrap_or_default()).await
623 }
624
625 pub async fn debug_execution_witness(
630 &self,
631 block_id: BlockId,
632 mode: Option<ExecutionWitnessMode>,
633 ) -> Result<ExecutionWitness, Eth::Error> {
634 let this = self.clone();
635 let block = this
636 .eth_api()
637 .recovered_block(block_id)
638 .await?
639 .ok_or(EthApiError::HeaderNotFound(block_id))?;
640
641 self.debug_execution_witness_for_block(block, mode.unwrap_or_default()).await
642 }
643
644 pub async fn debug_execution_witness_for_block(
646 &self,
647 block: Arc<RecoveredBlock<ProviderBlock<Eth::Provider>>>,
648 mode: ExecutionWitnessMode,
649 ) -> Result<ExecutionWitness, Eth::Error> {
650 let block_number = block.header().number();
651 self.eth_api()
652 .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
653 let block_executor = eth_api.evm_config().executor(&mut db);
654
655 let mut witness = None;
656 let _ = block_executor
657 .execute_with_state_closure(&block, |statedb: &State<_>| {
658 witness = Some(
659 ExecutionWitnessRecord::new(statedb)
660 .into_execution_witness::<StateProviderBox, _>(
661 &statedb.database.database,
662 eth_api.provider(),
663 block_number,
664 mode,
665 ),
666 );
667 })
668 .map_err(|err| EthApiError::Internal(err.into()))?;
669
670 Ok(witness
671 .expect("state closure is called after successful execution")
672 .map_err(EthApiError::from)?)
673 })
674 .await
675 }
676
677 pub async fn debug_account_at(
680 &self,
681 block_id: BlockId,
682 tx_index: Index,
683 address: Address,
684 ) -> Result<Option<Account>, Eth::Error> {
685 self.replay_block_until(block_id, tx_index, move |db| Self::account(db, address))
686 .await
687 .map(Option::flatten)
688 }
689
690 pub async fn debug_account_info_at(
693 &self,
694 block_id: BlockId,
695 tx_index: Index,
696 address: Address,
697 ) -> Result<Option<AccountInfo>, Eth::Error> {
698 self.replay_block_until(block_id, tx_index, move |db| Self::account_info(db, address)).await
699 }
700
701 async fn replay_block_until<F, R>(
704 &self,
705 block_id: BlockId,
706 tx_index: Index,
707 f: F,
708 ) -> Result<Option<R>, Eth::Error>
709 where
710 F: FnOnce(&mut StateCacheDb) -> Result<R, Eth::Error> + Send + 'static,
711 R: Send + 'static,
712 {
713 let block = self
714 .eth_api()
715 .recovered_block(block_id)
716 .await?
717 .ok_or(EthApiError::HeaderNotFound(block_id))?;
718 let tx_index = usize::from(tx_index);
719 let transaction_count = block.transaction_count();
720 if tx_index >= transaction_count {
721 return Err(EthApiError::InvalidParams(format!(
722 "tx_index {tx_index} out of bounds for block with {transaction_count} transactions"
723 ))
724 .into())
725 }
726
727 self.eth_api()
728 .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
729 let mut executor = eth_api
730 .evm_config()
731 .executor_for_block(&mut db, block.sealed_block())
732 .map_err(RethError::other)
733 .map_err(Eth::Error::from_eth_err)?;
734 executor.apply_pre_execution_changes().map_err(Eth::Error::from_eth_err)?;
735
736 for tx in block.transactions_recovered().take(tx_index + 1) {
737 if is_cancelled() {
738 return Err(EthApiError::InternalEthError.into())
739 }
740 executor.execute_transaction(tx).map_err(Eth::Error::from_eth_err)?;
741 }
742 drop(executor);
743
744 f(&mut db)
745 })
746 .await
747 .map(Some)
748 }
749
750 fn account(db: &mut StateCacheDb, address: Address) -> Result<Option<Account>, Eth::Error> {
752 let account = db.basic(address).map_err(Eth::Error::from_eth_err)?;
753 let Some(account) = account else { return Ok(None) };
754
755 let (hashed_storage, status) = db
756 .cache
757 .accounts
758 .get(&address)
759 .and_then(|account| {
760 account.account.as_ref().map(|plain_account| {
761 (HashedStorage::from_plain_storage(&plain_account.storage), account.status)
762 })
763 })
764 .unwrap_or_default();
765 let storage_root = if status.was_destroyed() {
766 storage_root_unsorted(
769 hashed_storage.storage.into_iter().filter(|(_, value)| !value.is_zero()),
770 )
771 } else {
772 db.database.storage_root(address, hashed_storage).map_err(Eth::Error::from_eth_err)?
773 };
774
775 Ok(Some(reth_primitives_traits::Account::from(account).into_trie_account(storage_root)))
776 }
777
778 #[allow(clippy::needless_update)]
780 fn account_info<DB>(db: &mut DB, address: Address) -> Result<AccountInfo, Eth::Error>
781 where
782 DB: Database,
783 EthApiError: From<DB::Error>,
784 {
785 let account = db.basic(address).map_err(Eth::Error::from_eth_err)?.unwrap_or_default();
786 let code = if account.is_empty_code_hash() {
787 Default::default()
788 } else if let Some(code) = account.code {
789 code.original_bytes()
790 } else {
791 db.code_by_hash(account.code_hash).map_err(Eth::Error::from_eth_err)?.original_bytes()
792 };
793
794 Ok(AccountInfo {
795 balance: account.balance,
796 nonce: account.nonce,
797 code,
798 #[cfg(feature = "account-ext")]
799 extension: reth_primitives_traits::AccountExtension::from_shared(
800 account.extension.into_shared(),
801 ),
802 ..Default::default()
803 })
804 }
805
806 pub async fn debug_code_by_hash(
809 &self,
810 hash: B256,
811 block_id: Option<BlockId>,
812 ) -> Result<Option<Bytes>, Eth::Error> {
813 Ok(self
814 .provider()
815 .state_by_block_id(block_id.unwrap_or_default())
816 .map_err(Eth::Error::from_eth_err)?
817 .bytecode_by_hash(&hash)
818 .map_err(Eth::Error::from_eth_err)?
819 .map(|b| b.original_bytes()))
820 }
821
822 async fn debug_state_root_with_updates(
825 &self,
826 hashed_state: HashedPostState,
827 block_id: Option<BlockId>,
828 ) -> Result<(B256, TrieUpdates), Eth::Error> {
829 self.inner
830 .eth_api
831 .spawn_blocking_io(move |this| {
832 let state = this
833 .provider()
834 .state_by_block_id(block_id.unwrap_or_default())
835 .map_err(Eth::Error::from_eth_err)?;
836 state.state_root_with_updates(hashed_state).map_err(Eth::Error::from_eth_err)
837 })
838 .await
839 }
840
841 pub async fn intermediate_roots(&self, block_hash: B256) -> Result<Vec<B256>, Eth::Error> {
843 let block = self
844 .eth_api()
845 .recovered_block(block_hash.into())
846 .await?
847 .ok_or(EthApiError::HeaderNotFound(block_hash.into()))?;
848 let evm_env = self.eth_api().evm_env_for_header(block.sealed_block().sealed_header())?;
849
850 self.eth_api()
851 .spawn_with_state_at_block(block.parent_hash(), move |eth_api, mut db| {
852 db.transition_state = Some(Default::default());
854
855 eth_api.apply_pre_execution_changes(&block, &mut db)?;
856
857 let mut roots = Vec::with_capacity(block.body().transactions().len());
858 let mut evm = eth_api.evm_config().evm_with_env(&mut db, evm_env);
859 for tx in block.transactions_recovered() {
860 if is_cancelled() {
861 return Err(EthApiError::InternalEthError.into())
862 }
863 let tx_env = eth_api.evm_config().tx_env(tx);
864 evm.transact_commit(tx_env).map_err(Eth::Error::from_evm_err)?;
865
866 let state = evm.db_mut();
867 state.merge_transitions(BundleRetention::PlainState);
869 let hashed_state = state
871 .database
872 .hashed_post_state(&state.bundle_state)
873 .map_err(Eth::Error::from_eth_err)?;
874 let root = state
875 .database
876 .state_root(hashed_state)
877 .map_err(Eth::Error::from_eth_err)?;
878 roots.push(root);
879 }
880
881 Ok(roots)
882 })
883 .await
884 }
885}
886
887#[async_trait]
888impl<Eth> DebugApiServer<RpcTxReq<Eth::NetworkTypes>> for DebugApi<Eth>
889where
890 Eth: EthTransactions + TraceExt,
891{
892 async fn raw_header(&self, block_id: BlockId) -> RpcResult<Bytes> {
894 let header = match block_id {
895 BlockId::Hash(hash) => self.provider().header(hash.into()).to_rpc_result()?,
896 BlockId::Number(number_or_tag) => {
897 let number = self
898 .provider()
899 .convert_block_number(number_or_tag)
900 .to_rpc_result()?
901 .ok_or(EthApiError::HeaderNotFound(block_id))?;
902 self.provider().header_by_number(number).to_rpc_result()?
903 }
904 }
905 .ok_or(EthApiError::HeaderNotFound(block_id))?;
906
907 let mut res = Vec::new();
908 header.encode(&mut res);
909 Ok(res.into())
910 }
911
912 async fn raw_block(&self, block_id: BlockId) -> RpcResult<Bytes> {
914 let block = self
915 .provider()
916 .block_by_id(block_id)
917 .to_rpc_result()?
918 .ok_or(EthApiError::HeaderNotFound(block_id))?;
919 let mut res = Vec::new();
920 block.encode(&mut res);
921 Ok(res.into())
922 }
923
924 async fn raw_block_access_list(&self, block_id: BlockId) -> RpcResult<Bytes> {
926 self.eth_api()
927 .get_raw_block_access_list(block_id)
928 .await
929 .map_err(Into::into)?
930 .ok_or_else(|| EthApiError::HeaderNotFound(block_id).into())
931 }
932
933 async fn raw_transaction(&self, hash: B256) -> RpcResult<Option<Bytes>> {
939 self.eth_api().raw_transaction_by_hash(hash).await.map_err(Into::into)
940 }
941
942 async fn raw_transactions(&self, block_id: BlockId) -> RpcResult<Vec<Bytes>> {
945 let block: RecoveredBlock<BlockTy<Eth::Primitives>> = self
946 .provider()
947 .block_with_senders_by_id(block_id, TransactionVariant::NoHash)
948 .to_rpc_result()?
949 .unwrap_or_default();
950 Ok(block.into_transactions_recovered().map(|tx| tx.encoded_2718().into()).collect())
951 }
952
953 async fn raw_receipts(&self, block_id: BlockId) -> RpcResult<Vec<Bytes>> {
955 Ok(self
956 .provider()
957 .receipts_by_block_id(block_id)
958 .to_rpc_result()?
959 .ok_or(EthApiError::HeaderNotFound(block_id))?
960 .into_iter()
961 .map(|receipt| ReceiptWithBloom::from(receipt).encoded_2718().into())
962 .collect())
963 }
964
965 async fn bad_blocks(&self) -> RpcResult<Vec<serde_json::Value>> {
967 let entries = self.inner.bad_block_store.all();
968 let mut bad_blocks = Vec::with_capacity(entries.len());
969
970 #[derive(Serialize, Deserialize)]
971 struct BadBlockSerde<T> {
972 block: T,
973 hash: B256,
974 rlp: Bytes,
975 reason: String,
976 }
977
978 for entry in entries {
979 let rlp = Bytes::from(alloy_rlp::encode(entry.block.sealed_block()));
980 let hash = entry.block.hash();
981
982 let block = entry
983 .block
984 .clone_into_rpc_block(
985 BlockTransactionsKind::Full,
986 |tx, tx_info| self.eth_api().converter().fill(tx, tx_info),
987 |header, block_size| {
988 self.eth_api().converter().convert_header(header, Some(block_size))
989 },
990 )
991 .map_err(|err| Eth::Error::from(err).into())?;
992
993 let bad_block =
994 serde_json::to_value(BadBlockSerde { block, hash, rlp, reason: entry.reason })
995 .map_err(|err| EthApiError::other(internal_rpc_err(err.to_string())))?;
996
997 bad_blocks.push(bad_block);
998 }
999
1000 Ok(bad_blocks)
1001 }
1002
1003 async fn debug_clear_txpool(&self) -> RpcResult<()> {
1005 let pool = self.eth_api().pool();
1006 let all_hashes = pool.all_transaction_hashes();
1007 let _ = pool.remove_transactions(all_hashes);
1008 Ok(())
1009 }
1010
1011 async fn debug_subscribe(
1013 &self,
1014 pending: PendingSubscriptionSink,
1015 subscription: String,
1016 start_exclusive: BlockNumberOrTag,
1017 end_inclusive: BlockNumberOrTag,
1018 opts: Option<GethDebugTracingOptions>,
1019 ) -> jsonrpsee::core::SubscriptionResult {
1020 if subscription != "traceChain" {
1021 pending
1022 .reject(EthApiError::InvalidParams(format!(
1023 "unsupported debug subscription: {subscription}"
1024 )))
1025 .await;
1026 return Ok(())
1027 }
1028
1029 let start_id = BlockId::Number(start_exclusive);
1030 let start = match self.eth_api().recovered_block(start_id).await {
1031 Ok(Some(block)) => block,
1032 Ok(None) => {
1033 pending.reject(EthApiError::TracingBlockNotFound(start_id)).await;
1034 return Ok(())
1035 }
1036 Err(err) => {
1037 pending.reject(err).await;
1038 return Ok(())
1039 }
1040 };
1041 let end_id = BlockId::Number(end_inclusive);
1042 let end = match self.eth_api().recovered_block(end_id).await {
1043 Ok(Some(block)) => block,
1044 Ok(None) => {
1045 pending.reject(EthApiError::TracingBlockNotFound(end_id)).await;
1046 return Ok(())
1047 }
1048 Err(err) => {
1049 pending.reject(err).await;
1050 return Ok(())
1051 }
1052 };
1053
1054 if start.number() >= end.number() {
1055 pending
1056 .reject(EthApiError::InvalidParams(format!(
1057 "end block (#{}) needs to come after start block (#{})",
1058 end.number(),
1059 start.number()
1060 )))
1061 .await;
1062 return Ok(())
1063 }
1064
1065 let sink = pending.accept().await?;
1066 let this = self.clone();
1067 let task_spawner = self.inner.task_spawner.clone();
1068 task_spawner.spawn_task(async move {
1069 let end_number = end.number();
1070 let opts = opts.unwrap_or_default();
1071
1072 for number in (start.number() + 1)..=end_number {
1073 if sink.is_closed() {
1074 break
1075 }
1076
1077 let block_id = BlockId::number(number);
1078 let block = match this.eth_api().recovered_block(block_id).await {
1079 Ok(Some(block)) => block,
1080 Ok(None) => {
1081 tracing::warn!(target: "rpc::debug", %number, "Chain tracing block not found");
1082 break
1083 }
1084 Err(err) => {
1085 tracing::warn!(target: "rpc::debug", %number, %err, "Failed to load chain tracing block");
1086 break
1087 }
1088 };
1089 let permit = tokio::select! {
1090 _ = sink.closed() => break,
1091 permit = this.acquire_trace_permit() => match permit {
1092 Ok(permit) => permit,
1093 Err(err) => {
1094 tracing::debug!(target: "rpc::debug", %err, "Failed to acquire trace permit");
1095 break
1096 }
1097 }
1098 };
1099 if sink.is_closed() {
1100 break
1101 }
1102 let traces = match this.trace_chain_block(block.clone(), opts.clone()).await {
1103 Ok(traces) => traces,
1104 Err(err) => {
1105 tracing::warn!(target: "rpc::debug", %number, %err, "Failed to trace chain block");
1106 break
1107 }
1108 };
1109 drop(permit);
1110
1111 if traces.is_empty() && number != end_number {
1112 continue
1113 }
1114 let result = ChainBlockTraceResult {
1115 block: U256::from(number),
1116 hash: block.hash(),
1117 traces,
1118 };
1119 let message = match SubscriptionMessage::new(
1120 sink.method_name(),
1121 sink.subscription_id(),
1122 &result,
1123 ) {
1124 Ok(message) => message,
1125 Err(err) => {
1126 tracing::warn!(target: "rpc::debug", %number, %err, "Failed to serialize chain trace");
1127 break
1128 }
1129 };
1130 if sink.send(message).await.is_err() {
1131 break
1132 }
1133 }
1134
1135 sink.closed().await;
1139 });
1140
1141 Ok(())
1142 }
1143
1144 async fn debug_trace_block(
1146 &self,
1147 rlp_block: Bytes,
1148 opts: Option<GethDebugTracingOptions>,
1149 ) -> RpcResult<Vec<TraceResult>> {
1150 let _permit = self.acquire_trace_permit().await;
1151 Self::debug_trace_raw_block(self, rlp_block, opts.unwrap_or_default())
1152 .await
1153 .map_err(Into::into)
1154 }
1155
1156 async fn debug_trace_block_by_hash(
1158 &self,
1159 block: B256,
1160 opts: Option<GethDebugTracingOptions>,
1161 ) -> RpcResult<Vec<TraceResult>> {
1162 let _permit = self.acquire_trace_permit().await;
1163 Self::debug_trace_block(self, block.into(), opts.unwrap_or_default())
1164 .await
1165 .map_err(Into::into)
1166 }
1167
1168 async fn debug_trace_block_by_number(
1170 &self,
1171 block: BlockNumberOrTag,
1172 opts: Option<GethDebugTracingOptions>,
1173 ) -> RpcResult<Vec<TraceResult>> {
1174 let _permit = self.acquire_trace_permit().await;
1175 Self::debug_trace_block(self, block.into(), opts.unwrap_or_default())
1176 .await
1177 .map_err(Into::into)
1178 }
1179
1180 async fn debug_trace_transaction(
1182 &self,
1183 tx_hash: B256,
1184 opts: Option<GethDebugTracingOptions>,
1185 ) -> RpcResult<GethTrace> {
1186 let _permit = self.acquire_trace_permit().await;
1187 Self::debug_trace_transaction(self, tx_hash, opts.unwrap_or_default())
1188 .await
1189 .map_err(Into::into)
1190 }
1191
1192 async fn debug_trace_call(
1194 &self,
1195 request: RpcTxReq<Eth::NetworkTypes>,
1196 block_id: Option<BlockId>,
1197 opts: Option<GethDebugTracingCallOptions>,
1198 ) -> RpcResult<GethTrace> {
1199 let _permit = self.acquire_trace_permit().await;
1200 Self::debug_trace_call(self, request, block_id, opts.unwrap_or_default())
1201 .await
1202 .map_err(Into::into)
1203 }
1204
1205 async fn debug_trace_call_many(
1206 &self,
1207 bundles: Vec<Bundle<RpcTxReq<Eth::NetworkTypes>>>,
1208 state_context: Option<StateContext>,
1209 opts: Option<GethDebugTracingCallOptions>,
1210 ) -> RpcResult<Vec<Vec<GethTrace>>> {
1211 let _permit = self.acquire_trace_permit().await;
1212 Self::debug_trace_call_many(self, bundles, state_context, opts).await.map_err(Into::into)
1213 }
1214
1215 async fn debug_execution_witness(
1217 &self,
1218 block: BlockId,
1219 mode: Option<ExecutionWitnessMode>,
1220 ) -> RpcResult<ExecutionWitness> {
1221 let _permit = self.acquire_trace_permit().await;
1222 Self::debug_execution_witness(self, block, mode).await.map_err(Into::into)
1223 }
1224
1225 async fn debug_execution_witness_by_block_hash(
1227 &self,
1228 hash: B256,
1229 mode: Option<ExecutionWitnessMode>,
1230 ) -> RpcResult<ExecutionWitness> {
1231 let _permit = self.acquire_trace_permit().await;
1232 Self::debug_execution_witness_by_block_hash(self, hash, mode).await.map_err(Into::into)
1233 }
1234
1235 async fn debug_account_at(
1237 &self,
1238 block_id: BlockId,
1239 tx_index: Index,
1240 address: Address,
1241 ) -> RpcResult<Option<Account>> {
1242 let _permit = self.acquire_trace_permit().await;
1243 Self::debug_account_at(self, block_id, tx_index, address).await.map_err(Into::into)
1244 }
1245
1246 async fn debug_account_info_at(
1248 &self,
1249 block_id: BlockId,
1250 tx_index: Index,
1251 address: Address,
1252 ) -> RpcResult<Option<AccountInfo>> {
1253 let _permit = self.acquire_trace_permit().await;
1254 Self::debug_account_info_at(self, block_id, tx_index, address).await.map_err(Into::into)
1255 }
1256
1257 async fn debug_account_range(
1258 &self,
1259 _block_number: BlockNumberOrTag,
1260 _start: Bytes,
1261 _max_results: u64,
1262 _nocode: bool,
1263 _nostorage: bool,
1264 _incompletes: bool,
1265 ) -> RpcResult<()> {
1266 Ok(())
1267 }
1268
1269 async fn debug_chaindb_compact(&self) -> RpcResult<()> {
1270 Ok(())
1271 }
1272
1273 async fn debug_chain_config(&self) -> RpcResult<ChainConfig> {
1274 Ok(crate::chain_config::chain_config(&*self.provider().chain_spec()))
1275 }
1276
1277 async fn debug_chaindb_property(&self, _property: String) -> RpcResult<()> {
1278 Ok(())
1279 }
1280
1281 async fn debug_code_by_hash(
1282 &self,
1283 hash: B256,
1284 block_id: Option<BlockId>,
1285 ) -> RpcResult<Option<Bytes>> {
1286 Self::debug_code_by_hash(self, hash, block_id).await.map_err(Into::into)
1287 }
1288
1289 async fn debug_db_ancient(&self, _kind: String, _number: u64) -> RpcResult<()> {
1290 Ok(())
1291 }
1292
1293 async fn debug_db_ancients(&self) -> RpcResult<()> {
1294 Ok(())
1295 }
1296
1297 async fn debug_db_get(&self, key: String) -> RpcResult<Option<Bytes>> {
1308 let key_bytes = if key.starts_with("0x") {
1309 decode(&key).map_err(|_| EthApiError::InvalidParams("Invalid hex key".to_string()))?
1310 } else {
1311 key.into_bytes()
1312 };
1313
1314 if key_bytes.len() != 33 {
1315 return Err(EthApiError::InvalidParams(format!(
1316 "Key must be 33 bytes, got {}",
1317 key_bytes.len()
1318 ))
1319 .into());
1320 }
1321 if key_bytes[0] != 0x63 {
1322 return Err(EthApiError::InvalidParams("Key prefix must be 0x63".to_string()).into());
1323 }
1324
1325 let code_hash = B256::from_slice(&key_bytes[1..33]);
1326
1327 self.debug_code_by_hash(code_hash, None).await.map_err(Into::into)
1329 }
1330
1331 async fn debug_dump_block(&self, _number: BlockId) -> RpcResult<()> {
1332 Ok(())
1333 }
1334
1335 async fn debug_free_os_memory(&self) -> RpcResult<()> {
1336 Ok(())
1337 }
1338
1339 async fn debug_gc_stats(&self) -> RpcResult<()> {
1340 Ok(())
1341 }
1342
1343 async fn debug_get_accessible_state(
1344 &self,
1345 _from: BlockNumberOrTag,
1346 _to: BlockNumberOrTag,
1347 ) -> RpcResult<()> {
1348 Ok(())
1349 }
1350
1351 async fn debug_get_modified_accounts_by_hash(
1352 &self,
1353 _start_hash: B256,
1354 _end_hash: B256,
1355 ) -> RpcResult<()> {
1356 Ok(())
1357 }
1358
1359 async fn debug_get_modified_accounts_by_number(
1360 &self,
1361 _start_number: u64,
1362 _end_number: u64,
1363 ) -> RpcResult<()> {
1364 Ok(())
1365 }
1366
1367 async fn debug_intermediate_roots(
1368 &self,
1369 block_hash: B256,
1370 _opts: Option<GethDebugTracingCallOptions>,
1371 ) -> RpcResult<Vec<B256>> {
1372 let _permit = self.acquire_trace_permit().await;
1373 self.intermediate_roots(block_hash).await.map_err(Into::into)
1374 }
1375
1376 async fn debug_mem_stats(&self) -> RpcResult<()> {
1377 Ok(())
1378 }
1379
1380 async fn debug_preimage(&self, _hash: B256) -> RpcResult<()> {
1381 Ok(())
1382 }
1383
1384 async fn debug_print_block(&self, _number: u64) -> RpcResult<()> {
1385 Ok(())
1386 }
1387
1388 async fn debug_seed_hash(&self, _number: u64) -> RpcResult<B256> {
1389 Ok(Default::default())
1390 }
1391
1392 async fn debug_set_gc_percent(&self, _v: i32) -> RpcResult<()> {
1393 Ok(())
1394 }
1395
1396 async fn debug_set_head(&self, _number: U64) -> RpcResult<()> {
1397 Ok(())
1398 }
1399
1400 async fn debug_set_trie_flush_interval(&self, _interval: String) -> RpcResult<()> {
1401 Ok(())
1402 }
1403
1404 async fn debug_standard_trace_bad_block_to_file(
1405 &self,
1406 _block: BlockNumberOrTag,
1407 _opts: Option<GethDebugTracingCallOptions>,
1408 ) -> RpcResult<()> {
1409 Ok(())
1410 }
1411
1412 async fn debug_standard_trace_block_to_file(
1413 &self,
1414 _block: BlockNumberOrTag,
1415 _opts: Option<GethDebugTracingCallOptions>,
1416 ) -> RpcResult<()> {
1417 Ok(())
1418 }
1419
1420 async fn debug_state_root_with_updates(
1421 &self,
1422 hashed_state: HashedPostState,
1423 block_id: Option<BlockId>,
1424 ) -> RpcResult<(B256, TrieUpdates)> {
1425 Self::debug_state_root_with_updates(self, hashed_state, block_id).await.map_err(Into::into)
1426 }
1427
1428 async fn debug_storage_range_at(
1429 &self,
1430 _block_hash: B256,
1431 _tx_idx: usize,
1432 _contract_address: Address,
1433 _key_start: B256,
1434 _max_result: u64,
1435 ) -> RpcResult<()> {
1436 Ok(())
1437 }
1438
1439 async fn debug_trace_bad_block(
1440 &self,
1441 block_hash: B256,
1442 opts: Option<GethDebugTracingCallOptions>,
1443 ) -> RpcResult<Vec<TraceResult>> {
1444 let _permit = self.acquire_trace_permit().await;
1445 let entry = self
1446 .inner
1447 .bad_block_store
1448 .get(block_hash)
1449 .ok_or_else(|| internal_rpc_err("bad block not found in cache"))?;
1450
1451 let evm_env = self
1452 .eth_api()
1453 .evm_config()
1454 .evm_env(entry.block.header())
1455 .map_err(RethError::other)
1456 .to_rpc_result()?;
1457
1458 let opts = opts.map(|o| o.tracing_options).unwrap_or_default();
1459 self.trace_block(entry.block.clone(), evm_env, opts).await.map_err(Into::into)
1460 }
1461}
1462
1463impl<Eth: RpcNodeCore> std::fmt::Debug for DebugApi<Eth> {
1464 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1465 f.debug_struct("DebugApi").finish_non_exhaustive()
1466 }
1467}
1468
1469impl<Eth: RpcNodeCore> Clone for DebugApi<Eth> {
1470 fn clone(&self) -> Self {
1471 Self { inner: Arc::clone(&self.inner) }
1472 }
1473}
1474
1475struct DebugApiInner<Eth: RpcNodeCore> {
1476 eth_api: Eth,
1478 blocking_task_guard: BlockingTaskGuard,
1480 task_spawner: Runtime,
1482 bad_block_store: BadBlockStore<BlockTy<Eth::Primitives>>,
1484}
1485
1486#[derive(Clone, Debug)]
1488struct BadBlockStore<B: BlockTrait> {
1489 inner: Arc<RwLock<VecDeque<BadBlockEntry<B>>>>,
1490 limit: usize,
1491}
1492
1493#[derive(Clone, Debug)]
1495struct BadBlockEntry<B: BlockTrait> {
1496 block: Arc<RecoveredBlock<B>>,
1497 reason: String,
1498}
1499
1500impl<B: BlockTrait> BadBlockStore<B> {
1501 fn new(limit: usize) -> Self {
1503 Self { inner: Arc::new(RwLock::new(VecDeque::with_capacity(limit))), limit }
1504 }
1505
1506 fn insert(&self, block: RecoveredBlock<B>, reason: String) {
1509 let hash = block.hash();
1510 let mut guard = self.inner.write();
1511
1512 if guard.iter().any(|entry| entry.block.hash() == hash) {
1514 return;
1515 }
1516 guard.push_back(BadBlockEntry { block: Arc::new(block), reason });
1517
1518 while guard.len() > self.limit {
1519 guard.pop_front();
1520 }
1521 }
1522
1523 fn all(&self) -> Vec<BadBlockEntry<B>> {
1525 let guard = self.inner.read();
1526 guard.iter().rev().cloned().collect()
1527 }
1528
1529 fn get(&self, hash: B256) -> Option<BadBlockEntry<B>> {
1531 let guard = self.inner.read();
1532 guard.iter().find(|entry| entry.block.hash() == hash).cloned()
1533 }
1534}
1535
1536impl<B: BlockTrait> Default for BadBlockStore<B> {
1537 fn default() -> Self {
1538 Self::new(64)
1539 }
1540}
1541
1542#[cfg(feature = "js-tracer")]
1544const DEFAULT_JS_TRACER_TIMEOUT: Duration = Duration::from_secs(5);
1545
1546#[derive(Clone, Debug, Default)]
1550struct JsTraceInterrupt {
1551 #[cfg(feature = "js-tracer")]
1552 js: Option<(JsInspectorInterrupt, Duration)>,
1553}
1554
1555impl JsTraceInterrupt {
1556 #[cfg_attr(
1558 not(feature = "js-tracer"),
1559 expect(unused_variables, reason = "only JS tracers can be interrupted")
1560 )]
1561 fn new(opts: &GethDebugTracingOptions) -> Result<Self, EthApiError> {
1562 #[cfg(feature = "js-tracer")]
1563 if let Some(GethDebugTracerType::JsTracer(_)) = &opts.tracer {
1564 let timeout = match &opts.timeout {
1565 Some(timeout) => humantime::parse_duration(timeout).map_err(|err| {
1566 EthApiError::InvalidParams(format!("invalid tracer timeout {timeout:?}: {err}"))
1567 })?,
1568 None => DEFAULT_JS_TRACER_TIMEOUT,
1569 };
1570 return Ok(Self { js: Some((JsInspectorInterrupt::new(), timeout)) })
1571 }
1572 Ok(Self::default())
1573 }
1574
1575 #[cfg_attr(
1577 not(feature = "js-tracer"),
1578 expect(clippy::missing_const_for_fn, reason = "only JS tracers can be interrupted")
1579 )]
1580 fn attach(&self, inspector: DebugInspector) -> DebugInspector {
1581 #[cfg(feature = "js-tracer")]
1582 if let Some((interrupt, _)) = &self.js {
1583 return match inspector {
1584 DebugInspector::Js(js) => {
1585 DebugInspector::Js(Box::new(js.with_interrupt(interrupt.clone())))
1586 }
1587 inspector => inspector,
1588 }
1589 }
1590 inspector
1591 }
1592
1593 #[cfg_attr(
1599 not(feature = "js-tracer"),
1600 expect(
1601 clippy::manual_async_fn,
1602 clippy::redundant_async_block,
1603 reason = "only JS tracers can be interrupted"
1604 )
1605 )]
1606 fn run<T, E: FromEthApiError>(
1607 &self,
1608 trace: impl Future<Output = Result<T, E>>,
1609 ) -> impl Future<Output = Result<T, E>> {
1610 #[cfg(feature = "js-tracer")]
1611 let js = self.js.as_ref().map(|(interrupt, timeout)| (interrupt.drop_guard(), *timeout));
1612 async move {
1613 #[cfg(feature = "js-tracer")]
1614 if let Some((_interrupt_on_drop, timeout)) = js {
1615 return tokio::time::timeout(timeout, trace)
1616 .await
1617 .map_err(|_| E::from_eth_err(EthApiError::ExecutionTimedOut(timeout)))?
1618 }
1619 trace.await
1620 }
1621 }
1622}
1623
1624#[cfg(test)]
1625mod tests {
1626 use super::*;
1627 use crate::{eth::helpers::types::EthRpcConverter, EthApi};
1628 use alloy_primitives::{keccak256, U256};
1629 use reth_chainspec::ChainSpec;
1630 use reth_db_api::{tables, transaction::DbTxMut};
1631 use reth_evm_ethereum::EthEvmConfig;
1632 use reth_network_api::noop::NoopNetwork;
1633 use reth_primitives_traits::StorageEntry;
1634 use reth_provider::test_utils::{create_test_provider_factory, NoopProvider};
1635 use reth_rpc_eth_api::EthApiServer;
1636 use reth_transaction_pool::test_utils::testing_pool;
1637 use revm::{
1638 database::{states::StorageSlot, AccountStatus, BundleAccount, BundleState},
1639 state::AccountInfo as RevmAccountInfo,
1640 };
1641
1642 #[tokio::test]
1643 async fn trace_call_out_of_range_block_error() {
1644 let eth_api = EthApi::<_, EthRpcConverter<ChainSpec>>::builder(
1645 NoopProvider::default(),
1646 testing_pool(),
1647 NoopNetwork::default(),
1648 EthEvmConfig::mainnet(),
1649 )
1650 .build();
1651 let debug_api = DebugApi::new(
1652 eth_api.clone(),
1653 BlockingTaskGuard::new(1),
1654 &Runtime::test(),
1655 futures::stream::empty(),
1656 );
1657 let block_id = BlockId::number(0xfffffffff);
1658 for tx_index in [None, Some(0)] {
1659 let mut opts: GethDebugTracingCallOptions =
1660 serde_json::from_value(serde_json::json!({ "tracer": "callTracer" })).unwrap();
1661 opts.tx_index = tx_index;
1662 let err = DebugApiServer::debug_trace_call(
1663 &debug_api,
1664 Default::default(),
1665 Some(block_id),
1666 Some(opts),
1667 )
1668 .await
1669 .unwrap_err();
1670 assert_eq!(err.code(), -32000);
1671 assert!(err.message().contains("not found"));
1672 }
1673
1674 let err = EthApiServer::call(ð_api, Default::default(), Some(block_id), None, None)
1675 .await
1676 .unwrap_err();
1677 assert_eq!(err.code(), -32001);
1678 }
1679
1680 #[cfg(feature = "js-tracer")]
1681 fn js_tracer_opts(timeout: Option<&str>) -> GethDebugTracingOptions {
1682 GethDebugTracingOptions {
1683 tracer: Some(GethDebugTracerType::JsTracer(
1684 "{ fault: function() {}, result: function() { return null; } }".into(),
1685 )),
1686 timeout: timeout.map(Into::into),
1687 ..Default::default()
1688 }
1689 }
1690
1691 #[cfg(feature = "js-tracer")]
1692 #[test]
1693 fn js_trace_interrupt_stops_attached_js_tracer() {
1694 use revm::{
1695 context::TxEnv,
1696 context_interface::result::EVMError,
1697 database::{CacheDB, EmptyDB},
1698 primitives::TxKind,
1699 InspectEvm, MainBuilder, MainContext,
1700 };
1701
1702 let opts = js_tracer_opts(None);
1703 let interrupt = JsTraceInterrupt::new(&opts).unwrap();
1704 let inspector = interrupt.attach(DebugInspector::new(opts).unwrap());
1705 interrupt.js.as_ref().unwrap().0.interrupt();
1706
1707 let mut evm = revm::Context::mainnet()
1708 .with_db(CacheDB::<EmptyDB>::default())
1709 .build_mainnet_with_inspector(inspector);
1710 let result = evm.inspect_tx(TxEnv {
1711 gas_limit: 1_000_000,
1712 kind: TxKind::Call(Address::with_last_byte(1)),
1713 ..Default::default()
1714 });
1715 assert!(
1716 matches!(&result, Err(EVMError::Custom(msg)) if msg == "JavaScript tracing interrupted"),
1717 "{result:?}"
1718 );
1719 }
1720
1721 #[cfg(feature = "js-tracer")]
1722 #[tokio::test]
1723 async fn js_trace_interrupt_times_out_and_interrupts_tracer() {
1724 let interrupt = JsTraceInterrupt::new(&js_tracer_opts(Some("10ms"))).unwrap();
1725 let err =
1726 interrupt.run(std::future::pending::<Result<(), EthApiError>>()).await.unwrap_err();
1727 assert!(
1728 matches!(err, EthApiError::ExecutionTimedOut(timeout) if timeout == Duration::from_millis(10))
1729 );
1730 assert!(interrupt.js.unwrap().0.is_interrupted());
1731 }
1732
1733 #[cfg(feature = "js-tracer")]
1734 #[test]
1735 fn js_trace_interrupt_interrupts_tracer_when_dropped_unpolled() {
1736 let interrupt = JsTraceInterrupt::new(&js_tracer_opts(None)).unwrap();
1737 let (tracer_interrupt, _) = interrupt.js.clone().unwrap();
1738 let trace = interrupt.run(std::future::pending::<Result<(), EthApiError>>());
1739 assert!(!tracer_interrupt.is_interrupted());
1740 drop(trace);
1741 assert!(tracer_interrupt.is_interrupted());
1742 }
1743
1744 #[test]
1745 fn hashed_post_state_zeroes_destroyed_account_parent_storage() {
1746 let factory = create_test_provider_factory();
1747 let address = Address::with_last_byte(1);
1748 let old_slot = U256::ONE;
1749 let new_slot = U256::from(2);
1750 let old_value = U256::from(10);
1751 let new_value = U256::from(20);
1752 let hashed_address = keccak256(address);
1753 let hashed_old_slot = keccak256(B256::from(old_slot));
1754 let hashed_new_slot = keccak256(B256::from(new_slot));
1755
1756 let provider_rw = factory.provider_rw().unwrap();
1757 provider_rw
1758 .tx_ref()
1759 .put::<tables::HashedStorages>(
1760 hashed_address,
1761 StorageEntry { key: hashed_old_slot, value: old_value },
1762 )
1763 .unwrap();
1764 provider_rw.commit().unwrap();
1765
1766 let mut bundle_state = BundleState::default();
1767 bundle_state.state.insert(
1768 address,
1769 BundleAccount::new(
1770 Some(RevmAccountInfo::default()),
1771 Some(RevmAccountInfo::default()),
1772 std::iter::once((new_slot, StorageSlot::new_changed(U256::ZERO, new_value)))
1773 .collect(),
1774 AccountStatus::DestroyedChanged,
1775 ),
1776 );
1777
1778 let provider = factory.latest().unwrap();
1779 let hashed_state = provider.hashed_post_state(&bundle_state).unwrap();
1780 let storage = &hashed_state.storages[&hashed_address];
1781
1782 assert_eq!(storage.storage[&hashed_old_slot], U256::ZERO);
1783 assert_eq!(storage.storage[&hashed_new_slot], new_value);
1784 }
1785}