alloy_provider/ext/
debug.rs

1//! This module extends the Ethereum JSON-RPC provider with the Debug namespace's RPC methods.
2use crate::Provider;
3use alloy_json_rpc::RpcRecv;
4use alloy_network::{Ethereum, Network};
5use alloy_primitives::{hex, Bytes, TxHash, B256};
6use alloy_rpc_types_debug::ExecutionWitness;
7use alloy_rpc_types_eth::{BadBlock, BlockId, BlockNumberOrTag, Bundle, StateContext};
8use alloy_rpc_types_trace::geth::{
9    BlockTraceResult, CallFrame, GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace,
10    PreStateFrame, TraceResult,
11};
12use alloy_transport::TransportResult;
13
14/// Debug namespace rpc interface that gives access to several non-standard RPC methods.
15#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
16#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
17pub trait DebugApi<N: Network = Ethereum>: Send + Sync {
18    /// Returns an RLP-encoded header.
19    async fn debug_get_raw_header(&self, block: BlockId) -> TransportResult<Bytes>;
20
21    /// Retrieves and returns the RLP encoded block by number, hash or tag.
22    async fn debug_get_raw_block(&self, block: BlockId) -> TransportResult<Bytes>;
23
24    /// Returns an EIP-2718 binary-encoded transaction.
25    async fn debug_get_raw_transaction(&self, hash: TxHash) -> TransportResult<Bytes>;
26
27    /// Returns an array of EIP-2718 binary-encoded receipts.
28    async fn debug_get_raw_receipts(&self, block: BlockId) -> TransportResult<Vec<Bytes>>;
29
30    /// Returns an array of recent bad blocks that the client has seen on the network.
31    async fn debug_get_bad_blocks(&self) -> TransportResult<Vec<BadBlock>>;
32
33    /// Returns the structured logs created during the execution of EVM between two blocks
34    /// (excluding start) as a JSON object.
35    async fn debug_trace_chain(
36        &self,
37        start_exclusive: BlockNumberOrTag,
38        end_inclusive: BlockNumberOrTag,
39    ) -> TransportResult<Vec<BlockTraceResult>>;
40
41    /// The debug_traceBlock method will return a full stack trace of all invoked opcodes of all
42    /// transaction that were included in this block.
43    ///
44    /// This expects an RLP-encoded block.
45    ///
46    /// # Note
47    ///
48    /// The parent of this block must be present, or it will fail.
49    async fn debug_trace_block(
50        &self,
51        rlp_block: &[u8],
52        trace_options: GethDebugTracingOptions,
53    ) -> TransportResult<Vec<TraceResult>>;
54
55    /// Reruns the transaction specified by the hash and returns the trace.
56    ///
57    /// It will replay any prior transactions to achieve the same state the transaction was executed
58    /// in.
59    ///
60    /// [GethDebugTracingOptions] can be used to specify the trace options.
61    ///
62    /// # Note
63    ///
64    /// Not all nodes support this call.
65    async fn debug_trace_transaction(
66        &self,
67        hash: TxHash,
68        trace_options: GethDebugTracingOptions,
69    ) -> TransportResult<GethTrace>;
70
71    /// Reruns the transaction specified by the hash and returns the trace in a specified format.
72    ///
73    /// This method allows for the trace to be returned as a type that implements `RpcRecv` and
74    /// `serde::de::DeserializeOwned`.
75    ///
76    /// [GethDebugTracingOptions] can be used to specify the trace options.
77    ///
78    /// # Note
79    ///
80    /// Not all nodes support this call.
81    async fn debug_trace_transaction_as<R>(
82        &self,
83        hash: TxHash,
84        trace_options: GethDebugTracingOptions,
85    ) -> TransportResult<R>
86    where
87        R: RpcRecv + serde::de::DeserializeOwned;
88
89    /// Reruns the transaction specified by the hash and returns the trace as a JSON object.
90    ///
91    /// This method provides the trace in a JSON format, which can be useful for further processing
92    /// or inspection.
93    ///
94    /// [GethDebugTracingOptions] can be used to specify the trace options.
95    ///
96    /// # Note
97    ///
98    /// Not all nodes support this call.
99    async fn debug_trace_transaction_js(
100        &self,
101        hash: TxHash,
102        trace_options: GethDebugTracingOptions,
103    ) -> TransportResult<serde_json::Value>;
104
105    /// Reruns the transaction specified by the hash and returns the trace as a call frame.
106    ///
107    /// This method provides the trace in the form of a `CallFrame`, which can be useful for
108    /// analyzing the call stack and execution details.
109    ///
110    /// [GethDebugTracingOptions] can be used to specify the trace options.
111    ///
112    /// # Note
113    ///
114    /// Not all nodes support this call.
115    async fn debug_trace_transaction_call(
116        &self,
117        hash: TxHash,
118        trace_options: GethDebugTracingOptions,
119    ) -> TransportResult<CallFrame>;
120
121    /// Reruns the transaction specified by the hash and returns the trace in a specified format.
122    ///
123    /// This method allows for the trace to be returned as a type that implements `RpcRecv` and
124    /// `serde::de::DeserializeOwned`.
125    ///
126    /// [GethDebugTracingOptions] can be used to specify the trace options.
127    ///
128    /// # Note
129    ///
130    /// Not all nodes support this call.
131    async fn debug_trace_call_as<R>(
132        &self,
133        tx: N::TransactionRequest,
134        block: BlockId,
135        trace_options: GethDebugTracingCallOptions,
136    ) -> TransportResult<R>
137    where
138        R: RpcRecv + serde::de::DeserializeOwned;
139
140    /// Reruns the transaction specified by the hash and returns the trace as a JSON object.
141    ///
142    /// This method provides the trace in a JSON format, which can be useful for further processing
143    /// or inspection.
144    ///
145    /// [GethDebugTracingOptions] can be used to specify the trace options.
146    ///
147    /// # Note
148    ///
149    /// Not all nodes support this call.
150    async fn debug_trace_call_js(
151        &self,
152        tx: N::TransactionRequest,
153        block: BlockId,
154        trace_options: GethDebugTracingCallOptions,
155    ) -> TransportResult<serde_json::Value>;
156
157    /// Reruns the transaction specified by the hash and returns the trace as a call frame.
158    ///
159    /// This method provides the trace in the form of a `CallFrame`, which can be useful for
160    /// analyzing the call stack and execution details.
161    ///
162    /// [GethDebugTracingOptions] can be used to specify the trace options.
163    ///
164    /// # Note
165    ///
166    /// Not all nodes support this call.
167    async fn debug_trace_call_callframe(
168        &self,
169        tx: N::TransactionRequest,
170        block: BlockId,
171        trace_options: GethDebugTracingCallOptions,
172    ) -> TransportResult<CallFrame>;
173
174    /// Reruns the transaction specified by the hash and returns the pre-state trace.
175    ///
176    /// This method provides the trace in the form of a `PreStateFrame`, which can be useful for
177    /// analyzing the state before execution.
178    ///
179    /// [GethDebugTracingOptions] can be used to specify the trace options.
180    ///
181    /// # Note
182    ///
183    /// Not all nodes support this call.
184    async fn debug_trace_call_prestate(
185        &self,
186        tx: N::TransactionRequest,
187        block: BlockId,
188        trace_options: GethDebugTracingCallOptions,
189    ) -> TransportResult<PreStateFrame>;
190
191    /// Return a full stack trace of all invoked opcodes of all transaction that were included in
192    /// this block.
193    ///
194    /// The parent of the block must be present or it will fail.
195    ///
196    /// [GethDebugTracingOptions] can be used to specify the trace options.
197    ///
198    /// # Note
199    ///
200    /// Not all nodes support this call.
201    async fn debug_trace_block_by_hash(
202        &self,
203        block: B256,
204        trace_options: GethDebugTracingOptions,
205    ) -> TransportResult<Vec<TraceResult>>;
206
207    /// Same as `debug_trace_block_by_hash` but block is specified by number.
208    ///
209    /// [GethDebugTracingOptions] can be used to specify the trace options.
210    ///
211    /// # Note
212    ///
213    /// Not all nodes support this call.
214    async fn debug_trace_block_by_number(
215        &self,
216        block: BlockNumberOrTag,
217        trace_options: GethDebugTracingOptions,
218    ) -> TransportResult<Vec<TraceResult>>;
219
220    /// Executes the given transaction without publishing it like `eth_call` and returns the trace
221    /// of the execution.
222    ///
223    /// The transaction will be executed in the context of the given block number or tag.
224    /// The state its run on is the state of the previous block.
225    ///
226    /// [GethDebugTracingOptions] can be used to specify the trace options.
227    ///
228    /// # Note
229    ///
230    ///
231    /// Not all nodes support this call.
232    async fn debug_trace_call(
233        &self,
234        tx: N::TransactionRequest,
235        block: BlockId,
236        trace_options: GethDebugTracingCallOptions,
237    ) -> TransportResult<GethTrace>;
238
239    /// Same as `debug_trace_call` but it used to run and trace multiple transactions at once.
240    ///
241    /// [GethDebugTracingOptions] can be used to specify the trace options.
242    ///
243    /// # Note
244    ///
245    /// Not all nodes support this call.
246    async fn debug_trace_call_many(
247        &self,
248        bundles: Vec<Bundle>,
249        state_context: StateContext,
250        trace_options: GethDebugTracingCallOptions,
251    ) -> TransportResult<Vec<Vec<GethTrace>>>;
252
253    /// Same as `debug_trace_call_many` but returns the traces as a type that implements `RpcRecv`.
254    ///
255    /// This method allows for the traces to be returned as a type that implements `RpcRecv` and
256    /// `serde::de::DeserializeOwned`.
257    ///
258    /// [GethDebugTracingOptions] can be used to specify the trace options.
259    ///
260    /// # Note
261    ///
262    /// Not all nodes support this call.
263    async fn debug_trace_call_many_as<R>(
264        &self,
265        bundles: Vec<Bundle>,
266        state_context: StateContext,
267        trace_options: GethDebugTracingCallOptions,
268    ) -> TransportResult<Vec<Vec<R>>>
269    where
270        R: RpcRecv + serde::de::DeserializeOwned;
271
272    /// Same as `debug_trace_call_many` but returns the traces as JSON objects.
273    ///
274    /// This method provides the traces in a JSON format, which can be useful for further processing
275    /// or inspection.
276    ///
277    /// [GethDebugTracingOptions] can be used to specify the trace options.
278    ///
279    /// # Note
280    ///
281    /// Not all nodes support this call.
282    async fn debug_trace_call_many_js(
283        &self,
284        bundles: Vec<Bundle>,
285        state_context: StateContext,
286        trace_options: GethDebugTracingCallOptions,
287    ) -> TransportResult<Vec<Vec<serde_json::Value>>>;
288
289    /// Same as `debug_trace_call_many` but returns the traces as call frames.
290    ///
291    /// This method provides the traces in the form of `CallFrame`s, which can be useful for
292    /// analyzing the call stack and execution details.
293    ///
294    /// [GethDebugTracingOptions] can be used to specify the trace options.
295    ///
296    /// # Note
297    ///
298    /// Not all nodes support this call.
299    async fn debug_trace_call_many_callframe(
300        &self,
301        bundles: Vec<Bundle>,
302        state_context: StateContext,
303        trace_options: GethDebugTracingCallOptions,
304    ) -> TransportResult<Vec<Vec<CallFrame>>>;
305
306    /// Same as `debug_trace_call_many` but returns the pre-state traces.
307    ///
308    /// This method provides the traces in the form of `PreStateFrame`s, which can be useful for
309    /// analyzing the state before execution.
310    ///
311    /// [GethDebugTracingOptions] can be used to specify the trace options.
312    ///
313    /// # Note
314    ///
315    /// Not all nodes support this call.
316    async fn debug_trace_call_many_prestate(
317        &self,
318        bundles: Vec<Bundle>,
319        state_context: StateContext,
320        trace_options: GethDebugTracingCallOptions,
321    ) -> TransportResult<Vec<Vec<PreStateFrame>>>;
322
323    /// The `debug_executionWitness` method allows for re-execution of a block with the purpose of
324    /// generating an execution witness. The witness comprises of a map of all hashed trie nodes to
325    /// their preimages that were required during the execution of the block, including during
326    /// state root recomputation.
327    ///
328    /// The first argument is the block number or block hash.
329    ///
330    /// # Note
331    ///
332    /// Not all nodes support this call.
333    async fn debug_execution_witness(
334        &self,
335        block: BlockNumberOrTag,
336    ) -> TransportResult<ExecutionWitness>;
337
338    /// The `debug_codeByHash` method returns the code associated with a given hash at the specified
339    /// block. If no code is found, it returns None. If no block is provided, it defaults to the
340    /// latest block.
341    ///
342    /// # Note
343    ///
344    /// Not all nodes support this call.
345    async fn debug_code_by_hash(
346        &self,
347        hash: B256,
348        block: Option<BlockId>,
349    ) -> TransportResult<Option<Bytes>>;
350}
351
352#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
353#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
354impl<N, P> DebugApi<N> for P
355where
356    N: Network,
357    P: Provider<N>,
358{
359    async fn debug_get_raw_header(&self, block: BlockId) -> TransportResult<Bytes> {
360        self.client().request("debug_getRawHeader", (block,)).await
361    }
362
363    async fn debug_get_raw_block(&self, block: BlockId) -> TransportResult<Bytes> {
364        self.client().request("debug_getRawBlock", (block,)).await
365    }
366
367    async fn debug_get_raw_transaction(&self, hash: TxHash) -> TransportResult<Bytes> {
368        self.client().request("debug_getRawTransaction", (hash,)).await
369    }
370
371    async fn debug_get_raw_receipts(&self, block: BlockId) -> TransportResult<Vec<Bytes>> {
372        self.client().request("debug_getRawReceipts", (block,)).await
373    }
374
375    async fn debug_get_bad_blocks(&self) -> TransportResult<Vec<BadBlock>> {
376        self.client().request_noparams("debug_getBadBlocks").await
377    }
378
379    async fn debug_trace_chain(
380        &self,
381        start_exclusive: BlockNumberOrTag,
382        end_inclusive: BlockNumberOrTag,
383    ) -> TransportResult<Vec<BlockTraceResult>> {
384        self.client().request("debug_traceChain", (start_exclusive, end_inclusive)).await
385    }
386
387    async fn debug_trace_block(
388        &self,
389        rlp_block: &[u8],
390        trace_options: GethDebugTracingOptions,
391    ) -> TransportResult<Vec<TraceResult>> {
392        let rlp_block = hex::encode_prefixed(rlp_block);
393        self.client().request("debug_traceBlock", (rlp_block, trace_options)).await
394    }
395
396    async fn debug_trace_transaction(
397        &self,
398        hash: TxHash,
399        trace_options: GethDebugTracingOptions,
400    ) -> TransportResult<GethTrace> {
401        self.client().request("debug_traceTransaction", (hash, trace_options)).await
402    }
403
404    async fn debug_trace_transaction_as<R>(
405        &self,
406        hash: TxHash,
407        trace_options: GethDebugTracingOptions,
408    ) -> TransportResult<R>
409    where
410        R: RpcRecv,
411    {
412        self.client().request("debug_traceTransaction", (hash, trace_options)).await
413    }
414
415    async fn debug_trace_transaction_js(
416        &self,
417        hash: TxHash,
418        trace_options: GethDebugTracingOptions,
419    ) -> TransportResult<serde_json::Value> {
420        self.debug_trace_transaction_as::<serde_json::Value>(hash, trace_options).await
421    }
422
423    async fn debug_trace_transaction_call(
424        &self,
425        hash: TxHash,
426        trace_options: GethDebugTracingOptions,
427    ) -> TransportResult<CallFrame> {
428        self.debug_trace_transaction_as::<CallFrame>(hash, trace_options).await
429    }
430
431    async fn debug_trace_call_as<R>(
432        &self,
433        tx: N::TransactionRequest,
434        block: BlockId,
435        trace_options: GethDebugTracingCallOptions,
436    ) -> TransportResult<R>
437    where
438        R: RpcRecv,
439    {
440        self.client().request("debug_traceCall", (tx, block, trace_options)).await
441    }
442
443    async fn debug_trace_call_js(
444        &self,
445        tx: N::TransactionRequest,
446        block: BlockId,
447        trace_options: GethDebugTracingCallOptions,
448    ) -> TransportResult<serde_json::Value> {
449        self.debug_trace_call_as::<serde_json::Value>(tx, block, trace_options).await
450    }
451
452    async fn debug_trace_call_callframe(
453        &self,
454        tx: N::TransactionRequest,
455        block: BlockId,
456        trace_options: GethDebugTracingCallOptions,
457    ) -> TransportResult<CallFrame> {
458        self.debug_trace_call_as::<CallFrame>(tx, block, trace_options).await
459    }
460
461    async fn debug_trace_call_prestate(
462        &self,
463        tx: N::TransactionRequest,
464        block: BlockId,
465        trace_options: GethDebugTracingCallOptions,
466    ) -> TransportResult<PreStateFrame> {
467        self.debug_trace_call_as::<PreStateFrame>(tx, block, trace_options).await
468    }
469
470    async fn debug_trace_block_by_hash(
471        &self,
472        block: B256,
473        trace_options: GethDebugTracingOptions,
474    ) -> TransportResult<Vec<TraceResult>> {
475        self.client().request("debug_traceBlockByHash", (block, trace_options)).await
476    }
477
478    async fn debug_trace_block_by_number(
479        &self,
480        block: BlockNumberOrTag,
481        trace_options: GethDebugTracingOptions,
482    ) -> TransportResult<Vec<TraceResult>> {
483        self.client().request("debug_traceBlockByNumber", (block, trace_options)).await
484    }
485
486    async fn debug_trace_call(
487        &self,
488        tx: N::TransactionRequest,
489        block: BlockId,
490        trace_options: GethDebugTracingCallOptions,
491    ) -> TransportResult<GethTrace> {
492        self.client().request("debug_traceCall", (tx, block, trace_options)).await
493    }
494
495    async fn debug_trace_call_many(
496        &self,
497        bundles: Vec<Bundle>,
498        state_context: StateContext,
499        trace_options: GethDebugTracingCallOptions,
500    ) -> TransportResult<Vec<Vec<GethTrace>>> {
501        self.client().request("debug_traceCallMany", (bundles, state_context, trace_options)).await
502    }
503
504    async fn debug_trace_call_many_as<R>(
505        &self,
506        bundles: Vec<Bundle>,
507        state_context: StateContext,
508        trace_options: GethDebugTracingCallOptions,
509    ) -> TransportResult<Vec<Vec<R>>>
510    where
511        R: RpcRecv,
512    {
513        self.client().request("debug_traceCallMany", (bundles, state_context, trace_options)).await
514    }
515
516    async fn debug_trace_call_many_js(
517        &self,
518        bundles: Vec<Bundle>,
519        state_context: StateContext,
520        trace_options: GethDebugTracingCallOptions,
521    ) -> TransportResult<Vec<Vec<serde_json::Value>>> {
522        self.debug_trace_call_many_as::<serde_json::Value>(bundles, state_context, trace_options)
523            .await
524    }
525
526    async fn debug_trace_call_many_callframe(
527        &self,
528        bundles: Vec<Bundle>,
529        state_context: StateContext,
530        trace_options: GethDebugTracingCallOptions,
531    ) -> TransportResult<Vec<Vec<CallFrame>>> {
532        self.debug_trace_call_many_as::<CallFrame>(bundles, state_context, trace_options).await
533    }
534
535    async fn debug_trace_call_many_prestate(
536        &self,
537        bundles: Vec<Bundle>,
538        state_context: StateContext,
539        trace_options: GethDebugTracingCallOptions,
540    ) -> TransportResult<Vec<Vec<PreStateFrame>>> {
541        self.debug_trace_call_many_as::<PreStateFrame>(bundles, state_context, trace_options).await
542    }
543
544    async fn debug_execution_witness(
545        &self,
546        block: BlockNumberOrTag,
547    ) -> TransportResult<ExecutionWitness> {
548        self.client().request("debug_executionWitness", (block,)).await
549    }
550
551    async fn debug_code_by_hash(
552        &self,
553        hash: B256,
554        block: Option<BlockId>,
555    ) -> TransportResult<Option<Bytes>> {
556        self.client().request("debug_codeByHash", (hash, block)).await
557    }
558}
559
560#[cfg(test)]
561mod test {
562    use super::*;
563    use crate::{ext::test::async_ci_only, ProviderBuilder, WalletProvider};
564    use alloy_network::TransactionBuilder;
565    use alloy_node_bindings::{utils::run_with_tempdir, Geth, Reth};
566    use alloy_primitives::{address, U256};
567    use alloy_rpc_types_eth::TransactionRequest;
568
569    #[tokio::test]
570    async fn test_debug_trace_transaction() {
571        async_ci_only(|| async move {
572            let provider = ProviderBuilder::new().connect_anvil_with_wallet();
573            let from = provider.default_signer_address();
574
575            let gas_price = provider.get_gas_price().await.unwrap();
576            let tx = TransactionRequest::default()
577                .from(from)
578                .to(address!("deadbeef00000000deadbeef00000000deadbeef"))
579                .value(U256::from(100))
580                .max_fee_per_gas(gas_price + 1)
581                .max_priority_fee_per_gas(gas_price + 1);
582            let pending = provider.send_transaction(tx).await.unwrap();
583            let receipt = pending.get_receipt().await.unwrap();
584
585            let hash = receipt.transaction_hash;
586            let trace_options = GethDebugTracingOptions::default();
587
588            let trace = provider.debug_trace_transaction(hash, trace_options).await.unwrap();
589
590            if let GethTrace::Default(trace) = trace {
591                assert_eq!(trace.gas, 21000)
592            }
593        })
594        .await;
595    }
596
597    #[tokio::test]
598    async fn test_debug_trace_call() {
599        async_ci_only(|| async move {
600            let provider = ProviderBuilder::new().connect_anvil_with_wallet();
601            let from = provider.default_signer_address();
602            let gas_price = provider.get_gas_price().await.unwrap();
603            let tx = TransactionRequest::default()
604                .from(from)
605                .with_input("0xdeadbeef")
606                .max_fee_per_gas(gas_price + 1)
607                .max_priority_fee_per_gas(gas_price + 1);
608
609            let trace = provider
610                .debug_trace_call(
611                    tx,
612                    BlockNumberOrTag::Latest.into(),
613                    GethDebugTracingCallOptions::default(),
614                )
615                .await
616                .unwrap();
617
618            if let GethTrace::Default(trace) = trace {
619                assert!(!trace.struct_logs.is_empty());
620            }
621        })
622        .await;
623    }
624
625    #[tokio::test]
626    async fn call_debug_get_raw_header() {
627        async_ci_only(|| async move {
628            run_with_tempdir("geth-test-", |temp_dir| async move {
629                let geth = Geth::new().disable_discovery().data_dir(temp_dir).spawn();
630                let provider = ProviderBuilder::new().connect_http(geth.endpoint_url());
631
632                let rlp_header = provider
633                    .debug_get_raw_header(BlockId::Number(BlockNumberOrTag::Latest))
634                    .await
635                    .expect("debug_getRawHeader call should succeed");
636
637                assert!(!rlp_header.is_empty());
638            })
639            .await;
640        })
641        .await;
642    }
643
644    #[tokio::test]
645    async fn call_debug_get_raw_block() {
646        async_ci_only(|| async move {
647            run_with_tempdir("geth-test-", |temp_dir| async move {
648                let geth = Geth::new().disable_discovery().data_dir(temp_dir).spawn();
649                let provider = ProviderBuilder::new().connect_http(geth.endpoint_url());
650
651                let rlp_block = provider
652                    .debug_get_raw_block(BlockId::Number(BlockNumberOrTag::Latest))
653                    .await
654                    .expect("debug_getRawBlock call should succeed");
655
656                assert!(!rlp_block.is_empty());
657            })
658            .await;
659        })
660        .await;
661    }
662
663    #[tokio::test]
664    async fn call_debug_get_raw_receipts() {
665        async_ci_only(|| async move {
666            run_with_tempdir("geth-test-", |temp_dir| async move {
667                let geth = Geth::new().disable_discovery().data_dir(temp_dir).spawn();
668                let provider = ProviderBuilder::new().connect_http(geth.endpoint_url());
669
670                let result = provider
671                    .debug_get_raw_receipts(BlockId::Number(BlockNumberOrTag::Latest))
672                    .await;
673                assert!(result.is_ok());
674            })
675            .await;
676        })
677        .await;
678    }
679
680    #[tokio::test]
681    async fn call_debug_get_bad_blocks() {
682        async_ci_only(|| async move {
683            run_with_tempdir("geth-test-", |temp_dir| async move {
684                let geth = Geth::new().disable_discovery().data_dir(temp_dir).spawn();
685                let provider = ProviderBuilder::new().connect_http(geth.endpoint_url());
686
687                let result = provider.debug_get_bad_blocks().await;
688                assert!(result.is_ok());
689            })
690            .await;
691        })
692        .await;
693    }
694
695    #[tokio::test]
696    #[cfg_attr(windows, ignore = "no reth on windows")]
697    async fn debug_trace_call_many() {
698        async_ci_only(|| async move {
699            run_with_tempdir("reth-test-", |temp_dir| async move {
700                let reth = Reth::new().dev().disable_discovery().data_dir(temp_dir).spawn();
701                let provider = ProviderBuilder::new().connect_http(reth.endpoint_url());
702
703                let tx1 = TransactionRequest::default()
704                    .with_from(address!("0000000000000000000000000000000000000123"))
705                    .with_to(address!("0000000000000000000000000000000000000456"));
706
707                let tx2 = TransactionRequest::default()
708                    .with_from(address!("0000000000000000000000000000000000000456"))
709                    .with_to(address!("0000000000000000000000000000000000000789"));
710
711                let bundles = vec![Bundle { transactions: vec![tx1, tx2], block_override: None }];
712                let state_context = StateContext::default();
713                let trace_options = GethDebugTracingCallOptions::default();
714                let result =
715                    provider.debug_trace_call_many(bundles, state_context, trace_options).await;
716                assert!(result.is_ok());
717
718                let traces = result.unwrap();
719                assert_eq!(
720                    serde_json::to_string_pretty(&traces).unwrap().trim(),
721                    r#"
722[
723  [
724    {
725      "failed": false,
726      "gas": 21000,
727      "returnValue": "0x",
728      "structLogs": []
729    },
730    {
731      "failed": false,
732      "gas": 21000,
733      "returnValue": "0x",
734      "structLogs": []
735    }
736  ]
737]
738"#
739                    .trim(),
740                );
741            })
742            .await;
743        })
744        .await;
745    }
746
747    // TODO: Enable for next reth release > v1.2.0
748    /*
749    #[tokio::test]
750    #[cfg_attr(windows, ignore = "no reth on windows")]
751    async fn test_debug_code_by_hash() {
752        async_ci_only(|| async move {
753            run_with_tempdir("reth-test-", |temp_dir| async move {
754                let reth = Reth::new().dev().disable_discovery().data_dir(temp_dir).spawn();
755                let provider = ProviderBuilder::new().connect_http(reth.endpoint_url());
756
757                // Contract (mainnet): 0x4e59b44847b379578588920ca78fbf26c0b4956c
758                let code = provider.debug_code_by_hash(
759                    b256!("2fa86add0aed31f33a762c9d88e807c475bd51d0f52bd0955754b2608f7e4989"),
760                    None
761                ).await.unwrap().unwrap();
762                assert_eq!(code,
763                           Bytes::from_static(&hex!("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\
764                           e03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3")));
765            }).await;
766        }).await;
767    }
768    */
769}