reth_rpc/
debug.rs

1use alloy_consensus::BlockHeader;
2use alloy_eips::{eip2718::Encodable2718, BlockId, BlockNumberOrTag};
3use alloy_genesis::ChainConfig;
4use alloy_primitives::{Address, Bytes, B256};
5use alloy_rlp::{Decodable, Encodable};
6use alloy_rpc_types_debug::ExecutionWitness;
7use alloy_rpc_types_eth::{
8    state::EvmOverrides, transaction::TransactionRequest, Block as RpcBlock, BlockError, Bundle,
9    StateContext, TransactionInfo,
10};
11use alloy_rpc_types_trace::geth::{
12    call::FlatCallFrame, BlockTraceResult, FourByteFrame, GethDebugBuiltInTracerType,
13    GethDebugTracerType, GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace,
14    NoopFrame, TraceResult,
15};
16use async_trait::async_trait;
17use jsonrpsee::core::RpcResult;
18use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks};
19use reth_evm::{
20    execute::{BlockExecutorProvider, Executor},
21    ConfigureEvm, EvmEnvFor, TxEnvFor,
22};
23use reth_primitives_traits::{
24    Block as _, BlockBody, NodePrimitives, ReceiptWithBloom, RecoveredBlock, SignedTransaction,
25};
26use reth_revm::{
27    database::StateProviderDatabase,
28    db::{CacheDB, State},
29    witness::ExecutionWitnessRecord,
30};
31use reth_rpc_api::DebugApiServer;
32use reth_rpc_eth_api::{
33    helpers::{EthTransactions, TraceExt},
34    EthApiTypes, FromEthApiError, RpcNodeCore,
35};
36use reth_rpc_eth_types::{EthApiError, StateCacheDb};
37use reth_rpc_server_types::{result::internal_rpc_err, ToRpcResult};
38use reth_storage_api::{
39    BlockIdReader, BlockReaderIdExt, HeaderProvider, ProviderBlock, ReceiptProviderIdExt,
40    StateProofProvider, StateProvider, StateProviderFactory, TransactionVariant,
41};
42use reth_tasks::pool::BlockingTaskGuard;
43use revm::{context_interface::Transaction, state::EvmState, DatabaseCommit};
44use revm_inspectors::tracing::{
45    FourByteInspector, MuxInspector, TracingInspector, TracingInspectorConfig, TransactionContext,
46};
47use std::sync::Arc;
48use tokio::sync::{AcquireError, OwnedSemaphorePermit};
49
50/// `debug` API implementation.
51///
52/// This type provides the functionality for handling `debug` related requests.
53pub struct DebugApi<Eth, BlockExecutor> {
54    inner: Arc<DebugApiInner<Eth, BlockExecutor>>,
55}
56
57// === impl DebugApi ===
58
59impl<Eth, BlockExecutor> DebugApi<Eth, BlockExecutor> {
60    /// Create a new instance of the [`DebugApi`]
61    pub fn new(
62        eth: Eth,
63        blocking_task_guard: BlockingTaskGuard,
64        block_executor: BlockExecutor,
65    ) -> Self {
66        let inner = Arc::new(DebugApiInner { eth_api: eth, blocking_task_guard, block_executor });
67        Self { inner }
68    }
69
70    /// Access the underlying `Eth` API.
71    pub fn eth_api(&self) -> &Eth {
72        &self.inner.eth_api
73    }
74}
75
76impl<Eth: RpcNodeCore, BlockExecutor> DebugApi<Eth, BlockExecutor> {
77    /// Access the underlying provider.
78    pub fn provider(&self) -> &Eth::Provider {
79        self.inner.eth_api.provider()
80    }
81}
82
83// === impl DebugApi ===
84
85impl<Eth, BlockExecutor> DebugApi<Eth, BlockExecutor>
86where
87    Eth: EthApiTypes + TraceExt + 'static,
88    BlockExecutor:
89        BlockExecutorProvider<Primitives: NodePrimitives<Block = ProviderBlock<Eth::Provider>>>,
90{
91    /// Acquires a permit to execute a tracing call.
92    async fn acquire_trace_permit(&self) -> Result<OwnedSemaphorePermit, AcquireError> {
93        self.inner.blocking_task_guard.clone().acquire_owned().await
94    }
95
96    /// Trace the entire block asynchronously
97    async fn trace_block(
98        &self,
99        block: Arc<RecoveredBlock<ProviderBlock<Eth::Provider>>>,
100        evm_env: EvmEnvFor<Eth::Evm>,
101        opts: GethDebugTracingOptions,
102    ) -> Result<Vec<TraceResult>, Eth::Error> {
103        // replay all transactions of the block
104        let this = self.clone();
105        self.eth_api()
106            .spawn_with_state_at_block(block.parent_hash().into(), move |state| {
107                let mut results = Vec::with_capacity(block.body().transactions().len());
108                let mut db = CacheDB::new(StateProviderDatabase::new(state));
109
110                this.eth_api().apply_pre_execution_changes(&block, &mut db, &evm_env)?;
111
112                let mut transactions = block.transactions_recovered().enumerate().peekable();
113                let mut inspector = None;
114                while let Some((index, tx)) = transactions.next() {
115                    let tx_hash = *tx.tx_hash();
116
117                    let tx_env = this.eth_api().evm_config().tx_env(tx);
118
119                    let (result, state_changes) = this.trace_transaction(
120                        &opts,
121                        evm_env.clone(),
122                        tx_env,
123                        &mut db,
124                        Some(TransactionContext {
125                            block_hash: Some(block.hash()),
126                            tx_hash: Some(tx_hash),
127                            tx_index: Some(index),
128                        }),
129                        &mut inspector,
130                    )?;
131
132                    inspector = inspector.map(|insp| insp.fused());
133
134                    results.push(TraceResult::Success { result, tx_hash: Some(tx_hash) });
135                    if transactions.peek().is_some() {
136                        // need to apply the state changes of this transaction before executing the
137                        // next transaction
138                        db.commit(state_changes)
139                    }
140                }
141
142                Ok(results)
143            })
144            .await
145    }
146
147    /// Replays the given block and returns the trace of each transaction.
148    ///
149    /// This expects a rlp encoded block
150    ///
151    /// Note, the parent of this block must be present, or it will fail.
152    pub async fn debug_trace_raw_block(
153        &self,
154        rlp_block: Bytes,
155        opts: GethDebugTracingOptions,
156    ) -> Result<Vec<TraceResult>, Eth::Error> {
157        let block: ProviderBlock<Eth::Provider> = Decodable::decode(&mut rlp_block.as_ref())
158            .map_err(BlockError::RlpDecodeRawBlock)
159            .map_err(Eth::Error::from_eth_err)?;
160
161        let evm_env = self.eth_api().evm_config().evm_env(block.header());
162
163        // Depending on EIP-2 we need to recover the transactions differently
164        let senders =
165            if self.provider().chain_spec().is_homestead_active_at_block(block.header().number()) {
166                block
167                    .body()
168                    .transactions()
169                    .iter()
170                    .map(|tx| tx.recover_signer().map_err(Eth::Error::from_eth_err))
171                    .collect::<Result<Vec<_>, _>>()?
172                    .into_iter()
173                    .collect()
174            } else {
175                block
176                    .body()
177                    .transactions()
178                    .iter()
179                    .map(|tx| tx.recover_signer_unchecked().map_err(Eth::Error::from_eth_err))
180                    .collect::<Result<Vec<_>, _>>()?
181                    .into_iter()
182                    .collect()
183            };
184
185        self.trace_block(Arc::new(block.into_recovered_with_signers(senders)), evm_env, opts).await
186    }
187
188    /// Replays a block and returns the trace of each transaction.
189    pub async fn debug_trace_block(
190        &self,
191        block_id: BlockId,
192        opts: GethDebugTracingOptions,
193    ) -> Result<Vec<TraceResult>, Eth::Error> {
194        let block_hash = self
195            .provider()
196            .block_hash_for_id(block_id)
197            .map_err(Eth::Error::from_eth_err)?
198            .ok_or(EthApiError::HeaderNotFound(block_id))?;
199
200        let ((evm_env, _), block) = futures::try_join!(
201            self.eth_api().evm_env_at(block_hash.into()),
202            self.eth_api().recovered_block(block_hash.into()),
203        )?;
204
205        let block = block.ok_or(EthApiError::HeaderNotFound(block_id))?;
206
207        self.trace_block(block, evm_env, opts).await
208    }
209
210    /// Trace the transaction according to the provided options.
211    ///
212    /// Ref: <https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers>
213    pub async fn debug_trace_transaction(
214        &self,
215        tx_hash: B256,
216        opts: GethDebugTracingOptions,
217    ) -> Result<GethTrace, Eth::Error> {
218        let (transaction, block) = match self.eth_api().transaction_and_block(tx_hash).await? {
219            None => return Err(EthApiError::TransactionNotFound.into()),
220            Some(res) => res,
221        };
222        let (evm_env, _) = self.eth_api().evm_env_at(block.hash().into()).await?;
223
224        // we need to get the state of the parent block because we're essentially replaying the
225        // block the transaction is included in
226        let state_at: BlockId = block.parent_hash().into();
227        let block_hash = block.hash();
228
229        let this = self.clone();
230        self.eth_api()
231            .spawn_with_state_at_block(state_at, move |state| {
232                let block_txs = block.transactions_recovered();
233
234                // configure env for the target transaction
235                let tx = transaction.into_recovered();
236
237                let mut db = CacheDB::new(StateProviderDatabase::new(state));
238
239                this.eth_api().apply_pre_execution_changes(&block, &mut db, &evm_env)?;
240
241                // replay all transactions prior to the targeted transaction
242                let index = this.eth_api().replay_transactions_until(
243                    &mut db,
244                    evm_env.clone(),
245                    block_txs,
246                    *tx.tx_hash(),
247                )?;
248
249                let tx_env = this.eth_api().evm_config().tx_env(&tx);
250
251                this.trace_transaction(
252                    &opts,
253                    evm_env,
254                    tx_env,
255                    &mut db,
256                    Some(TransactionContext {
257                        block_hash: Some(block_hash),
258                        tx_index: Some(index),
259                        tx_hash: Some(*tx.tx_hash()),
260                    }),
261                    &mut None,
262                )
263                .map(|(trace, _)| trace)
264            })
265            .await
266    }
267
268    /// The `debug_traceCall` method lets you run an `eth_call` within the context of the given
269    /// block execution using the final state of parent block as the base.
270    ///
271    /// Differences compare to `eth_call`:
272    ///  - `debug_traceCall` executes with __enabled__ basefee check, `eth_call` does not: <https://github.com/paradigmxyz/reth/issues/6240>
273    pub async fn debug_trace_call(
274        &self,
275        call: TransactionRequest,
276        block_id: Option<BlockId>,
277        opts: GethDebugTracingCallOptions,
278    ) -> Result<GethTrace, Eth::Error> {
279        let at = block_id.unwrap_or_default();
280        let GethDebugTracingCallOptions { tracing_options, state_overrides, block_overrides } =
281            opts;
282        let overrides = EvmOverrides::new(state_overrides, block_overrides.map(Box::new));
283        let GethDebugTracingOptions { config, tracer, tracer_config, .. } = tracing_options;
284
285        let this = self.clone();
286        if let Some(tracer) = tracer {
287            return match tracer {
288                GethDebugTracerType::BuiltInTracer(tracer) => match tracer {
289                    GethDebugBuiltInTracerType::FourByteTracer => {
290                        let mut inspector = FourByteInspector::default();
291                        let inspector = self
292                            .eth_api()
293                            .spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| {
294                                this.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?;
295                                Ok(inspector)
296                            })
297                            .await?;
298                        return Ok(FourByteFrame::from(&inspector).into())
299                    }
300                    GethDebugBuiltInTracerType::CallTracer => {
301                        let call_config = tracer_config
302                            .into_call_config()
303                            .map_err(|_| EthApiError::InvalidTracerConfig)?;
304
305                        let mut inspector = TracingInspector::new(
306                            TracingInspectorConfig::from_geth_call_config(&call_config),
307                        );
308
309                        let frame = self
310                            .eth_api()
311                            .spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| {
312                                let (res, (_, tx_env)) =
313                                    this.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?;
314                                let frame = inspector
315                                    .with_transaction_gas_limit(tx_env.gas_limit())
316                                    .into_geth_builder()
317                                    .geth_call_traces(call_config, res.result.gas_used());
318                                Ok(frame.into())
319                            })
320                            .await?;
321                        return Ok(frame)
322                    }
323                    GethDebugBuiltInTracerType::PreStateTracer => {
324                        let prestate_config = tracer_config
325                            .into_pre_state_config()
326                            .map_err(|_| EthApiError::InvalidTracerConfig)?;
327                        let mut inspector = TracingInspector::new(
328                            TracingInspectorConfig::from_geth_prestate_config(&prestate_config),
329                        );
330
331                        let frame = self
332                            .eth_api()
333                            .spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| {
334                                // wrapper is hack to get around 'higher-ranked lifetime error',
335                                // see <https://github.com/rust-lang/rust/issues/100013>
336                                let db = db.0;
337
338                                let (res, (_, tx_env)) = this.eth_api().inspect(
339                                    &mut *db,
340                                    evm_env,
341                                    tx_env,
342                                    &mut inspector,
343                                )?;
344                                let frame = inspector
345                                    .with_transaction_gas_limit(tx_env.gas_limit())
346                                    .into_geth_builder()
347                                    .geth_prestate_traces(&res, &prestate_config, db)
348                                    .map_err(Eth::Error::from_eth_err)?;
349                                Ok(frame)
350                            })
351                            .await?;
352                        return Ok(frame.into())
353                    }
354                    GethDebugBuiltInTracerType::NoopTracer => Ok(NoopFrame::default().into()),
355                    GethDebugBuiltInTracerType::MuxTracer => {
356                        let mux_config = tracer_config
357                            .into_mux_config()
358                            .map_err(|_| EthApiError::InvalidTracerConfig)?;
359
360                        let mut inspector = MuxInspector::try_from_config(mux_config)
361                            .map_err(Eth::Error::from_eth_err)?;
362
363                        let frame = self
364                            .inner
365                            .eth_api
366                            .spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| {
367                                // wrapper is hack to get around 'higher-ranked lifetime error', see
368                                // <https://github.com/rust-lang/rust/issues/100013>
369                                let db = db.0;
370
371                                let tx_info = TransactionInfo {
372                                    block_number: Some(evm_env.block_env.number),
373                                    base_fee: Some(evm_env.block_env.basefee),
374                                    hash: None,
375                                    block_hash: None,
376                                    index: None,
377                                };
378
379                                let (res, _) = this.eth_api().inspect(
380                                    &mut *db,
381                                    evm_env,
382                                    tx_env,
383                                    &mut inspector,
384                                )?;
385                                let frame = inspector
386                                    .try_into_mux_frame(&res, db, tx_info)
387                                    .map_err(Eth::Error::from_eth_err)?;
388                                Ok(frame.into())
389                            })
390                            .await?;
391                        return Ok(frame)
392                    }
393                    GethDebugBuiltInTracerType::FlatCallTracer => {
394                        let flat_call_config = tracer_config
395                            .into_flat_call_config()
396                            .map_err(|_| EthApiError::InvalidTracerConfig)?;
397
398                        let mut inspector = TracingInspector::new(
399                            TracingInspectorConfig::from_flat_call_config(&flat_call_config),
400                        );
401
402                        let frame: FlatCallFrame = self
403                            .inner
404                            .eth_api
405                            .spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| {
406                                let (_res, (_, tx_env)) =
407                                    this.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?;
408                                let tx_info = TransactionInfo::default();
409                                let frame: FlatCallFrame = inspector
410                                    .with_transaction_gas_limit(tx_env.gas_limit())
411                                    .into_parity_builder()
412                                    .into_localized_transaction_traces(tx_info);
413                                Ok(frame)
414                            })
415                            .await?;
416
417                        return Ok(frame.into());
418                    }
419                },
420                #[cfg(not(feature = "js-tracer"))]
421                GethDebugTracerType::JsTracer(_) => {
422                    Err(EthApiError::Unsupported("JS Tracer is not enabled").into())
423                }
424                #[cfg(feature = "js-tracer")]
425                GethDebugTracerType::JsTracer(code) => {
426                    let config = tracer_config.into_json();
427
428                    let (_, at) = self.eth_api().evm_env_at(at).await?;
429
430                    let res = self
431                        .eth_api()
432                        .spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| {
433                            // wrapper is hack to get around 'higher-ranked lifetime error', see
434                            // <https://github.com/rust-lang/rust/issues/100013>
435                            let db = db.0;
436
437                            let mut inspector =
438                                revm_inspectors::tracing::js::JsInspector::new(code, config)
439                                    .map_err(Eth::Error::from_eth_err)?;
440                            let (res, _) = this.eth_api().inspect(
441                                &mut *db,
442                                evm_env.clone(),
443                                tx_env.clone(),
444                                &mut inspector,
445                            )?;
446                            inspector
447                                .json_result(res, &tx_env, &evm_env.block_env, db)
448                                .map_err(Eth::Error::from_eth_err)
449                        })
450                        .await?;
451
452                    Ok(GethTrace::JS(res))
453                }
454            }
455        }
456
457        // default structlog tracer
458        let inspector_config = TracingInspectorConfig::from_geth_config(&config);
459
460        let mut inspector = TracingInspector::new(inspector_config);
461
462        let (res, tx_gas_limit, inspector) = self
463            .eth_api()
464            .spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| {
465                let (res, (_, tx_env)) =
466                    this.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?;
467                Ok((res, tx_env.gas_limit(), inspector))
468            })
469            .await?;
470        let gas_used = res.result.gas_used();
471        let return_value = res.result.into_output().unwrap_or_default();
472        let frame = inspector
473            .with_transaction_gas_limit(tx_gas_limit)
474            .into_geth_builder()
475            .geth_traces(gas_used, return_value, config);
476
477        Ok(frame.into())
478    }
479
480    /// The `debug_traceCallMany` method lets you run an `eth_callMany` within the context of the
481    /// given block execution using the first n transactions in the given block as base.
482    /// Each following bundle increments block number by 1 and block timestamp by 12 seconds
483    pub async fn debug_trace_call_many(
484        &self,
485        bundles: Vec<Bundle>,
486        state_context: Option<StateContext>,
487        opts: Option<GethDebugTracingCallOptions>,
488    ) -> Result<Vec<Vec<GethTrace>>, Eth::Error> {
489        if bundles.is_empty() {
490            return Err(EthApiError::InvalidParams(String::from("bundles are empty.")).into())
491        }
492
493        let StateContext { transaction_index, block_number } = state_context.unwrap_or_default();
494        let transaction_index = transaction_index.unwrap_or_default();
495
496        let target_block = block_number.unwrap_or_default();
497        let ((mut evm_env, _), block) = futures::try_join!(
498            self.eth_api().evm_env_at(target_block),
499            self.eth_api().recovered_block(target_block),
500        )?;
501
502        let opts = opts.unwrap_or_default();
503        let block = block.ok_or(EthApiError::HeaderNotFound(target_block))?;
504        let GethDebugTracingCallOptions { tracing_options, mut state_overrides, .. } = opts;
505
506        // we're essentially replaying the transactions in the block here, hence we need the state
507        // that points to the beginning of the block, which is the state at the parent block
508        let mut at = block.parent_hash();
509        let mut replay_block_txs = true;
510
511        // if a transaction index is provided, we need to replay the transactions until the index
512        let num_txs =
513            transaction_index.index().unwrap_or_else(|| block.body().transactions().len());
514        // but if all transactions are to be replayed, we can use the state at the block itself
515        // this works with the exception of the PENDING block, because its state might not exist if
516        // built locally
517        if !target_block.is_pending() && num_txs == block.body().transactions().len() {
518            at = block.hash();
519            replay_block_txs = false;
520        }
521
522        let this = self.clone();
523
524        self.eth_api()
525            .spawn_with_state_at_block(at.into(), move |state| {
526                // the outer vec for the bundles
527                let mut all_bundles = Vec::with_capacity(bundles.len());
528                let mut db = CacheDB::new(StateProviderDatabase::new(state));
529
530                if replay_block_txs {
531                    // only need to replay the transactions in the block if not all transactions are
532                    // to be replayed
533                    let transactions = block.transactions_recovered().take(num_txs);
534
535                    // Execute all transactions until index
536                    for tx in transactions {
537                        let tx_env = this.eth_api().evm_config().tx_env(tx);
538                        let (res, _) = this.eth_api().transact(&mut db, evm_env.clone(), tx_env)?;
539                        db.commit(res.state);
540                    }
541                }
542
543                // Trace all bundles
544                let mut bundles = bundles.into_iter().peekable();
545                while let Some(bundle) = bundles.next() {
546                    let mut results = Vec::with_capacity(bundle.transactions.len());
547                    let Bundle { transactions, block_override } = bundle;
548
549                    let block_overrides = block_override.map(Box::new);
550                    let mut inspector = None;
551
552                    let mut transactions = transactions.into_iter().peekable();
553                    while let Some(tx) = transactions.next() {
554                        // apply state overrides only once, before the first transaction
555                        let state_overrides = state_overrides.take();
556                        let overrides = EvmOverrides::new(state_overrides, block_overrides.clone());
557
558                        let (evm_env, tx_env) = this.eth_api().prepare_call_env(
559                            evm_env.clone(),
560                            tx,
561                            &mut db,
562                            overrides,
563                        )?;
564
565                        let (trace, state) = this.trace_transaction(
566                            &tracing_options,
567                            evm_env,
568                            tx_env,
569                            &mut db,
570                            None,
571                            &mut inspector,
572                        )?;
573
574                        inspector = inspector.map(|insp| insp.fused());
575
576                        // If there is more transactions, commit the database
577                        // If there is no transactions, but more bundles, commit to the database too
578                        if transactions.peek().is_some() || bundles.peek().is_some() {
579                            db.commit(state);
580                        }
581                        results.push(trace);
582                    }
583                    // Increment block_env number and timestamp for the next bundle
584                    evm_env.block_env.number += 1;
585                    evm_env.block_env.timestamp += 12;
586
587                    all_bundles.push(results);
588                }
589                Ok(all_bundles)
590            })
591            .await
592    }
593
594    /// Generates an execution witness for the given block hash. see
595    /// [`Self::debug_execution_witness`] for more info.
596    pub async fn debug_execution_witness_by_block_hash(
597        &self,
598        hash: B256,
599    ) -> Result<ExecutionWitness, Eth::Error> {
600        let this = self.clone();
601        let block = this
602            .eth_api()
603            .recovered_block(hash.into())
604            .await?
605            .ok_or(EthApiError::HeaderNotFound(hash.into()))?;
606
607        self.debug_execution_witness_for_block(block).await
608    }
609
610    /// The `debug_executionWitness` method allows for re-execution of a block with the purpose of
611    /// generating an execution witness. The witness comprises of a map of all hashed trie nodes to
612    /// their preimages that were required during the execution of the block, including during state
613    /// root recomputation.
614    pub async fn debug_execution_witness(
615        &self,
616        block_id: BlockNumberOrTag,
617    ) -> Result<ExecutionWitness, Eth::Error> {
618        let this = self.clone();
619        let block = this
620            .eth_api()
621            .recovered_block(block_id.into())
622            .await?
623            .ok_or(EthApiError::HeaderNotFound(block_id.into()))?;
624
625        self.debug_execution_witness_for_block(block).await
626    }
627
628    /// Generates an execution witness, using the given recovered block.
629    pub async fn debug_execution_witness_for_block(
630        &self,
631        block: Arc<RecoveredBlock<ProviderBlock<Eth::Provider>>>,
632    ) -> Result<ExecutionWitness, Eth::Error> {
633        let this = self.clone();
634        let block_number = block.header().number();
635
636        let (mut exec_witness, lowest_block_number) = self
637            .eth_api()
638            .spawn_with_state_at_block(block.parent_hash().into(), move |state_provider| {
639                let db = StateProviderDatabase::new(&state_provider);
640                let block_executor = this.inner.block_executor.executor(db);
641
642                let mut witness_record = ExecutionWitnessRecord::default();
643
644                let _ = block_executor
645                    .execute_with_state_closure(&(*block).clone(), |statedb: &State<_>| {
646                        witness_record.record_executed_state(statedb);
647                    })
648                    .map_err(|err| EthApiError::Internal(err.into()))?;
649
650                let ExecutionWitnessRecord { hashed_state, codes, keys, lowest_block_number } =
651                    witness_record;
652
653                let state = state_provider
654                    .witness(Default::default(), hashed_state)
655                    .map_err(EthApiError::from)?;
656                Ok((
657                    ExecutionWitness { state, codes, keys, ..Default::default() },
658                    lowest_block_number,
659                ))
660            })
661            .await?;
662
663        let smallest = match lowest_block_number {
664            Some(smallest) => smallest,
665            None => {
666                // Return only the parent header, if there were no calls to the
667                // BLOCKHASH opcode.
668                block_number.saturating_sub(1)
669            }
670        };
671
672        let range = smallest..block_number;
673        // TODO: Check if headers_range errors when one of the headers in the range is missing
674        exec_witness.headers = self
675            .provider()
676            .headers_range(range)
677            .map_err(EthApiError::from)?
678            .into_iter()
679            .map(|header| {
680                let mut serialized_header = Vec::new();
681                header.encode(&mut serialized_header);
682                serialized_header.into()
683            })
684            .collect();
685
686        Ok(exec_witness)
687    }
688
689    /// Returns the code associated with a given hash at the specified block ID. If no code is
690    /// found, it returns None. If no block ID is provided, it defaults to the latest block.
691    pub async fn debug_code_by_hash(
692        &self,
693        hash: B256,
694        block_id: Option<BlockId>,
695    ) -> Result<Option<Bytes>, Eth::Error> {
696        Ok(self
697            .provider()
698            .state_by_block_id(block_id.unwrap_or_default())
699            .map_err(Eth::Error::from_eth_err)?
700            .bytecode_by_hash(&hash)
701            .map_err(Eth::Error::from_eth_err)?
702            .map(|b| b.original_bytes()))
703    }
704
705    /// Executes the configured transaction with the environment on the given database.
706    ///
707    /// It optionally takes fused inspector ([`TracingInspector::fused`]) to avoid re-creating the
708    /// inspector for each transaction. This is useful when tracing multiple transactions in a
709    /// block. This is only useful for block tracing which uses the same tracer for all transactions
710    /// in the block.
711    ///
712    /// Caution: If the inspector is provided then `opts.tracer_config` is ignored.
713    ///
714    /// Returns the trace frame and the state that got updated after executing the transaction.
715    ///
716    /// Note: this does not apply any state overrides if they're configured in the `opts`.
717    ///
718    /// Caution: this is blocking and should be performed on a blocking task.
719    fn trace_transaction(
720        &self,
721        opts: &GethDebugTracingOptions,
722        evm_env: EvmEnvFor<Eth::Evm>,
723        tx_env: TxEnvFor<Eth::Evm>,
724        db: &mut StateCacheDb<'_>,
725        transaction_context: Option<TransactionContext>,
726        fused_inspector: &mut Option<TracingInspector>,
727    ) -> Result<(GethTrace, EvmState), Eth::Error> {
728        let GethDebugTracingOptions { config, tracer, tracer_config, .. } = opts;
729
730        let tx_info = TransactionInfo {
731            hash: transaction_context.as_ref().map(|c| c.tx_hash).unwrap_or_default(),
732            index: transaction_context
733                .as_ref()
734                .map(|c| c.tx_index.map(|i| i as u64))
735                .unwrap_or_default(),
736            block_hash: transaction_context.as_ref().map(|c| c.block_hash).unwrap_or_default(),
737            block_number: Some(evm_env.block_env.number),
738            base_fee: Some(evm_env.block_env.basefee),
739        };
740
741        if let Some(tracer) = tracer {
742            return match tracer {
743                GethDebugTracerType::BuiltInTracer(tracer) => match tracer {
744                    GethDebugBuiltInTracerType::FourByteTracer => {
745                        let mut inspector = FourByteInspector::default();
746                        let (res, _) =
747                            self.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?;
748                        return Ok((FourByteFrame::from(&inspector).into(), res.state))
749                    }
750                    GethDebugBuiltInTracerType::CallTracer => {
751                        let call_config = tracer_config
752                            .clone()
753                            .into_call_config()
754                            .map_err(|_| EthApiError::InvalidTracerConfig)?;
755
756                        let mut inspector = fused_inspector.get_or_insert_with(|| {
757                            TracingInspector::new(TracingInspectorConfig::from_geth_call_config(
758                                &call_config,
759                            ))
760                        });
761
762                        let (res, (_, tx_env)) =
763                            self.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?;
764
765                        inspector.set_transaction_gas_limit(tx_env.gas_limit());
766
767                        let frame = inspector
768                            .geth_builder()
769                            .geth_call_traces(call_config, res.result.gas_used());
770
771                        return Ok((frame.into(), res.state))
772                    }
773                    GethDebugBuiltInTracerType::PreStateTracer => {
774                        let prestate_config = tracer_config
775                            .clone()
776                            .into_pre_state_config()
777                            .map_err(|_| EthApiError::InvalidTracerConfig)?;
778
779                        let mut inspector = fused_inspector.get_or_insert_with(|| {
780                            TracingInspector::new(
781                                TracingInspectorConfig::from_geth_prestate_config(&prestate_config),
782                            )
783                        });
784                        let (res, (_, tx_env)) =
785                            self.eth_api().inspect(&mut *db, evm_env, tx_env, &mut inspector)?;
786
787                        inspector.set_transaction_gas_limit(tx_env.gas_limit());
788                        let frame = inspector
789                            .geth_builder()
790                            .geth_prestate_traces(&res, &prestate_config, db)
791                            .map_err(Eth::Error::from_eth_err)?;
792
793                        return Ok((frame.into(), res.state))
794                    }
795                    GethDebugBuiltInTracerType::NoopTracer => {
796                        Ok((NoopFrame::default().into(), Default::default()))
797                    }
798                    GethDebugBuiltInTracerType::MuxTracer => {
799                        let mux_config = tracer_config
800                            .clone()
801                            .into_mux_config()
802                            .map_err(|_| EthApiError::InvalidTracerConfig)?;
803
804                        let mut inspector = MuxInspector::try_from_config(mux_config)
805                            .map_err(Eth::Error::from_eth_err)?;
806
807                        let (res, _) =
808                            self.eth_api().inspect(&mut *db, evm_env, tx_env, &mut inspector)?;
809                        let frame = inspector
810                            .try_into_mux_frame(&res, db, tx_info)
811                            .map_err(Eth::Error::from_eth_err)?;
812                        return Ok((frame.into(), res.state))
813                    }
814                    GethDebugBuiltInTracerType::FlatCallTracer => {
815                        let flat_call_config = tracer_config
816                            .clone()
817                            .into_flat_call_config()
818                            .map_err(|_| EthApiError::InvalidTracerConfig)?;
819
820                        let mut inspector = TracingInspector::new(
821                            TracingInspectorConfig::from_flat_call_config(&flat_call_config),
822                        );
823
824                        let (res, (_, tx_env)) =
825                            self.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?;
826                        let frame: FlatCallFrame = inspector
827                            .with_transaction_gas_limit(tx_env.gas_limit())
828                            .into_parity_builder()
829                            .into_localized_transaction_traces(tx_info);
830
831                        return Ok((frame.into(), res.state));
832                    }
833                },
834                #[cfg(not(feature = "js-tracer"))]
835                GethDebugTracerType::JsTracer(_) => {
836                    Err(EthApiError::Unsupported("JS Tracer is not enabled").into())
837                }
838                #[cfg(feature = "js-tracer")]
839                GethDebugTracerType::JsTracer(code) => {
840                    let config = tracer_config.clone().into_json();
841                    let mut inspector =
842                        revm_inspectors::tracing::js::JsInspector::with_transaction_context(
843                            code.clone(),
844                            config,
845                            transaction_context.unwrap_or_default(),
846                        )
847                        .map_err(Eth::Error::from_eth_err)?;
848                    let (res, (evm_env, tx_env)) =
849                        self.eth_api().inspect(&mut *db, evm_env, tx_env, &mut inspector)?;
850
851                    let state = res.state.clone();
852                    let result = inspector
853                        .json_result(res, &tx_env, &evm_env.block_env, db)
854                        .map_err(Eth::Error::from_eth_err)?;
855                    Ok((GethTrace::JS(result), state))
856                }
857            }
858        }
859
860        // default structlog tracer
861        let mut inspector = fused_inspector.get_or_insert_with(|| {
862            let inspector_config = TracingInspectorConfig::from_geth_config(config);
863            TracingInspector::new(inspector_config)
864        });
865        let (res, (_, tx_env)) = self.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?;
866        let gas_used = res.result.gas_used();
867        let return_value = res.result.into_output().unwrap_or_default();
868        inspector.set_transaction_gas_limit(tx_env.gas_limit());
869        let frame = inspector.geth_builder().geth_traces(gas_used, return_value, *config);
870
871        Ok((frame.into(), res.state))
872    }
873}
874
875#[async_trait]
876impl<Eth, BlockExecutor> DebugApiServer for DebugApi<Eth, BlockExecutor>
877where
878    Eth: EthApiTypes + EthTransactions + TraceExt + 'static,
879    BlockExecutor:
880        BlockExecutorProvider<Primitives: NodePrimitives<Block = ProviderBlock<Eth::Provider>>>,
881{
882    /// Handler for `debug_getRawHeader`
883    async fn raw_header(&self, block_id: BlockId) -> RpcResult<Bytes> {
884        let header = match block_id {
885            BlockId::Hash(hash) => self.provider().header(&hash.into()).to_rpc_result()?,
886            BlockId::Number(number_or_tag) => {
887                let number = self
888                    .provider()
889                    .convert_block_number(number_or_tag)
890                    .to_rpc_result()?
891                    .ok_or_else(|| {
892                    internal_rpc_err("Pending block not supported".to_string())
893                })?;
894                self.provider().header_by_number(number).to_rpc_result()?
895            }
896        };
897
898        let mut res = Vec::new();
899        if let Some(header) = header {
900            header.encode(&mut res);
901        }
902
903        Ok(res.into())
904    }
905
906    /// Handler for `debug_getRawBlock`
907    async fn raw_block(&self, block_id: BlockId) -> RpcResult<Bytes> {
908        let block = self
909            .provider()
910            .block_by_id(block_id)
911            .to_rpc_result()?
912            .ok_or(EthApiError::HeaderNotFound(block_id))?;
913        let mut res = Vec::new();
914        block.encode(&mut res);
915        Ok(res.into())
916    }
917
918    /// Handler for `debug_getRawTransaction`
919    ///
920    /// If this is a pooled EIP-4844 transaction, the blob sidecar is included.
921    ///
922    /// Returns the bytes of the transaction for the given hash.
923    async fn raw_transaction(&self, hash: B256) -> RpcResult<Option<Bytes>> {
924        self.eth_api().raw_transaction_by_hash(hash).await.map_err(Into::into)
925    }
926
927    /// Handler for `debug_getRawTransactions`
928    /// Returns the bytes of the transaction for the given hash.
929    async fn raw_transactions(&self, block_id: BlockId) -> RpcResult<Vec<Bytes>> {
930        let block = self
931            .provider()
932            .block_with_senders_by_id(block_id, TransactionVariant::NoHash)
933            .to_rpc_result()?
934            .unwrap_or_default();
935        Ok(block.into_transactions_recovered().map(|tx| tx.encoded_2718().into()).collect())
936    }
937
938    /// Handler for `debug_getRawReceipts`
939    async fn raw_receipts(&self, block_id: BlockId) -> RpcResult<Vec<Bytes>> {
940        Ok(self
941            .provider()
942            .receipts_by_block_id(block_id)
943            .to_rpc_result()?
944            .unwrap_or_default()
945            .into_iter()
946            .map(|receipt| ReceiptWithBloom::from(receipt).encoded_2718().into())
947            .collect())
948    }
949
950    /// Handler for `debug_getBadBlocks`
951    async fn bad_blocks(&self) -> RpcResult<Vec<RpcBlock>> {
952        Err(internal_rpc_err("unimplemented"))
953    }
954
955    /// Handler for `debug_traceChain`
956    async fn debug_trace_chain(
957        &self,
958        _start_exclusive: BlockNumberOrTag,
959        _end_inclusive: BlockNumberOrTag,
960    ) -> RpcResult<Vec<BlockTraceResult>> {
961        Err(internal_rpc_err("unimplemented"))
962    }
963
964    /// Handler for `debug_traceBlock`
965    async fn debug_trace_block(
966        &self,
967        rlp_block: Bytes,
968        opts: Option<GethDebugTracingOptions>,
969    ) -> RpcResult<Vec<TraceResult>> {
970        let _permit = self.acquire_trace_permit().await;
971        Self::debug_trace_raw_block(self, rlp_block, opts.unwrap_or_default())
972            .await
973            .map_err(Into::into)
974    }
975
976    /// Handler for `debug_traceBlockByHash`
977    async fn debug_trace_block_by_hash(
978        &self,
979        block: B256,
980        opts: Option<GethDebugTracingOptions>,
981    ) -> RpcResult<Vec<TraceResult>> {
982        let _permit = self.acquire_trace_permit().await;
983        Self::debug_trace_block(self, block.into(), opts.unwrap_or_default())
984            .await
985            .map_err(Into::into)
986    }
987
988    /// Handler for `debug_traceBlockByNumber`
989    async fn debug_trace_block_by_number(
990        &self,
991        block: BlockNumberOrTag,
992        opts: Option<GethDebugTracingOptions>,
993    ) -> RpcResult<Vec<TraceResult>> {
994        let _permit = self.acquire_trace_permit().await;
995        Self::debug_trace_block(self, block.into(), opts.unwrap_or_default())
996            .await
997            .map_err(Into::into)
998    }
999
1000    /// Handler for `debug_traceTransaction`
1001    async fn debug_trace_transaction(
1002        &self,
1003        tx_hash: B256,
1004        opts: Option<GethDebugTracingOptions>,
1005    ) -> RpcResult<GethTrace> {
1006        let _permit = self.acquire_trace_permit().await;
1007        Self::debug_trace_transaction(self, tx_hash, opts.unwrap_or_default())
1008            .await
1009            .map_err(Into::into)
1010    }
1011
1012    /// Handler for `debug_traceCall`
1013    async fn debug_trace_call(
1014        &self,
1015        request: TransactionRequest,
1016        block_id: Option<BlockId>,
1017        opts: Option<GethDebugTracingCallOptions>,
1018    ) -> RpcResult<GethTrace> {
1019        let _permit = self.acquire_trace_permit().await;
1020        Self::debug_trace_call(self, request, block_id, opts.unwrap_or_default())
1021            .await
1022            .map_err(Into::into)
1023    }
1024
1025    async fn debug_trace_call_many(
1026        &self,
1027        bundles: Vec<Bundle>,
1028        state_context: Option<StateContext>,
1029        opts: Option<GethDebugTracingCallOptions>,
1030    ) -> RpcResult<Vec<Vec<GethTrace>>> {
1031        let _permit = self.acquire_trace_permit().await;
1032        Self::debug_trace_call_many(self, bundles, state_context, opts).await.map_err(Into::into)
1033    }
1034
1035    /// Handler for `debug_executionWitness`
1036    async fn debug_execution_witness(
1037        &self,
1038        block: BlockNumberOrTag,
1039    ) -> RpcResult<ExecutionWitness> {
1040        let _permit = self.acquire_trace_permit().await;
1041        Self::debug_execution_witness(self, block).await.map_err(Into::into)
1042    }
1043
1044    /// Handler for `debug_executionWitnessByBlockHash`
1045    async fn debug_execution_witness_by_block_hash(
1046        &self,
1047        hash: B256,
1048    ) -> RpcResult<ExecutionWitness> {
1049        let _permit = self.acquire_trace_permit().await;
1050        Self::debug_execution_witness_by_block_hash(self, hash).await.map_err(Into::into)
1051    }
1052
1053    async fn debug_backtrace_at(&self, _location: &str) -> RpcResult<()> {
1054        Ok(())
1055    }
1056
1057    async fn debug_account_range(
1058        &self,
1059        _block_number: BlockNumberOrTag,
1060        _start: Bytes,
1061        _max_results: u64,
1062        _nocode: bool,
1063        _nostorage: bool,
1064        _incompletes: bool,
1065    ) -> RpcResult<()> {
1066        Ok(())
1067    }
1068
1069    async fn debug_block_profile(&self, _file: String, _seconds: u64) -> RpcResult<()> {
1070        Ok(())
1071    }
1072
1073    async fn debug_chaindb_compact(&self) -> RpcResult<()> {
1074        Ok(())
1075    }
1076
1077    async fn debug_chain_config(&self) -> RpcResult<ChainConfig> {
1078        Ok(self.provider().chain_spec().genesis().config.clone())
1079    }
1080
1081    async fn debug_chaindb_property(&self, _property: String) -> RpcResult<()> {
1082        Ok(())
1083    }
1084
1085    async fn debug_code_by_hash(
1086        &self,
1087        hash: B256,
1088        block_id: Option<BlockId>,
1089    ) -> RpcResult<Option<Bytes>> {
1090        Self::debug_code_by_hash(self, hash, block_id).await.map_err(Into::into)
1091    }
1092
1093    async fn debug_cpu_profile(&self, _file: String, _seconds: u64) -> RpcResult<()> {
1094        Ok(())
1095    }
1096
1097    async fn debug_db_ancient(&self, _kind: String, _number: u64) -> RpcResult<()> {
1098        Ok(())
1099    }
1100
1101    async fn debug_db_ancients(&self) -> RpcResult<()> {
1102        Ok(())
1103    }
1104
1105    async fn debug_db_get(&self, _key: String) -> RpcResult<()> {
1106        Ok(())
1107    }
1108
1109    async fn debug_dump_block(&self, _number: BlockId) -> RpcResult<()> {
1110        Ok(())
1111    }
1112
1113    async fn debug_free_os_memory(&self) -> RpcResult<()> {
1114        Ok(())
1115    }
1116
1117    async fn debug_freeze_client(&self, _node: String) -> RpcResult<()> {
1118        Ok(())
1119    }
1120
1121    async fn debug_gc_stats(&self) -> RpcResult<()> {
1122        Ok(())
1123    }
1124
1125    async fn debug_get_accessible_state(
1126        &self,
1127        _from: BlockNumberOrTag,
1128        _to: BlockNumberOrTag,
1129    ) -> RpcResult<()> {
1130        Ok(())
1131    }
1132
1133    async fn debug_get_modified_accounts_by_hash(
1134        &self,
1135        _start_hash: B256,
1136        _end_hash: B256,
1137    ) -> RpcResult<()> {
1138        Ok(())
1139    }
1140
1141    async fn debug_get_modified_accounts_by_number(
1142        &self,
1143        _start_number: u64,
1144        _end_number: u64,
1145    ) -> RpcResult<()> {
1146        Ok(())
1147    }
1148
1149    async fn debug_go_trace(&self, _file: String, _seconds: u64) -> RpcResult<()> {
1150        Ok(())
1151    }
1152
1153    async fn debug_intermediate_roots(
1154        &self,
1155        _block_hash: B256,
1156        _opts: Option<GethDebugTracingCallOptions>,
1157    ) -> RpcResult<()> {
1158        Ok(())
1159    }
1160
1161    async fn debug_mem_stats(&self) -> RpcResult<()> {
1162        Ok(())
1163    }
1164
1165    async fn debug_mutex_profile(&self, _file: String, _nsec: u64) -> RpcResult<()> {
1166        Ok(())
1167    }
1168
1169    async fn debug_preimage(&self, _hash: B256) -> RpcResult<()> {
1170        Ok(())
1171    }
1172
1173    async fn debug_print_block(&self, _number: u64) -> RpcResult<()> {
1174        Ok(())
1175    }
1176
1177    async fn debug_seed_hash(&self, _number: u64) -> RpcResult<B256> {
1178        Ok(Default::default())
1179    }
1180
1181    async fn debug_set_block_profile_rate(&self, _rate: u64) -> RpcResult<()> {
1182        Ok(())
1183    }
1184
1185    async fn debug_set_gc_percent(&self, _v: i32) -> RpcResult<()> {
1186        Ok(())
1187    }
1188
1189    async fn debug_set_head(&self, _number: u64) -> RpcResult<()> {
1190        Ok(())
1191    }
1192
1193    async fn debug_set_mutex_profile_fraction(&self, _rate: i32) -> RpcResult<()> {
1194        Ok(())
1195    }
1196
1197    async fn debug_set_trie_flush_interval(&self, _interval: String) -> RpcResult<()> {
1198        Ok(())
1199    }
1200
1201    async fn debug_stacks(&self) -> RpcResult<()> {
1202        Ok(())
1203    }
1204
1205    async fn debug_standard_trace_bad_block_to_file(
1206        &self,
1207        _block: BlockNumberOrTag,
1208        _opts: Option<GethDebugTracingCallOptions>,
1209    ) -> RpcResult<()> {
1210        Ok(())
1211    }
1212
1213    async fn debug_standard_trace_block_to_file(
1214        &self,
1215        _block: BlockNumberOrTag,
1216        _opts: Option<GethDebugTracingCallOptions>,
1217    ) -> RpcResult<()> {
1218        Ok(())
1219    }
1220
1221    async fn debug_start_cpu_profile(&self, _file: String) -> RpcResult<()> {
1222        Ok(())
1223    }
1224
1225    async fn debug_start_go_trace(&self, _file: String) -> RpcResult<()> {
1226        Ok(())
1227    }
1228
1229    async fn debug_stop_cpu_profile(&self) -> RpcResult<()> {
1230        Ok(())
1231    }
1232
1233    async fn debug_stop_go_trace(&self) -> RpcResult<()> {
1234        Ok(())
1235    }
1236
1237    async fn debug_storage_range_at(
1238        &self,
1239        _block_hash: B256,
1240        _tx_idx: usize,
1241        _contract_address: Address,
1242        _key_start: B256,
1243        _max_result: u64,
1244    ) -> RpcResult<()> {
1245        Ok(())
1246    }
1247
1248    async fn debug_trace_bad_block(
1249        &self,
1250        _block_hash: B256,
1251        _opts: Option<GethDebugTracingCallOptions>,
1252    ) -> RpcResult<()> {
1253        Ok(())
1254    }
1255
1256    async fn debug_verbosity(&self, _level: usize) -> RpcResult<()> {
1257        Ok(())
1258    }
1259
1260    async fn debug_vmodule(&self, _pattern: String) -> RpcResult<()> {
1261        Ok(())
1262    }
1263
1264    async fn debug_write_block_profile(&self, _file: String) -> RpcResult<()> {
1265        Ok(())
1266    }
1267
1268    async fn debug_write_mem_profile(&self, _file: String) -> RpcResult<()> {
1269        Ok(())
1270    }
1271
1272    async fn debug_write_mutex_profile(&self, _file: String) -> RpcResult<()> {
1273        Ok(())
1274    }
1275}
1276
1277impl<Eth, BlockExecutor> std::fmt::Debug for DebugApi<Eth, BlockExecutor> {
1278    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1279        f.debug_struct("DebugApi").finish_non_exhaustive()
1280    }
1281}
1282
1283impl<Eth, BlockExecutor> Clone for DebugApi<Eth, BlockExecutor> {
1284    fn clone(&self) -> Self {
1285        Self { inner: Arc::clone(&self.inner) }
1286    }
1287}
1288
1289struct DebugApiInner<Eth, BlockExecutor> {
1290    /// The implementation of `eth` API
1291    eth_api: Eth,
1292    // restrict the number of concurrent calls to blocking calls
1293    blocking_task_guard: BlockingTaskGuard,
1294    /// block executor for debug & trace apis
1295    block_executor: BlockExecutor,
1296}