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