reth_node_builder/
rpc.rs

1//! Builder support for rpc components.
2
3use crate::{BeaconConsensusEngineEvent, BeaconConsensusEngineHandle};
4use alloy_rpc_types::engine::ClientVersionV1;
5use alloy_rpc_types_engine::ExecutionData;
6use futures::TryFutureExt;
7use jsonrpsee::RpcModule;
8use reth_chain_state::CanonStateSubscriptions;
9use reth_chainspec::{ChainSpecProvider, EthereumHardforks};
10use reth_node_api::{
11    AddOnsContext, BlockTy, EngineTypes, EngineValidator, FullNodeComponents, FullNodeTypes,
12    NodeAddOns, NodeTypes, PayloadTypes, ReceiptTy,
13};
14use reth_node_core::{
15    node_config::NodeConfig,
16    version::{CARGO_PKG_VERSION, CLIENT_CODE, NAME_CLIENT, VERGEN_GIT_SHA},
17};
18use reth_payload_builder::{PayloadBuilderHandle, PayloadStore};
19use reth_rpc::eth::{EthApiTypes, FullEthApiServer};
20use reth_rpc_api::{eth::helpers::AddDevSigners, IntoEngineApiRpcModule};
21use reth_rpc_builder::{
22    auth::{AuthRpcModule, AuthServerHandle},
23    config::RethRpcServerConfig,
24    RpcModuleBuilder, RpcRegistryInner, RpcServerHandle, TransportRpcModules,
25};
26use reth_rpc_engine_api::{capabilities::EngineCapabilities, EngineApi};
27use reth_rpc_eth_types::{cache::cache_new_blocks_task, EthConfig, EthStateCache};
28use reth_tasks::TaskExecutor;
29use reth_tokio_util::EventSender;
30use reth_tracing::tracing::{debug, info};
31use std::{
32    fmt::{self, Debug},
33    future::Future,
34    ops::{Deref, DerefMut},
35};
36
37/// Contains the handles to the spawned RPC servers.
38///
39/// This can be used to access the endpoints of the servers.
40#[derive(Debug, Clone)]
41pub struct RethRpcServerHandles {
42    /// The regular RPC server handle to all configured transports.
43    pub rpc: RpcServerHandle,
44    /// The handle to the auth server (engine API)
45    pub auth: AuthServerHandle,
46}
47
48/// Contains hooks that are called during the rpc setup.
49pub struct RpcHooks<Node: FullNodeComponents, EthApi> {
50    /// Hooks to run once RPC server is running.
51    pub on_rpc_started: Box<dyn OnRpcStarted<Node, EthApi>>,
52    /// Hooks to run to configure RPC server API.
53    pub extend_rpc_modules: Box<dyn ExtendRpcModules<Node, EthApi>>,
54}
55
56impl<Node, EthApi> Default for RpcHooks<Node, EthApi>
57where
58    Node: FullNodeComponents,
59    EthApi: EthApiTypes,
60{
61    fn default() -> Self {
62        Self { on_rpc_started: Box::<()>::default(), extend_rpc_modules: Box::<()>::default() }
63    }
64}
65
66impl<Node, EthApi> RpcHooks<Node, EthApi>
67where
68    Node: FullNodeComponents,
69    EthApi: EthApiTypes,
70{
71    /// Sets the hook that is run once the rpc server is started.
72    pub(crate) fn set_on_rpc_started<F>(&mut self, hook: F) -> &mut Self
73    where
74        F: OnRpcStarted<Node, EthApi> + 'static,
75    {
76        self.on_rpc_started = Box::new(hook);
77        self
78    }
79
80    /// Sets the hook that is run once the rpc server is started.
81    #[expect(unused)]
82    pub(crate) fn on_rpc_started<F>(mut self, hook: F) -> Self
83    where
84        F: OnRpcStarted<Node, EthApi> + 'static,
85    {
86        self.set_on_rpc_started(hook);
87        self
88    }
89
90    /// Sets the hook that is run to configure the rpc modules.
91    pub(crate) fn set_extend_rpc_modules<F>(&mut self, hook: F) -> &mut Self
92    where
93        F: ExtendRpcModules<Node, EthApi> + 'static,
94    {
95        self.extend_rpc_modules = Box::new(hook);
96        self
97    }
98
99    /// Sets the hook that is run to configure the rpc modules.
100    #[expect(unused)]
101    pub(crate) fn extend_rpc_modules<F>(mut self, hook: F) -> Self
102    where
103        F: ExtendRpcModules<Node, EthApi> + 'static,
104    {
105        self.set_extend_rpc_modules(hook);
106        self
107    }
108}
109
110impl<Node, EthApi> fmt::Debug for RpcHooks<Node, EthApi>
111where
112    Node: FullNodeComponents,
113    EthApi: EthApiTypes,
114{
115    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116        f.debug_struct("RpcHooks")
117            .field("on_rpc_started", &"...")
118            .field("extend_rpc_modules", &"...")
119            .finish()
120    }
121}
122
123/// Event hook that is called once the rpc server is started.
124pub trait OnRpcStarted<Node: FullNodeComponents, EthApi: EthApiTypes>: Send {
125    /// The hook that is called once the rpc server is started.
126    fn on_rpc_started(
127        self: Box<Self>,
128        ctx: RpcContext<'_, Node, EthApi>,
129        handles: RethRpcServerHandles,
130    ) -> eyre::Result<()>;
131}
132
133impl<Node, EthApi, F> OnRpcStarted<Node, EthApi> for F
134where
135    F: FnOnce(RpcContext<'_, Node, EthApi>, RethRpcServerHandles) -> eyre::Result<()> + Send,
136    Node: FullNodeComponents,
137    EthApi: EthApiTypes,
138{
139    fn on_rpc_started(
140        self: Box<Self>,
141        ctx: RpcContext<'_, Node, EthApi>,
142        handles: RethRpcServerHandles,
143    ) -> eyre::Result<()> {
144        (*self)(ctx, handles)
145    }
146}
147
148impl<Node, EthApi> OnRpcStarted<Node, EthApi> for ()
149where
150    Node: FullNodeComponents,
151    EthApi: EthApiTypes,
152{
153    fn on_rpc_started(
154        self: Box<Self>,
155        _: RpcContext<'_, Node, EthApi>,
156        _: RethRpcServerHandles,
157    ) -> eyre::Result<()> {
158        Ok(())
159    }
160}
161
162/// Event hook that is called when the rpc server is started.
163pub trait ExtendRpcModules<Node: FullNodeComponents, EthApi: EthApiTypes>: Send {
164    /// The hook that is called once the rpc server is started.
165    fn extend_rpc_modules(self: Box<Self>, ctx: RpcContext<'_, Node, EthApi>) -> eyre::Result<()>;
166}
167
168impl<Node, EthApi, F> ExtendRpcModules<Node, EthApi> for F
169where
170    F: FnOnce(RpcContext<'_, Node, EthApi>) -> eyre::Result<()> + Send,
171    Node: FullNodeComponents,
172    EthApi: EthApiTypes,
173{
174    fn extend_rpc_modules(self: Box<Self>, ctx: RpcContext<'_, Node, EthApi>) -> eyre::Result<()> {
175        (*self)(ctx)
176    }
177}
178
179impl<Node, EthApi> ExtendRpcModules<Node, EthApi> for ()
180where
181    Node: FullNodeComponents,
182    EthApi: EthApiTypes,
183{
184    fn extend_rpc_modules(self: Box<Self>, _: RpcContext<'_, Node, EthApi>) -> eyre::Result<()> {
185        Ok(())
186    }
187}
188
189/// Helper wrapper type to encapsulate the [`RpcRegistryInner`] over components trait.
190#[derive(Debug, Clone)]
191#[expect(clippy::type_complexity)]
192pub struct RpcRegistry<Node: FullNodeComponents, EthApi: EthApiTypes> {
193    pub(crate) registry: RpcRegistryInner<
194        Node::Provider,
195        Node::Pool,
196        Node::Network,
197        TaskExecutor,
198        EthApi,
199        Node::Evm,
200        Node::Consensus,
201    >,
202}
203
204impl<Node, EthApi> Deref for RpcRegistry<Node, EthApi>
205where
206    Node: FullNodeComponents,
207    EthApi: EthApiTypes,
208{
209    type Target = RpcRegistryInner<
210        Node::Provider,
211        Node::Pool,
212        Node::Network,
213        TaskExecutor,
214        EthApi,
215        Node::Evm,
216        Node::Consensus,
217    >;
218
219    fn deref(&self) -> &Self::Target {
220        &self.registry
221    }
222}
223
224impl<Node, EthApi> DerefMut for RpcRegistry<Node, EthApi>
225where
226    Node: FullNodeComponents,
227    EthApi: EthApiTypes,
228{
229    fn deref_mut(&mut self) -> &mut Self::Target {
230        &mut self.registry
231    }
232}
233
234/// Helper container to encapsulate [`RpcRegistryInner`], [`TransportRpcModules`] and
235/// [`AuthRpcModule`].
236///
237/// This can be used to access installed modules, or create commonly used handlers like
238/// [`reth_rpc::eth::EthApi`], and ultimately merge additional rpc handler into the configured
239/// transport modules [`TransportRpcModules`] as well as configured authenticated methods
240/// [`AuthRpcModule`].
241#[expect(missing_debug_implementations)]
242pub struct RpcContext<'a, Node: FullNodeComponents, EthApi: EthApiTypes> {
243    /// The node components.
244    pub(crate) node: Node,
245
246    /// Gives access to the node configuration.
247    pub(crate) config: &'a NodeConfig<<Node::Types as NodeTypes>::ChainSpec>,
248
249    /// A Helper type the holds instances of the configured modules.
250    ///
251    /// This provides easy access to rpc handlers, such as [`RpcRegistryInner::eth_api`].
252    pub registry: &'a mut RpcRegistry<Node, EthApi>,
253    /// Holds installed modules per transport type.
254    ///
255    /// This can be used to merge additional modules into the configured transports (http, ipc,
256    /// ws). See [`TransportRpcModules::merge_configured`]
257    pub modules: &'a mut TransportRpcModules,
258    /// Holds jwt authenticated rpc module.
259    ///
260    /// This can be used to merge additional modules into the configured authenticated methods
261    pub auth_module: &'a mut AuthRpcModule,
262}
263
264impl<Node, EthApi> RpcContext<'_, Node, EthApi>
265where
266    Node: FullNodeComponents,
267    EthApi: EthApiTypes,
268{
269    /// Returns the config of the node.
270    pub const fn config(&self) -> &NodeConfig<<Node::Types as NodeTypes>::ChainSpec> {
271        self.config
272    }
273
274    /// Returns a reference to the configured node.
275    pub const fn node(&self) -> &Node {
276        &self.node
277    }
278
279    /// Returns the transaction pool instance.
280    pub fn pool(&self) -> &Node::Pool {
281        self.node.pool()
282    }
283
284    /// Returns provider to interact with the node.
285    pub fn provider(&self) -> &Node::Provider {
286        self.node.provider()
287    }
288
289    /// Returns the handle to the network
290    pub fn network(&self) -> &Node::Network {
291        self.node.network()
292    }
293
294    /// Returns the handle to the payload builder service
295    pub fn payload_builder_handle(
296        &self,
297    ) -> &PayloadBuilderHandle<<Node::Types as NodeTypes>::Payload> {
298        self.node.payload_builder_handle()
299    }
300}
301
302/// Handle to the launched RPC servers.
303pub struct RpcHandle<Node: FullNodeComponents, EthApi: EthApiTypes> {
304    /// Handles to launched servers.
305    pub rpc_server_handles: RethRpcServerHandles,
306    /// Configured RPC modules.
307    pub rpc_registry: RpcRegistry<Node, EthApi>,
308    /// Notification channel for engine API events
309    ///
310    /// Caution: This is a multi-producer, multi-consumer broadcast and allows grants access to
311    /// dispatch events
312    pub engine_events:
313        EventSender<BeaconConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>>,
314    /// Handle to the beacon consensus engine.
315    pub beacon_engine_handle: BeaconConsensusEngineHandle<<Node::Types as NodeTypes>::Payload>,
316}
317
318impl<Node: FullNodeComponents, EthApi: EthApiTypes> Clone for RpcHandle<Node, EthApi> {
319    fn clone(&self) -> Self {
320        Self {
321            rpc_server_handles: self.rpc_server_handles.clone(),
322            rpc_registry: self.rpc_registry.clone(),
323            engine_events: self.engine_events.clone(),
324            beacon_engine_handle: self.beacon_engine_handle.clone(),
325        }
326    }
327}
328
329impl<Node: FullNodeComponents, EthApi: EthApiTypes> Deref for RpcHandle<Node, EthApi> {
330    type Target = RpcRegistry<Node, EthApi>;
331
332    fn deref(&self) -> &Self::Target {
333        &self.rpc_registry
334    }
335}
336
337impl<Node: FullNodeComponents, EthApi: EthApiTypes> Debug for RpcHandle<Node, EthApi>
338where
339    RpcRegistry<Node, EthApi>: Debug,
340{
341    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
342        f.debug_struct("RpcHandle")
343            .field("rpc_server_handles", &self.rpc_server_handles)
344            .field("rpc_registry", &self.rpc_registry)
345            .finish()
346    }
347}
348
349/// Node add-ons containing RPC server configuration, with customizable eth API handler.
350///
351/// This struct can be used to provide the RPC server functionality. It is responsible for launching
352/// the regular RPC and the authenticated RPC server (engine API). It is intended to be used and
353/// modified as part of the [`NodeAddOns`] see for example `OpRpcAddons`, `EthereumAddOns`.
354///
355/// It can be modified to register RPC API handlers, see [`RpcAddOns::launch_add_ons_with`] which
356/// takes a closure that provides access to all the configured modules (namespaces), and is invoked
357/// just before the servers are launched. This can be used to extend the node with custom RPC
358/// methods or even replace existing method handlers, see also [`TransportRpcModules`].
359pub struct RpcAddOns<
360    Node: FullNodeComponents,
361    EthB: EthApiBuilder<Node>,
362    EV,
363    EB = BasicEngineApiBuilder<EV>,
364> {
365    /// Additional RPC add-ons.
366    pub hooks: RpcHooks<Node, EthB::EthApi>,
367    /// Builder for `EthApi`
368    eth_api_builder: EthB,
369    /// Engine validator
370    engine_validator_builder: EV,
371    /// Builder for `EngineApi`
372    engine_api_builder: EB,
373}
374
375impl<Node, EthB, EV, EB> Debug for RpcAddOns<Node, EthB, EV, EB>
376where
377    Node: FullNodeComponents,
378    EthB: EthApiBuilder<Node>,
379    EV: Debug,
380    EB: Debug,
381{
382    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
383        f.debug_struct("RpcAddOns")
384            .field("hooks", &self.hooks)
385            .field("eth_api_builder", &"...")
386            .field("engine_validator_builder", &self.engine_validator_builder)
387            .field("engine_api_builder", &self.engine_api_builder)
388            .finish()
389    }
390}
391
392impl<Node, EthB, EV, EB> RpcAddOns<Node, EthB, EV, EB>
393where
394    Node: FullNodeComponents,
395    EthB: EthApiBuilder<Node>,
396{
397    /// Creates a new instance of the RPC add-ons.
398    pub fn new(
399        eth_api_builder: EthB,
400        engine_validator_builder: EV,
401        engine_api_builder: EB,
402    ) -> Self {
403        Self {
404            hooks: RpcHooks::default(),
405            eth_api_builder,
406            engine_validator_builder,
407            engine_api_builder,
408        }
409    }
410
411    /// Sets the hook that is run once the rpc server is started.
412    pub fn on_rpc_started<F>(mut self, hook: F) -> Self
413    where
414        F: FnOnce(RpcContext<'_, Node, EthB::EthApi>, RethRpcServerHandles) -> eyre::Result<()>
415            + Send
416            + 'static,
417    {
418        self.hooks.set_on_rpc_started(hook);
419        self
420    }
421
422    /// Sets the hook that is run to configure the rpc modules.
423    pub fn extend_rpc_modules<F>(mut self, hook: F) -> Self
424    where
425        F: FnOnce(RpcContext<'_, Node, EthB::EthApi>) -> eyre::Result<()> + Send + 'static,
426    {
427        self.hooks.set_extend_rpc_modules(hook);
428        self
429    }
430}
431
432impl<Node, EthB, EV, EB> Default for RpcAddOns<Node, EthB, EV, EB>
433where
434    Node: FullNodeComponents,
435    EthB: EthApiBuilder<Node>,
436    EV: Default,
437    EB: Default,
438{
439    fn default() -> Self {
440        Self::new(EthB::default(), EV::default(), EB::default())
441    }
442}
443
444impl<N, EthB, EV, EB> RpcAddOns<N, EthB, EV, EB>
445where
446    N: FullNodeComponents,
447    N::Provider: ChainSpecProvider<ChainSpec: EthereumHardforks>,
448    EthB: EthApiBuilder<N>,
449    EV: EngineValidatorBuilder<N>,
450    EB: EngineApiBuilder<N>,
451{
452    /// Launches the RPC servers with the given context and an additional hook for extending
453    /// modules.
454    pub async fn launch_add_ons_with<F>(
455        self,
456        ctx: AddOnsContext<'_, N>,
457        ext: F,
458    ) -> eyre::Result<RpcHandle<N, EthB::EthApi>>
459    where
460        F: FnOnce(
461            &mut TransportRpcModules,
462            &mut AuthRpcModule,
463            &mut RpcRegistry<N, EthB::EthApi>,
464        ) -> eyre::Result<()>,
465    {
466        let Self { eth_api_builder, engine_api_builder, hooks, .. } = self;
467
468        let engine_api = engine_api_builder.build_engine_api(&ctx).await?;
469        let AddOnsContext { node, config, beacon_engine_handle, jwt_secret, engine_events } = ctx;
470
471        info!(target: "reth::cli", "Engine API handler initialized");
472
473        let cache = EthStateCache::spawn_with(
474            node.provider().clone(),
475            config.rpc.eth_config().cache,
476            node.task_executor().clone(),
477        );
478
479        let new_canonical_blocks = node.provider().canonical_state_stream();
480        let c = cache.clone();
481        node.task_executor().spawn_critical(
482            "cache canonical blocks task",
483            Box::pin(async move {
484                cache_new_blocks_task(c, new_canonical_blocks).await;
485            }),
486        );
487
488        let ctx = EthApiCtx { components: &node, config: config.rpc.eth_config(), cache };
489        let eth_api = eth_api_builder.build_eth_api(ctx).await?;
490
491        let auth_config = config.rpc.auth_server_config(jwt_secret)?;
492        let module_config = config.rpc.transport_rpc_module_config();
493        debug!(target: "reth::cli", http=?module_config.http(), ws=?module_config.ws(), "Using RPC module config");
494
495        let (mut modules, mut auth_module, registry) = RpcModuleBuilder::default()
496            .with_provider(node.provider().clone())
497            .with_pool(node.pool().clone())
498            .with_network(node.network().clone())
499            .with_executor(node.task_executor().clone())
500            .with_evm_config(node.evm_config().clone())
501            .with_consensus(node.consensus().clone())
502            .build_with_auth_server(module_config, engine_api, eth_api);
503
504        // in dev mode we generate 20 random dev-signer accounts
505        if config.dev.dev {
506            registry.eth_api().with_dev_accounts();
507        }
508
509        let mut registry = RpcRegistry { registry };
510        let ctx = RpcContext {
511            node: node.clone(),
512            config,
513            registry: &mut registry,
514            modules: &mut modules,
515            auth_module: &mut auth_module,
516        };
517
518        let RpcHooks { on_rpc_started, extend_rpc_modules } = hooks;
519
520        ext(ctx.modules, ctx.auth_module, ctx.registry)?;
521        extend_rpc_modules.extend_rpc_modules(ctx)?;
522
523        let server_config = config.rpc.rpc_server_config();
524        let cloned_modules = modules.clone();
525        let launch_rpc = server_config.start(&cloned_modules).map_ok(|handle| {
526            if let Some(path) = handle.ipc_endpoint() {
527                info!(target: "reth::cli", %path, "RPC IPC server started");
528            }
529            if let Some(addr) = handle.http_local_addr() {
530                info!(target: "reth::cli", url=%addr, "RPC HTTP server started");
531            }
532            if let Some(addr) = handle.ws_local_addr() {
533                info!(target: "reth::cli", url=%addr, "RPC WS server started");
534            }
535            handle
536        });
537
538        let launch_auth = auth_module.clone().start_server(auth_config).map_ok(|handle| {
539            let addr = handle.local_addr();
540            if let Some(ipc_endpoint) = handle.ipc_endpoint() {
541                info!(target: "reth::cli", url=%addr, ipc_endpoint=%ipc_endpoint, "RPC auth server started");
542            } else {
543                info!(target: "reth::cli", url=%addr, "RPC auth server started");
544            }
545            handle
546        });
547
548        // launch servers concurrently
549        let (rpc, auth) = futures::future::try_join(launch_rpc, launch_auth).await?;
550
551        let handles = RethRpcServerHandles { rpc, auth };
552
553        let ctx = RpcContext {
554            node: node.clone(),
555            config,
556            registry: &mut registry,
557            modules: &mut modules,
558            auth_module: &mut auth_module,
559        };
560
561        on_rpc_started.on_rpc_started(ctx, handles.clone())?;
562
563        Ok(RpcHandle {
564            rpc_server_handles: handles,
565            rpc_registry: registry,
566            engine_events,
567            beacon_engine_handle,
568        })
569    }
570}
571
572impl<N, EthB, EV, EB> NodeAddOns<N> for RpcAddOns<N, EthB, EV, EB>
573where
574    N: FullNodeComponents,
575    <N as FullNodeTypes>::Provider: ChainSpecProvider<ChainSpec: EthereumHardforks>,
576    EthB: EthApiBuilder<N>,
577    EV: EngineValidatorBuilder<N>,
578    EB: EngineApiBuilder<N>,
579{
580    type Handle = RpcHandle<N, EthB::EthApi>;
581
582    async fn launch_add_ons(self, ctx: AddOnsContext<'_, N>) -> eyre::Result<Self::Handle> {
583        self.launch_add_ons_with(ctx, |_, _, _| Ok(())).await
584    }
585}
586
587/// Helper trait implemented for add-ons producing [`RpcHandle`]. Used by common node launcher
588/// implementations.
589pub trait RethRpcAddOns<N: FullNodeComponents>:
590    NodeAddOns<N, Handle = RpcHandle<N, Self::EthApi>>
591{
592    /// eth API implementation.
593    type EthApi: EthApiTypes;
594
595    /// Returns a mutable reference to RPC hooks.
596    fn hooks_mut(&mut self) -> &mut RpcHooks<N, Self::EthApi>;
597}
598
599impl<N: FullNodeComponents, EthB, EV, EB> RethRpcAddOns<N> for RpcAddOns<N, EthB, EV, EB>
600where
601    Self: NodeAddOns<N, Handle = RpcHandle<N, EthB::EthApi>>,
602    EthB: EthApiBuilder<N>,
603{
604    type EthApi = EthB::EthApi;
605
606    fn hooks_mut(&mut self) -> &mut RpcHooks<N, Self::EthApi> {
607        &mut self.hooks
608    }
609}
610
611/// `EthApiCtx` struct
612/// This struct is used to pass the necessary context to the `EthApiBuilder` to build the `EthApi`.
613#[derive(Debug)]
614pub struct EthApiCtx<'a, N: FullNodeTypes> {
615    /// Reference to the node components
616    pub components: &'a N,
617    /// Eth API configuration
618    pub config: EthConfig,
619    /// Cache for eth state
620    pub cache: EthStateCache<BlockTy<N::Types>, ReceiptTy<N::Types>>,
621}
622
623/// A `EthApi` that knows how to build `eth` namespace API from [`FullNodeComponents`].
624pub trait EthApiBuilder<N: FullNodeComponents>: Default + Send + 'static {
625    /// The Ethapi implementation this builder will build.
626    type EthApi: EthApiTypes
627        + FullEthApiServer<Provider = N::Provider, Pool = N::Pool>
628        + AddDevSigners
629        + Unpin
630        + 'static;
631
632    /// Builds the [`EthApiServer`](reth_rpc_api::eth::EthApiServer) from the given context.
633    fn build_eth_api(
634        self,
635        ctx: EthApiCtx<'_, N>,
636    ) -> impl Future<Output = eyre::Result<Self::EthApi>> + Send;
637}
638
639/// Helper trait that provides the validator for the engine API
640pub trait EngineValidatorAddOn<Node: FullNodeComponents>: Send {
641    /// The Validator type to use for the engine API.
642    type Validator: EngineValidator<<Node::Types as NodeTypes>::Payload, Block = BlockTy<Node::Types>>
643        + Clone;
644
645    /// Creates the engine validator for an engine API based node.
646    fn engine_validator(
647        &self,
648        ctx: &AddOnsContext<'_, Node>,
649    ) -> impl Future<Output = eyre::Result<Self::Validator>>;
650}
651
652impl<N, EthB, EV, EB> EngineValidatorAddOn<N> for RpcAddOns<N, EthB, EV, EB>
653where
654    N: FullNodeComponents,
655    EthB: EthApiBuilder<N>,
656    EV: EngineValidatorBuilder<N>,
657    EB: EngineApiBuilder<N>,
658{
659    type Validator = EV::Validator;
660
661    async fn engine_validator(&self, ctx: &AddOnsContext<'_, N>) -> eyre::Result<Self::Validator> {
662        self.engine_validator_builder.clone().build(ctx).await
663    }
664}
665
666/// A type that knows how to build the engine validator.
667pub trait EngineValidatorBuilder<Node: FullNodeComponents>: Send + Sync + Clone {
668    /// The consensus implementation to build.
669    type Validator: EngineValidator<<Node::Types as NodeTypes>::Payload, Block = BlockTy<Node::Types>>
670        + Clone;
671
672    /// Creates the engine validator.
673    fn build(
674        self,
675        ctx: &AddOnsContext<'_, Node>,
676    ) -> impl Future<Output = eyre::Result<Self::Validator>> + Send;
677}
678
679impl<Node, F, Fut, Validator> EngineValidatorBuilder<Node> for F
680where
681    Node: FullNodeComponents,
682    Validator: EngineValidator<<Node::Types as NodeTypes>::Payload, Block = BlockTy<Node::Types>>
683        + Clone
684        + Unpin
685        + 'static,
686    F: FnOnce(&AddOnsContext<'_, Node>) -> Fut + Send + Sync + Clone,
687    Fut: Future<Output = eyre::Result<Validator>> + Send,
688{
689    type Validator = Validator;
690
691    fn build(
692        self,
693        ctx: &AddOnsContext<'_, Node>,
694    ) -> impl Future<Output = eyre::Result<Self::Validator>> {
695        self(ctx)
696    }
697}
698
699/// Builder for engine API RPC module.
700///
701/// This builder type is responsible for providing an instance of [`IntoEngineApiRpcModule`], which
702/// is effectively a helper trait that provides the type erased [`jsonrpsee::RpcModule`] instance
703/// that contains the method handlers for the engine API. See [`EngineApi`] for an implementation of
704/// [`IntoEngineApiRpcModule`].
705pub trait EngineApiBuilder<Node: FullNodeComponents>: Send + Sync {
706    /// The engine API RPC module. Only required to be convertible to an [`jsonrpsee::RpcModule`].
707    type EngineApi: IntoEngineApiRpcModule + Send + Sync;
708
709    /// Builds the engine API instance given the provided [`AddOnsContext`].
710    ///
711    /// [`Self::EngineApi`] will be converted into the method handlers of the authenticated RPC
712    /// server (engine API).
713    fn build_engine_api(
714        self,
715        ctx: &AddOnsContext<'_, Node>,
716    ) -> impl Future<Output = eyre::Result<Self::EngineApi>> + Send;
717}
718
719/// Builder for basic [`EngineApi`] implementation.
720///
721/// This provides a basic default implementation for opstack and ethereum engine API via
722/// [`EngineTypes`] and uses the general purpose [`EngineApi`] implementation as the builder's
723/// output.
724#[derive(Debug, Default)]
725pub struct BasicEngineApiBuilder<EV> {
726    engine_validator_builder: EV,
727}
728
729impl<N, EV> EngineApiBuilder<N> for BasicEngineApiBuilder<EV>
730where
731    N: FullNodeComponents<
732        Types: NodeTypes<
733            ChainSpec: EthereumHardforks,
734            Payload: PayloadTypes<ExecutionData = ExecutionData> + EngineTypes,
735        >,
736    >,
737    EV: EngineValidatorBuilder<N>,
738{
739    type EngineApi = EngineApi<
740        N::Provider,
741        <N::Types as NodeTypes>::Payload,
742        N::Pool,
743        EV::Validator,
744        <N::Types as NodeTypes>::ChainSpec,
745    >;
746
747    async fn build_engine_api(self, ctx: &AddOnsContext<'_, N>) -> eyre::Result<Self::EngineApi> {
748        let Self { engine_validator_builder } = self;
749
750        let engine_validator = engine_validator_builder.build(ctx).await?;
751        let client = ClientVersionV1 {
752            code: CLIENT_CODE,
753            name: NAME_CLIENT.to_string(),
754            version: CARGO_PKG_VERSION.to_string(),
755            commit: VERGEN_GIT_SHA.to_string(),
756        };
757        Ok(EngineApi::new(
758            ctx.node.provider().clone(),
759            ctx.config.chain.clone(),
760            ctx.beacon_engine_handle.clone(),
761            PayloadStore::new(ctx.node.payload_builder_handle().clone()),
762            ctx.node.pool().clone(),
763            Box::new(ctx.node.task_executor().clone()),
764            client,
765            EngineCapabilities::default(),
766            engine_validator,
767            ctx.config.engine.accept_execution_requests_hash,
768        ))
769    }
770}
771
772/// A noop Builder that satisfies the [`EngineApiBuilder`] trait without actually configuring an
773/// engine API module
774///
775/// This is intended to be used as a workaround for re-using all the existing ethereum node launch
776/// utilities which require an engine API.
777#[derive(Debug, Clone, Default)]
778#[non_exhaustive]
779pub struct NoopEngineApiBuilder;
780
781impl<N: FullNodeComponents> EngineApiBuilder<N> for NoopEngineApiBuilder {
782    type EngineApi = NoopEngineApi;
783
784    async fn build_engine_api(self, _ctx: &AddOnsContext<'_, N>) -> eyre::Result<Self::EngineApi> {
785        Ok(NoopEngineApi::default())
786    }
787}
788
789/// Represents an empty Engine API [`RpcModule`].
790///
791/// This is only intended to be used in combination with the [`NoopEngineApiBuilder`] in order to
792/// satisfy trait bounds in the regular ethereum launch routine that mandate an engine API instance.
793#[derive(Debug, Clone, Default)]
794#[non_exhaustive]
795pub struct NoopEngineApi;
796
797impl IntoEngineApiRpcModule for NoopEngineApi {
798    fn into_rpc_module(self) -> RpcModule<()> {
799        RpcModule::new(())
800    }
801}