reth_node_builder/launch/
debug.rs

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