1pub 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#[derive(Debug, Clone)]
73pub struct RethRpcServerHandles {
74 pub rpc: RpcServerHandle,
76 pub auth: AuthServerHandle,
78}
79
80pub struct RpcHooks<Node: FullNodeComponents, EthApi> {
82 pub on_rpc_started: Box<dyn OnRpcStarted<Node, EthApi>>,
84 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 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 #[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 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 #[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
155pub trait OnRpcStarted<Node: FullNodeComponents, EthApi: EthApiTypes>: Send {
157 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
194pub trait ExtendRpcModules<Node: FullNodeComponents, EthApi: EthApiTypes>: Send {
196 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#[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#[expect(missing_debug_implementations)]
266pub struct RpcModuleContainer<'a, Node: FullNodeComponents, EthApi: EthApiTypes> {
267 pub modules: &'a mut TransportRpcModules,
269 pub auth_module: &'a mut AuthRpcModule,
271 pub registry: &'a mut RpcRegistry<Node, EthApi>,
273}
274
275#[expect(missing_debug_implementations)]
283pub struct RpcContext<'a, Node: FullNodeComponents, EthApi: EthApiTypes> {
284 pub(crate) node: Node,
286
287 pub(crate) config: &'a NodeConfig<<Node::Types as NodeTypes>::ChainSpec>,
289
290 pub registry: &'a mut RpcRegistry<Node, EthApi>,
294 pub modules: &'a mut TransportRpcModules,
299 pub auth_module: &'a mut AuthRpcModule,
303}
304
305impl<Node, EthApi> RpcContext<'_, Node, EthApi>
306where
307 Node: FullNodeComponents,
308 EthApi: EthApiTypes,
309{
310 pub const fn config(&self) -> &NodeConfig<<Node::Types as NodeTypes>::ChainSpec> {
312 self.config
313 }
314
315 pub const fn node(&self) -> &Node {
319 &self.node
320 }
321
322 pub fn pool(&self) -> &Node::Pool {
324 self.node.pool()
325 }
326
327 pub fn provider(&self) -> &Node::Provider {
329 self.node.provider()
330 }
331
332 pub fn network(&self) -> &Node::Network {
334 self.node.network()
335 }
336
337 pub fn payload_builder_handle(
339 &self,
340 ) -> &PayloadBuilderHandle<<Node::Types as NodeTypes>::Payload> {
341 self.node.payload_builder_handle()
342 }
343}
344
345pub struct RpcHandle<Node: FullNodeComponents, EthApi: EthApiTypes> {
347 pub rpc_server_handles: RethRpcServerHandles,
349 pub rpc_registry: RpcRegistry<Node, EthApi>,
351 pub engine_events: EventSender<ConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>>,
356 pub beacon_engine_handle: ConsensusEngineHandle<<Node::Types as NodeTypes>::Payload>,
358 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 pub const fn rpc_server_handles(&self) -> &RethRpcServerHandles {
398 &self.rpc_server_handles
399 }
400
401 pub const fn consensus_engine_handle(
405 &self,
406 ) -> &ConsensusEngineHandle<<Node::Types as NodeTypes>::Payload> {
407 &self.beacon_engine_handle
408 }
409
410 pub const fn consensus_engine_events(
412 &self,
413 ) -> &EventSender<ConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>> {
414 &self.engine_events
415 }
416
417 pub const fn eth_api(&self) -> &EthApi {
419 self.rpc_registry.registry.eth_api()
420 }
421
422 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#[derive(Debug, Clone)]
439pub struct RpcServerOnlyHandle<Node: FullNodeComponents, EthApi: EthApiTypes> {
440 pub rpc_server_handle: RpcServerHandle,
442 pub rpc_registry: RpcRegistry<Node, EthApi>,
444 pub engine_events: EventSender<ConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>>,
446 pub engine_handle: ConsensusEngineHandle<<Node::Types as NodeTypes>::Payload>,
448}
449
450impl<Node: FullNodeComponents, EthApi: EthApiTypes> RpcServerOnlyHandle<Node, EthApi> {
451 pub const fn rpc_server_handle(&self) -> &RpcServerHandle {
453 &self.rpc_server_handle
454 }
455
456 pub const fn consensus_engine_handle(
460 &self,
461 ) -> &ConsensusEngineHandle<<Node::Types as NodeTypes>::Payload> {
462 &self.engine_handle
463 }
464
465 pub const fn consensus_engine_events(
467 &self,
468 ) -> &EventSender<ConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>> {
469 &self.engine_events
470 }
471}
472
473#[derive(Debug, Clone)]
479pub struct AuthServerOnlyHandle<Node: FullNodeComponents, EthApi: EthApiTypes> {
480 pub auth_server_handle: AuthServerHandle,
482 pub rpc_registry: RpcRegistry<Node, EthApi>,
484 pub engine_events: EventSender<ConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>>,
486 pub engine_handle: ConsensusEngineHandle<<Node::Types as NodeTypes>::Payload>,
488}
489
490impl<Node: FullNodeComponents, EthApi: EthApiTypes> AuthServerOnlyHandle<Node, EthApi> {
491 pub const fn consensus_engine_handle(
495 &self,
496 ) -> &ConsensusEngineHandle<<Node::Types as NodeTypes>::Payload> {
497 &self.engine_handle
498 }
499
500 pub const fn consensus_engine_events(
502 &self,
503 ) -> &EventSender<ConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>> {
504 &self.engine_events
505 }
506}
507
508struct 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
521pub 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 pub hooks: RpcHooks<Node, EthB::EthApi>,
542 eth_api_builder: EthB,
544 payload_validator_builder: PVB,
546 engine_api_builder: EB,
548 engine_validator_builder: EVB,
550 rpc_middleware: RpcMiddleware,
555 auth_http_middleware: AuthHttpMiddleware,
560 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 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 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 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 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 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 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 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 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 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 #[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 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 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 #[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 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 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 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 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 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 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 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 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 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 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 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 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 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
1329pub trait RethRpcAddOns<N: FullNodeComponents>:
1332 NodeAddOns<N, Handle = RpcHandle<N, Self::EthApi>>
1333{
1334 type EthApi: EthApiTypes;
1336
1337 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
1354pub 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
1363pub type EthRpcConverterFor<N, NetworkT = Ethereum> = RpcConverter<
1365 NetworkT,
1366 <N as FullNodeComponents>::Evm,
1367 EthReceiptConverter<<<N as FullNodeTypes>::Provider as ChainSpecProvider>::ChainSpec>,
1368>;
1369
1370pub type EthApiFor<N, NetworkT = Ethereum> =
1372 EthApi<RpcNodeCoreFor<N>, EthRpcConverterFor<N, NetworkT>>;
1373
1374pub type EthApiBuilderFor<N, NetworkT = Ethereum> =
1377 reth_rpc::EthApiBuilder<RpcNodeCoreFor<N>, EthRpcConverterFor<N, NetworkT>>;
1378
1379#[derive(Debug)]
1382pub struct EthApiCtx<'a, N: FullNodeTypes> {
1383 pub components: &'a N,
1385 pub config: EthConfig,
1387 pub cache: EthStateCache<PrimitivesTy<N::Types>>,
1389 pub sender_recovery_cache: Option<reth_evm::SenderRecoveryCache>,
1391 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 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
1426pub trait EthApiBuilder<N: FullNodeComponents>: Default + Send + 'static {
1428 type EthApi: FullEthApiServer<Provider = N::Provider, Pool = N::Pool>;
1430
1431 fn build_eth_api(
1433 self,
1434 ctx: EthApiCtx<'_, N>,
1435 ) -> impl Future<Output = eyre::Result<Self::EthApi>> + Send;
1436}
1437
1438pub trait EngineValidatorAddOn<Node: FullNodeComponents>: Send {
1440 type ValidatorBuilder: EngineValidatorBuilder<Node>;
1442
1443 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
1465pub trait EngineApiBuilder<Node: FullNodeComponents>: Send + Sync {
1472 type EngineApi: IntoEngineApiRpcModule + Send + Sync;
1474
1475 fn build_engine_api(
1480 self,
1481 ctx: &AddOnsContext<'_, Node>,
1482 ) -> impl Future<Output = eyre::Result<Self::EngineApi>> + Send;
1483}
1484
1485pub trait PayloadValidatorBuilder<Node: FullNodeComponents>: Send + Sync + Clone {
1490 type Validator: PayloadValidator<<Node::Types as NodeTypes>::Payload>;
1492
1493 fn build(
1498 self,
1499 ctx: &AddOnsContext<'_, Node>,
1500 ) -> impl Future<Output = eyre::Result<Self::Validator>> + Send;
1501}
1502
1503pub trait EngineValidatorBuilder<Node: FullNodeComponents>: Send + Sync + Clone {
1508 type EngineValidator: EngineValidator<<Node::Types as NodeTypes>::Payload, <Node::Types as NodeTypes>::Primitives>
1510 + WaitForCaches;
1511
1512 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#[derive(Debug, Clone)]
1527pub struct BasicEngineValidatorBuilder<EV> {
1528 payload_validator_builder: EV,
1530}
1531
1532impl<EV> BasicEngineValidatorBuilder<EV> {
1533 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#[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#[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#[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#[derive(Clone, Debug)]
1686pub struct EngineShutdown {
1687 tx: Arc<Mutex<Option<oneshot::Sender<EngineShutdownRequest>>>>,
1689}
1690
1691impl EngineShutdown {
1692 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 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#[derive(Debug)]
1721pub struct EngineShutdownRequest {
1722 pub done_tx: oneshot::Sender<()>,
1724}
1725
1726async 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 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 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}