reth_node_builder/
rpc.rs

1//! Builder support for rpc components.
2
3pub use jsonrpsee::server::middleware::rpc::{RpcService, RpcServiceBuilder};
4pub use reth_engine_tree::tree::{BasicEngineValidator, EngineValidator};
5pub use reth_rpc_builder::{middleware::RethRpcMiddleware, Identity, Stack};
6
7use crate::{
8    invalid_block_hook::InvalidBlockHookExt, ConfigureEngineEvm, ConsensusEngineEvent,
9    ConsensusEngineHandle,
10};
11use alloy_rpc_types::engine::ClientVersionV1;
12use alloy_rpc_types_engine::ExecutionData;
13use jsonrpsee::{core::middleware::layer::Either, RpcModule};
14use parking_lot::Mutex;
15use reth_chain_state::CanonStateSubscriptions;
16use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks, Hardforks};
17use reth_node_api::{
18    AddOnsContext, BlockTy, EngineApiValidator, EngineTypes, FullNodeComponents, FullNodeTypes,
19    NodeAddOns, NodeTypes, PayloadTypes, PayloadValidator, PrimitivesTy, TreeConfig,
20};
21use reth_node_core::{
22    cli::config::RethTransactionPoolConfig,
23    node_config::NodeConfig,
24    version::{version_metadata, CLIENT_CODE},
25};
26use reth_payload_builder::{PayloadBuilderHandle, PayloadStore};
27use reth_rpc::{
28    eth::{core::EthRpcConverterFor, DevSigner, EthApiTypes, FullEthApiServer},
29    AdminApi,
30};
31use reth_rpc_api::{eth::helpers::EthTransactions, IntoEngineApiRpcModule};
32use reth_rpc_builder::{
33    auth::{AuthRpcModule, AuthServerHandle},
34    config::RethRpcServerConfig,
35    RpcModuleBuilder, RpcRegistryInner, RpcServerConfig, RpcServerHandle, TransportRpcModules,
36};
37use reth_rpc_engine_api::{capabilities::EngineCapabilities, EngineApi};
38use reth_rpc_eth_types::{cache::cache_new_blocks_task, EthConfig, EthStateCache};
39use reth_tokio_util::EventSender;
40use reth_tracing::tracing::{debug, info};
41use std::{
42    fmt::{self, Debug},
43    future::Future,
44    ops::{Deref, DerefMut},
45    sync::Arc,
46};
47use tokio::sync::oneshot;
48
49/// Contains the handles to the spawned RPC servers.
50///
51/// This can be used to access the endpoints of the servers.
52#[derive(Debug, Clone)]
53pub struct RethRpcServerHandles {
54    /// The regular RPC server handle to all configured transports.
55    pub rpc: RpcServerHandle,
56    /// The handle to the auth server (engine API)
57    pub auth: AuthServerHandle,
58}
59
60/// Contains hooks that are called during the rpc setup.
61pub struct RpcHooks<Node: FullNodeComponents, EthApi> {
62    /// Hooks to run once RPC server is running.
63    pub on_rpc_started: Box<dyn OnRpcStarted<Node, EthApi>>,
64    /// Hooks to run to configure RPC server API.
65    pub extend_rpc_modules: Box<dyn ExtendRpcModules<Node, EthApi>>,
66}
67
68impl<Node, EthApi> Default for RpcHooks<Node, EthApi>
69where
70    Node: FullNodeComponents,
71    EthApi: EthApiTypes,
72{
73    fn default() -> Self {
74        Self { on_rpc_started: Box::<()>::default(), extend_rpc_modules: Box::<()>::default() }
75    }
76}
77
78impl<Node, EthApi> RpcHooks<Node, EthApi>
79where
80    Node: FullNodeComponents,
81    EthApi: EthApiTypes,
82{
83    /// Sets the hook that is run once the rpc server is started.
84    pub(crate) fn set_on_rpc_started<F>(&mut self, hook: F) -> &mut Self
85    where
86        F: OnRpcStarted<Node, EthApi> + 'static,
87    {
88        self.on_rpc_started = Box::new(hook);
89        self
90    }
91
92    /// Sets the hook that is run once the rpc server is started.
93    #[expect(unused)]
94    pub(crate) fn on_rpc_started<F>(mut self, hook: F) -> Self
95    where
96        F: OnRpcStarted<Node, EthApi> + 'static,
97    {
98        self.set_on_rpc_started(hook);
99        self
100    }
101
102    /// Sets the hook that is run to configure the rpc modules.
103    pub(crate) fn set_extend_rpc_modules<F>(&mut self, hook: F) -> &mut Self
104    where
105        F: ExtendRpcModules<Node, EthApi> + 'static,
106    {
107        self.extend_rpc_modules = Box::new(hook);
108        self
109    }
110
111    /// Sets the hook that is run to configure the rpc modules.
112    #[expect(unused)]
113    pub(crate) fn extend_rpc_modules<F>(mut self, hook: F) -> Self
114    where
115        F: ExtendRpcModules<Node, EthApi> + 'static,
116    {
117        self.set_extend_rpc_modules(hook);
118        self
119    }
120}
121
122impl<Node, EthApi> fmt::Debug for RpcHooks<Node, EthApi>
123where
124    Node: FullNodeComponents,
125    EthApi: EthApiTypes,
126{
127    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128        f.debug_struct("RpcHooks")
129            .field("on_rpc_started", &"...")
130            .field("extend_rpc_modules", &"...")
131            .finish()
132    }
133}
134
135/// Event hook that is called once the rpc server is started.
136pub trait OnRpcStarted<Node: FullNodeComponents, EthApi: EthApiTypes>: Send {
137    /// The hook that is called once the rpc server is started.
138    fn on_rpc_started(
139        self: Box<Self>,
140        ctx: RpcContext<'_, Node, EthApi>,
141        handles: RethRpcServerHandles,
142    ) -> eyre::Result<()>;
143}
144
145impl<Node, EthApi, F> OnRpcStarted<Node, EthApi> for F
146where
147    F: FnOnce(RpcContext<'_, Node, EthApi>, RethRpcServerHandles) -> eyre::Result<()> + Send,
148    Node: FullNodeComponents,
149    EthApi: EthApiTypes,
150{
151    fn on_rpc_started(
152        self: Box<Self>,
153        ctx: RpcContext<'_, Node, EthApi>,
154        handles: RethRpcServerHandles,
155    ) -> eyre::Result<()> {
156        (*self)(ctx, handles)
157    }
158}
159
160impl<Node, EthApi> OnRpcStarted<Node, EthApi> for ()
161where
162    Node: FullNodeComponents,
163    EthApi: EthApiTypes,
164{
165    fn on_rpc_started(
166        self: Box<Self>,
167        _: RpcContext<'_, Node, EthApi>,
168        _: RethRpcServerHandles,
169    ) -> eyre::Result<()> {
170        Ok(())
171    }
172}
173
174/// Event hook that is called when the rpc server is started.
175pub trait ExtendRpcModules<Node: FullNodeComponents, EthApi: EthApiTypes>: Send {
176    /// The hook that is called once the rpc server is started.
177    fn extend_rpc_modules(self: Box<Self>, ctx: RpcContext<'_, Node, EthApi>) -> eyre::Result<()>;
178}
179
180impl<Node, EthApi, F> ExtendRpcModules<Node, EthApi> for F
181where
182    F: FnOnce(RpcContext<'_, Node, EthApi>) -> eyre::Result<()> + Send,
183    Node: FullNodeComponents,
184    EthApi: EthApiTypes,
185{
186    fn extend_rpc_modules(self: Box<Self>, ctx: RpcContext<'_, Node, EthApi>) -> eyre::Result<()> {
187        (*self)(ctx)
188    }
189}
190
191impl<Node, EthApi> ExtendRpcModules<Node, EthApi> for ()
192where
193    Node: FullNodeComponents,
194    EthApi: EthApiTypes,
195{
196    fn extend_rpc_modules(self: Box<Self>, _: RpcContext<'_, Node, EthApi>) -> eyre::Result<()> {
197        Ok(())
198    }
199}
200
201/// Helper wrapper type to encapsulate the [`RpcRegistryInner`] over components trait.
202#[derive(Debug, Clone)]
203#[expect(clippy::type_complexity)]
204pub struct RpcRegistry<Node: FullNodeComponents, EthApi: EthApiTypes> {
205    pub(crate) registry: RpcRegistryInner<
206        Node::Provider,
207        Node::Pool,
208        Node::Network,
209        EthApi,
210        Node::Evm,
211        Node::Consensus,
212    >,
213}
214
215impl<Node, EthApi> Deref for RpcRegistry<Node, EthApi>
216where
217    Node: FullNodeComponents,
218    EthApi: EthApiTypes,
219{
220    type Target = RpcRegistryInner<
221        Node::Provider,
222        Node::Pool,
223        Node::Network,
224        EthApi,
225        Node::Evm,
226        Node::Consensus,
227    >;
228
229    fn deref(&self) -> &Self::Target {
230        &self.registry
231    }
232}
233
234impl<Node, EthApi> DerefMut for RpcRegistry<Node, EthApi>
235where
236    Node: FullNodeComponents,
237    EthApi: EthApiTypes,
238{
239    fn deref_mut(&mut self) -> &mut Self::Target {
240        &mut self.registry
241    }
242}
243
244/// Helper container for the parameters commonly passed to RPC module extension functions.
245#[expect(missing_debug_implementations)]
246pub struct RpcModuleContainer<'a, Node: FullNodeComponents, EthApi: EthApiTypes> {
247    /// Holds installed modules per transport type.
248    pub modules: &'a mut TransportRpcModules,
249    /// Holds jwt authenticated rpc module.
250    pub auth_module: &'a mut AuthRpcModule,
251    /// A Helper type the holds instances of the configured modules.
252    pub registry: &'a mut RpcRegistry<Node, EthApi>,
253}
254
255/// Helper container to encapsulate [`RpcRegistryInner`], [`TransportRpcModules`] and
256/// [`AuthRpcModule`].
257///
258/// This can be used to access installed modules, or create commonly used handlers like
259/// [`reth_rpc::eth::EthApi`], and ultimately merge additional rpc handler into the configured
260/// transport modules [`TransportRpcModules`] as well as configured authenticated methods
261/// [`AuthRpcModule`].
262#[expect(missing_debug_implementations)]
263pub struct RpcContext<'a, Node: FullNodeComponents, EthApi: EthApiTypes> {
264    /// The node components.
265    pub(crate) node: Node,
266
267    /// Gives access to the node configuration.
268    pub(crate) config: &'a NodeConfig<<Node::Types as NodeTypes>::ChainSpec>,
269
270    /// A Helper type the holds instances of the configured modules.
271    ///
272    /// This provides easy access to rpc handlers, such as [`RpcRegistryInner::eth_api`].
273    pub registry: &'a mut RpcRegistry<Node, EthApi>,
274    /// Holds installed modules per transport type.
275    ///
276    /// This can be used to merge additional modules into the configured transports (http, ipc,
277    /// ws). See [`TransportRpcModules::merge_configured`]
278    pub modules: &'a mut TransportRpcModules,
279    /// Holds jwt authenticated rpc module.
280    ///
281    /// This can be used to merge additional modules into the configured authenticated methods
282    pub auth_module: &'a mut AuthRpcModule,
283}
284
285impl<Node, EthApi> RpcContext<'_, Node, EthApi>
286where
287    Node: FullNodeComponents,
288    EthApi: EthApiTypes,
289{
290    /// Returns the config of the node.
291    pub const fn config(&self) -> &NodeConfig<<Node::Types as NodeTypes>::ChainSpec> {
292        self.config
293    }
294
295    /// Returns a reference to the configured node.
296    ///
297    /// This gives access to the node's components.
298    pub const fn node(&self) -> &Node {
299        &self.node
300    }
301
302    /// Returns the transaction pool instance.
303    pub fn pool(&self) -> &Node::Pool {
304        self.node.pool()
305    }
306
307    /// Returns provider to interact with the node.
308    pub fn provider(&self) -> &Node::Provider {
309        self.node.provider()
310    }
311
312    /// Returns the handle to the network
313    pub fn network(&self) -> &Node::Network {
314        self.node.network()
315    }
316
317    /// Returns the handle to the payload builder service
318    pub fn payload_builder_handle(
319        &self,
320    ) -> &PayloadBuilderHandle<<Node::Types as NodeTypes>::Payload> {
321        self.node.payload_builder_handle()
322    }
323}
324
325/// Handle to the launched RPC servers.
326pub struct RpcHandle<Node: FullNodeComponents, EthApi: EthApiTypes> {
327    /// Handles to launched servers.
328    pub rpc_server_handles: RethRpcServerHandles,
329    /// Configured RPC modules.
330    pub rpc_registry: RpcRegistry<Node, EthApi>,
331    /// Notification channel for engine API events
332    ///
333    /// Caution: This is a multi-producer, multi-consumer broadcast and allows grants access to
334    /// dispatch events
335    pub engine_events: EventSender<ConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>>,
336    /// Handle to the beacon consensus engine.
337    pub beacon_engine_handle: ConsensusEngineHandle<<Node::Types as NodeTypes>::Payload>,
338    /// Handle to trigger engine shutdown.
339    pub engine_shutdown: EngineShutdown,
340}
341
342impl<Node: FullNodeComponents, EthApi: EthApiTypes> Clone for RpcHandle<Node, EthApi> {
343    fn clone(&self) -> Self {
344        Self {
345            rpc_server_handles: self.rpc_server_handles.clone(),
346            rpc_registry: self.rpc_registry.clone(),
347            engine_events: self.engine_events.clone(),
348            beacon_engine_handle: self.beacon_engine_handle.clone(),
349            engine_shutdown: self.engine_shutdown.clone(),
350        }
351    }
352}
353
354impl<Node: FullNodeComponents, EthApi: EthApiTypes> Deref for RpcHandle<Node, EthApi> {
355    type Target = RpcRegistry<Node, EthApi>;
356
357    fn deref(&self) -> &Self::Target {
358        &self.rpc_registry
359    }
360}
361
362impl<Node: FullNodeComponents, EthApi: EthApiTypes> Debug for RpcHandle<Node, EthApi>
363where
364    RpcRegistry<Node, EthApi>: Debug,
365{
366    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
367        f.debug_struct("RpcHandle")
368            .field("rpc_server_handles", &self.rpc_server_handles)
369            .field("rpc_registry", &self.rpc_registry)
370            .field("engine_shutdown", &self.engine_shutdown)
371            .finish()
372    }
373}
374
375impl<Node: FullNodeComponents, EthApi: EthApiTypes> RpcHandle<Node, EthApi> {
376    /// Returns the RPC server handles.
377    pub const fn rpc_server_handles(&self) -> &RethRpcServerHandles {
378        &self.rpc_server_handles
379    }
380
381    /// Returns the consensus engine handle.
382    ///
383    /// This handle can be used to interact with the engine service directly.
384    pub const fn consensus_engine_handle(
385        &self,
386    ) -> &ConsensusEngineHandle<<Node::Types as NodeTypes>::Payload> {
387        &self.beacon_engine_handle
388    }
389
390    /// Returns the consensus engine events sender.
391    pub const fn consensus_engine_events(
392        &self,
393    ) -> &EventSender<ConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>> {
394        &self.engine_events
395    }
396
397    /// Returns the `EthApi` instance of the rpc server.
398    pub const fn eth_api(&self) -> &EthApi {
399        self.rpc_registry.registry.eth_api()
400    }
401
402    /// Returns an instance of the [`AdminApi`] for the rpc server.
403    pub fn admin_api(
404        &self,
405    ) -> AdminApi<Node::Network, <Node::Types as NodeTypes>::ChainSpec, Node::Pool>
406    where
407        <Node::Types as NodeTypes>::ChainSpec: EthereumHardforks,
408    {
409        self.rpc_registry.registry.admin_api()
410    }
411}
412
413/// Handle returned when only the regular RPC server (HTTP/WS/IPC) is launched.
414///
415/// This handle provides access to the RPC server endpoints and registry, but does not
416/// include an authenticated Engine API server. Use this when you only need regular
417/// RPC functionality.
418#[derive(Debug, Clone)]
419pub struct RpcServerOnlyHandle<Node: FullNodeComponents, EthApi: EthApiTypes> {
420    /// Handle to the RPC server
421    pub rpc_server_handle: RpcServerHandle,
422    /// Configured RPC modules.
423    pub rpc_registry: RpcRegistry<Node, EthApi>,
424    /// Notification channel for engine API events
425    pub engine_events: EventSender<ConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>>,
426    /// Handle to the consensus engine.
427    pub engine_handle: ConsensusEngineHandle<<Node::Types as NodeTypes>::Payload>,
428}
429
430impl<Node: FullNodeComponents, EthApi: EthApiTypes> RpcServerOnlyHandle<Node, EthApi> {
431    /// Returns the RPC server handle.
432    pub const fn rpc_server_handle(&self) -> &RpcServerHandle {
433        &self.rpc_server_handle
434    }
435
436    /// Returns the consensus engine handle.
437    ///
438    /// This handle can be used to interact with the engine service directly.
439    pub const fn consensus_engine_handle(
440        &self,
441    ) -> &ConsensusEngineHandle<<Node::Types as NodeTypes>::Payload> {
442        &self.engine_handle
443    }
444
445    /// Returns the consensus engine events sender.
446    pub const fn consensus_engine_events(
447        &self,
448    ) -> &EventSender<ConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>> {
449        &self.engine_events
450    }
451}
452
453/// Handle returned when only the authenticated Engine API server is launched.
454///
455/// This handle provides access to the Engine API server and registry, but does not
456/// include the regular RPC servers (HTTP/WS/IPC). Use this for specialized setups
457/// that only need Engine API functionality.
458#[derive(Debug, Clone)]
459pub struct AuthServerOnlyHandle<Node: FullNodeComponents, EthApi: EthApiTypes> {
460    /// Handle to the auth server (engine API)
461    pub auth_server_handle: AuthServerHandle,
462    /// Configured RPC modules.
463    pub rpc_registry: RpcRegistry<Node, EthApi>,
464    /// Notification channel for engine API events
465    pub engine_events: EventSender<ConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>>,
466    /// Handle to the consensus engine.
467    pub engine_handle: ConsensusEngineHandle<<Node::Types as NodeTypes>::Payload>,
468}
469
470impl<Node: FullNodeComponents, EthApi: EthApiTypes> AuthServerOnlyHandle<Node, EthApi> {
471    /// Returns the consensus engine handle.
472    ///
473    /// This handle can be used to interact with the engine service directly.
474    pub const fn consensus_engine_handle(
475        &self,
476    ) -> &ConsensusEngineHandle<<Node::Types as NodeTypes>::Payload> {
477        &self.engine_handle
478    }
479
480    /// Returns the consensus engine events sender.
481    pub const fn consensus_engine_events(
482        &self,
483    ) -> &EventSender<ConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>> {
484        &self.engine_events
485    }
486}
487
488/// Internal context struct for RPC setup shared between different launch methods
489struct RpcSetupContext<'a, Node: FullNodeComponents, EthApi: EthApiTypes> {
490    node: Node,
491    config: &'a NodeConfig<<Node::Types as NodeTypes>::ChainSpec>,
492    modules: TransportRpcModules,
493    auth_module: AuthRpcModule,
494    auth_config: reth_rpc_builder::auth::AuthServerConfig,
495    registry: RpcRegistry<Node, EthApi>,
496    on_rpc_started: Box<dyn OnRpcStarted<Node, EthApi>>,
497    engine_events: EventSender<ConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>>,
498    engine_handle: ConsensusEngineHandle<<Node::Types as NodeTypes>::Payload>,
499}
500
501/// Node add-ons containing RPC server configuration, with customizable eth API handler.
502///
503/// This struct can be used to provide the RPC server functionality. It is responsible for launching
504/// the regular RPC and the authenticated RPC server (engine API). It is intended to be used and
505/// modified as part of the [`NodeAddOns`] see for example `OpRpcAddons`, `EthereumAddOns`.
506///
507/// It can be modified to register RPC API handlers, see [`RpcAddOns::launch_add_ons_with`] which
508/// takes a closure that provides access to all the configured modules (namespaces), and is invoked
509/// just before the servers are launched. This can be used to extend the node with custom RPC
510/// methods or even replace existing method handlers, see also [`TransportRpcModules`].
511pub struct RpcAddOns<
512    Node: FullNodeComponents,
513    EthB: EthApiBuilder<Node>,
514    PVB,
515    EB = BasicEngineApiBuilder<PVB>,
516    EVB = BasicEngineValidatorBuilder<PVB>,
517    RpcMiddleware = Identity,
518> {
519    /// Additional RPC add-ons.
520    pub hooks: RpcHooks<Node, EthB::EthApi>,
521    /// Builder for `EthApi`
522    eth_api_builder: EthB,
523    /// Payload validator builder
524    payload_validator_builder: PVB,
525    /// Builder for `EngineApi`
526    engine_api_builder: EB,
527    /// Builder for tree validator
528    engine_validator_builder: EVB,
529    /// Configurable RPC middleware stack.
530    ///
531    /// This middleware is applied to all RPC requests across all transports (HTTP, WS, IPC).
532    /// See [`RpcAddOns::with_rpc_middleware`] for more details.
533    rpc_middleware: RpcMiddleware,
534    /// Optional custom tokio runtime for the RPC server.
535    tokio_runtime: Option<tokio::runtime::Handle>,
536}
537
538impl<Node, EthB, PVB, EB, EVB, RpcMiddleware> Debug
539    for RpcAddOns<Node, EthB, PVB, EB, EVB, RpcMiddleware>
540where
541    Node: FullNodeComponents,
542    EthB: EthApiBuilder<Node>,
543    PVB: Debug,
544    EB: Debug,
545    EVB: Debug,
546{
547    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
548        f.debug_struct("RpcAddOns")
549            .field("hooks", &self.hooks)
550            .field("eth_api_builder", &"...")
551            .field("payload_validator_builder", &self.payload_validator_builder)
552            .field("engine_api_builder", &self.engine_api_builder)
553            .field("engine_validator_builder", &self.engine_validator_builder)
554            .field("rpc_middleware", &"...")
555            .finish()
556    }
557}
558
559impl<Node, EthB, PVB, EB, EVB, RpcMiddleware> RpcAddOns<Node, EthB, PVB, EB, EVB, RpcMiddleware>
560where
561    Node: FullNodeComponents,
562    EthB: EthApiBuilder<Node>,
563{
564    /// Creates a new instance of the RPC add-ons.
565    pub fn new(
566        eth_api_builder: EthB,
567        payload_validator_builder: PVB,
568        engine_api_builder: EB,
569        engine_validator_builder: EVB,
570        rpc_middleware: RpcMiddleware,
571    ) -> Self {
572        Self {
573            hooks: RpcHooks::default(),
574            eth_api_builder,
575            payload_validator_builder,
576            engine_api_builder,
577            engine_validator_builder,
578            rpc_middleware,
579            tokio_runtime: None,
580        }
581    }
582
583    /// Maps the [`EngineApiBuilder`] builder type.
584    pub fn with_engine_api<T>(
585        self,
586        engine_api_builder: T,
587    ) -> RpcAddOns<Node, EthB, PVB, T, EVB, RpcMiddleware> {
588        let Self {
589            hooks,
590            eth_api_builder,
591            payload_validator_builder,
592            engine_validator_builder,
593            rpc_middleware,
594            tokio_runtime,
595            ..
596        } = self;
597        RpcAddOns {
598            hooks,
599            eth_api_builder,
600            payload_validator_builder,
601            engine_api_builder,
602            engine_validator_builder,
603            rpc_middleware,
604            tokio_runtime,
605        }
606    }
607
608    /// Maps the [`PayloadValidatorBuilder`] builder type.
609    pub fn with_payload_validator<T>(
610        self,
611        payload_validator_builder: T,
612    ) -> RpcAddOns<Node, EthB, T, EB, EVB, RpcMiddleware> {
613        let Self {
614            hooks,
615            eth_api_builder,
616            engine_api_builder,
617            engine_validator_builder,
618            rpc_middleware,
619            tokio_runtime,
620            ..
621        } = self;
622        RpcAddOns {
623            hooks,
624            eth_api_builder,
625            payload_validator_builder,
626            engine_api_builder,
627            engine_validator_builder,
628            rpc_middleware,
629            tokio_runtime,
630        }
631    }
632
633    /// Maps the [`EngineValidatorBuilder`] builder type.
634    pub fn with_engine_validator<T>(
635        self,
636        engine_validator_builder: T,
637    ) -> RpcAddOns<Node, EthB, PVB, EB, T, RpcMiddleware> {
638        let Self {
639            hooks,
640            eth_api_builder,
641            payload_validator_builder,
642            engine_api_builder,
643            rpc_middleware,
644            tokio_runtime,
645            ..
646        } = self;
647        RpcAddOns {
648            hooks,
649            eth_api_builder,
650            payload_validator_builder,
651            engine_api_builder,
652            engine_validator_builder,
653            rpc_middleware,
654            tokio_runtime,
655        }
656    }
657
658    /// Sets the RPC middleware stack for processing RPC requests.
659    ///
660    /// This method configures a custom middleware stack that will be applied to all RPC requests
661    /// across HTTP, `WebSocket`, and IPC transports. The middleware is applied to the RPC service
662    /// layer, allowing you to intercept, modify, or enhance RPC request processing.
663    ///
664    ///
665    /// # How It Works
666    ///
667    /// The middleware uses the Tower ecosystem's `Layer` pattern. When an RPC server is started,
668    /// the configured middleware stack is applied to create a layered service that processes
669    /// requests in the order the layers were added.
670    ///
671    /// # Examples
672    ///
673    /// ```ignore
674    /// use reth_rpc_builder::{RpcServiceBuilder, RpcRequestMetrics};
675    /// use tower::Layer;
676    ///
677    /// // Simple example with metrics
678    /// let metrics_layer = RpcRequestMetrics::new(metrics_recorder);
679    /// let with_metrics = rpc_addons.with_rpc_middleware(
680    ///     RpcServiceBuilder::new().layer(metrics_layer)
681    /// );
682    ///
683    /// // Composing multiple middleware layers
684    /// let middleware_stack = RpcServiceBuilder::new()
685    ///     .layer(rate_limit_layer)
686    ///     .layer(logging_layer)
687    ///     .layer(metrics_layer);
688    /// let with_full_stack = rpc_addons.with_rpc_middleware(middleware_stack);
689    /// ```
690    ///
691    /// # Notes
692    ///
693    /// - Middleware is applied to the RPC service layer, not the HTTP transport layer
694    /// - The default middleware is `Identity` (no-op), which passes through requests unchanged
695    /// - Middleware layers are applied in the order they are added via `.layer()`
696    pub fn with_rpc_middleware<T>(
697        self,
698        rpc_middleware: T,
699    ) -> RpcAddOns<Node, EthB, PVB, EB, EVB, T> {
700        let Self {
701            hooks,
702            eth_api_builder,
703            payload_validator_builder,
704            engine_api_builder,
705            engine_validator_builder,
706            tokio_runtime,
707            ..
708        } = self;
709        RpcAddOns {
710            hooks,
711            eth_api_builder,
712            payload_validator_builder,
713            engine_api_builder,
714            engine_validator_builder,
715            rpc_middleware,
716            tokio_runtime,
717        }
718    }
719
720    /// Sets the tokio runtime for the RPC servers.
721    ///
722    /// Caution: This runtime must not be created from within asynchronous context.
723    pub fn with_tokio_runtime(self, tokio_runtime: Option<tokio::runtime::Handle>) -> Self {
724        let Self {
725            hooks,
726            eth_api_builder,
727            payload_validator_builder,
728            engine_validator_builder,
729            engine_api_builder,
730            rpc_middleware,
731            ..
732        } = self;
733        Self {
734            hooks,
735            eth_api_builder,
736            payload_validator_builder,
737            engine_validator_builder,
738            engine_api_builder,
739            rpc_middleware,
740            tokio_runtime,
741        }
742    }
743
744    /// Add a new layer `T` to the configured [`RpcServiceBuilder`].
745    pub fn layer_rpc_middleware<T>(
746        self,
747        layer: T,
748    ) -> RpcAddOns<Node, EthB, PVB, EB, EVB, Stack<RpcMiddleware, T>> {
749        let Self {
750            hooks,
751            eth_api_builder,
752            payload_validator_builder,
753            engine_api_builder,
754            engine_validator_builder,
755            rpc_middleware,
756            tokio_runtime,
757        } = self;
758        let rpc_middleware = Stack::new(rpc_middleware, layer);
759        RpcAddOns {
760            hooks,
761            eth_api_builder,
762            payload_validator_builder,
763            engine_api_builder,
764            engine_validator_builder,
765            rpc_middleware,
766            tokio_runtime,
767        }
768    }
769
770    /// Optionally adds a new layer `T` to the configured [`RpcServiceBuilder`].
771    #[expect(clippy::type_complexity)]
772    pub fn option_layer_rpc_middleware<T>(
773        self,
774        layer: Option<T>,
775    ) -> RpcAddOns<Node, EthB, PVB, EB, EVB, Stack<RpcMiddleware, Either<T, Identity>>> {
776        let layer = layer.map(Either::Left).unwrap_or(Either::Right(Identity::new()));
777        self.layer_rpc_middleware(layer)
778    }
779
780    /// Sets the hook that is run once the rpc server is started.
781    pub fn on_rpc_started<F>(mut self, hook: F) -> Self
782    where
783        F: FnOnce(RpcContext<'_, Node, EthB::EthApi>, RethRpcServerHandles) -> eyre::Result<()>
784            + Send
785            + 'static,
786    {
787        self.hooks.set_on_rpc_started(hook);
788        self
789    }
790
791    /// Sets the hook that is run to configure the rpc modules.
792    pub fn extend_rpc_modules<F>(mut self, hook: F) -> Self
793    where
794        F: FnOnce(RpcContext<'_, Node, EthB::EthApi>) -> eyre::Result<()> + Send + 'static,
795    {
796        self.hooks.set_extend_rpc_modules(hook);
797        self
798    }
799}
800
801impl<Node, EthB, EV, EB, Engine> Default for RpcAddOns<Node, EthB, EV, EB, Engine, Identity>
802where
803    Node: FullNodeComponents,
804    EthB: EthApiBuilder<Node>,
805    EV: Default,
806    EB: Default,
807    Engine: Default,
808{
809    fn default() -> Self {
810        Self::new(
811            EthB::default(),
812            EV::default(),
813            EB::default(),
814            Engine::default(),
815            Default::default(),
816        )
817    }
818}
819
820impl<N, EthB, PVB, EB, EVB, RpcMiddleware> RpcAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
821where
822    N: FullNodeComponents,
823    N::Provider: ChainSpecProvider<ChainSpec: EthereumHardforks>,
824    EthB: EthApiBuilder<N>,
825    EB: EngineApiBuilder<N>,
826    EVB: EngineValidatorBuilder<N>,
827    RpcMiddleware: RethRpcMiddleware,
828{
829    /// Launches only the regular RPC server (HTTP/WS/IPC), without the authenticated Engine API
830    /// server.
831    ///
832    /// This is useful when you only need the regular RPC functionality and want to avoid
833    /// starting the auth server.
834    pub async fn launch_rpc_server<F>(
835        self,
836        ctx: AddOnsContext<'_, N>,
837        ext: F,
838    ) -> eyre::Result<RpcServerOnlyHandle<N, EthB::EthApi>>
839    where
840        F: FnOnce(RpcModuleContainer<'_, N, EthB::EthApi>) -> eyre::Result<()>,
841    {
842        let rpc_middleware = self.rpc_middleware.clone();
843        let tokio_runtime = self.tokio_runtime.clone();
844        let setup_ctx = self.setup_rpc_components(ctx, ext).await?;
845        let RpcSetupContext {
846            node,
847            config,
848            mut modules,
849            mut auth_module,
850            auth_config: _,
851            mut registry,
852            on_rpc_started,
853            engine_events,
854            engine_handle,
855        } = setup_ctx;
856
857        let server_config = config
858            .rpc
859            .rpc_server_config()
860            .set_rpc_middleware(rpc_middleware)
861            .with_tokio_runtime(tokio_runtime);
862        let rpc_server_handle = Self::launch_rpc_server_internal(server_config, &modules).await?;
863
864        let handles =
865            RethRpcServerHandles { rpc: rpc_server_handle.clone(), auth: AuthServerHandle::noop() };
866        Self::finalize_rpc_setup(
867            &mut registry,
868            &mut modules,
869            &mut auth_module,
870            &node,
871            config,
872            on_rpc_started,
873            handles,
874        )?;
875
876        Ok(RpcServerOnlyHandle {
877            rpc_server_handle,
878            rpc_registry: registry,
879            engine_events,
880            engine_handle,
881        })
882    }
883
884    /// Launches the RPC servers with the given context and an additional hook for extending
885    /// modules. Whether the auth server is launched depends on the CLI configuration.
886    pub async fn launch_add_ons_with<F>(
887        self,
888        ctx: AddOnsContext<'_, N>,
889        ext: F,
890    ) -> eyre::Result<RpcHandle<N, EthB::EthApi>>
891    where
892        F: FnOnce(RpcModuleContainer<'_, N, EthB::EthApi>) -> eyre::Result<()>,
893    {
894        // Check CLI config to determine if auth server should be disabled
895        let disable_auth = ctx.config.rpc.disable_auth_server;
896        self.launch_add_ons_with_opt_engine(ctx, ext, disable_auth).await
897    }
898
899    /// Launches the RPC servers with the given context and an additional hook for extending
900    /// modules. Optionally disables the auth server based on the `disable_auth` parameter.
901    ///
902    /// When `disable_auth` is true, the auth server will not be started and a noop handle
903    /// will be used instead.
904    pub async fn launch_add_ons_with_opt_engine<F>(
905        self,
906        ctx: AddOnsContext<'_, N>,
907        ext: F,
908        disable_auth: bool,
909    ) -> eyre::Result<RpcHandle<N, EthB::EthApi>>
910    where
911        F: FnOnce(RpcModuleContainer<'_, N, EthB::EthApi>) -> eyre::Result<()>,
912    {
913        let rpc_middleware = self.rpc_middleware.clone();
914        let tokio_runtime = self.tokio_runtime.clone();
915        let setup_ctx = self.setup_rpc_components(ctx, ext).await?;
916        let RpcSetupContext {
917            node,
918            config,
919            mut modules,
920            mut auth_module,
921            auth_config,
922            mut registry,
923            on_rpc_started,
924            engine_events,
925            engine_handle,
926        } = setup_ctx;
927
928        let server_config = config
929            .rpc
930            .rpc_server_config()
931            .set_rpc_middleware(rpc_middleware)
932            .with_tokio_runtime(tokio_runtime);
933
934        let (rpc, auth) = if disable_auth {
935            // Only launch the RPC server, use a noop auth handle
936            let rpc = Self::launch_rpc_server_internal(server_config, &modules).await?;
937            (rpc, AuthServerHandle::noop())
938        } else {
939            let auth_module_clone = auth_module.clone();
940            // launch servers concurrently
941            let (rpc, auth) = futures::future::try_join(
942                Self::launch_rpc_server_internal(server_config, &modules),
943                Self::launch_auth_server_internal(auth_module_clone, auth_config),
944            )
945            .await?;
946            (rpc, auth)
947        };
948
949        let handles = RethRpcServerHandles { rpc, auth };
950
951        Self::finalize_rpc_setup(
952            &mut registry,
953            &mut modules,
954            &mut auth_module,
955            &node,
956            config,
957            on_rpc_started,
958            handles.clone(),
959        )?;
960
961        Ok(RpcHandle {
962            rpc_server_handles: handles,
963            rpc_registry: registry,
964            engine_events,
965            beacon_engine_handle: engine_handle,
966            engine_shutdown: EngineShutdown::default(),
967        })
968    }
969
970    /// Common setup for RPC server initialization
971    async fn setup_rpc_components<'a, F>(
972        self,
973        ctx: AddOnsContext<'a, N>,
974        ext: F,
975    ) -> eyre::Result<RpcSetupContext<'a, N, EthB::EthApi>>
976    where
977        F: FnOnce(RpcModuleContainer<'_, N, EthB::EthApi>) -> eyre::Result<()>,
978    {
979        let Self { eth_api_builder, engine_api_builder, hooks, .. } = self;
980
981        let engine_api = engine_api_builder.build_engine_api(&ctx).await?;
982        let AddOnsContext { node, config, beacon_engine_handle, jwt_secret, engine_events } = ctx;
983
984        info!(target: "reth::cli", "Engine API handler initialized");
985
986        let cache = EthStateCache::spawn_with(
987            node.provider().clone(),
988            config.rpc.eth_config().cache,
989            node.task_executor().clone(),
990        );
991
992        let new_canonical_blocks = node.provider().canonical_state_stream();
993        let c = cache.clone();
994        node.task_executor().spawn_critical(
995            "cache canonical blocks task",
996            Box::pin(async move {
997                cache_new_blocks_task(c, new_canonical_blocks).await;
998            }),
999        );
1000
1001        let eth_config = config.rpc.eth_config().max_batch_size(config.txpool.max_batch_size());
1002        let ctx = EthApiCtx {
1003            components: &node,
1004            config: eth_config,
1005            cache,
1006            engine_handle: beacon_engine_handle.clone(),
1007        };
1008        let eth_api = eth_api_builder.build_eth_api(ctx).await?;
1009
1010        let auth_config = config.rpc.auth_server_config(jwt_secret)?;
1011        let module_config = config.rpc.transport_rpc_module_config();
1012        debug!(target: "reth::cli", http=?module_config.http(), ws=?module_config.ws(), "Using RPC module config");
1013
1014        let (mut modules, mut auth_module, registry) = RpcModuleBuilder::default()
1015            .with_provider(node.provider().clone())
1016            .with_pool(node.pool().clone())
1017            .with_network(node.network().clone())
1018            .with_executor(Box::new(node.task_executor().clone()))
1019            .with_evm_config(node.evm_config().clone())
1020            .with_consensus(node.consensus().clone())
1021            .build_with_auth_server(module_config, engine_api, eth_api, engine_events.clone());
1022
1023        // in dev mode we generate 20 random dev-signer accounts
1024        if config.dev.dev {
1025            let signers = DevSigner::from_mnemonic(config.dev.dev_mnemonic.as_str(), 20);
1026            registry.eth_api().signers().write().extend(signers);
1027        }
1028
1029        let mut registry = RpcRegistry { registry };
1030        let ctx = RpcContext {
1031            node: node.clone(),
1032            config,
1033            registry: &mut registry,
1034            modules: &mut modules,
1035            auth_module: &mut auth_module,
1036        };
1037
1038        let RpcHooks { on_rpc_started, extend_rpc_modules } = hooks;
1039
1040        ext(RpcModuleContainer {
1041            modules: ctx.modules,
1042            auth_module: ctx.auth_module,
1043            registry: ctx.registry,
1044        })?;
1045        extend_rpc_modules.extend_rpc_modules(ctx)?;
1046
1047        Ok(RpcSetupContext {
1048            node,
1049            config,
1050            modules,
1051            auth_module,
1052            auth_config,
1053            registry,
1054            on_rpc_started,
1055            engine_events,
1056            engine_handle: beacon_engine_handle,
1057        })
1058    }
1059
1060    /// Helper to launch the RPC server
1061    async fn launch_rpc_server_internal<M>(
1062        server_config: RpcServerConfig<M>,
1063        modules: &TransportRpcModules,
1064    ) -> eyre::Result<RpcServerHandle>
1065    where
1066        M: RethRpcMiddleware,
1067    {
1068        let handle = server_config.start(modules).await?;
1069
1070        if let Some(path) = handle.ipc_endpoint() {
1071            info!(target: "reth::cli", %path, "RPC IPC server started");
1072        }
1073        if let Some(addr) = handle.http_local_addr() {
1074            info!(target: "reth::cli", url=%addr, "RPC HTTP server started");
1075        }
1076        if let Some(addr) = handle.ws_local_addr() {
1077            info!(target: "reth::cli", url=%addr, "RPC WS server started");
1078        }
1079
1080        Ok(handle)
1081    }
1082
1083    /// Helper to launch the auth server
1084    async fn launch_auth_server_internal(
1085        auth_module: AuthRpcModule,
1086        auth_config: reth_rpc_builder::auth::AuthServerConfig,
1087    ) -> eyre::Result<AuthServerHandle> {
1088        auth_module.start_server(auth_config)
1089            .await
1090            .map_err(Into::into)
1091            .inspect(|handle| {
1092                let addr = handle.local_addr();
1093                if let Some(ipc_endpoint) = handle.ipc_endpoint() {
1094                    info!(target: "reth::cli", url=%addr, ipc_endpoint=%ipc_endpoint, "RPC auth server started");
1095                } else {
1096                    info!(target: "reth::cli", url=%addr, "RPC auth server started");
1097                }
1098            })
1099    }
1100
1101    /// Helper to finalize RPC setup by creating context and calling hooks
1102    fn finalize_rpc_setup(
1103        registry: &mut RpcRegistry<N, EthB::EthApi>,
1104        modules: &mut TransportRpcModules,
1105        auth_module: &mut AuthRpcModule,
1106        node: &N,
1107        config: &NodeConfig<<N::Types as NodeTypes>::ChainSpec>,
1108        on_rpc_started: Box<dyn OnRpcStarted<N, EthB::EthApi>>,
1109        handles: RethRpcServerHandles,
1110    ) -> eyre::Result<()> {
1111        let ctx = RpcContext { node: node.clone(), config, registry, modules, auth_module };
1112
1113        on_rpc_started.on_rpc_started(ctx, handles)?;
1114        Ok(())
1115    }
1116}
1117
1118impl<N, EthB, PVB, EB, EVB, RpcMiddleware> NodeAddOns<N>
1119    for RpcAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
1120where
1121    N: FullNodeComponents,
1122    <N as FullNodeTypes>::Provider: ChainSpecProvider<ChainSpec: EthereumHardforks>,
1123    EthB: EthApiBuilder<N>,
1124    PVB: PayloadValidatorBuilder<N>,
1125    EB: EngineApiBuilder<N>,
1126    EVB: EngineValidatorBuilder<N>,
1127    RpcMiddleware: RethRpcMiddleware,
1128{
1129    type Handle = RpcHandle<N, EthB::EthApi>;
1130
1131    async fn launch_add_ons(self, ctx: AddOnsContext<'_, N>) -> eyre::Result<Self::Handle> {
1132        self.launch_add_ons_with(ctx, |_| Ok(())).await
1133    }
1134}
1135
1136/// Helper trait implemented for add-ons producing [`RpcHandle`]. Used by common node launcher
1137/// implementations.
1138pub trait RethRpcAddOns<N: FullNodeComponents>:
1139    NodeAddOns<N, Handle = RpcHandle<N, Self::EthApi>>
1140{
1141    /// eth API implementation.
1142    type EthApi: EthApiTypes;
1143
1144    /// Returns a mutable reference to RPC hooks.
1145    fn hooks_mut(&mut self) -> &mut RpcHooks<N, Self::EthApi>;
1146}
1147
1148impl<N: FullNodeComponents, EthB, EV, EB, Engine, RpcMiddleware> RethRpcAddOns<N>
1149    for RpcAddOns<N, EthB, EV, EB, Engine, RpcMiddleware>
1150where
1151    Self: NodeAddOns<N, Handle = RpcHandle<N, EthB::EthApi>>,
1152    EthB: EthApiBuilder<N>,
1153{
1154    type EthApi = EthB::EthApi;
1155
1156    fn hooks_mut(&mut self) -> &mut RpcHooks<N, Self::EthApi> {
1157        &mut self.hooks
1158    }
1159}
1160
1161/// `EthApiCtx` struct
1162/// This struct is used to pass the necessary context to the `EthApiBuilder` to build the `EthApi`.
1163#[derive(Debug)]
1164pub struct EthApiCtx<'a, N: FullNodeTypes> {
1165    /// Reference to the node components
1166    pub components: &'a N,
1167    /// Eth API configuration
1168    pub config: EthConfig,
1169    /// Cache for eth state
1170    pub cache: EthStateCache<PrimitivesTy<N::Types>>,
1171    /// Handle to the beacon consensus engine
1172    pub engine_handle: ConsensusEngineHandle<<N::Types as NodeTypes>::Payload>,
1173}
1174
1175impl<'a, N: FullNodeComponents<Types: NodeTypes<ChainSpec: Hardforks + EthereumHardforks>>>
1176    EthApiCtx<'a, N>
1177{
1178    /// Provides a [`EthApiBuilder`] with preconfigured config and components.
1179    pub fn eth_api_builder(self) -> reth_rpc::EthApiBuilder<N, EthRpcConverterFor<N>> {
1180        reth_rpc::EthApiBuilder::new_with_components(self.components.clone())
1181            .eth_cache(self.cache)
1182            .task_spawner(self.components.task_executor().clone())
1183            .gas_cap(self.config.rpc_gas_cap.into())
1184            .max_simulate_blocks(self.config.rpc_max_simulate_blocks)
1185            .eth_proof_window(self.config.eth_proof_window)
1186            .fee_history_cache_config(self.config.fee_history_cache)
1187            .proof_permits(self.config.proof_permits)
1188            .gas_oracle_config(self.config.gas_oracle)
1189            .max_batch_size(self.config.max_batch_size)
1190            .max_blocking_io_requests(self.config.max_blocking_io_requests)
1191            .pending_block_kind(self.config.pending_block_kind)
1192            .raw_tx_forwarder(self.config.raw_tx_forwarder)
1193            .evm_memory_limit(self.config.rpc_evm_memory_limit)
1194    }
1195}
1196
1197/// A `EthApi` that knows how to build `eth` namespace API from [`FullNodeComponents`].
1198pub trait EthApiBuilder<N: FullNodeComponents>: Default + Send + 'static {
1199    /// The Ethapi implementation this builder will build.
1200    type EthApi: FullEthApiServer<Provider = N::Provider, Pool = N::Pool>;
1201
1202    /// Builds the [`EthApiServer`](reth_rpc_api::eth::EthApiServer) from the given context.
1203    fn build_eth_api(
1204        self,
1205        ctx: EthApiCtx<'_, N>,
1206    ) -> impl Future<Output = eyre::Result<Self::EthApi>> + Send;
1207}
1208
1209/// Helper trait that provides the validator builder for the engine API
1210pub trait EngineValidatorAddOn<Node: FullNodeComponents>: Send {
1211    /// The validator builder type to use.
1212    type ValidatorBuilder: EngineValidatorBuilder<Node>;
1213
1214    /// Returns the validator builder.
1215    fn engine_validator_builder(&self) -> Self::ValidatorBuilder;
1216}
1217
1218impl<N, EthB, PVB, EB, EVB, RpcMiddleware> EngineValidatorAddOn<N>
1219    for RpcAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
1220where
1221    N: FullNodeComponents,
1222    EthB: EthApiBuilder<N>,
1223    PVB: Send,
1224    EB: EngineApiBuilder<N>,
1225    EVB: EngineValidatorBuilder<N>,
1226    RpcMiddleware: Send,
1227{
1228    type ValidatorBuilder = EVB;
1229
1230    fn engine_validator_builder(&self) -> Self::ValidatorBuilder {
1231        self.engine_validator_builder.clone()
1232    }
1233}
1234
1235/// Builder for engine API RPC module.
1236///
1237/// This builder type is responsible for providing an instance of [`IntoEngineApiRpcModule`], which
1238/// is effectively a helper trait that provides the type erased [`jsonrpsee::RpcModule`] instance
1239/// that contains the method handlers for the engine API. See [`EngineApi`] for an implementation of
1240/// [`IntoEngineApiRpcModule`].
1241pub trait EngineApiBuilder<Node: FullNodeComponents>: Send + Sync {
1242    /// The engine API RPC module. Only required to be convertible to an [`jsonrpsee::RpcModule`].
1243    type EngineApi: IntoEngineApiRpcModule + Send + Sync;
1244
1245    /// Builds the engine API instance given the provided [`AddOnsContext`].
1246    ///
1247    /// [`Self::EngineApi`] will be converted into the method handlers of the authenticated RPC
1248    /// server (engine API).
1249    fn build_engine_api(
1250        self,
1251        ctx: &AddOnsContext<'_, Node>,
1252    ) -> impl Future<Output = eyre::Result<Self::EngineApi>> + Send;
1253}
1254
1255/// Builder trait for creating payload validators specifically for the Engine API.
1256///
1257/// This trait is responsible for building validators that the Engine API will use
1258/// to validate payloads.
1259pub trait PayloadValidatorBuilder<Node: FullNodeComponents>: Send + Sync + Clone {
1260    /// The validator type that will be used by the Engine API.
1261    type Validator: PayloadValidator<<Node::Types as NodeTypes>::Payload>;
1262
1263    /// Builds the engine API validator.
1264    ///
1265    /// Returns a validator that validates engine API version-specific fields and payload
1266    /// attributes.
1267    fn build(
1268        self,
1269        ctx: &AddOnsContext<'_, Node>,
1270    ) -> impl Future<Output = eyre::Result<Self::Validator>> + Send;
1271}
1272
1273/// Builder trait for creating engine validators for the consensus engine.
1274///
1275/// This trait is responsible for building validators that the consensus engine will use
1276/// for block execution, state validation, and fork handling.
1277pub trait EngineValidatorBuilder<Node: FullNodeComponents>: Send + Sync + Clone {
1278    /// The tree validator type that will be used by the consensus engine.
1279    type EngineValidator: EngineValidator<
1280        <Node::Types as NodeTypes>::Payload,
1281        <Node::Types as NodeTypes>::Primitives,
1282    >;
1283
1284    /// Builds the tree validator for the consensus engine.
1285    ///
1286    /// Returns a validator that handles block execution, state validation, and fork handling.
1287    fn build_tree_validator(
1288        self,
1289        ctx: &AddOnsContext<'_, Node>,
1290        tree_config: TreeConfig,
1291    ) -> impl Future<Output = eyre::Result<Self::EngineValidator>> + Send;
1292}
1293
1294/// Basic implementation of [`EngineValidatorBuilder`].
1295///
1296/// This builder creates a [`BasicEngineValidator`] using the provided payload validator builder.
1297#[derive(Debug, Clone)]
1298pub struct BasicEngineValidatorBuilder<EV> {
1299    /// The payload validator builder used to create the engine validator.
1300    payload_validator_builder: EV,
1301}
1302
1303impl<EV> BasicEngineValidatorBuilder<EV> {
1304    /// Creates a new instance with the given payload validator builder.
1305    pub const fn new(payload_validator_builder: EV) -> Self {
1306        Self { payload_validator_builder }
1307    }
1308}
1309
1310impl<EV> Default for BasicEngineValidatorBuilder<EV>
1311where
1312    EV: Default,
1313{
1314    fn default() -> Self {
1315        Self::new(EV::default())
1316    }
1317}
1318
1319impl<Node, EV> EngineValidatorBuilder<Node> for BasicEngineValidatorBuilder<EV>
1320where
1321    Node: FullNodeComponents<
1322        Evm: ConfigureEngineEvm<
1323            <<Node::Types as NodeTypes>::Payload as PayloadTypes>::ExecutionData,
1324        >,
1325    >,
1326    EV: PayloadValidatorBuilder<Node>,
1327    EV::Validator: reth_engine_primitives::PayloadValidator<
1328        <Node::Types as NodeTypes>::Payload,
1329        Block = BlockTy<Node::Types>,
1330    >,
1331{
1332    type EngineValidator = BasicEngineValidator<Node::Provider, Node::Evm, EV::Validator>;
1333
1334    async fn build_tree_validator(
1335        self,
1336        ctx: &AddOnsContext<'_, Node>,
1337        tree_config: TreeConfig,
1338    ) -> eyre::Result<Self::EngineValidator> {
1339        let validator = self.payload_validator_builder.build(ctx).await?;
1340        let data_dir = ctx.config.datadir.clone().resolve_datadir(ctx.config.chain.chain());
1341        let invalid_block_hook = ctx.create_invalid_block_hook(&data_dir).await?;
1342        Ok(BasicEngineValidator::new(
1343            ctx.node.provider().clone(),
1344            std::sync::Arc::new(ctx.node.consensus().clone()),
1345            ctx.node.evm_config().clone(),
1346            validator,
1347            tree_config,
1348            invalid_block_hook,
1349        ))
1350    }
1351}
1352
1353/// Builder for basic [`EngineApi`] implementation.
1354///
1355/// This provides a basic default implementation for opstack and ethereum engine API via
1356/// [`EngineTypes`] and uses the general purpose [`EngineApi`] implementation as the builder's
1357/// output.
1358#[derive(Debug, Default)]
1359pub struct BasicEngineApiBuilder<PVB> {
1360    payload_validator_builder: PVB,
1361}
1362
1363impl<N, PVB> EngineApiBuilder<N> for BasicEngineApiBuilder<PVB>
1364where
1365    N: FullNodeComponents<
1366        Types: NodeTypes<
1367            ChainSpec: EthereumHardforks,
1368            Payload: PayloadTypes<ExecutionData = ExecutionData> + EngineTypes,
1369        >,
1370    >,
1371    PVB: PayloadValidatorBuilder<N>,
1372    PVB::Validator: EngineApiValidator<<N::Types as NodeTypes>::Payload>,
1373{
1374    type EngineApi = EngineApi<
1375        N::Provider,
1376        <N::Types as NodeTypes>::Payload,
1377        N::Pool,
1378        PVB::Validator,
1379        <N::Types as NodeTypes>::ChainSpec,
1380    >;
1381
1382    async fn build_engine_api(self, ctx: &AddOnsContext<'_, N>) -> eyre::Result<Self::EngineApi> {
1383        let Self { payload_validator_builder } = self;
1384
1385        let engine_validator = payload_validator_builder.build(ctx).await?;
1386        let client = ClientVersionV1 {
1387            code: CLIENT_CODE,
1388            name: version_metadata().name_client.to_string(),
1389            version: version_metadata().cargo_pkg_version.to_string(),
1390            commit: version_metadata().vergen_git_sha.to_string(),
1391        };
1392
1393        Ok(EngineApi::new(
1394            ctx.node.provider().clone(),
1395            ctx.config.chain.clone(),
1396            ctx.beacon_engine_handle.clone(),
1397            PayloadStore::new(ctx.node.payload_builder_handle().clone()),
1398            ctx.node.pool().clone(),
1399            Box::new(ctx.node.task_executor().clone()),
1400            client,
1401            EngineCapabilities::default(),
1402            engine_validator,
1403            ctx.config.engine.accept_execution_requests_hash,
1404            ctx.node.network().clone(),
1405        ))
1406    }
1407}
1408
1409/// A noop Builder that satisfies the [`EngineApiBuilder`] trait without actually configuring an
1410/// engine API module
1411///
1412/// This is intended to be used as a workaround for reusing all the existing ethereum node launch
1413/// utilities which require an engine API.
1414#[derive(Debug, Clone, Default)]
1415#[non_exhaustive]
1416pub struct NoopEngineApiBuilder;
1417
1418impl<N: FullNodeComponents> EngineApiBuilder<N> for NoopEngineApiBuilder {
1419    type EngineApi = NoopEngineApi;
1420
1421    async fn build_engine_api(self, _ctx: &AddOnsContext<'_, N>) -> eyre::Result<Self::EngineApi> {
1422        Ok(NoopEngineApi::default())
1423    }
1424}
1425
1426/// Represents an empty Engine API [`RpcModule`].
1427///
1428/// This is only intended to be used in combination with the [`NoopEngineApiBuilder`] in order to
1429/// satisfy trait bounds in the regular ethereum launch routine that mandate an engine API instance.
1430#[derive(Debug, Clone, Default)]
1431#[non_exhaustive]
1432pub struct NoopEngineApi;
1433
1434impl IntoEngineApiRpcModule for NoopEngineApi {
1435    fn into_rpc_module(self) -> RpcModule<()> {
1436        RpcModule::new(())
1437    }
1438}
1439
1440/// Handle to trigger graceful engine shutdown.
1441///
1442/// This handle can be used to request a graceful shutdown of the engine,
1443/// which will persist all remaining in-memory blocks before terminating.
1444#[derive(Clone, Debug)]
1445pub struct EngineShutdown {
1446    /// Channel to send shutdown signal.
1447    tx: Arc<Mutex<Option<oneshot::Sender<EngineShutdownRequest>>>>,
1448}
1449
1450impl EngineShutdown {
1451    /// Creates a new [`EngineShutdown`] handle and returns the receiver.
1452    pub fn new() -> (Self, oneshot::Receiver<EngineShutdownRequest>) {
1453        let (tx, rx) = oneshot::channel();
1454        (Self { tx: Arc::new(Mutex::new(Some(tx))) }, rx)
1455    }
1456
1457    /// Requests a graceful engine shutdown.
1458    ///
1459    /// All remaining in-memory blocks will be persisted before the engine terminates.
1460    ///
1461    /// Returns a receiver that resolves when shutdown is complete.
1462    /// Returns `None` if shutdown was already triggered.
1463    pub fn shutdown(&self) -> Option<oneshot::Receiver<()>> {
1464        let mut guard = self.tx.lock();
1465        let tx = guard.take()?;
1466        let (done_tx, done_rx) = oneshot::channel();
1467        let _ = tx.send(EngineShutdownRequest { done_tx });
1468        Some(done_rx)
1469    }
1470}
1471
1472impl Default for EngineShutdown {
1473    fn default() -> Self {
1474        Self { tx: Arc::new(Mutex::new(None)) }
1475    }
1476}
1477
1478/// Request to shutdown the engine.
1479#[derive(Debug)]
1480pub struct EngineShutdownRequest {
1481    /// Channel to signal shutdown completion.
1482    pub done_tx: oneshot::Sender<()>,
1483}