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    /// The `debug_dbGet` method retrieves a value from the database using the given key.
352    ///
353    /// The key can be provided in two formats:
354    /// - Hex-encoded string with `0x` prefix: `0x[hex_string]` - decoded as hex bytes
355    /// - Raw byte string without `0x` prefix: `[raw_byte_string]` - treated as raw bytes
356    ///
357    /// # Note
358    ///
359    /// Not all nodes support this call.
360    ///
361    /// # References
362    /// - [Reth implementation](https://github.com/paradigmxyz/reth/pull/19369)
363    /// - [Geth schema](https://github.com/ethereum/go-ethereum/blob/737ffd1bf0cbee378d0111a5b17ae4724fb2216c/core/rawdb/schema.go#L29)
364    async fn debug_db_get(&self, key: &str) -> TransportResult<Bytes>;
365}
366
367#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
368#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
369impl<N, P> DebugApi<N> for P
370where
371    N: Network,
372    P: Provider<N>,
373{
374    async fn debug_get_raw_header(&self, block: BlockId) -> TransportResult<Bytes> {
375        self.client().request("debug_getRawHeader", (block,)).await
376    }
377
378    async fn debug_get_raw_block(&self, block: BlockId) -> TransportResult<Bytes> {
379        self.client().request("debug_getRawBlock", (block,)).await
380    }
381
382    async fn debug_get_raw_transaction(&self, hash: TxHash) -> TransportResult<Bytes> {
383        self.client().request("debug_getRawTransaction", (hash,)).await
384    }
385
386    async fn debug_get_raw_receipts(&self, block: BlockId) -> TransportResult<Vec<Bytes>> {
387        self.client().request("debug_getRawReceipts", (block,)).await
388    }
389
390    async fn debug_get_bad_blocks(&self) -> TransportResult<Vec<BadBlock>> {
391        self.client().request_noparams("debug_getBadBlocks").await
392    }
393
394    async fn debug_trace_chain(
395        &self,
396        start_exclusive: BlockNumberOrTag,
397        end_inclusive: BlockNumberOrTag,
398    ) -> TransportResult<Vec<BlockTraceResult>> {
399        self.client().request("debug_traceChain", (start_exclusive, end_inclusive)).await
400    }
401
402    async fn debug_trace_block(
403        &self,
404        rlp_block: &[u8],
405        trace_options: GethDebugTracingOptions,
406    ) -> TransportResult<Vec<TraceResult>> {
407        let rlp_block = hex::encode_prefixed(rlp_block);
408        self.client().request("debug_traceBlock", (rlp_block, trace_options)).await
409    }
410
411    async fn debug_trace_transaction(
412        &self,
413        hash: TxHash,
414        trace_options: GethDebugTracingOptions,
415    ) -> TransportResult<GethTrace> {
416        self.client().request("debug_traceTransaction", (hash, trace_options)).await
417    }
418
419    async fn debug_trace_transaction_as<R>(
420        &self,
421        hash: TxHash,
422        trace_options: GethDebugTracingOptions,
423    ) -> TransportResult<R>
424    where
425        R: RpcRecv,
426    {
427        self.client().request("debug_traceTransaction", (hash, trace_options)).await
428    }
429
430    async fn debug_trace_transaction_js(
431        &self,
432        hash: TxHash,
433        trace_options: GethDebugTracingOptions,
434    ) -> TransportResult<serde_json::Value> {
435        self.debug_trace_transaction_as::<serde_json::Value>(hash, trace_options).await
436    }
437
438    async fn debug_trace_transaction_call(
439        &self,
440        hash: TxHash,
441        trace_options: GethDebugTracingOptions,
442    ) -> TransportResult<CallFrame> {
443        self.debug_trace_transaction_as::<CallFrame>(hash, trace_options).await
444    }
445
446    async fn debug_trace_call_as<R>(
447        &self,
448        tx: N::TransactionRequest,
449        block: BlockId,
450        trace_options: GethDebugTracingCallOptions,
451    ) -> TransportResult<R>
452    where
453        R: RpcRecv,
454    {
455        self.client().request("debug_traceCall", (tx, block, trace_options)).await
456    }
457
458    async fn debug_trace_call_js(
459        &self,
460        tx: N::TransactionRequest,
461        block: BlockId,
462        trace_options: GethDebugTracingCallOptions,
463    ) -> TransportResult<serde_json::Value> {
464        self.debug_trace_call_as::<serde_json::Value>(tx, block, trace_options).await
465    }
466
467    async fn debug_trace_call_callframe(
468        &self,
469        tx: N::TransactionRequest,
470        block: BlockId,
471        trace_options: GethDebugTracingCallOptions,
472    ) -> TransportResult<CallFrame> {
473        self.debug_trace_call_as::<CallFrame>(tx, block, trace_options).await
474    }
475
476    async fn debug_trace_call_prestate(
477        &self,
478        tx: N::TransactionRequest,
479        block: BlockId,
480        trace_options: GethDebugTracingCallOptions,
481    ) -> TransportResult<PreStateFrame> {
482        self.debug_trace_call_as::<PreStateFrame>(tx, block, trace_options).await
483    }
484
485    async fn debug_trace_block_by_hash(
486        &self,
487        block: B256,
488        trace_options: GethDebugTracingOptions,
489    ) -> TransportResult<Vec<TraceResult>> {
490        self.client().request("debug_traceBlockByHash", (block, trace_options)).await
491    }
492
493    async fn debug_trace_block_by_number(
494        &self,
495        block: BlockNumberOrTag,
496        trace_options: GethDebugTracingOptions,
497    ) -> TransportResult<Vec<TraceResult>> {
498        self.client().request("debug_traceBlockByNumber", (block, trace_options)).await
499    }
500
501    async fn debug_trace_call(
502        &self,
503        tx: N::TransactionRequest,
504        block: BlockId,
505        trace_options: GethDebugTracingCallOptions,
506    ) -> TransportResult<GethTrace> {
507        self.client().request("debug_traceCall", (tx, block, trace_options)).await
508    }
509
510    async fn debug_trace_call_many(
511        &self,
512        bundles: Vec<Bundle>,
513        state_context: StateContext,
514        trace_options: GethDebugTracingCallOptions,
515    ) -> TransportResult<Vec<Vec<GethTrace>>> {
516        self.client().request("debug_traceCallMany", (bundles, state_context, trace_options)).await
517    }
518
519    async fn debug_trace_call_many_as<R>(
520        &self,
521        bundles: Vec<Bundle>,
522        state_context: StateContext,
523        trace_options: GethDebugTracingCallOptions,
524    ) -> TransportResult<Vec<Vec<R>>>
525    where
526        R: RpcRecv,
527    {
528        self.client().request("debug_traceCallMany", (bundles, state_context, trace_options)).await
529    }
530
531    async fn debug_trace_call_many_js(
532        &self,
533        bundles: Vec<Bundle>,
534        state_context: StateContext,
535        trace_options: GethDebugTracingCallOptions,
536    ) -> TransportResult<Vec<Vec<serde_json::Value>>> {
537        self.debug_trace_call_many_as::<serde_json::Value>(bundles, state_context, trace_options)
538            .await
539    }
540
541    async fn debug_trace_call_many_callframe(
542        &self,
543        bundles: Vec<Bundle>,
544        state_context: StateContext,
545        trace_options: GethDebugTracingCallOptions,
546    ) -> TransportResult<Vec<Vec<CallFrame>>> {
547        self.debug_trace_call_many_as::<CallFrame>(bundles, state_context, trace_options).await
548    }
549
550    async fn debug_trace_call_many_prestate(
551        &self,
552        bundles: Vec<Bundle>,
553        state_context: StateContext,
554        trace_options: GethDebugTracingCallOptions,
555    ) -> TransportResult<Vec<Vec<PreStateFrame>>> {
556        self.debug_trace_call_many_as::<PreStateFrame>(bundles, state_context, trace_options).await
557    }
558
559    async fn debug_execution_witness(
560        &self,
561        block: BlockNumberOrTag,
562    ) -> TransportResult<ExecutionWitness> {
563        self.client().request("debug_executionWitness", (block,)).await
564    }
565
566    async fn debug_code_by_hash(
567        &self,
568        hash: B256,
569        block: Option<BlockId>,
570    ) -> TransportResult<Option<Bytes>> {
571        self.client().request("debug_codeByHash", (hash, block)).await
572    }
573
574    async fn debug_db_get(&self, key: &str) -> TransportResult<Bytes> {
575        self.client().request("debug_dbGet", (key,)).await
576    }
577}
578
579#[cfg(test)]
580mod test {
581    use super::*;
582    use crate::{ext::test::async_ci_only, ProviderBuilder, WalletProvider};
583    use alloy_network::TransactionBuilder;
584    use alloy_node_bindings::{utils::run_with_tempdir, Geth, Reth};
585    use alloy_primitives::{address, U256};
586    use alloy_rpc_types_eth::TransactionRequest;
587
588    #[tokio::test]
589    async fn test_debug_trace_transaction() {
590        async_ci_only(|| async move {
591            let provider = ProviderBuilder::new().connect_anvil_with_wallet();
592            let from = provider.default_signer_address();
593
594            let gas_price = provider.get_gas_price().await.unwrap();
595            let tx = TransactionRequest::default()
596                .from(from)
597                .to(address!("deadbeef00000000deadbeef00000000deadbeef"))
598                .value(U256::from(100))
599                .max_fee_per_gas(gas_price + 1)
600                .max_priority_fee_per_gas(gas_price + 1);
601            let pending = provider.send_transaction(tx).await.unwrap();
602            let receipt = pending.get_receipt().await.unwrap();
603
604            let hash = receipt.transaction_hash;
605            let trace_options = GethDebugTracingOptions::default();
606
607            let trace = provider.debug_trace_transaction(hash, trace_options).await.unwrap();
608
609            if let GethTrace::Default(trace) = trace {
610                assert_eq!(trace.gas, 21000)
611            }
612        })
613        .await;
614    }
615
616    #[tokio::test]
617    async fn test_debug_trace_call() {
618        async_ci_only(|| async move {
619            let provider = ProviderBuilder::new().connect_anvil_with_wallet();
620            let from = provider.default_signer_address();
621            let gas_price = provider.get_gas_price().await.unwrap();
622            let tx = TransactionRequest::default()
623                .from(from)
624                .with_input("0xdeadbeef")
625                .max_fee_per_gas(gas_price + 1)
626                .max_priority_fee_per_gas(gas_price + 1);
627
628            let trace = provider
629                .debug_trace_call(
630                    tx,
631                    BlockNumberOrTag::Latest.into(),
632                    GethDebugTracingCallOptions::default(),
633                )
634                .await
635                .unwrap();
636
637            if let GethTrace::Default(trace) = trace {
638                assert!(!trace.struct_logs.is_empty());
639            }
640        })
641        .await;
642    }
643
644    #[tokio::test]
645    async fn call_debug_get_raw_header() {
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_header = provider
652                    .debug_get_raw_header(BlockId::Number(BlockNumberOrTag::Latest))
653                    .await
654                    .expect("debug_getRawHeader call should succeed");
655
656                assert!(!rlp_header.is_empty());
657            })
658            .await;
659        })
660        .await;
661    }
662
663    #[tokio::test]
664    async fn call_debug_get_raw_block() {
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 rlp_block = provider
671                    .debug_get_raw_block(BlockId::Number(BlockNumberOrTag::Latest))
672                    .await
673                    .expect("debug_getRawBlock call should succeed");
674
675                assert!(!rlp_block.is_empty());
676            })
677            .await;
678        })
679        .await;
680    }
681
682    #[tokio::test]
683    async fn call_debug_get_raw_receipts() {
684        async_ci_only(|| async move {
685            run_with_tempdir("geth-test-", |temp_dir| async move {
686                let geth = Geth::new().disable_discovery().data_dir(temp_dir).spawn();
687                let provider = ProviderBuilder::new().connect_http(geth.endpoint_url());
688
689                let result = provider
690                    .debug_get_raw_receipts(BlockId::Number(BlockNumberOrTag::Latest))
691                    .await;
692                assert!(result.is_ok());
693            })
694            .await;
695        })
696        .await;
697    }
698
699    #[tokio::test]
700    async fn call_debug_get_bad_blocks() {
701        async_ci_only(|| async move {
702            run_with_tempdir("geth-test-", |temp_dir| async move {
703                let geth = Geth::new().disable_discovery().data_dir(temp_dir).spawn();
704                let provider = ProviderBuilder::new().connect_http(geth.endpoint_url());
705
706                let result = provider.debug_get_bad_blocks().await;
707                assert!(result.is_ok());
708            })
709            .await;
710        })
711        .await;
712    }
713
714    #[tokio::test]
715    #[cfg_attr(windows, ignore = "no reth on windows")]
716    async fn debug_trace_call_many() {
717        async_ci_only(|| async move {
718            run_with_tempdir("reth-test-", |temp_dir| async move {
719                let reth = Reth::new().dev().disable_discovery().data_dir(temp_dir).spawn();
720                let provider = ProviderBuilder::new().connect_http(reth.endpoint_url());
721
722                let tx1 = TransactionRequest::default()
723                    .with_from(address!("0000000000000000000000000000000000000123"))
724                    .with_to(address!("0000000000000000000000000000000000000456"));
725
726                let tx2 = TransactionRequest::default()
727                    .with_from(address!("0000000000000000000000000000000000000456"))
728                    .with_to(address!("0000000000000000000000000000000000000789"));
729
730                let bundles = vec![Bundle { transactions: vec![tx1, tx2], block_override: None }];
731                let state_context = StateContext::default();
732                let trace_options = GethDebugTracingCallOptions::default();
733                let result =
734                    provider.debug_trace_call_many(bundles, state_context, trace_options).await;
735                assert!(result.is_ok());
736
737                let traces = result.unwrap();
738                assert_eq!(
739                    serde_json::to_string_pretty(&traces).unwrap().trim(),
740                    r#"
741[
742  [
743    {
744      "failed": false,
745      "gas": 21000,
746      "returnValue": "0x",
747      "structLogs": []
748    },
749    {
750      "failed": false,
751      "gas": 21000,
752      "returnValue": "0x",
753      "structLogs": []
754    }
755  ]
756]
757"#
758                    .trim(),
759                );
760            })
761            .await;
762        })
763        .await;
764    }
765
766    #[tokio::test]
767    #[cfg_attr(windows, ignore = "no reth on windows")]
768    async fn test_debug_code_by_hash() {
769        use alloy_primitives::b256;
770
771        async_ci_only(|| async move {
772            run_with_tempdir("reth-test-", |temp_dir| async move {
773                let reth = Reth::new().dev().disable_discovery().data_dir(temp_dir).spawn();
774                let provider = ProviderBuilder::new().connect_http(reth.endpoint_url());
775
776                // Test 1: Empty code hash (keccak256 of empty bytes)
777                // This is a valid hash that exists for EOA accounts
778                let empty_code_hash =
779                    b256!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470");
780                let empty_code = provider.debug_code_by_hash(empty_code_hash, None).await.unwrap();
781                // Reth might return Some(empty) or None for empty code
782                if let Some(code) = empty_code {
783                    assert!(
784                        code.is_empty() || code == Bytes::from_static(&[]),
785                        "Empty code hash should return empty bytes"
786                    );
787                }
788
789                // Test 2: Non-existent hash should return None
790                let non_existent_hash =
791                    b256!("0000000000000000000000000000000000000000000000000000000000000001");
792                let no_code = provider.debug_code_by_hash(non_existent_hash, None).await.unwrap();
793                assert!(no_code.is_none(), "Non-existent hash should return None");
794
795                // Test 3: Verify the API is callable and doesn't error
796                // This confirms Reth has the method implemented
797                let another_hash =
798                    b256!("1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef");
799                let result = provider.debug_code_by_hash(another_hash, None).await;
800                assert!(result.is_ok(), "API call should not error even for random hashes");
801            })
802            .await;
803        })
804        .await;
805    }
806}