reth_node_builder/launch/
debug.rs

1use super::LaunchNode;
2use crate::{rpc::RethRpcAddOns, EngineNodeLauncher, Node, NodeHandle};
3use alloy_provider::network::AnyNetwork;
4use jsonrpsee::core::{DeserializeOwned, Serialize};
5use reth_chainspec::EthChainSpec;
6use reth_consensus_debug_client::{DebugConsensusClient, EtherscanBlockProvider, RpcBlockProvider};
7use reth_node_api::{BlockTy, FullNodeComponents};
8use std::sync::Arc;
9use tracing::info;
10/// [`Node`] extension with support for debugging utilities, see [`DebugNodeLauncher`] for more
11/// context.
12pub trait DebugNode<N: FullNodeComponents>: Node<N> {
13    /// RPC block type. Used by [`DebugConsensusClient`] to fetch blocks and submit them to the
14    /// engine.
15    type RpcBlock: Serialize + DeserializeOwned + 'static;
16
17    /// Converts an RPC block to a primitive block.
18    fn rpc_to_primitive_block(rpc_block: Self::RpcBlock) -> BlockTy<Self>;
19}
20
21/// Node launcher with support for launching various debugging utilities.
22#[derive(Debug, Clone)]
23pub struct DebugNodeLauncher<L = EngineNodeLauncher> {
24    inner: L,
25}
26
27impl<L> DebugNodeLauncher<L> {
28    /// Creates a new instance of the [`DebugNodeLauncher`].
29    pub const fn new(inner: L) -> Self {
30        Self { inner }
31    }
32}
33
34impl<L, Target, N, AddOns> LaunchNode<Target> for DebugNodeLauncher<L>
35where
36    N: FullNodeComponents<Types: DebugNode<N>>,
37    AddOns: RethRpcAddOns<N>,
38    L: LaunchNode<Target, Node = NodeHandle<N, AddOns>>,
39{
40    type Node = NodeHandle<N, AddOns>;
41
42    async fn launch_node(self, target: Target) -> eyre::Result<Self::Node> {
43        let handle = self.inner.launch_node(target).await?;
44
45        let config = &handle.node.config;
46        if let Some(ws_url) = config.debug.rpc_consensus_ws.clone() {
47            info!(target: "reth::cli", "Using RPC WebSocket consensus client: {}", ws_url);
48
49            let block_provider =
50                RpcBlockProvider::<AnyNetwork, _>::new(ws_url.as_str(), |block_response| {
51                    let json = serde_json::to_value(block_response)
52                        .expect("Block serialization cannot fail");
53                    let rpc_block =
54                        serde_json::from_value(json).expect("Block deserialization cannot fail");
55                    N::Types::rpc_to_primitive_block(rpc_block)
56                })
57                .await?;
58
59            let rpc_consensus_client = DebugConsensusClient::new(
60                handle.node.add_ons_handle.beacon_engine_handle.clone(),
61                Arc::new(block_provider),
62            );
63
64            handle.node.task_executor.spawn_critical("rpc-ws consensus client", async move {
65                rpc_consensus_client.run().await
66            });
67        }
68
69        // TODO: migrate to devmode with https://github.com/paradigmxyz/reth/issues/10104
70        if let Some(maybe_custom_etherscan_url) = config.debug.etherscan.clone() {
71            info!(target: "reth::cli", "Using etherscan as consensus client");
72
73            let chain = config.chain.chain();
74            let etherscan_url = maybe_custom_etherscan_url.map(Ok).unwrap_or_else(|| {
75                // If URL isn't provided, use default Etherscan URL for the chain if it is known
76                chain
77                    .etherscan_urls()
78                    .map(|urls| urls.0.to_string())
79                    .ok_or_else(|| eyre::eyre!("failed to get etherscan url for chain: {chain}"))
80            })?;
81
82            let block_provider = EtherscanBlockProvider::new(
83                etherscan_url,
84                chain.etherscan_api_key().ok_or_else(|| {
85                    eyre::eyre!(
86                        "etherscan api key not found for rpc consensus client for chain: {chain}"
87                    )
88                })?,
89                N::Types::rpc_to_primitive_block,
90            );
91            let rpc_consensus_client = DebugConsensusClient::new(
92                handle.node.add_ons_handle.beacon_engine_handle.clone(),
93                Arc::new(block_provider),
94            );
95            handle.node.task_executor.spawn_critical("etherscan consensus client", async move {
96                rpc_consensus_client.run().await
97            });
98        }
99
100        Ok(handle)
101    }
102}