Skip to main content

reth_node_builder/
rpc.rs

1//! Builder support for rpc components.
2
3pub use jsonrpsee::{
4    core::middleware::layer::Either,
5    server::middleware::rpc::{RpcService, RpcServiceBuilder},
6};
7use reth_engine_tree::tree::WaitForCaches;
8pub use reth_engine_tree::tree::{BasicEngineValidator, EngineValidator};
9pub use reth_rpc_builder::{
10    middleware::{RethAuthHttpMiddleware, RethRpcMiddleware},
11    Identity, Stack,
12};
13use reth_storage_overlay::OverlayManager;
14
15use crate::{
16    invalid_block_hook::InvalidBlockHookExt, txpool_prewarm, ConfigureEngineEvm,
17    ConsensusEngineEvent, ConsensusEngineHandle,
18};
19use alloy_consensus::BlockHeader;
20use alloy_eips::BlockId;
21use alloy_network::Ethereum;
22use alloy_rpc_types::engine::ClientVersionV1;
23use alloy_rpc_types_engine::ExecutionData;
24use futures::{Stream, StreamExt};
25use jsonrpsee::RpcModule;
26use parking_lot::Mutex;
27use reth_chain_state::{CanonStateNotification, CanonStateSubscriptions};
28use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks, Hardforks};
29use reth_node_api::{
30    AddOnsContext, BlockTy, EngineApiValidator, EngineTypes, FullNodeComponents, FullNodeTypes,
31    NodeAddOns, NodeTypes, PayloadTypes, PayloadValidator, PrimitivesTy, TreeConfig,
32};
33use reth_node_core::{
34    cli::config::RethTransactionPoolConfig,
35    node_config::NodeConfig,
36    version::{version_metadata, CLIENT_CODE},
37};
38use reth_payload_builder::{PayloadBuilderHandle, PayloadStore};
39use reth_primitives_traits::NodePrimitives;
40use reth_rpc::{
41    eth::{DevSigner, EthApiTypes, FullEthApiServer},
42    AdminApi, EthApi,
43};
44use reth_rpc_api::{
45    eth::helpers::{EthTransactions, GetBlockAccessList},
46    IntoEngineApiRpcModule,
47};
48use reth_rpc_builder::{
49    auth::{AuthRpcModule, AuthServerHandle},
50    config::RethRpcServerConfig,
51    RpcModuleBuilder, RpcRegistryInner, RpcServerConfig, RpcServerHandle, TransportRpcModules,
52};
53use reth_rpc_convert::RpcConverter;
54use reth_rpc_engine_api::{capabilities::EngineCapabilities, EngineApi};
55use reth_rpc_eth_api::node::RpcNodeCoreAdapter;
56use reth_rpc_eth_types::{
57    cache::cache_new_blocks_task, receipt::EthReceiptConverter, EthConfig, EthStateCache,
58};
59use reth_tokio_util::EventSender;
60use reth_tracing::tracing::{debug, info};
61use std::{
62    fmt::{self, Debug},
63    future::Future,
64    ops::{Deref, DerefMut},
65    sync::Arc,
66};
67use tokio::sync::oneshot;
68
69/// Contains the handles to the spawned RPC servers.
70///
71/// This can be used to access the endpoints of the servers.
72#[derive(Debug, Clone)]
73pub struct RethRpcServerHandles {
74    /// The regular RPC server handle to all configured transports.
75    pub rpc: RpcServerHandle,
76    /// The handle to the auth server (engine API)
77    pub auth: AuthServerHandle,
78}
79
80/// Contains hooks that are called during the rpc setup.
81pub struct RpcHooks<Node: FullNodeComponents, EthApi> {
82    /// Hooks to run once RPC server is running.
83    pub on_rpc_started: Box<dyn OnRpcStarted<Node, EthApi>>,
84    /// Hooks to run to configure RPC server API.
85    pub extend_rpc_modules: Box<dyn ExtendRpcModules<Node, EthApi>>,
86}
87
88impl<Node, EthApi> Default for RpcHooks<Node, EthApi>
89where
90    Node: FullNodeComponents,
91    EthApi: EthApiTypes,
92{
93    fn default() -> Self {
94        Self { on_rpc_started: Box::<()>::default(), extend_rpc_modules: Box::<()>::default() }
95    }
96}
97
98impl<Node, EthApi> RpcHooks<Node, EthApi>
99where
100    Node: FullNodeComponents,
101    EthApi: EthApiTypes,
102{
103    /// Sets the hook that is run once the rpc server is started.
104    pub(crate) fn set_on_rpc_started<F>(&mut self, hook: F) -> &mut Self
105    where
106        F: OnRpcStarted<Node, EthApi> + 'static,
107    {
108        self.on_rpc_started = Box::new(hook);
109        self
110    }
111
112    /// Sets the hook that is run once the rpc server is started.
113    #[expect(unused)]
114    pub(crate) fn on_rpc_started<F>(mut self, hook: F) -> Self
115    where
116        F: OnRpcStarted<Node, EthApi> + 'static,
117    {
118        self.set_on_rpc_started(hook);
119        self
120    }
121
122    /// Sets the hook that is run to configure the rpc modules.
123    pub(crate) fn set_extend_rpc_modules<F>(&mut self, hook: F) -> &mut Self
124    where
125        F: ExtendRpcModules<Node, EthApi> + 'static,
126    {
127        self.extend_rpc_modules = Box::new(hook);
128        self
129    }
130
131    /// Sets the hook that is run to configure the rpc modules.
132    #[expect(unused)]
133    pub(crate) fn extend_rpc_modules<F>(mut self, hook: F) -> Self
134    where
135        F: ExtendRpcModules<Node, EthApi> + 'static,
136    {
137        self.set_extend_rpc_modules(hook);
138        self
139    }
140}
141
142impl<Node, EthApi> fmt::Debug for RpcHooks<Node, EthApi>
143where
144    Node: FullNodeComponents,
145    EthApi: EthApiTypes,
146{
147    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148        f.debug_struct("RpcHooks")
149            .field("on_rpc_started", &"...")
150            .field("extend_rpc_modules", &"...")
151            .finish()
152    }
153}
154
155/// Event hook that is called once the rpc server is started.
156pub trait OnRpcStarted<Node: FullNodeComponents, EthApi: EthApiTypes>: Send {
157    /// The hook that is called once the rpc server is started.
158    fn on_rpc_started(
159        self: Box<Self>,
160        ctx: RpcContext<'_, Node, EthApi>,
161        handles: RethRpcServerHandles,
162    ) -> eyre::Result<()>;
163}
164
165impl<Node, EthApi, F> OnRpcStarted<Node, EthApi> for F
166where
167    F: FnOnce(RpcContext<'_, Node, EthApi>, RethRpcServerHandles) -> eyre::Result<()> + Send,
168    Node: FullNodeComponents,
169    EthApi: EthApiTypes,
170{
171    fn on_rpc_started(
172        self: Box<Self>,
173        ctx: RpcContext<'_, Node, EthApi>,
174        handles: RethRpcServerHandles,
175    ) -> eyre::Result<()> {
176        (*self)(ctx, handles)
177    }
178}
179
180impl<Node, EthApi> OnRpcStarted<Node, EthApi> for ()
181where
182    Node: FullNodeComponents,
183    EthApi: EthApiTypes,
184{
185    fn on_rpc_started(
186        self: Box<Self>,
187        _: RpcContext<'_, Node, EthApi>,
188        _: RethRpcServerHandles,
189    ) -> eyre::Result<()> {
190        Ok(())
191    }
192}
193
194/// Event hook that is called when the rpc server is started.
195pub trait ExtendRpcModules<Node: FullNodeComponents, EthApi: EthApiTypes>: Send {
196    /// The hook that is called once the rpc server is started.
197    fn extend_rpc_modules(self: Box<Self>, ctx: RpcContext<'_, Node, EthApi>) -> eyre::Result<()>;
198}
199
200impl<Node, EthApi, F> ExtendRpcModules<Node, EthApi> for F
201where
202    F: FnOnce(RpcContext<'_, Node, EthApi>) -> eyre::Result<()> + Send,
203    Node: FullNodeComponents,
204    EthApi: EthApiTypes,
205{
206    fn extend_rpc_modules(self: Box<Self>, ctx: RpcContext<'_, Node, EthApi>) -> eyre::Result<()> {
207        (*self)(ctx)
208    }
209}
210
211impl<Node, EthApi> ExtendRpcModules<Node, EthApi> for ()
212where
213    Node: FullNodeComponents,
214    EthApi: EthApiTypes,
215{
216    fn extend_rpc_modules(self: Box<Self>, _: RpcContext<'_, Node, EthApi>) -> eyre::Result<()> {
217        Ok(())
218    }
219}
220
221/// Helper wrapper type to encapsulate the [`RpcRegistryInner`] over components trait.
222#[derive(Debug, Clone)]
223#[expect(clippy::type_complexity)]
224pub struct RpcRegistry<Node: FullNodeComponents, EthApi: EthApiTypes> {
225    pub(crate) registry: RpcRegistryInner<
226        Node::Provider,
227        Node::Pool,
228        Node::Network,
229        EthApi,
230        Node::Evm,
231        Node::Consensus,
232    >,
233}
234
235impl<Node, EthApi> Deref for RpcRegistry<Node, EthApi>
236where
237    Node: FullNodeComponents,
238    EthApi: EthApiTypes,
239{
240    type Target = RpcRegistryInner<
241        Node::Provider,
242        Node::Pool,
243        Node::Network,
244        EthApi,
245        Node::Evm,
246        Node::Consensus,
247    >;
248
249    fn deref(&self) -> &Self::Target {
250        &self.registry
251    }
252}
253
254impl<Node, EthApi> DerefMut for RpcRegistry<Node, EthApi>
255where
256    Node: FullNodeComponents,
257    EthApi: EthApiTypes,
258{
259    fn deref_mut(&mut self) -> &mut Self::Target {
260        &mut self.registry
261    }
262}
263
264/// Helper container for the parameters commonly passed to RPC module extension functions.
265#[expect(missing_debug_implementations)]
266pub struct RpcModuleContainer<'a, Node: FullNodeComponents, EthApi: EthApiTypes> {
267    /// Holds installed modules per transport type.
268    pub modules: &'a mut TransportRpcModules,
269    /// Holds jwt authenticated rpc module.
270    pub auth_module: &'a mut AuthRpcModule,
271    /// A Helper type the holds instances of the configured modules.
272    pub registry: &'a mut RpcRegistry<Node, EthApi>,
273}
274
275/// Helper container to encapsulate [`RpcRegistryInner`], [`TransportRpcModules`] and
276/// [`AuthRpcModule`].
277///
278/// This can be used to access installed modules, or create commonly used handlers like
279/// [`reth_rpc::eth::EthApi`], and ultimately merge additional rpc handler into the configured
280/// transport modules [`TransportRpcModules`] as well as configured authenticated methods
281/// [`AuthRpcModule`].
282#[expect(missing_debug_implementations)]
283pub struct RpcContext<'a, Node: FullNodeComponents, EthApi: EthApiTypes> {
284    /// The node components.
285    pub(crate) node: Node,
286
287    /// Gives access to the node configuration.
288    pub(crate) config: &'a NodeConfig<<Node::Types as NodeTypes>::ChainSpec>,
289
290    /// A Helper type the holds instances of the configured modules.
291    ///
292    /// This provides easy access to rpc handlers, such as [`RpcRegistryInner::eth_api`].
293    pub registry: &'a mut RpcRegistry<Node, EthApi>,
294    /// Holds installed modules per transport type.
295    ///
296    /// This can be used to merge additional modules into the configured transports (http, ipc,
297    /// ws). See [`TransportRpcModules::merge_configured`]
298    pub modules: &'a mut TransportRpcModules,
299    /// Holds jwt authenticated rpc module.
300    ///
301    /// This can be used to merge additional modules into the configured authenticated methods
302    pub auth_module: &'a mut AuthRpcModule,
303}
304
305impl<Node, EthApi> RpcContext<'_, Node, EthApi>
306where
307    Node: FullNodeComponents,
308    EthApi: EthApiTypes,
309{
310    /// Returns the config of the node.
311    pub const fn config(&self) -> &NodeConfig<<Node::Types as NodeTypes>::ChainSpec> {
312        self.config
313    }
314
315    /// Returns a reference to the configured node.
316    ///
317    /// This gives access to the node's components.
318    pub const fn node(&self) -> &Node {
319        &self.node
320    }
321
322    /// Returns the transaction pool instance.
323    pub fn pool(&self) -> &Node::Pool {
324        self.node.pool()
325    }
326
327    /// Returns provider to interact with the node.
328    pub fn provider(&self) -> &Node::Provider {
329        self.node.provider()
330    }
331
332    /// Returns the handle to the network
333    pub fn network(&self) -> &Node::Network {
334        self.node.network()
335    }
336
337    /// Returns the handle to the payload builder service
338    pub fn payload_builder_handle(
339        &self,
340    ) -> &PayloadBuilderHandle<<Node::Types as NodeTypes>::Payload> {
341        self.node.payload_builder_handle()
342    }
343}
344
345/// Handle to the launched RPC servers.
346pub struct RpcHandle<Node: FullNodeComponents, EthApi: EthApiTypes> {
347    /// Handles to launched servers.
348    pub rpc_server_handles: RethRpcServerHandles,
349    /// Configured RPC modules.
350    pub rpc_registry: RpcRegistry<Node, EthApi>,
351    /// Notification channel for engine API events
352    ///
353    /// Caution: This is a multi-producer, multi-consumer broadcast and allows grants access to
354    /// dispatch events
355    pub engine_events: EventSender<ConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>>,
356    /// Handle to the beacon consensus engine.
357    pub beacon_engine_handle: ConsensusEngineHandle<<Node::Types as NodeTypes>::Payload>,
358    /// Handle to trigger engine shutdown.
359    pub engine_shutdown: EngineShutdown,
360}
361
362impl<Node: FullNodeComponents, EthApi: EthApiTypes> Clone for RpcHandle<Node, EthApi> {
363    fn clone(&self) -> Self {
364        Self {
365            rpc_server_handles: self.rpc_server_handles.clone(),
366            rpc_registry: self.rpc_registry.clone(),
367            engine_events: self.engine_events.clone(),
368            beacon_engine_handle: self.beacon_engine_handle.clone(),
369            engine_shutdown: self.engine_shutdown.clone(),
370        }
371    }
372}
373
374impl<Node: FullNodeComponents, EthApi: EthApiTypes> Deref for RpcHandle<Node, EthApi> {
375    type Target = RpcRegistry<Node, EthApi>;
376
377    fn deref(&self) -> &Self::Target {
378        &self.rpc_registry
379    }
380}
381
382impl<Node: FullNodeComponents, EthApi: EthApiTypes> Debug for RpcHandle<Node, EthApi>
383where
384    RpcRegistry<Node, EthApi>: Debug,
385{
386    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
387        f.debug_struct("RpcHandle")
388            .field("rpc_server_handles", &self.rpc_server_handles)
389            .field("rpc_registry", &self.rpc_registry)
390            .field("engine_shutdown", &self.engine_shutdown)
391            .finish()
392    }
393}
394
395impl<Node: FullNodeComponents, EthApi: EthApiTypes> RpcHandle<Node, EthApi> {
396    /// Returns the RPC server handles.
397    pub const fn rpc_server_handles(&self) -> &RethRpcServerHandles {
398        &self.rpc_server_handles
399    }
400
401    /// Returns the consensus engine handle.
402    ///
403    /// This handle can be used to interact with the engine service directly.
404    pub const fn consensus_engine_handle(
405        &self,
406    ) -> &ConsensusEngineHandle<<Node::Types as NodeTypes>::Payload> {
407        &self.beacon_engine_handle
408    }
409
410    /// Returns the consensus engine events sender.
411    pub const fn consensus_engine_events(
412        &self,
413    ) -> &EventSender<ConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>> {
414        &self.engine_events
415    }
416
417    /// Returns the `EthApi` instance of the rpc server.
418    pub const fn eth_api(&self) -> &EthApi {
419        self.rpc_registry.registry.eth_api()
420    }
421
422    /// Returns an instance of the [`AdminApi`] for the rpc server.
423    pub fn admin_api(
424        &self,
425    ) -> AdminApi<Node::Network, <Node::Types as NodeTypes>::ChainSpec, Node::Pool>
426    where
427        <Node::Types as NodeTypes>::ChainSpec: EthereumHardforks,
428    {
429        self.rpc_registry.registry.admin_api()
430    }
431}
432
433/// Handle returned when only the regular RPC server (HTTP/WS/IPC) is launched.
434///
435/// This handle provides access to the RPC server endpoints and registry, but does not
436/// include an authenticated Engine API server. Use this when you only need regular
437/// RPC functionality.
438#[derive(Debug, Clone)]
439pub struct RpcServerOnlyHandle<Node: FullNodeComponents, EthApi: EthApiTypes> {
440    /// Handle to the RPC server
441    pub rpc_server_handle: RpcServerHandle,
442    /// Configured RPC modules.
443    pub rpc_registry: RpcRegistry<Node, EthApi>,
444    /// Notification channel for engine API events
445    pub engine_events: EventSender<ConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>>,
446    /// Handle to the consensus engine.
447    pub engine_handle: ConsensusEngineHandle<<Node::Types as NodeTypes>::Payload>,
448}
449
450impl<Node: FullNodeComponents, EthApi: EthApiTypes> RpcServerOnlyHandle<Node, EthApi> {
451    /// Returns the RPC server handle.
452    pub const fn rpc_server_handle(&self) -> &RpcServerHandle {
453        &self.rpc_server_handle
454    }
455
456    /// Returns the consensus engine handle.
457    ///
458    /// This handle can be used to interact with the engine service directly.
459    pub const fn consensus_engine_handle(
460        &self,
461    ) -> &ConsensusEngineHandle<<Node::Types as NodeTypes>::Payload> {
462        &self.engine_handle
463    }
464
465    /// Returns the consensus engine events sender.
466    pub const fn consensus_engine_events(
467        &self,
468    ) -> &EventSender<ConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>> {
469        &self.engine_events
470    }
471}
472
473/// Handle returned when only the authenticated Engine API server is launched.
474///
475/// This handle provides access to the Engine API server and registry, but does not
476/// include the regular RPC servers (HTTP/WS/IPC). Use this for specialized setups
477/// that only need Engine API functionality.
478#[derive(Debug, Clone)]
479pub struct AuthServerOnlyHandle<Node: FullNodeComponents, EthApi: EthApiTypes> {
480    /// Handle to the auth server (engine API)
481    pub auth_server_handle: AuthServerHandle,
482    /// Configured RPC modules.
483    pub rpc_registry: RpcRegistry<Node, EthApi>,
484    /// Notification channel for engine API events
485    pub engine_events: EventSender<ConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>>,
486    /// Handle to the consensus engine.
487    pub engine_handle: ConsensusEngineHandle<<Node::Types as NodeTypes>::Payload>,
488}
489
490impl<Node: FullNodeComponents, EthApi: EthApiTypes> AuthServerOnlyHandle<Node, EthApi> {
491    /// Returns the consensus engine handle.
492    ///
493    /// This handle can be used to interact with the engine service directly.
494    pub const fn consensus_engine_handle(
495        &self,
496    ) -> &ConsensusEngineHandle<<Node::Types as NodeTypes>::Payload> {
497        &self.engine_handle
498    }
499
500    /// Returns the consensus engine events sender.
501    pub const fn consensus_engine_events(
502        &self,
503    ) -> &EventSender<ConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>> {
504        &self.engine_events
505    }
506}
507
508/// Internal context struct for RPC setup shared between different launch methods
509struct RpcSetupContext<'a, Node: FullNodeComponents, EthApi: EthApiTypes> {
510    node: Node,
511    config: &'a NodeConfig<<Node::Types as NodeTypes>::ChainSpec>,
512    modules: TransportRpcModules,
513    auth_module: AuthRpcModule,
514    auth_config: reth_rpc_builder::auth::AuthServerConfig,
515    registry: RpcRegistry<Node, EthApi>,
516    on_rpc_started: Box<dyn OnRpcStarted<Node, EthApi>>,
517    engine_events: EventSender<ConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>>,
518    engine_handle: ConsensusEngineHandle<<Node::Types as NodeTypes>::Payload>,
519}
520
521/// Node add-ons containing RPC server configuration, with customizable eth API handler.
522///
523/// This struct can be used to provide the RPC server functionality. It is responsible for launching
524/// the regular RPC and the authenticated RPC server (engine API). It is intended to be used and
525/// modified as part of the [`NodeAddOns`] see for example `OpRpcAddons`, `EthereumAddOns`.
526///
527/// It can be modified to register RPC API handlers, see [`RpcAddOns::launch_add_ons_with`] which
528/// takes a closure that provides access to all the configured modules (namespaces), and is invoked
529/// just before the servers are launched. This can be used to extend the node with custom RPC
530/// methods or even replace existing method handlers, see also [`TransportRpcModules`].
531pub struct RpcAddOns<
532    Node: FullNodeComponents,
533    EthB: EthApiBuilder<Node>,
534    PVB,
535    EB = BasicEngineApiBuilder<PVB>,
536    EVB = BasicEngineValidatorBuilder<PVB>,
537    RpcMiddleware = Identity,
538    AuthHttpMiddleware = Identity,
539> {
540    /// Additional RPC add-ons.
541    pub hooks: RpcHooks<Node, EthB::EthApi>,
542    /// Builder for `EthApi`
543    eth_api_builder: EthB,
544    /// Payload validator builder
545    payload_validator_builder: PVB,
546    /// Builder for `EngineApi`
547    engine_api_builder: EB,
548    /// Builder for tree validator
549    engine_validator_builder: EVB,
550    /// Configurable RPC middleware stack.
551    ///
552    /// This middleware is applied to all RPC requests across all transports (HTTP, WS, IPC).
553    /// See [`RpcAddOns::with_rpc_middleware`] for more details.
554    rpc_middleware: RpcMiddleware,
555    /// Configurable HTTP transport middleware for the auth server.
556    ///
557    /// This middleware is applied after JWT authentication and before JSON-RPC parsing on the
558    /// auth / Engine API server, giving access to the raw HTTP request.
559    auth_http_middleware: AuthHttpMiddleware,
560    /// Optional custom tokio runtime for the RPC server.
561    tokio_runtime: Option<tokio::runtime::Handle>,
562}
563
564impl<Node, EthB, PVB, EB, EVB, RpcMiddleware, AuthHttpMiddleware> Debug
565    for RpcAddOns<Node, EthB, PVB, EB, EVB, RpcMiddleware, AuthHttpMiddleware>
566where
567    Node: FullNodeComponents,
568    EthB: EthApiBuilder<Node>,
569    PVB: Debug,
570    EB: Debug,
571    EVB: Debug,
572{
573    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
574        f.debug_struct("RpcAddOns")
575            .field("hooks", &self.hooks)
576            .field("eth_api_builder", &"...")
577            .field("payload_validator_builder", &self.payload_validator_builder)
578            .field("engine_api_builder", &self.engine_api_builder)
579            .field("engine_validator_builder", &self.engine_validator_builder)
580            .field("rpc_middleware", &"...")
581            .finish()
582    }
583}
584
585impl<Node, EthB, PVB, EB, EVB, RpcMiddleware, AuthHttpMiddleware>
586    RpcAddOns<Node, EthB, PVB, EB, EVB, RpcMiddleware, AuthHttpMiddleware>
587where
588    Node: FullNodeComponents,
589    EthB: EthApiBuilder<Node>,
590{
591    /// Creates a new instance of the RPC add-ons.
592    pub fn new(
593        eth_api_builder: EthB,
594        payload_validator_builder: PVB,
595        engine_api_builder: EB,
596        engine_validator_builder: EVB,
597        rpc_middleware: RpcMiddleware,
598        auth_http_middleware: AuthHttpMiddleware,
599    ) -> Self {
600        Self {
601            hooks: RpcHooks::default(),
602            eth_api_builder,
603            payload_validator_builder,
604            engine_api_builder,
605            engine_validator_builder,
606            rpc_middleware,
607            auth_http_middleware,
608            tokio_runtime: None,
609        }
610    }
611
612    /// Maps the [`EngineApiBuilder`] builder type.
613    pub fn with_engine_api<T>(
614        self,
615        engine_api_builder: T,
616    ) -> RpcAddOns<Node, EthB, PVB, T, EVB, RpcMiddleware, AuthHttpMiddleware> {
617        self.map_engine_api(|_| engine_api_builder)
618    }
619
620    /// Maps the existing [`EngineApiBuilder`] builder value.
621    pub fn map_engine_api<T>(
622        self,
623        f: impl FnOnce(EB) -> T,
624    ) -> RpcAddOns<Node, EthB, PVB, T, EVB, RpcMiddleware, AuthHttpMiddleware> {
625        let Self {
626            hooks,
627            eth_api_builder,
628            payload_validator_builder,
629            engine_api_builder,
630            engine_validator_builder,
631            rpc_middleware,
632            auth_http_middleware,
633            tokio_runtime,
634        } = self;
635        RpcAddOns {
636            hooks,
637            eth_api_builder,
638            payload_validator_builder,
639            engine_api_builder: f(engine_api_builder),
640            engine_validator_builder,
641            rpc_middleware,
642            auth_http_middleware,
643            tokio_runtime,
644        }
645    }
646
647    /// Maps the [`PayloadValidatorBuilder`] builder type.
648    pub fn with_payload_validator<T>(
649        self,
650        payload_validator_builder: T,
651    ) -> RpcAddOns<Node, EthB, T, EB, EVB, RpcMiddleware, AuthHttpMiddleware> {
652        let Self {
653            hooks,
654            eth_api_builder,
655            engine_api_builder,
656            engine_validator_builder,
657            rpc_middleware,
658            auth_http_middleware,
659            tokio_runtime,
660            ..
661        } = self;
662        RpcAddOns {
663            hooks,
664            eth_api_builder,
665            payload_validator_builder,
666            engine_api_builder,
667            engine_validator_builder,
668            rpc_middleware,
669            auth_http_middleware,
670            tokio_runtime,
671        }
672    }
673
674    /// Maps the [`EngineValidatorBuilder`] builder type.
675    pub fn with_engine_validator<T>(
676        self,
677        engine_validator_builder: T,
678    ) -> RpcAddOns<Node, EthB, PVB, EB, T, RpcMiddleware, AuthHttpMiddleware> {
679        let Self {
680            hooks,
681            eth_api_builder,
682            payload_validator_builder,
683            engine_api_builder,
684            rpc_middleware,
685            auth_http_middleware,
686            tokio_runtime,
687            ..
688        } = self;
689        RpcAddOns {
690            hooks,
691            eth_api_builder,
692            payload_validator_builder,
693            engine_api_builder,
694            engine_validator_builder,
695            rpc_middleware,
696            auth_http_middleware,
697            tokio_runtime,
698        }
699    }
700
701    /// Sets the RPC middleware stack for processing RPC requests.
702    ///
703    /// This method configures a custom middleware stack that will be applied to all RPC requests
704    /// across HTTP, `WebSocket`, and IPC transports. The middleware is applied to the RPC service
705    /// layer, allowing you to intercept, modify, or enhance RPC request processing.
706    ///
707    ///
708    /// # How It Works
709    ///
710    /// The middleware uses the Tower ecosystem's `Layer` pattern. When an RPC server is started,
711    /// the configured middleware stack is applied to create a layered service that processes
712    /// requests in the order the layers were added.
713    ///
714    /// # Examples
715    ///
716    /// ```ignore
717    /// use reth_rpc_builder::{RpcServiceBuilder, RpcRequestMetrics};
718    /// use tower::Layer;
719    ///
720    /// // Simple example with metrics
721    /// let metrics_layer = RpcRequestMetrics::new(metrics_recorder);
722    /// let with_metrics = rpc_addons.with_rpc_middleware(
723    ///     RpcServiceBuilder::new().layer(metrics_layer)
724    /// );
725    ///
726    /// // Composing multiple middleware layers
727    /// let middleware_stack = RpcServiceBuilder::new()
728    ///     .layer(rate_limit_layer)
729    ///     .layer(logging_layer)
730    ///     .layer(metrics_layer);
731    /// let with_full_stack = rpc_addons.with_rpc_middleware(middleware_stack);
732    /// ```
733    ///
734    /// # Notes
735    ///
736    /// - Middleware is applied to the RPC service layer, not the HTTP transport layer
737    /// - The default middleware is `Identity` (no-op), which passes through requests unchanged
738    /// - Middleware layers are applied in the order they are added via `.layer()`
739    pub fn with_rpc_middleware<T>(
740        self,
741        rpc_middleware: T,
742    ) -> RpcAddOns<Node, EthB, PVB, EB, EVB, T, AuthHttpMiddleware> {
743        let Self {
744            hooks,
745            eth_api_builder,
746            payload_validator_builder,
747            engine_api_builder,
748            engine_validator_builder,
749            auth_http_middleware,
750            tokio_runtime,
751            ..
752        } = self;
753        RpcAddOns {
754            hooks,
755            eth_api_builder,
756            payload_validator_builder,
757            engine_api_builder,
758            engine_validator_builder,
759            rpc_middleware,
760            auth_http_middleware,
761            tokio_runtime,
762        }
763    }
764
765    /// Configures the HTTP transport middleware for the auth / Engine API server.
766    ///
767    /// This middleware is applied after JWT authentication and before JSON-RPC parsing,
768    /// giving access to the raw HTTP request (headers, body, etc.).
769    pub fn with_auth_http_middleware<T>(
770        self,
771        auth_http_middleware: T,
772    ) -> RpcAddOns<Node, EthB, PVB, EB, EVB, RpcMiddleware, T> {
773        let Self {
774            hooks,
775            eth_api_builder,
776            payload_validator_builder,
777            engine_api_builder,
778            engine_validator_builder,
779            rpc_middleware,
780            tokio_runtime,
781            ..
782        } = self;
783        RpcAddOns {
784            hooks,
785            eth_api_builder,
786            payload_validator_builder,
787            engine_api_builder,
788            engine_validator_builder,
789            rpc_middleware,
790            auth_http_middleware,
791            tokio_runtime,
792        }
793    }
794
795    /// Stacks an additional HTTP transport middleware layer for the auth / Engine API server.
796    pub fn layer_auth_http_middleware<T>(
797        self,
798        layer: T,
799    ) -> RpcAddOns<Node, EthB, PVB, EB, EVB, RpcMiddleware, Stack<AuthHttpMiddleware, T>> {
800        let Self {
801            hooks,
802            eth_api_builder,
803            payload_validator_builder,
804            engine_api_builder,
805            engine_validator_builder,
806            rpc_middleware,
807            auth_http_middleware,
808            tokio_runtime,
809        } = self;
810        let auth_http_middleware = Stack::new(auth_http_middleware, layer);
811        RpcAddOns {
812            hooks,
813            eth_api_builder,
814            payload_validator_builder,
815            engine_api_builder,
816            engine_validator_builder,
817            rpc_middleware,
818            auth_http_middleware,
819            tokio_runtime,
820        }
821    }
822
823    /// Maps the existing auth HTTP middleware, preserving its configuration.
824    pub fn map_auth_http_middleware<T>(
825        self,
826        f: impl FnOnce(AuthHttpMiddleware) -> T,
827    ) -> RpcAddOns<Node, EthB, PVB, EB, EVB, RpcMiddleware, T> {
828        let Self {
829            hooks,
830            eth_api_builder,
831            payload_validator_builder,
832            engine_api_builder,
833            engine_validator_builder,
834            rpc_middleware,
835            auth_http_middleware,
836            tokio_runtime,
837        } = self;
838        RpcAddOns {
839            hooks,
840            eth_api_builder,
841            payload_validator_builder,
842            engine_api_builder,
843            engine_validator_builder,
844            rpc_middleware,
845            auth_http_middleware: f(auth_http_middleware),
846            tokio_runtime,
847        }
848    }
849
850    /// Conditionally stacks an HTTP transport middleware layer for the auth / Engine API server.
851    #[expect(clippy::type_complexity)]
852    pub fn option_layer_auth_http_middleware<T>(
853        self,
854        layer: Option<T>,
855    ) -> RpcAddOns<
856        Node,
857        EthB,
858        PVB,
859        EB,
860        EVB,
861        RpcMiddleware,
862        Stack<AuthHttpMiddleware, Either<T, Identity>>,
863    > {
864        let layer = layer.map(Either::Left).unwrap_or(Either::Right(Identity::new()));
865        self.layer_auth_http_middleware(layer)
866    }
867
868    /// Sets the tokio runtime for the RPC servers.
869    ///
870    /// Caution: This runtime must not be created from within asynchronous context.
871    pub fn with_tokio_runtime(self, tokio_runtime: Option<tokio::runtime::Handle>) -> Self {
872        let Self {
873            hooks,
874            eth_api_builder,
875            payload_validator_builder,
876            engine_validator_builder,
877            engine_api_builder,
878            rpc_middleware,
879            auth_http_middleware,
880            ..
881        } = self;
882        Self {
883            hooks,
884            eth_api_builder,
885            payload_validator_builder,
886            engine_validator_builder,
887            engine_api_builder,
888            rpc_middleware,
889            auth_http_middleware,
890            tokio_runtime,
891        }
892    }
893
894    /// Add a new layer `T` to the configured [`RpcServiceBuilder`].
895    pub fn layer_rpc_middleware<T>(
896        self,
897        layer: T,
898    ) -> RpcAddOns<Node, EthB, PVB, EB, EVB, Stack<RpcMiddleware, T>, AuthHttpMiddleware> {
899        let Self {
900            hooks,
901            eth_api_builder,
902            payload_validator_builder,
903            engine_api_builder,
904            engine_validator_builder,
905            rpc_middleware,
906            auth_http_middleware,
907            tokio_runtime,
908        } = self;
909        let rpc_middleware = Stack::new(rpc_middleware, layer);
910        RpcAddOns {
911            hooks,
912            eth_api_builder,
913            payload_validator_builder,
914            engine_api_builder,
915            engine_validator_builder,
916            rpc_middleware,
917            auth_http_middleware,
918            tokio_runtime,
919        }
920    }
921
922    /// Optionally adds a new layer `T` to the configured [`RpcServiceBuilder`].
923    #[expect(clippy::type_complexity)]
924    pub fn option_layer_rpc_middleware<T>(
925        self,
926        layer: Option<T>,
927    ) -> RpcAddOns<
928        Node,
929        EthB,
930        PVB,
931        EB,
932        EVB,
933        Stack<RpcMiddleware, Either<T, Identity>>,
934        AuthHttpMiddleware,
935    > {
936        let layer = layer.map(Either::Left).unwrap_or(Either::Right(Identity::new()));
937        self.layer_rpc_middleware(layer)
938    }
939
940    /// Sets the hook that is run once the rpc server is started.
941    pub fn on_rpc_started<F>(mut self, hook: F) -> Self
942    where
943        F: FnOnce(RpcContext<'_, Node, EthB::EthApi>, RethRpcServerHandles) -> eyre::Result<()>
944            + Send
945            + 'static,
946    {
947        self.hooks.set_on_rpc_started(hook);
948        self
949    }
950
951    /// Sets the hook that is run to configure the rpc modules.
952    pub fn extend_rpc_modules<F>(mut self, hook: F) -> Self
953    where
954        F: FnOnce(RpcContext<'_, Node, EthB::EthApi>) -> eyre::Result<()> + Send + 'static,
955    {
956        self.hooks.set_extend_rpc_modules(hook);
957        self
958    }
959}
960
961impl<Node, EthB, EV, EB, Engine> Default
962    for RpcAddOns<Node, EthB, EV, EB, Engine, Identity, Identity>
963where
964    Node: FullNodeComponents,
965    EthB: EthApiBuilder<Node>,
966    EV: Default,
967    EB: Default,
968    Engine: Default,
969{
970    fn default() -> Self {
971        Self::new(
972            EthB::default(),
973            EV::default(),
974            EB::default(),
975            Engine::default(),
976            Default::default(),
977            Identity::new(),
978        )
979    }
980}
981
982impl<N, EthB, PVB, EB, EVB, RpcMiddleware, AuthHttpMiddleware>
983    RpcAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware, AuthHttpMiddleware>
984where
985    N: FullNodeComponents,
986    N::Provider: ChainSpecProvider<ChainSpec: EthereumHardforks>,
987    EthB: EthApiBuilder<N>,
988    EB: EngineApiBuilder<N>,
989    EVB: EngineValidatorBuilder<N>,
990    RpcMiddleware: RethRpcMiddleware,
991    AuthHttpMiddleware: RethAuthHttpMiddleware<Identity>,
992{
993    /// Launches only the regular RPC server (HTTP/WS/IPC), without the authenticated Engine API
994    /// server.
995    ///
996    /// This is useful when you only need the regular RPC functionality and want to avoid
997    /// starting the auth server.
998    pub async fn launch_rpc_server<F>(
999        self,
1000        ctx: AddOnsContext<'_, N>,
1001        ext: F,
1002    ) -> eyre::Result<RpcServerOnlyHandle<N, EthB::EthApi>>
1003    where
1004        F: FnOnce(RpcModuleContainer<'_, N, EthB::EthApi>) -> eyre::Result<()>,
1005    {
1006        let rpc_middleware = self.rpc_middleware.clone();
1007        let tokio_runtime = self.tokio_runtime.clone();
1008        let setup_ctx = self.setup_rpc_components(ctx, ext).await?;
1009        let RpcSetupContext {
1010            node,
1011            config,
1012            mut modules,
1013            mut auth_module,
1014            auth_config: _,
1015            mut registry,
1016            on_rpc_started,
1017            engine_events,
1018            engine_handle,
1019        } = setup_ctx;
1020
1021        let server_config = config
1022            .rpc
1023            .rpc_server_config()
1024            .set_rpc_middleware(rpc_middleware)
1025            .with_tokio_runtime(tokio_runtime);
1026        let rpc_server_handle = Self::launch_rpc_server_internal(server_config, &modules).await?;
1027
1028        let handles =
1029            RethRpcServerHandles { rpc: rpc_server_handle.clone(), auth: AuthServerHandle::noop() };
1030        Self::finalize_rpc_setup(
1031            &mut registry,
1032            &mut modules,
1033            &mut auth_module,
1034            &node,
1035            config,
1036            on_rpc_started,
1037            handles,
1038        )?;
1039
1040        Ok(RpcServerOnlyHandle {
1041            rpc_server_handle,
1042            rpc_registry: registry,
1043            engine_events,
1044            engine_handle,
1045        })
1046    }
1047
1048    /// Launches the RPC servers with the given context and an additional hook for extending
1049    /// modules. Whether the auth server is launched depends on the CLI configuration.
1050    pub async fn launch_add_ons_with<F>(
1051        self,
1052        ctx: AddOnsContext<'_, N>,
1053        ext: F,
1054    ) -> eyre::Result<RpcHandle<N, EthB::EthApi>>
1055    where
1056        F: FnOnce(RpcModuleContainer<'_, N, EthB::EthApi>) -> eyre::Result<()>,
1057    {
1058        // Check CLI config to determine if auth server should be disabled
1059        let disable_auth = ctx.config.rpc.disable_auth_server;
1060        self.launch_add_ons_with_opt_engine(ctx, ext, disable_auth).await
1061    }
1062
1063    /// Launches the RPC servers with the given context and an additional hook for extending
1064    /// modules. Optionally disables the auth server based on the `disable_auth` parameter.
1065    ///
1066    /// When `disable_auth` is true, the auth server will not be started and a noop handle
1067    /// will be used instead.
1068    pub async fn launch_add_ons_with_opt_engine<F>(
1069        self,
1070        ctx: AddOnsContext<'_, N>,
1071        ext: F,
1072        disable_auth: bool,
1073    ) -> eyre::Result<RpcHandle<N, EthB::EthApi>>
1074    where
1075        F: FnOnce(RpcModuleContainer<'_, N, EthB::EthApi>) -> eyre::Result<()>,
1076    {
1077        let rpc_middleware = self.rpc_middleware.clone();
1078        let auth_http_middleware = self.auth_http_middleware.clone();
1079        let tokio_runtime = self.tokio_runtime.clone();
1080        let setup_ctx = self.setup_rpc_components(ctx, ext).await?;
1081        let RpcSetupContext {
1082            node,
1083            config,
1084            mut modules,
1085            mut auth_module,
1086            auth_config,
1087            mut registry,
1088            on_rpc_started,
1089            engine_events,
1090            engine_handle,
1091        } = setup_ctx;
1092
1093        let server_config = config
1094            .rpc
1095            .rpc_server_config()
1096            .set_rpc_middleware(rpc_middleware)
1097            .with_tokio_runtime(tokio_runtime);
1098
1099        let auth_config = auth_config.with_http_middleware(auth_http_middleware);
1100
1101        let (rpc, auth) = if disable_auth {
1102            // Only launch the RPC server, use a noop auth handle
1103            let rpc = Self::launch_rpc_server_internal(server_config, &modules).await?;
1104            (rpc, AuthServerHandle::noop())
1105        } else {
1106            let auth_module_clone = auth_module.clone();
1107            // launch servers concurrently
1108            let (rpc, auth) = futures::future::try_join(
1109                Self::launch_rpc_server_internal(server_config, &modules),
1110                Self::launch_auth_server_internal(auth_config.start(auth_module_clone)),
1111            )
1112            .await?;
1113            (rpc, auth)
1114        };
1115
1116        let handles = RethRpcServerHandles { rpc, auth };
1117
1118        Self::finalize_rpc_setup(
1119            &mut registry,
1120            &mut modules,
1121            &mut auth_module,
1122            &node,
1123            config,
1124            on_rpc_started,
1125            handles.clone(),
1126        )?;
1127
1128        Ok(RpcHandle {
1129            rpc_server_handles: handles,
1130            rpc_registry: registry,
1131            engine_events,
1132            beacon_engine_handle: engine_handle,
1133            engine_shutdown: EngineShutdown::default(),
1134        })
1135    }
1136
1137    /// Common setup for RPC server initialization
1138    async fn setup_rpc_components<'a, F>(
1139        self,
1140        ctx: AddOnsContext<'a, N>,
1141        ext: F,
1142    ) -> eyre::Result<RpcSetupContext<'a, N, EthB::EthApi>>
1143    where
1144        F: FnOnce(RpcModuleContainer<'_, N, EthB::EthApi>) -> eyre::Result<()>,
1145    {
1146        let Self { eth_api_builder, engine_api_builder, hooks, .. } = self;
1147
1148        let engine_api = engine_api_builder.build_engine_api(&ctx).await?;
1149        let AddOnsContext {
1150            node,
1151            config,
1152            beacon_engine_handle,
1153            jwt_secret,
1154            engine_events,
1155            sender_recovery_cache,
1156        } = ctx;
1157
1158        info!(target: "reth::cli", "Engine API handler initialized");
1159
1160        let cache = EthStateCache::spawn_with(
1161            node.provider().clone(),
1162            config.rpc.eth_config().cache,
1163            node.task_executor().clone(),
1164        );
1165
1166        let new_canonical_blocks = node.provider().canonical_state_stream();
1167        let c = cache.clone();
1168        node.task_executor().spawn_critical_task("cache canonical blocks task", async move {
1169            cache_new_blocks_task(c, new_canonical_blocks).await;
1170        });
1171
1172        let prewarm_bals = config
1173            .rpc
1174            .eth_config()
1175            .cache
1176            .prewarm_bals
1177            .map(|count| (count, node.provider().canonical_state_stream()));
1178
1179        let eth_config = config.rpc.eth_config().max_batch_size(config.txpool.max_batch_size());
1180        let ctx = EthApiCtx {
1181            components: &node,
1182            config: eth_config,
1183            cache,
1184            engine_handle: beacon_engine_handle.clone(),
1185            sender_recovery_cache,
1186        };
1187        let eth_api = eth_api_builder.build_eth_api(ctx).await?;
1188
1189        if let Some((count, events)) = prewarm_bals {
1190            node.task_executor().spawn_task(prewarm_new_block_bals_task(
1191                eth_api.clone(),
1192                events,
1193                count,
1194            ));
1195        }
1196
1197        let auth_config = config.rpc.auth_server_config(jwt_secret)?;
1198        let module_config = config.rpc.transport_rpc_module_config();
1199        debug!(target: "reth::cli", http=?module_config.http(), ws=?module_config.ws(), "Using RPC module config");
1200
1201        let (mut modules, mut auth_module, registry) = RpcModuleBuilder::default()
1202            .with_provider(node.provider().clone())
1203            .with_pool(node.pool().clone())
1204            .with_network(node.network().clone())
1205            .with_executor(node.task_executor().clone())
1206            .with_evm_config(node.evm_config().clone())
1207            .with_consensus(node.consensus().clone())
1208            .build_with_auth_server(
1209                module_config,
1210                engine_api,
1211                eth_api,
1212                engine_events.clone(),
1213                beacon_engine_handle.clone(),
1214            );
1215
1216        // in dev mode we generate 20 random dev-signer accounts
1217        if config.dev.dev {
1218            let signers = DevSigner::from_mnemonic(config.dev.dev_mnemonic.as_str(), 20);
1219            registry.eth_api().signers().write().extend(signers);
1220        }
1221
1222        let mut registry = RpcRegistry { registry };
1223        let ctx = RpcContext {
1224            node: node.clone(),
1225            config,
1226            registry: &mut registry,
1227            modules: &mut modules,
1228            auth_module: &mut auth_module,
1229        };
1230
1231        let RpcHooks { on_rpc_started, extend_rpc_modules } = hooks;
1232
1233        ext(RpcModuleContainer {
1234            modules: ctx.modules,
1235            auth_module: ctx.auth_module,
1236            registry: ctx.registry,
1237        })?;
1238        extend_rpc_modules.extend_rpc_modules(ctx)?;
1239
1240        Ok(RpcSetupContext {
1241            node,
1242            config,
1243            modules,
1244            auth_module,
1245            auth_config,
1246            registry,
1247            on_rpc_started,
1248            engine_events,
1249            engine_handle: beacon_engine_handle,
1250        })
1251    }
1252
1253    /// Helper to launch the RPC server
1254    async fn launch_rpc_server_internal<M>(
1255        server_config: RpcServerConfig<M>,
1256        modules: &TransportRpcModules,
1257    ) -> eyre::Result<RpcServerHandle>
1258    where
1259        M: RethRpcMiddleware,
1260    {
1261        let handle = server_config.start(modules).await?;
1262
1263        if let Some(path) = handle.ipc_endpoint() {
1264            info!(target: "reth::cli", %path, "RPC IPC server started");
1265        }
1266        if let Some(addr) = handle.http_local_addr() {
1267            info!(target: "reth::cli", url=%addr, "RPC HTTP server started");
1268        }
1269        if let Some(addr) = handle.ws_local_addr() {
1270            info!(target: "reth::cli", url=%addr, "RPC WS server started");
1271        }
1272
1273        Ok(handle)
1274    }
1275
1276    /// Helper to launch the auth server
1277    async fn launch_auth_server_internal(
1278        start_fut: impl Future<Output = Result<AuthServerHandle, reth_rpc_builder::error::RpcError>>,
1279    ) -> eyre::Result<AuthServerHandle> {
1280        start_fut
1281            .await
1282            .map_err(Into::into)
1283            .inspect(|handle| {
1284                let addr = handle.local_addr();
1285                if let Some(ipc_endpoint) = handle.ipc_endpoint() {
1286                    info!(target: "reth::cli", url=%addr, ipc_endpoint=%ipc_endpoint, "RPC auth server started");
1287                } else {
1288                    info!(target: "reth::cli", url=%addr, "RPC auth server started");
1289                }
1290            })
1291    }
1292
1293    /// Helper to finalize RPC setup by creating context and calling hooks
1294    fn finalize_rpc_setup(
1295        registry: &mut RpcRegistry<N, EthB::EthApi>,
1296        modules: &mut TransportRpcModules,
1297        auth_module: &mut AuthRpcModule,
1298        node: &N,
1299        config: &NodeConfig<<N::Types as NodeTypes>::ChainSpec>,
1300        on_rpc_started: Box<dyn OnRpcStarted<N, EthB::EthApi>>,
1301        handles: RethRpcServerHandles,
1302    ) -> eyre::Result<()> {
1303        let ctx = RpcContext { node: node.clone(), config, registry, modules, auth_module };
1304
1305        on_rpc_started.on_rpc_started(ctx, handles)?;
1306        Ok(())
1307    }
1308}
1309
1310impl<N, EthB, PVB, EB, EVB, RpcMiddleware, AuthHttpMiddleware> NodeAddOns<N>
1311    for RpcAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware, AuthHttpMiddleware>
1312where
1313    N: FullNodeComponents,
1314    <N as FullNodeTypes>::Provider: ChainSpecProvider<ChainSpec: EthereumHardforks>,
1315    EthB: EthApiBuilder<N>,
1316    PVB: PayloadValidatorBuilder<N>,
1317    EB: EngineApiBuilder<N>,
1318    EVB: EngineValidatorBuilder<N>,
1319    RpcMiddleware: RethRpcMiddleware,
1320    AuthHttpMiddleware: RethAuthHttpMiddleware<Identity>,
1321{
1322    type Handle = RpcHandle<N, EthB::EthApi>;
1323
1324    async fn launch_add_ons(self, ctx: AddOnsContext<'_, N>) -> eyre::Result<Self::Handle> {
1325        self.launch_add_ons_with(ctx, |_| Ok(())).await
1326    }
1327}
1328
1329/// Helper trait implemented for add-ons producing [`RpcHandle`]. Used by common node launcher
1330/// implementations.
1331pub trait RethRpcAddOns<N: FullNodeComponents>:
1332    NodeAddOns<N, Handle = RpcHandle<N, Self::EthApi>>
1333{
1334    /// eth API implementation.
1335    type EthApi: EthApiTypes;
1336
1337    /// Returns a mutable reference to RPC hooks.
1338    fn hooks_mut(&mut self) -> &mut RpcHooks<N, Self::EthApi>;
1339}
1340
1341impl<N: FullNodeComponents, EthB, EV, EB, Engine, RpcMiddleware, AuthHttpMiddleware>
1342    RethRpcAddOns<N> for RpcAddOns<N, EthB, EV, EB, Engine, RpcMiddleware, AuthHttpMiddleware>
1343where
1344    Self: NodeAddOns<N, Handle = RpcHandle<N, EthB::EthApi>>,
1345    EthB: EthApiBuilder<N>,
1346{
1347    type EthApi = EthB::EthApi;
1348
1349    fn hooks_mut(&mut self) -> &mut RpcHooks<N, Self::EthApi> {
1350        &mut self.hooks
1351    }
1352}
1353
1354/// Helper type alias for [`RpcNodeCoreAdapter`] with components from the given
1355/// [`FullNodeComponents`].
1356pub type RpcNodeCoreFor<N> = RpcNodeCoreAdapter<
1357    <N as FullNodeTypes>::Provider,
1358    <N as FullNodeComponents>::Pool,
1359    <N as FullNodeComponents>::Network,
1360    <N as FullNodeComponents>::Evm,
1361>;
1362
1363/// Helper type alias for [`RpcConverter`] with components from the given [`FullNodeComponents`].
1364pub type EthRpcConverterFor<N, NetworkT = Ethereum> = RpcConverter<
1365    NetworkT,
1366    <N as FullNodeComponents>::Evm,
1367    EthReceiptConverter<<<N as FullNodeTypes>::Provider as ChainSpecProvider>::ChainSpec>,
1368>;
1369
1370/// Helper type alias for [`EthApi`] with components from the given [`FullNodeComponents`].
1371pub type EthApiFor<N, NetworkT = Ethereum> =
1372    EthApi<RpcNodeCoreFor<N>, EthRpcConverterFor<N, NetworkT>>;
1373
1374/// Helper type alias for [`EthApiBuilder`](reth_rpc::EthApiBuilder) with components from the given
1375/// [`FullNodeComponents`].
1376pub type EthApiBuilderFor<N, NetworkT = Ethereum> =
1377    reth_rpc::EthApiBuilder<RpcNodeCoreFor<N>, EthRpcConverterFor<N, NetworkT>>;
1378
1379/// `EthApiCtx` struct
1380/// This struct is used to pass the necessary context to the `EthApiBuilder` to build the `EthApi`.
1381#[derive(Debug)]
1382pub struct EthApiCtx<'a, N: FullNodeTypes> {
1383    /// Reference to the node components
1384    pub components: &'a N,
1385    /// Eth API configuration
1386    pub config: EthConfig,
1387    /// Cache for eth state
1388    pub cache: EthStateCache<PrimitivesTy<N::Types>>,
1389    /// Cache of recovered transaction senders shared by node components, if enabled.
1390    pub sender_recovery_cache: Option<reth_evm::SenderRecoveryCache>,
1391    /// Handle to the beacon consensus engine
1392    pub engine_handle: ConsensusEngineHandle<<N::Types as NodeTypes>::Payload>,
1393}
1394
1395impl<'a, N: FullNodeComponents<Types: NodeTypes<ChainSpec: Hardforks + EthereumHardforks>>>
1396    EthApiCtx<'a, N>
1397{
1398    /// Provides a [`EthApiBuilder`] with preconfigured config and components.
1399    pub fn eth_api_builder(self) -> EthApiBuilderFor<N> {
1400        reth_rpc::EthApiBuilder::new(
1401            self.components.provider().clone(),
1402            self.components.pool().clone(),
1403            self.components.network().clone(),
1404            self.components.evm_config().clone(),
1405        )
1406        .eth_cache(self.cache)
1407        .sender_recovery_cache(self.sender_recovery_cache)
1408        .eth_state_cache_config(self.config.cache)
1409        .task_spawner(self.components.task_executor().clone())
1410        .gas_cap(self.config.rpc_gas_cap.into())
1411        .max_simulate_blocks(self.config.rpc_max_simulate_blocks)
1412        .compute_state_root_for_eth_simulate(self.config.compute_state_root_for_eth_simulate)
1413        .eth_proof_window(self.config.eth_proof_window)
1414        .fee_history_cache_config(self.config.fee_history_cache)
1415        .proof_permits(self.config.proof_permits)
1416        .gas_oracle_config(self.config.gas_oracle)
1417        .max_batch_size(self.config.max_batch_size)
1418        .max_blocking_io_requests(self.config.max_blocking_io_requests)
1419        .pending_block_kind(self.config.pending_block_kind)
1420        .raw_tx_forwarder(self.config.raw_tx_forwarder)
1421        .evm_memory_limit(self.config.rpc_evm_memory_limit)
1422        .force_blob_sidecar_upcasting(self.config.force_blob_sidecar_upcasting)
1423    }
1424}
1425
1426/// A `EthApi` that knows how to build `eth` namespace API from [`FullNodeComponents`].
1427pub trait EthApiBuilder<N: FullNodeComponents>: Default + Send + 'static {
1428    /// The Ethapi implementation this builder will build.
1429    type EthApi: FullEthApiServer<Provider = N::Provider, Pool = N::Pool>;
1430
1431    /// Builds the [`EthApiServer`](reth_rpc_api::eth::EthApiServer) from the given context.
1432    fn build_eth_api(
1433        self,
1434        ctx: EthApiCtx<'_, N>,
1435    ) -> impl Future<Output = eyre::Result<Self::EthApi>> + Send;
1436}
1437
1438/// Helper trait that provides the validator builder for the engine API
1439pub trait EngineValidatorAddOn<Node: FullNodeComponents>: Send {
1440    /// The validator builder type to use.
1441    type ValidatorBuilder: EngineValidatorBuilder<Node>;
1442
1443    /// Returns the validator builder.
1444    fn engine_validator_builder(&self) -> Self::ValidatorBuilder;
1445}
1446
1447impl<N, EthB, PVB, EB, EVB, RpcMiddleware, AuthHttpMiddleware> EngineValidatorAddOn<N>
1448    for RpcAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware, AuthHttpMiddleware>
1449where
1450    N: FullNodeComponents,
1451    EthB: EthApiBuilder<N>,
1452    PVB: Send,
1453    EB: EngineApiBuilder<N>,
1454    EVB: EngineValidatorBuilder<N>,
1455    RpcMiddleware: Send,
1456    AuthHttpMiddleware: Send,
1457{
1458    type ValidatorBuilder = EVB;
1459
1460    fn engine_validator_builder(&self) -> Self::ValidatorBuilder {
1461        self.engine_validator_builder.clone()
1462    }
1463}
1464
1465/// Builder for engine API RPC module.
1466///
1467/// This builder type is responsible for providing an instance of [`IntoEngineApiRpcModule`], which
1468/// is effectively a helper trait that provides the type erased [`jsonrpsee::RpcModule`] instance
1469/// that contains the method handlers for the engine API. See [`EngineApi`] for an implementation of
1470/// [`IntoEngineApiRpcModule`].
1471pub trait EngineApiBuilder<Node: FullNodeComponents>: Send + Sync {
1472    /// The engine API RPC module. Only required to be convertible to an [`jsonrpsee::RpcModule`].
1473    type EngineApi: IntoEngineApiRpcModule + Send + Sync;
1474
1475    /// Builds the engine API instance given the provided [`AddOnsContext`].
1476    ///
1477    /// [`Self::EngineApi`] will be converted into the method handlers of the authenticated RPC
1478    /// server (engine API).
1479    fn build_engine_api(
1480        self,
1481        ctx: &AddOnsContext<'_, Node>,
1482    ) -> impl Future<Output = eyre::Result<Self::EngineApi>> + Send;
1483}
1484
1485/// Builder trait for creating payload validators specifically for the Engine API.
1486///
1487/// This trait is responsible for building validators that the Engine API will use
1488/// to validate payloads.
1489pub trait PayloadValidatorBuilder<Node: FullNodeComponents>: Send + Sync + Clone {
1490    /// The validator type that will be used by the Engine API.
1491    type Validator: PayloadValidator<<Node::Types as NodeTypes>::Payload>;
1492
1493    /// Builds the engine API validator.
1494    ///
1495    /// Returns a validator that validates engine API version-specific fields and payload
1496    /// attributes.
1497    fn build(
1498        self,
1499        ctx: &AddOnsContext<'_, Node>,
1500    ) -> impl Future<Output = eyre::Result<Self::Validator>> + Send;
1501}
1502
1503/// Builder trait for creating engine validators for the consensus engine.
1504///
1505/// This trait is responsible for building validators that the consensus engine will use
1506/// for block execution, state validation, and fork handling.
1507pub trait EngineValidatorBuilder<Node: FullNodeComponents>: Send + Sync + Clone {
1508    /// The tree validator type that will be used by the consensus engine.
1509    type EngineValidator: EngineValidator<<Node::Types as NodeTypes>::Payload, <Node::Types as NodeTypes>::Primitives>
1510        + WaitForCaches;
1511
1512    /// Builds the tree validator for the consensus engine.
1513    ///
1514    /// Returns a validator that handles block execution, state validation, and fork handling.
1515    fn build_tree_validator(
1516        self,
1517        ctx: &AddOnsContext<'_, Node>,
1518        tree_config: TreeConfig,
1519        overlay_manager: OverlayManager<PrimitivesTy<Node::Types>>,
1520    ) -> impl Future<Output = eyre::Result<Self::EngineValidator>> + Send;
1521}
1522
1523/// Basic implementation of [`EngineValidatorBuilder`].
1524///
1525/// This builder creates a [`BasicEngineValidator`] using the provided payload validator builder.
1526#[derive(Debug, Clone)]
1527pub struct BasicEngineValidatorBuilder<EV> {
1528    /// The payload validator builder used to create the engine validator.
1529    payload_validator_builder: EV,
1530}
1531
1532impl<EV> BasicEngineValidatorBuilder<EV> {
1533    /// Creates a new instance with the given payload validator builder.
1534    pub const fn new(payload_validator_builder: EV) -> Self {
1535        Self { payload_validator_builder }
1536    }
1537}
1538
1539impl<EV> Default for BasicEngineValidatorBuilder<EV>
1540where
1541    EV: Default,
1542{
1543    fn default() -> Self {
1544        Self::new(EV::default())
1545    }
1546}
1547
1548impl<Node, EV> EngineValidatorBuilder<Node> for BasicEngineValidatorBuilder<EV>
1549where
1550    Node: FullNodeComponents<
1551        Evm: ConfigureEngineEvm<
1552            <<Node::Types as NodeTypes>::Payload as PayloadTypes>::ExecutionData,
1553        >,
1554    >,
1555    EV: PayloadValidatorBuilder<Node>,
1556    EV::Validator: reth_engine_primitives::PayloadValidator<
1557            <Node::Types as NodeTypes>::Payload,
1558            Block = BlockTy<Node::Types>,
1559        > + Clone,
1560{
1561    type EngineValidator = BasicEngineValidator<Node::Provider, Node::Evm, EV::Validator>;
1562
1563    async fn build_tree_validator(
1564        self,
1565        ctx: &AddOnsContext<'_, Node>,
1566        tree_config: TreeConfig,
1567        overlay_manager: OverlayManager<PrimitivesTy<Node::Types>>,
1568    ) -> eyre::Result<Self::EngineValidator> {
1569        let validator = self.payload_validator_builder.build(ctx).await?;
1570        let data_dir = ctx.config.datadir.clone().resolve_datadir(ctx.config.chain.chain());
1571        let invalid_block_hook = ctx.create_invalid_block_hook(&data_dir).await?;
1572
1573        let txpool_prewarming = tree_config.txpool_prewarming();
1574        let mut validator = BasicEngineValidator::new(
1575            ctx.node.provider().clone(),
1576            std::sync::Arc::new(ctx.node.consensus().clone()),
1577            ctx.node.evm_config().clone(),
1578            validator,
1579            tree_config,
1580            invalid_block_hook,
1581            overlay_manager,
1582            ctx.node.task_executor().clone(),
1583        );
1584
1585        if txpool_prewarming {
1586            validator = validator
1587                .with_txpool_prewarming(txpool_prewarm::Source::new(ctx.node.pool().clone()));
1588        }
1589
1590        Ok(validator)
1591    }
1592}
1593
1594/// Builder for basic [`EngineApi`] implementation.
1595///
1596/// This provides a basic default implementation for opstack and ethereum engine API via
1597/// [`EngineTypes`] and uses the general purpose [`EngineApi`] implementation as the builder's
1598/// output.
1599#[derive(Debug, Default)]
1600pub struct BasicEngineApiBuilder<PVB> {
1601    payload_validator_builder: PVB,
1602}
1603
1604impl<N, PVB> EngineApiBuilder<N> for BasicEngineApiBuilder<PVB>
1605where
1606    N: FullNodeComponents<
1607        Types: NodeTypes<
1608            ChainSpec: EthereumHardforks,
1609            Payload: PayloadTypes<ExecutionData = ExecutionData> + EngineTypes,
1610        >,
1611    >,
1612    PVB: PayloadValidatorBuilder<N>,
1613    PVB::Validator: EngineApiValidator<<N::Types as NodeTypes>::Payload>,
1614{
1615    type EngineApi = EngineApi<
1616        N::Provider,
1617        <N::Types as NodeTypes>::Payload,
1618        N::Pool,
1619        PVB::Validator,
1620        <N::Types as NodeTypes>::ChainSpec,
1621    >;
1622
1623    async fn build_engine_api(self, ctx: &AddOnsContext<'_, N>) -> eyre::Result<Self::EngineApi> {
1624        let Self { payload_validator_builder } = self;
1625
1626        let engine_validator = payload_validator_builder.build(ctx).await?;
1627        let client = ClientVersionV1 {
1628            code: CLIENT_CODE,
1629            name: version_metadata().name_client.to_string(),
1630            version: version_metadata().cargo_pkg_version.to_string(),
1631            commit: version_metadata().vergen_git_sha.to_string(),
1632        };
1633
1634        Ok(EngineApi::new(
1635            ctx.node.provider().clone(),
1636            ctx.config.chain.clone(),
1637            ctx.beacon_engine_handle.clone(),
1638            PayloadStore::new(ctx.node.payload_builder_handle().clone()),
1639            ctx.node.pool().clone(),
1640            ctx.node.task_executor().clone(),
1641            client,
1642            EngineCapabilities::default(),
1643            engine_validator,
1644            ctx.config.engine.accept_execution_requests_hash,
1645            ctx.node.network().clone(),
1646        ))
1647    }
1648}
1649
1650/// A noop Builder that satisfies the [`EngineApiBuilder`] trait without actually configuring an
1651/// engine API module
1652///
1653/// This is intended to be used as a workaround for reusing all the existing ethereum node launch
1654/// utilities which require an engine API.
1655#[derive(Debug, Clone, Default)]
1656#[non_exhaustive]
1657pub struct NoopEngineApiBuilder;
1658
1659impl<N: FullNodeComponents> EngineApiBuilder<N> for NoopEngineApiBuilder {
1660    type EngineApi = NoopEngineApi;
1661
1662    async fn build_engine_api(self, _ctx: &AddOnsContext<'_, N>) -> eyre::Result<Self::EngineApi> {
1663        Ok(NoopEngineApi::default())
1664    }
1665}
1666
1667/// Represents an empty Engine API [`RpcModule`].
1668///
1669/// This is only intended to be used in combination with the [`NoopEngineApiBuilder`] in order to
1670/// satisfy trait bounds in the regular ethereum launch routine that mandate an engine API instance.
1671#[derive(Debug, Clone, Default)]
1672#[non_exhaustive]
1673pub struct NoopEngineApi;
1674
1675impl IntoEngineApiRpcModule for NoopEngineApi {
1676    fn into_rpc_module(self) -> RpcModule<()> {
1677        RpcModule::new(())
1678    }
1679}
1680
1681/// Handle to trigger graceful engine shutdown.
1682///
1683/// This handle can be used to request a graceful shutdown of the engine,
1684/// which will persist all remaining in-memory blocks before terminating.
1685#[derive(Clone, Debug)]
1686pub struct EngineShutdown {
1687    /// Channel to send shutdown signal.
1688    tx: Arc<Mutex<Option<oneshot::Sender<EngineShutdownRequest>>>>,
1689}
1690
1691impl EngineShutdown {
1692    /// Creates a new [`EngineShutdown`] handle and returns the receiver.
1693    pub fn new() -> (Self, oneshot::Receiver<EngineShutdownRequest>) {
1694        let (tx, rx) = oneshot::channel();
1695        (Self { tx: Arc::new(Mutex::new(Some(tx))) }, rx)
1696    }
1697
1698    /// Requests a graceful engine shutdown.
1699    ///
1700    /// All remaining in-memory blocks will be persisted before the engine terminates.
1701    ///
1702    /// Returns a receiver that resolves when shutdown is complete.
1703    /// Returns `None` if shutdown was already triggered.
1704    pub fn shutdown(&self) -> Option<oneshot::Receiver<()>> {
1705        let mut guard = self.tx.lock();
1706        let tx = guard.take()?;
1707        let (done_tx, done_rx) = oneshot::channel();
1708        let _ = tx.send(EngineShutdownRequest { done_tx });
1709        Some(done_rx)
1710    }
1711}
1712
1713impl Default for EngineShutdown {
1714    fn default() -> Self {
1715        Self { tx: Arc::new(Mutex::new(None)) }
1716    }
1717}
1718
1719/// Request to shutdown the engine.
1720#[derive(Debug)]
1721pub struct EngineShutdownRequest {
1722    /// Channel to signal shutdown completion.
1723    pub done_tx: oneshot::Sender<()>,
1724}
1725
1726/// Prewarms recent and new canonical blocks until a block includes a BAL hash.
1727async fn prewarm_new_block_bals_task<EthApi: GetBlockAccessList, N: NodePrimitives>(
1728    eth_api: EthApi,
1729    mut events: impl Stream<Item = CanonStateNotification<N>> + Unpin,
1730    startup_blocks: usize,
1731) {
1732    let mut startup = match eth_api.recovered_block(BlockId::latest()).await {
1733        Ok(Some(head)) => {
1734            if head.block_access_list_hash().is_some() {
1735                debug!(target: "reth::cli", block_hash = ?head.hash(), "Stopping BAL prewarming: native BALs available");
1736                return;
1737            }
1738
1739            // Leave the newest BALs in the cache if the startup range exceeds its capacity.
1740            head.number().saturating_sub(startup_blocks as u64)..head.number()
1741        }
1742        Err(err) => {
1743            debug!(target: "reth::cli", %err, "Failed to load head for BAL prewarming");
1744            0..0
1745        }
1746        Ok(None) => 0..0,
1747    }
1748    .map(|number| number + 1);
1749
1750    loop {
1751        tokio::select! {
1752            // Observe new blocks (including native BAL activation) between startup replays.
1753            biased;
1754            Some(event) = events.next() => {
1755                let committed = event.committed();
1756                if let Some(block) = committed.blocks_iter().find(|block| block.block_access_list_hash().is_some()) {
1757                    debug!(target: "reth::cli", block_hash = ?block.hash(), "Stopping BAL prewarming: native BALs available");
1758                    return;
1759                }
1760
1761                for block in committed.blocks_iter() {
1762                    if let Err(err) = eth_api.get_block_access_list(block.hash().into()).await {
1763                        debug!(
1764                            target: "reth::cli",
1765                            %err,
1766                            block_hash = ?block.hash(),
1767                            "Failed to prewarm BAL for canonical block",
1768                        );
1769                    }
1770                }
1771            }
1772            Some(number) = async { startup.next() } => {
1773                if let Err(err) = eth_api.get_block_access_list(number.into()).await {
1774                    debug!(target: "reth::cli", %err, block_number = number, "Failed to prewarm BAL on startup");
1775                }
1776            }
1777            else => break,
1778        }
1779    }
1780}
1781
1782#[cfg(test)]
1783mod tests {
1784    use super::*;
1785    use alloy_consensus::{Block, BlockBody, Header};
1786    use alloy_primitives::B256;
1787    use reth_evm_ethereum::EthEvmConfig;
1788    use reth_network_api::noop::NoopNetwork;
1789    use reth_primitives_traits::RecoveredBlock;
1790    use reth_provider::{test_utils::MockEthProvider, Chain, ExecutionOutcome};
1791    use reth_rpc::EthApiBuilder;
1792    use reth_rpc_eth_types::EthStateCacheConfig;
1793    use reth_transaction_pool::noop::NoopTransactionPool;
1794
1795    #[tokio::test]
1796    async fn bal_prewarming_startup_range_and_native_stop() {
1797        for (count, native, native_event) in [
1798            (0, false, false),
1799            (2, false, false),
1800            (10, false, false),
1801            (2, true, false),
1802            (10, false, true),
1803        ] {
1804            let provider = MockEthProvider::default();
1805            let blocks: Vec<_> = (0..=3)
1806                .map(|number| {
1807                    let block = RecoveredBlock::new_unhashed(
1808                        Block {
1809                            header: Header {
1810                                number,
1811                                block_access_list_hash: (native && number == 3)
1812                                    .then_some(B256::ZERO),
1813                                ..Default::default()
1814                            },
1815                            body: BlockBody::default(),
1816                        },
1817                        Vec::new(),
1818                    );
1819                    provider.add_block(block.hash(), block.clone_block());
1820                    block
1821                })
1822                .collect();
1823            let api = EthApiBuilder::new(
1824                provider,
1825                NoopTransactionPool::default(),
1826                NoopNetwork::default(),
1827                EthEvmConfig::mainnet(),
1828            )
1829            .eth_state_cache_config(EthStateCacheConfig {
1830                prewarm_bals: Some(count),
1831                ..Default::default()
1832            })
1833            .build();
1834
1835            let chain = Chain::new(
1836                blocks.clone(),
1837                ExecutionOutcome::default().with_receipts(vec![vec![]; blocks.len()]),
1838                Default::default(),
1839            );
1840            cache_new_blocks_task(
1841                api.cache().clone(),
1842                futures::stream::iter([reth_chain_state::CanonStateNotification::Commit {
1843                    new: Arc::new(chain),
1844                }]),
1845            )
1846            .await;
1847            let events = futures::stream::iter(native_event.then(|| {
1848                let mut block = blocks.last().unwrap().clone_block();
1849                block.header.number += 1;
1850                block.header.block_access_list_hash = Some(B256::ZERO);
1851                CanonStateNotification::Commit {
1852                    new: Arc::new(Chain::<reth_chain_state::EthPrimitives>::new(
1853                        [RecoveredBlock::new_unhashed(block, Vec::new())],
1854                        Default::default(),
1855                        Default::default(),
1856                    )),
1857                }
1858            }));
1859            prewarm_new_block_bals_task(api.clone(), events, count).await;
1860            for (number, block) in blocks.iter().enumerate() {
1861                assert_eq!(
1862                    api.cache().get_bal(block.hash()).await.unwrap().is_some(),
1863                    !native &&
1864                        !native_event &&
1865                        number != 0 &&
1866                        number > 3_usize.saturating_sub(count),
1867                    "count={count}, native={native}, native_event={native_event}, block={number}",
1868                );
1869            }
1870        }
1871    }
1872}