reth_node_builder/
rpc.rs

1//! Builder support for rpc components.
2
3use 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/// Contains the handles to the spawned RPC servers.
42///
43/// This can be used to access the endpoints of the servers.
44#[derive(Debug, Clone)]
45pub struct RethRpcServerHandles {
46    /// The regular RPC server handle to all configured transports.
47    pub rpc: RpcServerHandle,
48    /// The handle to the auth server (engine API)
49    pub auth: AuthServerHandle,
50}
51
52/// Contains hooks that are called during the rpc setup.
53pub struct RpcHooks<Node: FullNodeComponents, EthApi> {
54    /// Hooks to run once RPC server is running.
55    pub on_rpc_started: Box<dyn OnRpcStarted<Node, EthApi>>,
56    /// Hooks to run to configure RPC server API.
57    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    /// Sets the hook that is run once the rpc server is started.
76    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    /// Sets the hook that is run once the rpc server is started.
85    #[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    /// Sets the hook that is run to configure the rpc modules.
95    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    /// Sets the hook that is run to configure the rpc modules.
104    #[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
127/// Event hook that is called once the rpc server is started.
128pub trait OnRpcStarted<Node: FullNodeComponents, EthApi: EthApiTypes>: Send {
129    /// The hook that is called once the rpc server is started.
130    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
166/// Event hook that is called when the rpc server is started.
167pub trait ExtendRpcModules<Node: FullNodeComponents, EthApi: EthApiTypes>: Send {
168    /// The hook that is called once the rpc server is started.
169    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/// Helper wrapper type to encapsulate the [`RpcRegistryInner`] over components trait.
194#[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/// Helper container to encapsulate [`RpcRegistryInner`], [`TransportRpcModules`] and
241/// [`AuthRpcModule`].
242///
243/// This can be used to access installed modules, or create commonly used handlers like
244/// [`reth_rpc::eth::EthApi`], and ultimately merge additional rpc handler into the configured
245/// transport modules [`TransportRpcModules`] as well as configured authenticated methods
246/// [`AuthRpcModule`].
247#[allow(missing_debug_implementations)]
248pub struct RpcContext<'a, Node: FullNodeComponents, EthApi: EthApiTypes> {
249    /// The node components.
250    pub(crate) node: Node,
251
252    /// Gives access to the node configuration.
253    pub(crate) config: &'a NodeConfig<<Node::Types as NodeTypes>::ChainSpec>,
254
255    /// A Helper type the holds instances of the configured modules.
256    ///
257    /// This provides easy access to rpc handlers, such as [`RpcRegistryInner::eth_api`].
258    pub registry: &'a mut RpcRegistry<Node, EthApi>,
259    /// Holds installed modules per transport type.
260    ///
261    /// This can be used to merge additional modules into the configured transports (http, ipc,
262    /// ws). See [`TransportRpcModules::merge_configured`]
263    pub modules: &'a mut TransportRpcModules,
264    /// Holds jwt authenticated rpc module.
265    ///
266    /// This can be used to merge additional modules into the configured authenticated methods
267    pub auth_module: &'a mut AuthRpcModule,
268}
269
270impl<Node, EthApi> RpcContext<'_, Node, EthApi>
271where
272    Node: FullNodeComponents,
273    EthApi: EthApiTypes,
274{
275    /// Returns the config of the node.
276    pub const fn config(&self) -> &NodeConfig<<Node::Types as NodeTypes>::ChainSpec> {
277        self.config
278    }
279
280    /// Returns a reference to the configured node.
281    pub const fn node(&self) -> &Node {
282        &self.node
283    }
284
285    /// Returns the transaction pool instance.
286    pub fn pool(&self) -> &Node::Pool {
287        self.node.pool()
288    }
289
290    /// Returns provider to interact with the node.
291    pub fn provider(&self) -> &Node::Provider {
292        self.node.provider()
293    }
294
295    /// Returns the handle to the network
296    pub fn network(&self) -> &Node::Network {
297        self.node.network()
298    }
299
300    /// Returns the handle to the payload builder service
301    pub fn payload_builder(&self) -> &Node::PayloadBuilder {
302        self.node.payload_builder()
303    }
304}
305
306/// Handle to the launched RPC servers.
307#[derive(Clone)]
308pub struct RpcHandle<Node: FullNodeComponents, EthApi: EthApiTypes> {
309    /// Handles to launched servers.
310    pub rpc_server_handles: RethRpcServerHandles,
311    /// Configured RPC modules.
312    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/// Node add-ons containing RPC server configuration, with customizable eth API handler.
336#[allow(clippy::type_complexity)]
337pub struct RpcAddOns<Node: FullNodeComponents, EthApi: EthApiTypes, EV> {
338    /// Additional RPC add-ons.
339    pub hooks: RpcHooks<Node, EthApi>,
340    /// Builder for `EthApi`
341    eth_api_builder: Box<dyn FnOnce(&EthApiBuilderCtx<Node>) -> EthApi + Send + Sync>,
342    /// Engine validator
343    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    /// Creates a new instance of the RPC add-ons.
361    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    /// Sets the hook that is run once the rpc server is started.
374    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    /// Sets the hook that is run to configure the rpc modules.
385    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    /// Launches the RPC servers with the given context and an additional hook for extending
418    /// modules.
419    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        // in dev mode we generate 20 random dev-signer accounts
473        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        // launch servers concurrently
517        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
555/// Helper trait implemented for add-ons producing [`RpcHandle`]. Used by common node launcher
556/// implementations.
557pub trait RethRpcAddOns<N: FullNodeComponents>:
558    NodeAddOns<N, Handle = RpcHandle<N, Self::EthApi>>
559{
560    /// eth API implementation.
561    type EthApi: EthApiTypes;
562
563    /// Returns a mutable reference to RPC hooks.
564    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
578/// A `EthApi` that knows how to build itself from [`EthApiBuilderCtx`].
579pub trait EthApiBuilder<N: FullNodeComponents>: 'static {
580    /// Builds the `EthApi` from the given context.
581    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
592/// Helper trait that provides the validator for the engine API
593pub trait EngineValidatorAddOn<Node: FullNodeComponents>: Send {
594    /// The Validator type to use for the engine API.
595    type Validator: EngineValidator<<Node::Types as NodeTypesWithEngine>::Engine, Block = BlockTy<Node::Types>>
596        + Clone;
597
598    /// Creates the engine validator for an engine API based node.
599    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
618/// A type that knows how to build the engine validator.
619pub trait EngineValidatorBuilder<Node: FullNodeComponents>: Send + Sync + Clone {
620    /// The consensus implementation to build.
621    type Validator: EngineValidator<<Node::Types as NodeTypesWithEngine>::Engine, Block = BlockTy<Node::Types>>
622        + Clone;
623
624    /// Creates the engine validator.
625    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}