1use std::{
4 fmt::{self, Debug},
5 future::Future,
6 marker::PhantomData,
7 ops::{Deref, DerefMut},
8};
9
10use alloy_rpc_types::engine::ClientVersionV1;
11use futures::TryFutureExt;
12use reth_node_api::{
13 AddOnsContext, BlockTy, EngineValidator, FullNodeComponents, NodeAddOns, NodeTypes,
14 NodeTypesWithEngine,
15};
16use reth_node_core::{
17 node_config::NodeConfig,
18 version::{CARGO_PKG_VERSION, CLIENT_CODE, NAME_CLIENT, VERGEN_GIT_SHA},
19};
20use reth_payload_builder::PayloadStore;
21use reth_primitives::{EthPrimitives, PooledTransactionsElement};
22use reth_provider::providers::ProviderNodeTypes;
23use reth_rpc::{
24 eth::{EthApiTypes, FullEthApiServer},
25 EthApi,
26};
27use reth_rpc_api::eth::helpers::AddDevSigners;
28use reth_rpc_builder::{
29 auth::{AuthRpcModule, AuthServerHandle},
30 config::RethRpcServerConfig,
31 RpcModuleBuilder, RpcRegistryInner, RpcServerHandle, TransportRpcModules,
32};
33use reth_rpc_engine_api::{capabilities::EngineCapabilities, EngineApi};
34use reth_tasks::TaskExecutor;
35use reth_tracing::tracing::{debug, info};
36use reth_transaction_pool::{PoolTransaction, TransactionPool};
37use std::sync::Arc;
38
39use crate::EthApiBuilderCtx;
40
41#[derive(Debug, Clone)]
45pub struct RethRpcServerHandles {
46 pub rpc: RpcServerHandle,
48 pub auth: AuthServerHandle,
50}
51
52pub struct RpcHooks<Node: FullNodeComponents, EthApi> {
54 pub on_rpc_started: Box<dyn OnRpcStarted<Node, EthApi>>,
56 pub extend_rpc_modules: Box<dyn ExtendRpcModules<Node, EthApi>>,
58}
59
60impl<Node, EthApi> Default for RpcHooks<Node, EthApi>
61where
62 Node: FullNodeComponents,
63 EthApi: EthApiTypes,
64{
65 fn default() -> Self {
66 Self { on_rpc_started: Box::<()>::default(), extend_rpc_modules: Box::<()>::default() }
67 }
68}
69
70impl<Node, EthApi> RpcHooks<Node, EthApi>
71where
72 Node: FullNodeComponents,
73 EthApi: EthApiTypes,
74{
75 pub(crate) fn set_on_rpc_started<F>(&mut self, hook: F) -> &mut Self
77 where
78 F: OnRpcStarted<Node, EthApi> + 'static,
79 {
80 self.on_rpc_started = Box::new(hook);
81 self
82 }
83
84 #[allow(unused)]
86 pub(crate) fn on_rpc_started<F>(mut self, hook: F) -> Self
87 where
88 F: OnRpcStarted<Node, EthApi> + 'static,
89 {
90 self.set_on_rpc_started(hook);
91 self
92 }
93
94 pub(crate) fn set_extend_rpc_modules<F>(&mut self, hook: F) -> &mut Self
96 where
97 F: ExtendRpcModules<Node, EthApi> + 'static,
98 {
99 self.extend_rpc_modules = Box::new(hook);
100 self
101 }
102
103 #[allow(unused)]
105 pub(crate) fn extend_rpc_modules<F>(mut self, hook: F) -> Self
106 where
107 F: ExtendRpcModules<Node, EthApi> + 'static,
108 {
109 self.set_extend_rpc_modules(hook);
110 self
111 }
112}
113
114impl<Node, EthApi> fmt::Debug for RpcHooks<Node, EthApi>
115where
116 Node: FullNodeComponents,
117 EthApi: EthApiTypes,
118{
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 f.debug_struct("RpcHooks")
121 .field("on_rpc_started", &"...")
122 .field("extend_rpc_modules", &"...")
123 .finish()
124 }
125}
126
127pub trait OnRpcStarted<Node: FullNodeComponents, EthApi: EthApiTypes>: Send {
129 fn on_rpc_started(
131 self: Box<Self>,
132 ctx: RpcContext<'_, Node, EthApi>,
133 handles: RethRpcServerHandles,
134 ) -> eyre::Result<()>;
135}
136
137impl<Node, EthApi, F> OnRpcStarted<Node, EthApi> for F
138where
139 F: FnOnce(RpcContext<'_, Node, EthApi>, RethRpcServerHandles) -> eyre::Result<()> + Send,
140 Node: FullNodeComponents,
141 EthApi: EthApiTypes,
142{
143 fn on_rpc_started(
144 self: Box<Self>,
145 ctx: RpcContext<'_, Node, EthApi>,
146 handles: RethRpcServerHandles,
147 ) -> eyre::Result<()> {
148 (*self)(ctx, handles)
149 }
150}
151
152impl<Node, EthApi> OnRpcStarted<Node, EthApi> for ()
153where
154 Node: FullNodeComponents,
155 EthApi: EthApiTypes,
156{
157 fn on_rpc_started(
158 self: Box<Self>,
159 _: RpcContext<'_, Node, EthApi>,
160 _: RethRpcServerHandles,
161 ) -> eyre::Result<()> {
162 Ok(())
163 }
164}
165
166pub trait ExtendRpcModules<Node: FullNodeComponents, EthApi: EthApiTypes>: Send {
168 fn extend_rpc_modules(self: Box<Self>, ctx: RpcContext<'_, Node, EthApi>) -> eyre::Result<()>;
170}
171
172impl<Node, EthApi, F> ExtendRpcModules<Node, EthApi> for F
173where
174 F: FnOnce(RpcContext<'_, Node, EthApi>) -> eyre::Result<()> + Send,
175 Node: FullNodeComponents,
176 EthApi: EthApiTypes,
177{
178 fn extend_rpc_modules(self: Box<Self>, ctx: RpcContext<'_, Node, EthApi>) -> eyre::Result<()> {
179 (*self)(ctx)
180 }
181}
182
183impl<Node, EthApi> ExtendRpcModules<Node, EthApi> for ()
184where
185 Node: FullNodeComponents,
186 EthApi: EthApiTypes,
187{
188 fn extend_rpc_modules(self: Box<Self>, _: RpcContext<'_, Node, EthApi>) -> eyre::Result<()> {
189 Ok(())
190 }
191}
192
193#[derive(Debug, Clone)]
195#[allow(clippy::type_complexity)]
196pub struct RpcRegistry<Node: FullNodeComponents, EthApi: EthApiTypes> {
197 pub(crate) registry: RpcRegistryInner<
198 Node::Provider,
199 Node::Pool,
200 Node::Network,
201 TaskExecutor,
202 Node::Provider,
203 EthApi,
204 Node::Executor,
205 Node::Consensus,
206 >,
207}
208
209impl<Node, EthApi> Deref for RpcRegistry<Node, EthApi>
210where
211 Node: FullNodeComponents,
212 EthApi: EthApiTypes,
213{
214 type Target = RpcRegistryInner<
215 Node::Provider,
216 Node::Pool,
217 Node::Network,
218 TaskExecutor,
219 Node::Provider,
220 EthApi,
221 Node::Executor,
222 Node::Consensus,
223 >;
224
225 fn deref(&self) -> &Self::Target {
226 &self.registry
227 }
228}
229
230impl<Node, EthApi> DerefMut for RpcRegistry<Node, EthApi>
231where
232 Node: FullNodeComponents,
233 EthApi: EthApiTypes,
234{
235 fn deref_mut(&mut self) -> &mut Self::Target {
236 &mut self.registry
237 }
238}
239
240#[allow(missing_debug_implementations)]
248pub struct RpcContext<'a, Node: FullNodeComponents, EthApi: EthApiTypes> {
249 pub(crate) node: Node,
251
252 pub(crate) config: &'a NodeConfig<<Node::Types as NodeTypes>::ChainSpec>,
254
255 pub registry: &'a mut RpcRegistry<Node, EthApi>,
259 pub modules: &'a mut TransportRpcModules,
264 pub auth_module: &'a mut AuthRpcModule,
268}
269
270impl<Node, EthApi> RpcContext<'_, Node, EthApi>
271where
272 Node: FullNodeComponents,
273 EthApi: EthApiTypes,
274{
275 pub const fn config(&self) -> &NodeConfig<<Node::Types as NodeTypes>::ChainSpec> {
277 self.config
278 }
279
280 pub const fn node(&self) -> &Node {
282 &self.node
283 }
284
285 pub fn pool(&self) -> &Node::Pool {
287 self.node.pool()
288 }
289
290 pub fn provider(&self) -> &Node::Provider {
292 self.node.provider()
293 }
294
295 pub fn network(&self) -> &Node::Network {
297 self.node.network()
298 }
299
300 pub fn payload_builder(&self) -> &Node::PayloadBuilder {
302 self.node.payload_builder()
303 }
304}
305
306#[derive(Clone)]
308pub struct RpcHandle<Node: FullNodeComponents, EthApi: EthApiTypes> {
309 pub rpc_server_handles: RethRpcServerHandles,
311 pub rpc_registry: RpcRegistry<Node, EthApi>,
313}
314
315impl<Node: FullNodeComponents, EthApi: EthApiTypes> Deref for RpcHandle<Node, EthApi> {
316 type Target = RpcRegistry<Node, EthApi>;
317
318 fn deref(&self) -> &Self::Target {
319 &self.rpc_registry
320 }
321}
322
323impl<Node: FullNodeComponents, EthApi: EthApiTypes> Debug for RpcHandle<Node, EthApi>
324where
325 RpcRegistry<Node, EthApi>: Debug,
326{
327 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
328 f.debug_struct("RpcHandle")
329 .field("rpc_server_handles", &self.rpc_server_handles)
330 .field("rpc_registry", &self.rpc_registry)
331 .finish()
332 }
333}
334
335#[allow(clippy::type_complexity)]
337pub struct RpcAddOns<Node: FullNodeComponents, EthApi: EthApiTypes, EV> {
338 pub hooks: RpcHooks<Node, EthApi>,
340 eth_api_builder: Box<dyn FnOnce(&EthApiBuilderCtx<Node>) -> EthApi + Send + Sync>,
342 engine_validator_builder: EV,
344 _pd: PhantomData<(Node, EthApi)>,
345}
346
347impl<Node: FullNodeComponents, EthApi: EthApiTypes, EV: Debug> Debug
348 for RpcAddOns<Node, EthApi, EV>
349{
350 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
351 f.debug_struct("RpcAddOns")
352 .field("hooks", &self.hooks)
353 .field("eth_api_builder", &"...")
354 .field("engine_validator_builder", &self.engine_validator_builder)
355 .finish()
356 }
357}
358
359impl<Node: FullNodeComponents, EthApi: EthApiTypes, EV> RpcAddOns<Node, EthApi, EV> {
360 pub fn new(
362 eth_api_builder: impl FnOnce(&EthApiBuilderCtx<Node>) -> EthApi + Send + Sync + 'static,
363 engine_validator_builder: EV,
364 ) -> Self {
365 Self {
366 hooks: RpcHooks::default(),
367 eth_api_builder: Box::new(eth_api_builder),
368 engine_validator_builder,
369 _pd: PhantomData,
370 }
371 }
372
373 pub fn on_rpc_started<F>(mut self, hook: F) -> Self
375 where
376 F: FnOnce(RpcContext<'_, Node, EthApi>, RethRpcServerHandles) -> eyre::Result<()>
377 + Send
378 + 'static,
379 {
380 self.hooks.set_on_rpc_started(hook);
381 self
382 }
383
384 pub fn extend_rpc_modules<F>(mut self, hook: F) -> Self
386 where
387 F: FnOnce(RpcContext<'_, Node, EthApi>) -> eyre::Result<()> + Send + 'static,
388 {
389 self.hooks.set_extend_rpc_modules(hook);
390 self
391 }
392}
393
394impl<Node, EthApi, EV> Default for RpcAddOns<Node, EthApi, EV>
395where
396 Node: FullNodeComponents,
397 EthApi: EthApiTypes + EthApiBuilder<Node>,
398 EV: Default,
399{
400 fn default() -> Self {
401 Self::new(EthApi::build, EV::default())
402 }
403}
404
405impl<N, EthApi, EV> RpcAddOns<N, EthApi, EV>
406where
407 N: FullNodeComponents<
408 Pool: TransactionPool<Transaction: PoolTransaction<Pooled = PooledTransactionsElement>>,
409 >,
410 EthApi: EthApiTypes
411 + FullEthApiServer<Provider = N::Provider, Pool = N::Pool, Network = N::Network>
412 + AddDevSigners
413 + Unpin
414 + 'static,
415 EV: EngineValidatorBuilder<N>,
416{
417 pub async fn launch_add_ons_with<F>(
420 self,
421 ctx: AddOnsContext<'_, N>,
422 ext: F,
423 ) -> eyre::Result<RpcHandle<N, EthApi>>
424 where
425 F: FnOnce(&mut TransportRpcModules, &mut AuthRpcModule) -> eyre::Result<()>,
426 {
427 let Self { eth_api_builder, engine_validator_builder, hooks, _pd: _ } = self;
428
429 let engine_validator = engine_validator_builder.build(&ctx).await?;
430 let AddOnsContext { node, config, beacon_engine_handle, jwt_secret } = ctx;
431
432 let client = ClientVersionV1 {
433 code: CLIENT_CODE,
434 name: NAME_CLIENT.to_string(),
435 version: CARGO_PKG_VERSION.to_string(),
436 commit: VERGEN_GIT_SHA.to_string(),
437 };
438
439 let engine_api = EngineApi::new(
440 node.provider().clone(),
441 config.chain.clone(),
442 beacon_engine_handle,
443 PayloadStore::new(node.payload_builder().clone()),
444 node.pool().clone(),
445 Box::new(node.task_executor().clone()),
446 client,
447 EngineCapabilities::default(),
448 engine_validator.clone(),
449 );
450 info!(target: "reth::cli", "Engine API handler initialized");
451
452 let auth_config = config.rpc.auth_server_config(jwt_secret)?;
453 let module_config = config.rpc.transport_rpc_module_config();
454 debug!(target: "reth::cli", http=?module_config.http(), ws=?module_config.ws(), "Using RPC module config");
455
456 let (mut modules, mut auth_module, registry) = RpcModuleBuilder::default()
457 .with_provider(node.provider().clone())
458 .with_pool(node.pool().clone())
459 .with_network(node.network().clone())
460 .with_events(node.provider().clone())
461 .with_executor(node.task_executor().clone())
462 .with_evm_config(node.evm_config().clone())
463 .with_block_executor(node.block_executor().clone())
464 .with_consensus(node.consensus().clone())
465 .build_with_auth_server(
466 module_config,
467 engine_api,
468 eth_api_builder,
469 Arc::new(engine_validator),
470 );
471
472 if config.dev.dev {
474 registry.eth_api().with_dev_accounts();
475 }
476
477 let mut registry = RpcRegistry { registry };
478 let ctx = RpcContext {
479 node: node.clone(),
480 config,
481 registry: &mut registry,
482 modules: &mut modules,
483 auth_module: &mut auth_module,
484 };
485
486 let RpcHooks { on_rpc_started, extend_rpc_modules } = hooks;
487
488 ext(ctx.modules, ctx.auth_module)?;
489 extend_rpc_modules.extend_rpc_modules(ctx)?;
490
491 let server_config = config.rpc.rpc_server_config();
492 let cloned_modules = modules.clone();
493 let launch_rpc = server_config.start(&cloned_modules).map_ok(|handle| {
494 if let Some(path) = handle.ipc_endpoint() {
495 info!(target: "reth::cli", %path, "RPC IPC server started");
496 }
497 if let Some(addr) = handle.http_local_addr() {
498 info!(target: "reth::cli", url=%addr, "RPC HTTP server started");
499 }
500 if let Some(addr) = handle.ws_local_addr() {
501 info!(target: "reth::cli", url=%addr, "RPC WS server started");
502 }
503 handle
504 });
505
506 let launch_auth = auth_module.clone().start_server(auth_config).map_ok(|handle| {
507 let addr = handle.local_addr();
508 if let Some(ipc_endpoint) = handle.ipc_endpoint() {
509 info!(target: "reth::cli", url=%addr, ipc_endpoint=%ipc_endpoint,"RPC auth server started");
510 } else {
511 info!(target: "reth::cli", url=%addr, "RPC auth server started");
512 }
513 handle
514 });
515
516 let (rpc, auth) = futures::future::try_join(launch_rpc, launch_auth).await?;
518
519 let handles = RethRpcServerHandles { rpc, auth };
520
521 let ctx = RpcContext {
522 node: node.clone(),
523 config,
524 registry: &mut registry,
525 modules: &mut modules,
526 auth_module: &mut auth_module,
527 };
528
529 on_rpc_started.on_rpc_started(ctx, handles.clone())?;
530
531 Ok(RpcHandle { rpc_server_handles: handles, rpc_registry: registry })
532 }
533}
534
535impl<N, EthApi, EV> NodeAddOns<N> for RpcAddOns<N, EthApi, EV>
536where
537 N: FullNodeComponents<
538 Types: ProviderNodeTypes<Primitives = EthPrimitives>,
539 Pool: TransactionPool<Transaction: PoolTransaction<Pooled = PooledTransactionsElement>>,
540 >,
541 EthApi: EthApiTypes
542 + FullEthApiServer<Provider = N::Provider, Pool = N::Pool, Network = N::Network>
543 + AddDevSigners
544 + Unpin
545 + 'static,
546 EV: EngineValidatorBuilder<N>,
547{
548 type Handle = RpcHandle<N, EthApi>;
549
550 async fn launch_add_ons(self, ctx: AddOnsContext<'_, N>) -> eyre::Result<Self::Handle> {
551 self.launch_add_ons_with(ctx, |_, _| Ok(())).await
552 }
553}
554
555pub trait RethRpcAddOns<N: FullNodeComponents>:
558 NodeAddOns<N, Handle = RpcHandle<N, Self::EthApi>>
559{
560 type EthApi: EthApiTypes;
562
563 fn hooks_mut(&mut self) -> &mut RpcHooks<N, Self::EthApi>;
565}
566
567impl<N: FullNodeComponents, EthApi: EthApiTypes, EV> RethRpcAddOns<N> for RpcAddOns<N, EthApi, EV>
568where
569 Self: NodeAddOns<N, Handle = RpcHandle<N, EthApi>>,
570{
571 type EthApi = EthApi;
572
573 fn hooks_mut(&mut self) -> &mut RpcHooks<N, Self::EthApi> {
574 &mut self.hooks
575 }
576}
577
578pub trait EthApiBuilder<N: FullNodeComponents>: 'static {
580 fn build(ctx: &EthApiBuilderCtx<N>) -> Self;
582}
583
584impl<N: FullNodeComponents<Types: NodeTypes<Primitives = EthPrimitives>>> EthApiBuilder<N>
585 for EthApi<N::Provider, N::Pool, N::Network, N::Evm>
586{
587 fn build(ctx: &EthApiBuilderCtx<N>) -> Self {
588 Self::with_spawner(ctx)
589 }
590}
591
592pub trait EngineValidatorAddOn<Node: FullNodeComponents>: Send {
594 type Validator: EngineValidator<<Node::Types as NodeTypesWithEngine>::Engine, Block = BlockTy<Node::Types>>
596 + Clone;
597
598 fn engine_validator(
600 &self,
601 ctx: &AddOnsContext<'_, Node>,
602 ) -> impl Future<Output = eyre::Result<Self::Validator>>;
603}
604
605impl<N, EthApi, EV> EngineValidatorAddOn<N> for RpcAddOns<N, EthApi, EV>
606where
607 N: FullNodeComponents,
608 EthApi: EthApiTypes,
609 EV: EngineValidatorBuilder<N>,
610{
611 type Validator = EV::Validator;
612
613 async fn engine_validator(&self, ctx: &AddOnsContext<'_, N>) -> eyre::Result<Self::Validator> {
614 self.engine_validator_builder.clone().build(ctx).await
615 }
616}
617
618pub trait EngineValidatorBuilder<Node: FullNodeComponents>: Send + Sync + Clone {
620 type Validator: EngineValidator<<Node::Types as NodeTypesWithEngine>::Engine, Block = BlockTy<Node::Types>>
622 + Clone;
623
624 fn build(
626 self,
627 ctx: &AddOnsContext<'_, Node>,
628 ) -> impl Future<Output = eyre::Result<Self::Validator>> + Send;
629}
630
631impl<Node, F, Fut, Validator> EngineValidatorBuilder<Node> for F
632where
633 Node: FullNodeComponents,
634 Validator: EngineValidator<<Node::Types as NodeTypesWithEngine>::Engine, Block = BlockTy<Node::Types>>
635 + Clone
636 + Unpin
637 + 'static,
638 F: FnOnce(&AddOnsContext<'_, Node>) -> Fut + Send + Sync + Clone,
639 Fut: Future<Output = eyre::Result<Validator>> + Send,
640{
641 type Validator = Validator;
642
643 fn build(
644 self,
645 ctx: &AddOnsContext<'_, Node>,
646 ) -> impl Future<Output = eyre::Result<Self::Validator>> {
647 self(ctx)
648 }
649}