reth_rpc/
debug.rs

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