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
62pub struct DebugApi<Eth: RpcNodeCore> {
66 inner: Arc<DebugApiInner<Eth>>,
67}
68
69impl<Eth> DebugApi<Eth>
70where
71 Eth: RpcNodeCore,
72{
73 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 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 pub fn eth_api(&self) -> &Eth {
104 &self.inner.eth_api
105 }
106
107 pub fn provider(&self) -> &Eth::Provider {
109 self.inner.eth_api.provider()
110 }
111}
112
113impl<Eth> DebugApi<Eth>
116where
117 Eth: TraceExt,
118{
119 async fn acquire_trace_permit(&self) -> Result<OwnedSemaphorePermit, AcquireError> {
121 self.inner.blocking_task_guard.clone().acquire_owned().await
122 }
123
124 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 executor.commit_transaction(output).map_err(Eth::Error::from_evm_err)?;
178 }
179 }
180
181 Ok(results)
182 })
183 .await
184 }
185
186 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 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 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 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 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 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 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 let (tx, tx_info) = transaction.split();
376 let tx_hash = *tx.tx_hash();
377
378 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 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 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 EthApiError::TracingBlockNotFound(at).into()
464 }
465 _ => err,
466 })
467 }
468
469 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 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 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 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 eth_api.replay_block_until(&mut db, &block, tx_index, bal.as_deref())?;
510
511 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 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 let mut at = block.parent_hash();
562 let mut replay_block_txs = true;
563
564 let num_txs =
566 transaction_index.index().unwrap_or_else(|| block.body().transactions().len());
567 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 let mut all_bundles = Vec::with_capacity(bundles.len());
579
580 if replay_block_txs {
581 eth_api.replay_block_until(&mut db, &block, num_txs, None)?;
587 }
588
589 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 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 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 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 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 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 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 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 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 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 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 #[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 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 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 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 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 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 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 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 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 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 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 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 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 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 sink.closed().await;
1165 });
1166
1167 Ok(())
1168 }
1169
1170 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 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 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 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 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 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 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 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 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 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 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 eth_api: Eth,
1505 blocking_task_guard: BlockingTaskGuard,
1507 task_spawner: Runtime,
1509 bad_block_store: BadBlockStore<BlockTy<Eth::Primitives>>,
1511}
1512
1513#[derive(Clone, Debug)]
1515struct BadBlockStore<B: BlockTrait> {
1516 inner: Arc<RwLock<VecDeque<BadBlockEntry<B>>>>,
1517 limit: usize,
1518}
1519
1520#[derive(Clone, Debug)]
1522struct BadBlockEntry<B: BlockTrait> {
1523 block: Arc<RecoveredBlock<B>>,
1524 reason: String,
1525}
1526
1527impl<B: BlockTrait> BadBlockStore<B> {
1528 fn new(limit: usize) -> Self {
1530 Self { inner: Arc::new(RwLock::new(VecDeque::with_capacity(limit))), limit }
1531 }
1532
1533 fn insert(&self, block: RecoveredBlock<B>, reason: String) {
1536 let hash = block.hash();
1537 let mut guard = self.inner.write();
1538
1539 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 fn all(&self) -> Vec<BadBlockEntry<B>> {
1552 let guard = self.inner.read();
1553 guard.iter().rev().cloned().collect()
1554 }
1555
1556 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
1569const RAW_BLOCK_TRACE_TIMEOUT: Duration = Duration::from_secs(30);
1571
1572#[cfg(feature = "js-tracer")]
1574const DEFAULT_JS_TRACER_TIMEOUT: Duration = Duration::from_secs(5);
1575
1576#[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#[derive(Clone, Debug, Default)]
1596struct JsTraceInterrupt {
1597 #[cfg(feature = "js-tracer")]
1598 js: Option<(JsInspectorInterrupt, Duration)>,
1599}
1600
1601impl JsTraceInterrupt {
1602 #[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 #[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 #[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(ð_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 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 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}