reth_node_builder/launch/
debug.rs1use 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;
10pub trait DebugNode<N: FullNodeComponents>: Node<N> {
13 type RpcBlock: Serialize + DeserializeOwned + 'static;
16
17 fn rpc_to_primitive_block(rpc_block: Self::RpcBlock) -> BlockTy<Self>;
19}
20
21#[derive(Debug, Clone)]
23pub struct DebugNodeLauncher<L = EngineNodeLauncher> {
24 inner: L,
25}
26
27impl<L> DebugNodeLauncher<L> {
28 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 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 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}