reth_node_builder/launch/
debug.rs1use super::LaunchNode;
2use crate::{rpc::RethRpcAddOns, EngineNodeLauncher, Node, NodeHandle};
3use alloy_consensus::transaction::Either;
4use alloy_provider::network::AnyNetwork;
5use jsonrpsee::core::{DeserializeOwned, Serialize};
6use reth_chainspec::EthChainSpec;
7use reth_consensus_debug_client::{
8 BlockProvider, DebugConsensusClient, EtherscanBlockProvider, RpcBlockProvider,
9};
10use reth_engine_local::LocalMiner;
11use reth_node_api::{
12 BlockTy, FullNodeComponents, FullNodeTypes, HeaderTy, PayloadAttrTy, PayloadAttributesBuilder,
13 PayloadTypes,
14};
15use std::{
16 future::{Future, IntoFuture},
17 pin::Pin,
18 sync::Arc,
19};
20use tracing::info;
21
22pub trait DebugNode<N: FullNodeComponents>: Node<N> {
56 type RpcBlock: Serialize + DeserializeOwned + 'static;
59
60 fn rpc_to_primitive_block(rpc_block: Self::RpcBlock) -> BlockTy<Self>;
70
71 fn local_payload_attributes_builder(
78 chain_spec: &Self::ChainSpec,
79 ) -> impl PayloadAttributesBuilder<<Self::Payload as PayloadTypes>::PayloadAttributes, HeaderTy<Self>>;
80}
81
82#[derive(Debug, Clone)]
104pub struct DebugNodeLauncher<L = EngineNodeLauncher> {
105 inner: L,
106}
107
108impl<L> DebugNodeLauncher<L> {
109 pub const fn new(inner: L) -> Self {
111 Self { inner }
112 }
113}
114
115pub type DefaultDebugBlockProvider<N> = EtherscanBlockProvider<
118 <<N as FullNodeTypes>::Types as DebugNode<N>>::RpcBlock,
119 BlockTy<<N as FullNodeTypes>::Types>,
120>;
121
122#[expect(missing_debug_implementations, clippy::type_complexity)]
124pub struct DebugNodeLauncherFuture<L, Target, N, B = DefaultDebugBlockProvider<N>>
125where
126 N: FullNodeComponents<Types: DebugNode<N>>,
127{
128 inner: L,
129 target: Target,
130 local_payload_attributes_builder:
131 Option<Box<dyn PayloadAttributesBuilder<PayloadAttrTy<N::Types>, HeaderTy<N::Types>>>>,
132 map_attributes:
133 Option<Box<dyn Fn(PayloadAttrTy<N::Types>) -> PayloadAttrTy<N::Types> + Send + Sync>>,
134 debug_block_provider: Option<B>,
135}
136
137impl<L, Target, N, AddOns, B> DebugNodeLauncherFuture<L, Target, N, B>
138where
139 N: FullNodeComponents<Types: DebugNode<N>>,
140 AddOns: RethRpcAddOns<N>,
141 L: LaunchNode<Target, Node = NodeHandle<N, AddOns>>,
142 B: BlockProvider<Block = BlockTy<N::Types>> + Clone,
143{
144 pub fn with_payload_attributes_builder(
146 self,
147 builder: impl PayloadAttributesBuilder<PayloadAttrTy<N::Types>, HeaderTy<N::Types>>,
148 ) -> Self {
149 Self {
150 inner: self.inner,
151 target: self.target,
152 local_payload_attributes_builder: Some(Box::new(builder)),
153 map_attributes: None,
154 debug_block_provider: self.debug_block_provider,
155 }
156 }
157
158 pub fn map_debug_payload_attributes(
160 self,
161 f: impl Fn(PayloadAttrTy<N::Types>) -> PayloadAttrTy<N::Types> + Send + Sync + 'static,
162 ) -> Self {
163 Self {
164 inner: self.inner,
165 target: self.target,
166 local_payload_attributes_builder: None,
167 map_attributes: Some(Box::new(f)),
168 debug_block_provider: self.debug_block_provider,
169 }
170 }
171
172 pub fn with_debug_block_provider<B2>(
177 self,
178 provider: B2,
179 ) -> DebugNodeLauncherFuture<L, Target, N, B2>
180 where
181 B2: BlockProvider<Block = BlockTy<N::Types>> + Clone,
182 {
183 DebugNodeLauncherFuture {
184 inner: self.inner,
185 target: self.target,
186 local_payload_attributes_builder: self.local_payload_attributes_builder,
187 map_attributes: self.map_attributes,
188 debug_block_provider: Some(provider),
189 }
190 }
191
192 async fn launch_node(self) -> eyre::Result<NodeHandle<N, AddOns>> {
193 let Self {
194 inner,
195 target,
196 local_payload_attributes_builder,
197 map_attributes,
198 debug_block_provider,
199 } = self;
200
201 let handle = inner.launch_node(target).await?;
202
203 let config = &handle.node.config;
204
205 if let Some(provider) = debug_block_provider {
206 info!(target: "reth::cli", "Using custom debug block provider");
207
208 let rpc_consensus_client = DebugConsensusClient::new(
209 handle.node.add_ons_handle.beacon_engine_handle.clone(),
210 Arc::new(provider),
211 );
212
213 handle
214 .node
215 .task_executor
216 .spawn_critical("custom debug block provider consensus client", async move {
217 rpc_consensus_client.run().await
218 });
219 } else if let Some(url) = config.debug.rpc_consensus_url.clone() {
220 info!(target: "reth::cli", "Using RPC consensus client: {}", url);
221
222 let block_provider =
223 RpcBlockProvider::<AnyNetwork, _>::new(url.as_str(), |block_response| {
224 let json = serde_json::to_value(block_response)
225 .expect("Block serialization cannot fail");
226 let rpc_block =
227 serde_json::from_value(json).expect("Block deserialization cannot fail");
228 N::Types::rpc_to_primitive_block(rpc_block)
229 })
230 .await?;
231
232 let rpc_consensus_client = DebugConsensusClient::new(
233 handle.node.add_ons_handle.beacon_engine_handle.clone(),
234 Arc::new(block_provider),
235 );
236
237 handle.node.task_executor.spawn_critical("rpc-ws consensus client", async move {
238 rpc_consensus_client.run().await
239 });
240 } else if let Some(maybe_custom_etherscan_url) = config.debug.etherscan.clone() {
241 info!(target: "reth::cli", "Using etherscan as consensus client");
242
243 let chain = config.chain.chain();
244 let etherscan_url = maybe_custom_etherscan_url.map(Ok).unwrap_or_else(|| {
245 chain
246 .etherscan_urls()
247 .map(|urls| urls.0.to_string())
248 .ok_or_else(|| eyre::eyre!("failed to get etherscan url for chain: {chain}"))
249 })?;
250
251 let block_provider = EtherscanBlockProvider::new(
252 etherscan_url,
253 chain.etherscan_api_key().ok_or_else(|| {
254 eyre::eyre!(
255 "etherscan api key not found for rpc consensus client for chain: {chain}"
256 )
257 })?,
258 chain.id(),
259 N::Types::rpc_to_primitive_block,
260 );
261 let rpc_consensus_client = DebugConsensusClient::new(
262 handle.node.add_ons_handle.beacon_engine_handle.clone(),
263 Arc::new(block_provider),
264 );
265 handle.node.task_executor.spawn_critical("etherscan consensus client", async move {
266 rpc_consensus_client.run().await
267 });
268 }
269
270 if config.dev.dev {
271 info!(target: "reth::cli", "Using local payload attributes builder for dev mode");
272
273 let blockchain_db = handle.node.provider.clone();
274 let chain_spec = config.chain.clone();
275 let beacon_engine_handle = handle.node.add_ons_handle.beacon_engine_handle.clone();
276 let pool = handle.node.pool.clone();
277 let payload_builder_handle = handle.node.payload_builder_handle.clone();
278
279 let builder = if let Some(builder) = local_payload_attributes_builder {
280 Either::Left(builder)
281 } else {
282 let local = N::Types::local_payload_attributes_builder(&chain_spec);
283 let builder = if let Some(f) = map_attributes {
284 Either::Left(move |parent| f(local.build(&parent)))
285 } else {
286 Either::Right(local)
287 };
288 Either::Right(builder)
289 };
290
291 let dev_mining_mode = handle.node.config.dev_mining_mode(pool);
292 handle.node.task_executor.spawn_critical("local engine", async move {
293 LocalMiner::new(
294 blockchain_db,
295 builder,
296 beacon_engine_handle,
297 dev_mining_mode,
298 payload_builder_handle,
299 )
300 .run()
301 .await
302 });
303 }
304
305 Ok(handle)
306 }
307}
308
309impl<L, Target, N, AddOns, B> IntoFuture for DebugNodeLauncherFuture<L, Target, N, B>
310where
311 Target: Send + 'static,
312 N: FullNodeComponents<Types: DebugNode<N>>,
313 AddOns: RethRpcAddOns<N> + 'static,
314 L: LaunchNode<Target, Node = NodeHandle<N, AddOns>> + 'static,
315 B: BlockProvider<Block = BlockTy<N::Types>> + Clone + 'static,
316{
317 type Output = eyre::Result<NodeHandle<N, AddOns>>;
318 type IntoFuture = Pin<Box<dyn Future<Output = eyre::Result<NodeHandle<N, AddOns>>> + Send>>;
319
320 fn into_future(self) -> Self::IntoFuture {
321 Box::pin(self.launch_node())
322 }
323}
324
325impl<L, Target, N, AddOns> LaunchNode<Target> for DebugNodeLauncher<L>
326where
327 Target: Send + 'static,
328 N: FullNodeComponents<Types: DebugNode<N>>,
329 AddOns: RethRpcAddOns<N> + 'static,
330 L: LaunchNode<Target, Node = NodeHandle<N, AddOns>> + 'static,
331 DefaultDebugBlockProvider<N>: BlockProvider<Block = BlockTy<N::Types>> + Clone,
332{
333 type Node = NodeHandle<N, AddOns>;
334 type Future = DebugNodeLauncherFuture<L, Target, N>;
335
336 fn launch_node(self, target: Target) -> Self::Future {
337 DebugNodeLauncherFuture {
338 inner: self.inner,
339 target,
340 local_payload_attributes_builder: None,
341 map_attributes: None,
342 debug_block_provider: None,
343 }
344 }
345}