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