reth_rpc/
debug.rs

1use alloy_consensus::{transaction::SignerRecoverable, BlockHeader};
2use alloy_eips::{eip2718::Encodable2718, BlockId, BlockNumberOrTag};
3use alloy_genesis::ChainConfig;
4use alloy_primitives::{uint, Address, Bytes, B256};
5use alloy_rlp::{Decodable, Encodable};
6use alloy_rpc_types_debug::ExecutionWitness;
7use alloy_rpc_types_eth::{
8    state::EvmOverrides, Block as RpcBlock, BlockError, Bundle, StateContext, TransactionInfo,
9};
10use alloy_rpc_types_trace::geth::{
11    call::FlatCallFrame, BlockTraceResult, FourByteFrame, GethDebugBuiltInTracerType,
12    GethDebugTracerType, GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace,
13    NoopFrame, TraceResult,
14};
15use async_trait::async_trait;
16use jsonrpsee::core::RpcResult;
17use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks};
18use reth_evm::{execute::Executor, ConfigureEvm, EvmEnvFor, TxEnvFor};
19use reth_primitives_traits::{
20    Block as _, BlockBody, ReceiptWithBloom, RecoveredBlock, SignedTransaction,
21};
22use reth_revm::{
23    database::StateProviderDatabase,
24    db::{CacheDB, State},
25    witness::ExecutionWitnessRecord,
26};
27use reth_rpc_api::DebugApiServer;
28use reth_rpc_convert::RpcTxReq;
29use reth_rpc_eth_api::{
30    helpers::{EthTransactions, TraceExt},
31    EthApiTypes, FromEthApiError, RpcNodeCore,
32};
33use reth_rpc_eth_types::{EthApiError, StateCacheDb};
34use reth_rpc_server_types::{result::internal_rpc_err, ToRpcResult};
35use reth_storage_api::{
36    BlockIdReader, BlockReaderIdExt, HeaderProvider, ProviderBlock, ReceiptProviderIdExt,
37    StateProofProvider, StateProviderFactory, StateRootProvider, TransactionVariant,
38};
39use reth_tasks::pool::BlockingTaskGuard;
40use reth_trie_common::{updates::TrieUpdates, HashedPostState};
41use revm::{context_interface::Transaction, state::EvmState, DatabaseCommit};
42use revm_inspectors::tracing::{
43    FourByteInspector, MuxInspector, TracingInspector, TracingInspectorConfig, TransactionContext,
44};
45use std::sync::Arc;
46use tokio::sync::{AcquireError, OwnedSemaphorePermit};
47
48/// `debug` API implementation.
49///
50/// This type provides the functionality for handling `debug` related requests.
51pub struct DebugApi<Eth> {
52    inner: Arc<DebugApiInner<Eth>>,
53}
54
55// === impl DebugApi ===
56
57impl<Eth> DebugApi<Eth> {
58    /// Create a new instance of the [`DebugApi`]
59    pub fn new(eth_api: Eth, blocking_task_guard: BlockingTaskGuard) -> Self {
60        let inner = Arc::new(DebugApiInner { eth_api, blocking_task_guard });
61        Self { inner }
62    }
63
64    /// Access the underlying `Eth` API.
65    pub fn eth_api(&self) -> &Eth {
66        &self.inner.eth_api
67    }
68}
69
70impl<Eth: RpcNodeCore> DebugApi<Eth> {
71    /// Access the underlying provider.
72    pub fn provider(&self) -> &Eth::Provider {
73        self.inner.eth_api.provider()
74    }
75}
76
77// === impl DebugApi ===
78
79impl<Eth> DebugApi<Eth>
80where
81    Eth: EthApiTypes + TraceExt + 'static,
82{
83    /// Acquires a permit to execute a tracing call.
84    async fn acquire_trace_permit(&self) -> Result<OwnedSemaphorePermit, AcquireError> {
85        self.inner.blocking_task_guard.clone().acquire_owned().await
86    }
87
88    /// Trace the entire block asynchronously
89    async fn trace_block(
90        &self,
91        block: Arc<RecoveredBlock<ProviderBlock<Eth::Provider>>>,
92        evm_env: EvmEnvFor<Eth::Evm>,
93        opts: GethDebugTracingOptions,
94    ) -> Result<Vec<TraceResult>, Eth::Error> {
95        // replay all transactions of the block
96        let this = self.clone();
97        self.eth_api()
98            .spawn_with_state_at_block(block.parent_hash().into(), move |state| {
99                let mut results = Vec::with_capacity(block.body().transactions().len());
100                let mut db = CacheDB::new(StateProviderDatabase::new(state));
101
102                this.eth_api().apply_pre_execution_changes(&block, &mut db, &evm_env)?;
103
104                let mut transactions = block.transactions_recovered().enumerate().peekable();
105                let mut inspector = None;
106                while let Some((index, tx)) = transactions.next() {
107                    let tx_hash = *tx.tx_hash();
108
109                    let tx_env = this.eth_api().evm_config().tx_env(tx);
110
111                    let (result, state_changes) = this.trace_transaction(
112                        &opts,
113                        evm_env.clone(),
114                        tx_env,
115                        &mut db,
116                        Some(TransactionContext {
117                            block_hash: Some(block.hash()),
118                            tx_hash: Some(tx_hash),
119                            tx_index: Some(index),
120                        }),
121                        &mut inspector,
122                    )?;
123
124                    inspector = inspector.map(|insp| insp.fused());
125
126                    results.push(TraceResult::Success { result, tx_hash: Some(tx_hash) });
127                    if transactions.peek().is_some() {
128                        // need to apply the state changes of this transaction before executing the
129                        // next transaction
130                        db.commit(state_changes)
131                    }
132                }
133
134                Ok(results)
135            })
136            .await
137    }
138
139    /// Replays the given block and returns the trace of each transaction.
140    ///
141    /// This expects a rlp encoded block
142    ///
143    /// Note, the parent of this block must be present, or it will fail.
144    pub async fn debug_trace_raw_block(
145        &self,
146        rlp_block: Bytes,
147        opts: GethDebugTracingOptions,
148    ) -> Result<Vec<TraceResult>, Eth::Error> {
149        let block: ProviderBlock<Eth::Provider> = Decodable::decode(&mut rlp_block.as_ref())
150            .map_err(BlockError::RlpDecodeRawBlock)
151            .map_err(Eth::Error::from_eth_err)?;
152
153        let evm_env = self.eth_api().evm_config().evm_env(block.header());
154
155        // Depending on EIP-2 we need to recover the transactions differently
156        let senders =
157            if self.provider().chain_spec().is_homestead_active_at_block(block.header().number()) {
158                block
159                    .body()
160                    .transactions()
161                    .iter()
162                    .map(|tx| tx.recover_signer().map_err(Eth::Error::from_eth_err))
163                    .collect::<Result<Vec<_>, _>>()?
164                    .into_iter()
165                    .collect()
166            } else {
167                block
168                    .body()
169                    .transactions()
170                    .iter()
171                    .map(|tx| tx.recover_signer_unchecked().map_err(Eth::Error::from_eth_err))
172                    .collect::<Result<Vec<_>, _>>()?
173                    .into_iter()
174                    .collect()
175            };
176
177        self.trace_block(Arc::new(block.into_recovered_with_signers(senders)), evm_env, opts).await
178    }
179
180    /// Replays a block and returns the trace of each transaction.
181    pub async fn debug_trace_block(
182        &self,
183        block_id: BlockId,
184        opts: GethDebugTracingOptions,
185    ) -> Result<Vec<TraceResult>, Eth::Error> {
186        let block_hash = self
187            .provider()
188            .block_hash_for_id(block_id)
189            .map_err(Eth::Error::from_eth_err)?
190            .ok_or(EthApiError::HeaderNotFound(block_id))?;
191
192        let ((evm_env, _), block) = futures::try_join!(
193            self.eth_api().evm_env_at(block_hash.into()),
194            self.eth_api().recovered_block(block_hash.into()),
195        )?;
196
197        let block = block.ok_or(EthApiError::HeaderNotFound(block_id))?;
198
199        self.trace_block(block, evm_env, opts).await
200    }
201
202    /// Trace the transaction according to the provided options.
203    ///
204    /// Ref: <https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers>
205    pub async fn debug_trace_transaction(
206        &self,
207        tx_hash: B256,
208        opts: GethDebugTracingOptions,
209    ) -> Result<GethTrace, Eth::Error> {
210        let (transaction, block) = match self.eth_api().transaction_and_block(tx_hash).await? {
211            None => return Err(EthApiError::TransactionNotFound.into()),
212            Some(res) => res,
213        };
214        let (evm_env, _) = self.eth_api().evm_env_at(block.hash().into()).await?;
215
216        // we need to get the state of the parent block because we're essentially replaying the
217        // block the transaction is included in
218        let state_at: BlockId = block.parent_hash().into();
219        let block_hash = block.hash();
220
221        let this = self.clone();
222        self.eth_api()
223            .spawn_with_state_at_block(state_at, move |state| {
224                let block_txs = block.transactions_recovered();
225
226                // configure env for the target transaction
227                let tx = transaction.into_recovered();
228
229                let mut db = CacheDB::new(StateProviderDatabase::new(state));
230
231                this.eth_api().apply_pre_execution_changes(&block, &mut db, &evm_env)?;
232
233                // replay all transactions prior to the targeted transaction
234                let index = this.eth_api().replay_transactions_until(
235                    &mut db,
236                    evm_env.clone(),
237                    block_txs,
238                    *tx.tx_hash(),
239                )?;
240
241                let tx_env = this.eth_api().evm_config().tx_env(&tx);
242
243                this.trace_transaction(
244                    &opts,
245                    evm_env,
246                    tx_env,
247                    &mut db,
248                    Some(TransactionContext {
249                        block_hash: Some(block_hash),
250                        tx_index: Some(index),
251                        tx_hash: Some(*tx.tx_hash()),
252                    }),
253                    &mut None,
254                )
255                .map(|(trace, _)| trace)
256            })
257            .await
258    }
259
260    /// The `debug_traceCall` method lets you run an `eth_call` within the context of the given
261    /// block execution using the final state of parent block as the base.
262    ///
263    /// Differences compare to `eth_call`:
264    ///  - `debug_traceCall` executes with __enabled__ basefee check, `eth_call` does not: <https://github.com/paradigmxyz/reth/issues/6240>
265    pub async fn debug_trace_call(
266        &self,
267        call: RpcTxReq<Eth::NetworkTypes>,
268        block_id: Option<BlockId>,
269        opts: GethDebugTracingCallOptions,
270    ) -> Result<GethTrace, Eth::Error> {
271        let at = block_id.unwrap_or_default();
272        let GethDebugTracingCallOptions { tracing_options, state_overrides, block_overrides } =
273            opts;
274        let overrides = EvmOverrides::new(state_overrides, block_overrides.map(Box::new));
275        let GethDebugTracingOptions { config, tracer, tracer_config, .. } = tracing_options;
276
277        let this = self.clone();
278        if let Some(tracer) = tracer {
279            #[allow(unreachable_patterns)]
280            return match tracer {
281                GethDebugTracerType::BuiltInTracer(tracer) => match tracer {
282                    GethDebugBuiltInTracerType::FourByteTracer => {
283                        let mut inspector = FourByteInspector::default();
284                        let inspector = self
285                            .eth_api()
286                            .spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| {
287                                this.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?;
288                                Ok(inspector)
289                            })
290                            .await?;
291                        Ok(FourByteFrame::from(&inspector).into())
292                    }
293                    GethDebugBuiltInTracerType::CallTracer => {
294                        let call_config = tracer_config
295                            .into_call_config()
296                            .map_err(|_| EthApiError::InvalidTracerConfig)?;
297
298                        let mut inspector = TracingInspector::new(
299                            TracingInspectorConfig::from_geth_call_config(&call_config),
300                        );
301
302                        let frame = self
303                            .eth_api()
304                            .spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| {
305                                let gas_limit = tx_env.gas_limit();
306                                let res =
307                                    this.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?;
308                                let frame = inspector
309                                    .with_transaction_gas_limit(gas_limit)
310                                    .into_geth_builder()
311                                    .geth_call_traces(call_config, res.result.gas_used());
312                                Ok(frame.into())
313                            })
314                            .await?;
315                        Ok(frame)
316                    }
317                    GethDebugBuiltInTracerType::PreStateTracer => {
318                        let prestate_config = tracer_config
319                            .into_pre_state_config()
320                            .map_err(|_| EthApiError::InvalidTracerConfig)?;
321                        let mut inspector = TracingInspector::new(
322                            TracingInspectorConfig::from_geth_prestate_config(&prestate_config),
323                        );
324
325                        let frame = self
326                            .eth_api()
327                            .spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| {
328                                // wrapper is hack to get around 'higher-ranked lifetime error',
329                                // see <https://github.com/rust-lang/rust/issues/100013>
330                                let db = db.0;
331
332                                let gas_limit = tx_env.gas_limit();
333                                let res = this.eth_api().inspect(
334                                    &mut *db,
335                                    evm_env,
336                                    tx_env,
337                                    &mut inspector,
338                                )?;
339                                let frame = inspector
340                                    .with_transaction_gas_limit(gas_limit)
341                                    .into_geth_builder()
342                                    .geth_prestate_traces(&res, &prestate_config, db)
343                                    .map_err(Eth::Error::from_eth_err)?;
344                                Ok(frame)
345                            })
346                            .await?;
347                        Ok(frame.into())
348                    }
349                    GethDebugBuiltInTracerType::NoopTracer => Ok(NoopFrame::default().into()),
350                    GethDebugBuiltInTracerType::MuxTracer => {
351                        let mux_config = tracer_config
352                            .into_mux_config()
353                            .map_err(|_| EthApiError::InvalidTracerConfig)?;
354
355                        let mut inspector = MuxInspector::try_from_config(mux_config)
356                            .map_err(Eth::Error::from_eth_err)?;
357
358                        let frame = self
359                            .inner
360                            .eth_api
361                            .spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| {
362                                // wrapper is hack to get around 'higher-ranked lifetime error', see
363                                // <https://github.com/rust-lang/rust/issues/100013>
364                                let db = db.0;
365
366                                let tx_info = TransactionInfo {
367                                    block_number: Some(evm_env.block_env.number.saturating_to()),
368                                    base_fee: Some(evm_env.block_env.basefee),
369                                    hash: None,
370                                    block_hash: None,
371                                    index: None,
372                                };
373
374                                let res = this.eth_api().inspect(
375                                    &mut *db,
376                                    evm_env,
377                                    tx_env,
378                                    &mut inspector,
379                                )?;
380                                let frame = inspector
381                                    .try_into_mux_frame(&res, db, tx_info)
382                                    .map_err(Eth::Error::from_eth_err)?;
383                                Ok(frame.into())
384                            })
385                            .await?;
386                        Ok(frame)
387                    }
388                    GethDebugBuiltInTracerType::FlatCallTracer => {
389                        let flat_call_config = tracer_config
390                            .into_flat_call_config()
391                            .map_err(|_| EthApiError::InvalidTracerConfig)?;
392
393                        let mut inspector = TracingInspector::new(
394                            TracingInspectorConfig::from_flat_call_config(&flat_call_config),
395                        );
396
397                        let frame: FlatCallFrame = self
398                            .inner
399                            .eth_api
400                            .spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| {
401                                let gas_limit = tx_env.gas_limit();
402                                this.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?;
403                                let tx_info = TransactionInfo::default();
404                                let frame: FlatCallFrame = inspector
405                                    .with_transaction_gas_limit(gas_limit)
406                                    .into_parity_builder()
407                                    .into_localized_transaction_traces(tx_info);
408                                Ok(frame)
409                            })
410                            .await?;
411
412                        Ok(frame.into())
413                    }
414                },
415                #[cfg(not(feature = "js-tracer"))]
416                GethDebugTracerType::JsTracer(_) => {
417                    Err(EthApiError::Unsupported("JS Tracer is not enabled").into())
418                }
419                #[cfg(feature = "js-tracer")]
420                GethDebugTracerType::JsTracer(code) => {
421                    let config = tracer_config.into_json();
422
423                    let (_, at) = self.eth_api().evm_env_at(at).await?;
424
425                    let res = self
426                        .eth_api()
427                        .spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| {
428                            // wrapper is hack to get around 'higher-ranked lifetime error', see
429                            // <https://github.com/rust-lang/rust/issues/100013>
430                            let db = db.0;
431
432                            let mut inspector =
433                                revm_inspectors::tracing::js::JsInspector::new(code, config)
434                                    .map_err(Eth::Error::from_eth_err)?;
435                            let res = this.eth_api().inspect(
436                                &mut *db,
437                                evm_env.clone(),
438                                tx_env.clone(),
439                                &mut inspector,
440                            )?;
441                            inspector
442                                .json_result(res, &tx_env, &evm_env.block_env, db)
443                                .map_err(Eth::Error::from_eth_err)
444                        })
445                        .await?;
446
447                    Ok(GethTrace::JS(res))
448                }
449                _ => {
450                    // Note: this match is non-exhaustive in case we need to add support for
451                    // additional tracers
452                    Err(EthApiError::Unsupported("unsupported tracer").into())
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 gas_limit = tx_env.gas_limit();
466                let res = this.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?;
467                Ok((res, 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<RpcTxReq<Eth::NetworkTypes>>>,
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 += uint!(1_U256);
585                    evm_env.block_env.timestamp += uint!(12_U256);
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.eth_api().evm_config().executor(db);
641
642                let mut witness_record = ExecutionWitnessRecord::default();
643
644                let _ = block_executor
645                    .execute_with_state_closure(&block, |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.saturating_to()),
738            base_fee: Some(evm_env.block_env.basefee),
739        };
740
741        if let Some(tracer) = tracer {
742            #[allow(unreachable_patterns)]
743            return match tracer {
744                GethDebugTracerType::BuiltInTracer(tracer) => match tracer {
745                    GethDebugBuiltInTracerType::FourByteTracer => {
746                        let mut inspector = FourByteInspector::default();
747                        let res = 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 gas_limit = tx_env.gas_limit();
763                        let res = self.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?;
764
765                        inspector.set_transaction_gas_limit(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 gas_limit = tx_env.gas_limit();
785                        let res =
786                            self.eth_api().inspect(&mut *db, evm_env, tx_env, &mut inspector)?;
787
788                        inspector.set_transaction_gas_limit(gas_limit);
789                        let frame = inspector
790                            .geth_builder()
791                            .geth_prestate_traces(&res, &prestate_config, db)
792                            .map_err(Eth::Error::from_eth_err)?;
793
794                        return Ok((frame.into(), res.state))
795                    }
796                    GethDebugBuiltInTracerType::NoopTracer => {
797                        Ok((NoopFrame::default().into(), Default::default()))
798                    }
799                    GethDebugBuiltInTracerType::MuxTracer => {
800                        let mux_config = tracer_config
801                            .clone()
802                            .into_mux_config()
803                            .map_err(|_| EthApiError::InvalidTracerConfig)?;
804
805                        let mut inspector = MuxInspector::try_from_config(mux_config)
806                            .map_err(Eth::Error::from_eth_err)?;
807
808                        let res =
809                            self.eth_api().inspect(&mut *db, evm_env, tx_env, &mut inspector)?;
810                        let frame = inspector
811                            .try_into_mux_frame(&res, db, tx_info)
812                            .map_err(Eth::Error::from_eth_err)?;
813                        return Ok((frame.into(), res.state))
814                    }
815                    GethDebugBuiltInTracerType::FlatCallTracer => {
816                        let flat_call_config = tracer_config
817                            .clone()
818                            .into_flat_call_config()
819                            .map_err(|_| EthApiError::InvalidTracerConfig)?;
820
821                        let mut inspector = TracingInspector::new(
822                            TracingInspectorConfig::from_flat_call_config(&flat_call_config),
823                        );
824
825                        let gas_limit = tx_env.gas_limit();
826                        let res = self.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?;
827                        let frame: FlatCallFrame = inspector
828                            .with_transaction_gas_limit(gas_limit)
829                            .into_parity_builder()
830                            .into_localized_transaction_traces(tx_info);
831
832                        return Ok((frame.into(), res.state));
833                    }
834                },
835                #[cfg(not(feature = "js-tracer"))]
836                GethDebugTracerType::JsTracer(_) => {
837                    Err(EthApiError::Unsupported("JS Tracer is not enabled").into())
838                }
839                #[cfg(feature = "js-tracer")]
840                GethDebugTracerType::JsTracer(code) => {
841                    let config = tracer_config.clone().into_json();
842                    let mut inspector =
843                        revm_inspectors::tracing::js::JsInspector::with_transaction_context(
844                            code.clone(),
845                            config,
846                            transaction_context.unwrap_or_default(),
847                        )
848                        .map_err(Eth::Error::from_eth_err)?;
849                    let res = self.eth_api().inspect(
850                        &mut *db,
851                        evm_env.clone(),
852                        tx_env.clone(),
853                        &mut inspector,
854                    )?;
855
856                    let state = res.state.clone();
857                    let result = inspector
858                        .json_result(res, &tx_env, &evm_env.block_env, db)
859                        .map_err(Eth::Error::from_eth_err)?;
860                    Ok((GethTrace::JS(result), state))
861                }
862                _ => {
863                    // Note: this match is non-exhaustive in case we need to add support for
864                    // additional tracers
865                    Err(EthApiError::Unsupported("unsupported tracer").into())
866                }
867            }
868        }
869
870        // default structlog tracer
871        let mut inspector = fused_inspector.get_or_insert_with(|| {
872            let inspector_config = TracingInspectorConfig::from_geth_config(config);
873            TracingInspector::new(inspector_config)
874        });
875        let gas_limit = tx_env.gas_limit();
876        let res = self.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?;
877        let gas_used = res.result.gas_used();
878        let return_value = res.result.into_output().unwrap_or_default();
879        inspector.set_transaction_gas_limit(gas_limit);
880        let frame = inspector.geth_builder().geth_traces(gas_used, return_value, *config);
881
882        Ok((frame.into(), res.state))
883    }
884
885    /// Returns the state root of the `HashedPostState` on top of the state for the given block with
886    /// trie updates.
887    async fn debug_state_root_with_updates(
888        &self,
889        hashed_state: HashedPostState,
890        block_id: Option<BlockId>,
891    ) -> Result<(B256, TrieUpdates), Eth::Error> {
892        self.inner
893            .eth_api
894            .spawn_blocking_io(move |this| {
895                let state = this
896                    .provider()
897                    .state_by_block_id(block_id.unwrap_or_default())
898                    .map_err(Eth::Error::from_eth_err)?;
899                state.state_root_with_updates(hashed_state).map_err(Eth::Error::from_eth_err)
900            })
901            .await
902    }
903}
904
905#[async_trait]
906impl<Eth> DebugApiServer<RpcTxReq<Eth::NetworkTypes>> for DebugApi<Eth>
907where
908    Eth: EthApiTypes + EthTransactions + TraceExt + 'static,
909{
910    /// Handler for `debug_getRawHeader`
911    async fn raw_header(&self, block_id: BlockId) -> RpcResult<Bytes> {
912        let header = match block_id {
913            BlockId::Hash(hash) => self.provider().header(&hash.into()).to_rpc_result()?,
914            BlockId::Number(number_or_tag) => {
915                let number = self
916                    .provider()
917                    .convert_block_number(number_or_tag)
918                    .to_rpc_result()?
919                    .ok_or_else(|| {
920                    internal_rpc_err("Pending block not supported".to_string())
921                })?;
922                self.provider().header_by_number(number).to_rpc_result()?
923            }
924        };
925
926        let mut res = Vec::new();
927        if let Some(header) = header {
928            header.encode(&mut res);
929        }
930
931        Ok(res.into())
932    }
933
934    /// Handler for `debug_getRawBlock`
935    async fn raw_block(&self, block_id: BlockId) -> RpcResult<Bytes> {
936        let block = self
937            .provider()
938            .block_by_id(block_id)
939            .to_rpc_result()?
940            .ok_or(EthApiError::HeaderNotFound(block_id))?;
941        let mut res = Vec::new();
942        block.encode(&mut res);
943        Ok(res.into())
944    }
945
946    /// Handler for `debug_getRawTransaction`
947    ///
948    /// If this is a pooled EIP-4844 transaction, the blob sidecar is included.
949    ///
950    /// Returns the bytes of the transaction for the given hash.
951    async fn raw_transaction(&self, hash: B256) -> RpcResult<Option<Bytes>> {
952        self.eth_api().raw_transaction_by_hash(hash).await.map_err(Into::into)
953    }
954
955    /// Handler for `debug_getRawTransactions`
956    /// Returns the bytes of the transaction for the given hash.
957    async fn raw_transactions(&self, block_id: BlockId) -> RpcResult<Vec<Bytes>> {
958        let block = self
959            .provider()
960            .block_with_senders_by_id(block_id, TransactionVariant::NoHash)
961            .to_rpc_result()?
962            .unwrap_or_default();
963        Ok(block.into_transactions_recovered().map(|tx| tx.encoded_2718().into()).collect())
964    }
965
966    /// Handler for `debug_getRawReceipts`
967    async fn raw_receipts(&self, block_id: BlockId) -> RpcResult<Vec<Bytes>> {
968        Ok(self
969            .provider()
970            .receipts_by_block_id(block_id)
971            .to_rpc_result()?
972            .unwrap_or_default()
973            .into_iter()
974            .map(|receipt| ReceiptWithBloom::from(receipt).encoded_2718().into())
975            .collect())
976    }
977
978    /// Handler for `debug_getBadBlocks`
979    async fn bad_blocks(&self) -> RpcResult<Vec<RpcBlock>> {
980        Ok(vec![])
981    }
982
983    /// Handler for `debug_traceChain`
984    async fn debug_trace_chain(
985        &self,
986        _start_exclusive: BlockNumberOrTag,
987        _end_inclusive: BlockNumberOrTag,
988    ) -> RpcResult<Vec<BlockTraceResult>> {
989        Err(internal_rpc_err("unimplemented"))
990    }
991
992    /// Handler for `debug_traceBlock`
993    async fn debug_trace_block(
994        &self,
995        rlp_block: Bytes,
996        opts: Option<GethDebugTracingOptions>,
997    ) -> RpcResult<Vec<TraceResult>> {
998        let _permit = self.acquire_trace_permit().await;
999        Self::debug_trace_raw_block(self, rlp_block, opts.unwrap_or_default())
1000            .await
1001            .map_err(Into::into)
1002    }
1003
1004    /// Handler for `debug_traceBlockByHash`
1005    async fn debug_trace_block_by_hash(
1006        &self,
1007        block: B256,
1008        opts: Option<GethDebugTracingOptions>,
1009    ) -> RpcResult<Vec<TraceResult>> {
1010        let _permit = self.acquire_trace_permit().await;
1011        Self::debug_trace_block(self, block.into(), opts.unwrap_or_default())
1012            .await
1013            .map_err(Into::into)
1014    }
1015
1016    /// Handler for `debug_traceBlockByNumber`
1017    async fn debug_trace_block_by_number(
1018        &self,
1019        block: BlockNumberOrTag,
1020        opts: Option<GethDebugTracingOptions>,
1021    ) -> RpcResult<Vec<TraceResult>> {
1022        let _permit = self.acquire_trace_permit().await;
1023        Self::debug_trace_block(self, block.into(), opts.unwrap_or_default())
1024            .await
1025            .map_err(Into::into)
1026    }
1027
1028    /// Handler for `debug_traceTransaction`
1029    async fn debug_trace_transaction(
1030        &self,
1031        tx_hash: B256,
1032        opts: Option<GethDebugTracingOptions>,
1033    ) -> RpcResult<GethTrace> {
1034        let _permit = self.acquire_trace_permit().await;
1035        Self::debug_trace_transaction(self, tx_hash, opts.unwrap_or_default())
1036            .await
1037            .map_err(Into::into)
1038    }
1039
1040    /// Handler for `debug_traceCall`
1041    async fn debug_trace_call(
1042        &self,
1043        request: RpcTxReq<Eth::NetworkTypes>,
1044        block_id: Option<BlockId>,
1045        opts: Option<GethDebugTracingCallOptions>,
1046    ) -> RpcResult<GethTrace> {
1047        let _permit = self.acquire_trace_permit().await;
1048        Self::debug_trace_call(self, request, block_id, opts.unwrap_or_default())
1049            .await
1050            .map_err(Into::into)
1051    }
1052
1053    async fn debug_trace_call_many(
1054        &self,
1055        bundles: Vec<Bundle<RpcTxReq<Eth::NetworkTypes>>>,
1056        state_context: Option<StateContext>,
1057        opts: Option<GethDebugTracingCallOptions>,
1058    ) -> RpcResult<Vec<Vec<GethTrace>>> {
1059        let _permit = self.acquire_trace_permit().await;
1060        Self::debug_trace_call_many(self, bundles, state_context, opts).await.map_err(Into::into)
1061    }
1062
1063    /// Handler for `debug_executionWitness`
1064    async fn debug_execution_witness(
1065        &self,
1066        block: BlockNumberOrTag,
1067    ) -> RpcResult<ExecutionWitness> {
1068        let _permit = self.acquire_trace_permit().await;
1069        Self::debug_execution_witness(self, block).await.map_err(Into::into)
1070    }
1071
1072    /// Handler for `debug_executionWitnessByBlockHash`
1073    async fn debug_execution_witness_by_block_hash(
1074        &self,
1075        hash: B256,
1076    ) -> RpcResult<ExecutionWitness> {
1077        let _permit = self.acquire_trace_permit().await;
1078        Self::debug_execution_witness_by_block_hash(self, hash).await.map_err(Into::into)
1079    }
1080
1081    async fn debug_backtrace_at(&self, _location: &str) -> RpcResult<()> {
1082        Ok(())
1083    }
1084
1085    async fn debug_account_range(
1086        &self,
1087        _block_number: BlockNumberOrTag,
1088        _start: Bytes,
1089        _max_results: u64,
1090        _nocode: bool,
1091        _nostorage: bool,
1092        _incompletes: bool,
1093    ) -> RpcResult<()> {
1094        Ok(())
1095    }
1096
1097    async fn debug_block_profile(&self, _file: String, _seconds: u64) -> RpcResult<()> {
1098        Ok(())
1099    }
1100
1101    async fn debug_chaindb_compact(&self) -> RpcResult<()> {
1102        Ok(())
1103    }
1104
1105    async fn debug_chain_config(&self) -> RpcResult<ChainConfig> {
1106        Ok(self.provider().chain_spec().genesis().config.clone())
1107    }
1108
1109    async fn debug_chaindb_property(&self, _property: String) -> RpcResult<()> {
1110        Ok(())
1111    }
1112
1113    async fn debug_code_by_hash(
1114        &self,
1115        hash: B256,
1116        block_id: Option<BlockId>,
1117    ) -> RpcResult<Option<Bytes>> {
1118        Self::debug_code_by_hash(self, hash, block_id).await.map_err(Into::into)
1119    }
1120
1121    async fn debug_cpu_profile(&self, _file: String, _seconds: u64) -> RpcResult<()> {
1122        Ok(())
1123    }
1124
1125    async fn debug_db_ancient(&self, _kind: String, _number: u64) -> RpcResult<()> {
1126        Ok(())
1127    }
1128
1129    async fn debug_db_ancients(&self) -> RpcResult<()> {
1130        Ok(())
1131    }
1132
1133    async fn debug_db_get(&self, _key: String) -> RpcResult<()> {
1134        Ok(())
1135    }
1136
1137    async fn debug_dump_block(&self, _number: BlockId) -> RpcResult<()> {
1138        Ok(())
1139    }
1140
1141    async fn debug_free_os_memory(&self) -> RpcResult<()> {
1142        Ok(())
1143    }
1144
1145    async fn debug_freeze_client(&self, _node: String) -> RpcResult<()> {
1146        Ok(())
1147    }
1148
1149    async fn debug_gc_stats(&self) -> RpcResult<()> {
1150        Ok(())
1151    }
1152
1153    async fn debug_get_accessible_state(
1154        &self,
1155        _from: BlockNumberOrTag,
1156        _to: BlockNumberOrTag,
1157    ) -> RpcResult<()> {
1158        Ok(())
1159    }
1160
1161    async fn debug_get_modified_accounts_by_hash(
1162        &self,
1163        _start_hash: B256,
1164        _end_hash: B256,
1165    ) -> RpcResult<()> {
1166        Ok(())
1167    }
1168
1169    async fn debug_get_modified_accounts_by_number(
1170        &self,
1171        _start_number: u64,
1172        _end_number: u64,
1173    ) -> RpcResult<()> {
1174        Ok(())
1175    }
1176
1177    async fn debug_go_trace(&self, _file: String, _seconds: u64) -> RpcResult<()> {
1178        Ok(())
1179    }
1180
1181    async fn debug_intermediate_roots(
1182        &self,
1183        _block_hash: B256,
1184        _opts: Option<GethDebugTracingCallOptions>,
1185    ) -> RpcResult<()> {
1186        Ok(())
1187    }
1188
1189    async fn debug_mem_stats(&self) -> RpcResult<()> {
1190        Ok(())
1191    }
1192
1193    async fn debug_mutex_profile(&self, _file: String, _nsec: u64) -> RpcResult<()> {
1194        Ok(())
1195    }
1196
1197    async fn debug_preimage(&self, _hash: B256) -> RpcResult<()> {
1198        Ok(())
1199    }
1200
1201    async fn debug_print_block(&self, _number: u64) -> RpcResult<()> {
1202        Ok(())
1203    }
1204
1205    async fn debug_seed_hash(&self, _number: u64) -> RpcResult<B256> {
1206        Ok(Default::default())
1207    }
1208
1209    async fn debug_set_block_profile_rate(&self, _rate: u64) -> RpcResult<()> {
1210        Ok(())
1211    }
1212
1213    async fn debug_set_gc_percent(&self, _v: i32) -> RpcResult<()> {
1214        Ok(())
1215    }
1216
1217    async fn debug_set_head(&self, _number: u64) -> RpcResult<()> {
1218        Ok(())
1219    }
1220
1221    async fn debug_set_mutex_profile_fraction(&self, _rate: i32) -> RpcResult<()> {
1222        Ok(())
1223    }
1224
1225    async fn debug_set_trie_flush_interval(&self, _interval: String) -> RpcResult<()> {
1226        Ok(())
1227    }
1228
1229    async fn debug_stacks(&self) -> RpcResult<()> {
1230        Ok(())
1231    }
1232
1233    async fn debug_standard_trace_bad_block_to_file(
1234        &self,
1235        _block: BlockNumberOrTag,
1236        _opts: Option<GethDebugTracingCallOptions>,
1237    ) -> RpcResult<()> {
1238        Ok(())
1239    }
1240
1241    async fn debug_standard_trace_block_to_file(
1242        &self,
1243        _block: BlockNumberOrTag,
1244        _opts: Option<GethDebugTracingCallOptions>,
1245    ) -> RpcResult<()> {
1246        Ok(())
1247    }
1248
1249    async fn debug_start_cpu_profile(&self, _file: String) -> RpcResult<()> {
1250        Ok(())
1251    }
1252
1253    async fn debug_start_go_trace(&self, _file: String) -> RpcResult<()> {
1254        Ok(())
1255    }
1256
1257    async fn debug_state_root_with_updates(
1258        &self,
1259        hashed_state: HashedPostState,
1260        block_id: Option<BlockId>,
1261    ) -> RpcResult<(B256, TrieUpdates)> {
1262        Self::debug_state_root_with_updates(self, hashed_state, block_id).await.map_err(Into::into)
1263    }
1264
1265    async fn debug_stop_cpu_profile(&self) -> RpcResult<()> {
1266        Ok(())
1267    }
1268
1269    async fn debug_stop_go_trace(&self) -> RpcResult<()> {
1270        Ok(())
1271    }
1272
1273    async fn debug_storage_range_at(
1274        &self,
1275        _block_hash: B256,
1276        _tx_idx: usize,
1277        _contract_address: Address,
1278        _key_start: B256,
1279        _max_result: u64,
1280    ) -> RpcResult<()> {
1281        Ok(())
1282    }
1283
1284    async fn debug_trace_bad_block(
1285        &self,
1286        _block_hash: B256,
1287        _opts: Option<GethDebugTracingCallOptions>,
1288    ) -> RpcResult<()> {
1289        Ok(())
1290    }
1291
1292    async fn debug_verbosity(&self, _level: usize) -> RpcResult<()> {
1293        Ok(())
1294    }
1295
1296    async fn debug_vmodule(&self, _pattern: String) -> RpcResult<()> {
1297        Ok(())
1298    }
1299
1300    async fn debug_write_block_profile(&self, _file: String) -> RpcResult<()> {
1301        Ok(())
1302    }
1303
1304    async fn debug_write_mem_profile(&self, _file: String) -> RpcResult<()> {
1305        Ok(())
1306    }
1307
1308    async fn debug_write_mutex_profile(&self, _file: String) -> RpcResult<()> {
1309        Ok(())
1310    }
1311}
1312
1313impl<Eth> std::fmt::Debug for DebugApi<Eth> {
1314    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1315        f.debug_struct("DebugApi").finish_non_exhaustive()
1316    }
1317}
1318
1319impl<Eth> Clone for DebugApi<Eth> {
1320    fn clone(&self) -> Self {
1321        Self { inner: Arc::clone(&self.inner) }
1322    }
1323}
1324
1325struct DebugApiInner<Eth> {
1326    /// The implementation of `eth` API
1327    eth_api: Eth,
1328    // restrict the number of concurrent calls to blocking calls
1329    blocking_task_guard: BlockingTaskGuard,
1330}