1use crate::{BeaconConsensusEngineEvent, BeaconConsensusEngineHandle};
4use alloy_rpc_types::engine::ClientVersionV1;
5use alloy_rpc_types_engine::ExecutionData;
6use futures::TryFutureExt;
7use jsonrpsee::RpcModule;
8use reth_chain_state::CanonStateSubscriptions;
9use reth_chainspec::{ChainSpecProvider, EthereumHardforks};
10use reth_node_api::{
11 AddOnsContext, BlockTy, EngineTypes, EngineValidator, FullNodeComponents, FullNodeTypes,
12 NodeAddOns, NodeTypes, PayloadTypes, ReceiptTy,
13};
14use reth_node_core::{
15 node_config::NodeConfig,
16 version::{CARGO_PKG_VERSION, CLIENT_CODE, NAME_CLIENT, VERGEN_GIT_SHA},
17};
18use reth_payload_builder::{PayloadBuilderHandle, PayloadStore};
19use reth_rpc::eth::{EthApiTypes, FullEthApiServer};
20use reth_rpc_api::{eth::helpers::AddDevSigners, IntoEngineApiRpcModule};
21use reth_rpc_builder::{
22 auth::{AuthRpcModule, AuthServerHandle},
23 config::RethRpcServerConfig,
24 RpcModuleBuilder, RpcRegistryInner, RpcServerHandle, TransportRpcModules,
25};
26use reth_rpc_engine_api::{capabilities::EngineCapabilities, EngineApi};
27use reth_rpc_eth_types::{cache::cache_new_blocks_task, EthConfig, EthStateCache};
28use reth_tasks::TaskExecutor;
29use reth_tokio_util::EventSender;
30use reth_tracing::tracing::{debug, info};
31use std::{
32 fmt::{self, Debug},
33 future::Future,
34 ops::{Deref, DerefMut},
35};
36
37#[derive(Debug, Clone)]
41pub struct RethRpcServerHandles {
42 pub rpc: RpcServerHandle,
44 pub auth: AuthServerHandle,
46}
47
48pub struct RpcHooks<Node: FullNodeComponents, EthApi> {
50 pub on_rpc_started: Box<dyn OnRpcStarted<Node, EthApi>>,
52 pub extend_rpc_modules: Box<dyn ExtendRpcModules<Node, EthApi>>,
54}
55
56impl<Node, EthApi> Default for RpcHooks<Node, EthApi>
57where
58 Node: FullNodeComponents,
59 EthApi: EthApiTypes,
60{
61 fn default() -> Self {
62 Self { on_rpc_started: Box::<()>::default(), extend_rpc_modules: Box::<()>::default() }
63 }
64}
65
66impl<Node, EthApi> RpcHooks<Node, EthApi>
67where
68 Node: FullNodeComponents,
69 EthApi: EthApiTypes,
70{
71 pub(crate) fn set_on_rpc_started<F>(&mut self, hook: F) -> &mut Self
73 where
74 F: OnRpcStarted<Node, EthApi> + 'static,
75 {
76 self.on_rpc_started = Box::new(hook);
77 self
78 }
79
80 #[expect(unused)]
82 pub(crate) fn on_rpc_started<F>(mut self, hook: F) -> Self
83 where
84 F: OnRpcStarted<Node, EthApi> + 'static,
85 {
86 self.set_on_rpc_started(hook);
87 self
88 }
89
90 pub(crate) fn set_extend_rpc_modules<F>(&mut self, hook: F) -> &mut Self
92 where
93 F: ExtendRpcModules<Node, EthApi> + 'static,
94 {
95 self.extend_rpc_modules = Box::new(hook);
96 self
97 }
98
99 #[expect(unused)]
101 pub(crate) fn extend_rpc_modules<F>(mut self, hook: F) -> Self
102 where
103 F: ExtendRpcModules<Node, EthApi> + 'static,
104 {
105 self.set_extend_rpc_modules(hook);
106 self
107 }
108}
109
110impl<Node, EthApi> fmt::Debug for RpcHooks<Node, EthApi>
111where
112 Node: FullNodeComponents,
113 EthApi: EthApiTypes,
114{
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 f.debug_struct("RpcHooks")
117 .field("on_rpc_started", &"...")
118 .field("extend_rpc_modules", &"...")
119 .finish()
120 }
121}
122
123pub trait OnRpcStarted<Node: FullNodeComponents, EthApi: EthApiTypes>: Send {
125 fn on_rpc_started(
127 self: Box<Self>,
128 ctx: RpcContext<'_, Node, EthApi>,
129 handles: RethRpcServerHandles,
130 ) -> eyre::Result<()>;
131}
132
133impl<Node, EthApi, F> OnRpcStarted<Node, EthApi> for F
134where
135 F: FnOnce(RpcContext<'_, Node, EthApi>, RethRpcServerHandles) -> eyre::Result<()> + Send,
136 Node: FullNodeComponents,
137 EthApi: EthApiTypes,
138{
139 fn on_rpc_started(
140 self: Box<Self>,
141 ctx: RpcContext<'_, Node, EthApi>,
142 handles: RethRpcServerHandles,
143 ) -> eyre::Result<()> {
144 (*self)(ctx, handles)
145 }
146}
147
148impl<Node, EthApi> OnRpcStarted<Node, EthApi> for ()
149where
150 Node: FullNodeComponents,
151 EthApi: EthApiTypes,
152{
153 fn on_rpc_started(
154 self: Box<Self>,
155 _: RpcContext<'_, Node, EthApi>,
156 _: RethRpcServerHandles,
157 ) -> eyre::Result<()> {
158 Ok(())
159 }
160}
161
162pub trait ExtendRpcModules<Node: FullNodeComponents, EthApi: EthApiTypes>: Send {
164 fn extend_rpc_modules(self: Box<Self>, ctx: RpcContext<'_, Node, EthApi>) -> eyre::Result<()>;
166}
167
168impl<Node, EthApi, F> ExtendRpcModules<Node, EthApi> for F
169where
170 F: FnOnce(RpcContext<'_, Node, EthApi>) -> eyre::Result<()> + Send,
171 Node: FullNodeComponents,
172 EthApi: EthApiTypes,
173{
174 fn extend_rpc_modules(self: Box<Self>, ctx: RpcContext<'_, Node, EthApi>) -> eyre::Result<()> {
175 (*self)(ctx)
176 }
177}
178
179impl<Node, EthApi> ExtendRpcModules<Node, EthApi> for ()
180where
181 Node: FullNodeComponents,
182 EthApi: EthApiTypes,
183{
184 fn extend_rpc_modules(self: Box<Self>, _: RpcContext<'_, Node, EthApi>) -> eyre::Result<()> {
185 Ok(())
186 }
187}
188
189#[derive(Debug, Clone)]
191#[expect(clippy::type_complexity)]
192pub struct RpcRegistry<Node: FullNodeComponents, EthApi: EthApiTypes> {
193 pub(crate) registry: RpcRegistryInner<
194 Node::Provider,
195 Node::Pool,
196 Node::Network,
197 TaskExecutor,
198 EthApi,
199 Node::Evm,
200 Node::Consensus,
201 >,
202}
203
204impl<Node, EthApi> Deref for RpcRegistry<Node, EthApi>
205where
206 Node: FullNodeComponents,
207 EthApi: EthApiTypes,
208{
209 type Target = RpcRegistryInner<
210 Node::Provider,
211 Node::Pool,
212 Node::Network,
213 TaskExecutor,
214 EthApi,
215 Node::Evm,
216 Node::Consensus,
217 >;
218
219 fn deref(&self) -> &Self::Target {
220 &self.registry
221 }
222}
223
224impl<Node, EthApi> DerefMut for RpcRegistry<Node, EthApi>
225where
226 Node: FullNodeComponents,
227 EthApi: EthApiTypes,
228{
229 fn deref_mut(&mut self) -> &mut Self::Target {
230 &mut self.registry
231 }
232}
233
234#[expect(missing_debug_implementations)]
242pub struct RpcContext<'a, Node: FullNodeComponents, EthApi: EthApiTypes> {
243 pub(crate) node: Node,
245
246 pub(crate) config: &'a NodeConfig<<Node::Types as NodeTypes>::ChainSpec>,
248
249 pub registry: &'a mut RpcRegistry<Node, EthApi>,
253 pub modules: &'a mut TransportRpcModules,
258 pub auth_module: &'a mut AuthRpcModule,
262}
263
264impl<Node, EthApi> RpcContext<'_, Node, EthApi>
265where
266 Node: FullNodeComponents,
267 EthApi: EthApiTypes,
268{
269 pub const fn config(&self) -> &NodeConfig<<Node::Types as NodeTypes>::ChainSpec> {
271 self.config
272 }
273
274 pub const fn node(&self) -> &Node {
276 &self.node
277 }
278
279 pub fn pool(&self) -> &Node::Pool {
281 self.node.pool()
282 }
283
284 pub fn provider(&self) -> &Node::Provider {
286 self.node.provider()
287 }
288
289 pub fn network(&self) -> &Node::Network {
291 self.node.network()
292 }
293
294 pub fn payload_builder_handle(
296 &self,
297 ) -> &PayloadBuilderHandle<<Node::Types as NodeTypes>::Payload> {
298 self.node.payload_builder_handle()
299 }
300}
301
302pub struct RpcHandle<Node: FullNodeComponents, EthApi: EthApiTypes> {
304 pub rpc_server_handles: RethRpcServerHandles,
306 pub rpc_registry: RpcRegistry<Node, EthApi>,
308 pub engine_events:
313 EventSender<BeaconConsensusEngineEvent<<Node::Types as NodeTypes>::Primitives>>,
314 pub beacon_engine_handle: BeaconConsensusEngineHandle<<Node::Types as NodeTypes>::Payload>,
316}
317
318impl<Node: FullNodeComponents, EthApi: EthApiTypes> Clone for RpcHandle<Node, EthApi> {
319 fn clone(&self) -> Self {
320 Self {
321 rpc_server_handles: self.rpc_server_handles.clone(),
322 rpc_registry: self.rpc_registry.clone(),
323 engine_events: self.engine_events.clone(),
324 beacon_engine_handle: self.beacon_engine_handle.clone(),
325 }
326 }
327}
328
329impl<Node: FullNodeComponents, EthApi: EthApiTypes> Deref for RpcHandle<Node, EthApi> {
330 type Target = RpcRegistry<Node, EthApi>;
331
332 fn deref(&self) -> &Self::Target {
333 &self.rpc_registry
334 }
335}
336
337impl<Node: FullNodeComponents, EthApi: EthApiTypes> Debug for RpcHandle<Node, EthApi>
338where
339 RpcRegistry<Node, EthApi>: Debug,
340{
341 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
342 f.debug_struct("RpcHandle")
343 .field("rpc_server_handles", &self.rpc_server_handles)
344 .field("rpc_registry", &self.rpc_registry)
345 .finish()
346 }
347}
348
349pub struct RpcAddOns<
360 Node: FullNodeComponents,
361 EthB: EthApiBuilder<Node>,
362 EV,
363 EB = BasicEngineApiBuilder<EV>,
364> {
365 pub hooks: RpcHooks<Node, EthB::EthApi>,
367 eth_api_builder: EthB,
369 engine_validator_builder: EV,
371 engine_api_builder: EB,
373}
374
375impl<Node, EthB, EV, EB> Debug for RpcAddOns<Node, EthB, EV, EB>
376where
377 Node: FullNodeComponents,
378 EthB: EthApiBuilder<Node>,
379 EV: Debug,
380 EB: Debug,
381{
382 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
383 f.debug_struct("RpcAddOns")
384 .field("hooks", &self.hooks)
385 .field("eth_api_builder", &"...")
386 .field("engine_validator_builder", &self.engine_validator_builder)
387 .field("engine_api_builder", &self.engine_api_builder)
388 .finish()
389 }
390}
391
392impl<Node, EthB, EV, EB> RpcAddOns<Node, EthB, EV, EB>
393where
394 Node: FullNodeComponents,
395 EthB: EthApiBuilder<Node>,
396{
397 pub fn new(
399 eth_api_builder: EthB,
400 engine_validator_builder: EV,
401 engine_api_builder: EB,
402 ) -> Self {
403 Self {
404 hooks: RpcHooks::default(),
405 eth_api_builder,
406 engine_validator_builder,
407 engine_api_builder,
408 }
409 }
410
411 pub fn on_rpc_started<F>(mut self, hook: F) -> Self
413 where
414 F: FnOnce(RpcContext<'_, Node, EthB::EthApi>, RethRpcServerHandles) -> eyre::Result<()>
415 + Send
416 + 'static,
417 {
418 self.hooks.set_on_rpc_started(hook);
419 self
420 }
421
422 pub fn extend_rpc_modules<F>(mut self, hook: F) -> Self
424 where
425 F: FnOnce(RpcContext<'_, Node, EthB::EthApi>) -> eyre::Result<()> + Send + 'static,
426 {
427 self.hooks.set_extend_rpc_modules(hook);
428 self
429 }
430}
431
432impl<Node, EthB, EV, EB> Default for RpcAddOns<Node, EthB, EV, EB>
433where
434 Node: FullNodeComponents,
435 EthB: EthApiBuilder<Node>,
436 EV: Default,
437 EB: Default,
438{
439 fn default() -> Self {
440 Self::new(EthB::default(), EV::default(), EB::default())
441 }
442}
443
444impl<N, EthB, EV, EB> RpcAddOns<N, EthB, EV, EB>
445where
446 N: FullNodeComponents,
447 N::Provider: ChainSpecProvider<ChainSpec: EthereumHardforks>,
448 EthB: EthApiBuilder<N>,
449 EV: EngineValidatorBuilder<N>,
450 EB: EngineApiBuilder<N>,
451{
452 pub async fn launch_add_ons_with<F>(
455 self,
456 ctx: AddOnsContext<'_, N>,
457 ext: F,
458 ) -> eyre::Result<RpcHandle<N, EthB::EthApi>>
459 where
460 F: FnOnce(
461 &mut TransportRpcModules,
462 &mut AuthRpcModule,
463 &mut RpcRegistry<N, EthB::EthApi>,
464 ) -> eyre::Result<()>,
465 {
466 let Self { eth_api_builder, engine_api_builder, hooks, .. } = self;
467
468 let engine_api = engine_api_builder.build_engine_api(&ctx).await?;
469 let AddOnsContext { node, config, beacon_engine_handle, jwt_secret, engine_events } = ctx;
470
471 info!(target: "reth::cli", "Engine API handler initialized");
472
473 let cache = EthStateCache::spawn_with(
474 node.provider().clone(),
475 config.rpc.eth_config().cache,
476 node.task_executor().clone(),
477 );
478
479 let new_canonical_blocks = node.provider().canonical_state_stream();
480 let c = cache.clone();
481 node.task_executor().spawn_critical(
482 "cache canonical blocks task",
483 Box::pin(async move {
484 cache_new_blocks_task(c, new_canonical_blocks).await;
485 }),
486 );
487
488 let ctx = EthApiCtx { components: &node, config: config.rpc.eth_config(), cache };
489 let eth_api = eth_api_builder.build_eth_api(ctx).await?;
490
491 let auth_config = config.rpc.auth_server_config(jwt_secret)?;
492 let module_config = config.rpc.transport_rpc_module_config();
493 debug!(target: "reth::cli", http=?module_config.http(), ws=?module_config.ws(), "Using RPC module config");
494
495 let (mut modules, mut auth_module, registry) = RpcModuleBuilder::default()
496 .with_provider(node.provider().clone())
497 .with_pool(node.pool().clone())
498 .with_network(node.network().clone())
499 .with_executor(node.task_executor().clone())
500 .with_evm_config(node.evm_config().clone())
501 .with_consensus(node.consensus().clone())
502 .build_with_auth_server(module_config, engine_api, eth_api);
503
504 if config.dev.dev {
506 registry.eth_api().with_dev_accounts();
507 }
508
509 let mut registry = RpcRegistry { registry };
510 let ctx = RpcContext {
511 node: node.clone(),
512 config,
513 registry: &mut registry,
514 modules: &mut modules,
515 auth_module: &mut auth_module,
516 };
517
518 let RpcHooks { on_rpc_started, extend_rpc_modules } = hooks;
519
520 ext(ctx.modules, ctx.auth_module, ctx.registry)?;
521 extend_rpc_modules.extend_rpc_modules(ctx)?;
522
523 let server_config = config.rpc.rpc_server_config();
524 let cloned_modules = modules.clone();
525 let launch_rpc = server_config.start(&cloned_modules).map_ok(|handle| {
526 if let Some(path) = handle.ipc_endpoint() {
527 info!(target: "reth::cli", %path, "RPC IPC server started");
528 }
529 if let Some(addr) = handle.http_local_addr() {
530 info!(target: "reth::cli", url=%addr, "RPC HTTP server started");
531 }
532 if let Some(addr) = handle.ws_local_addr() {
533 info!(target: "reth::cli", url=%addr, "RPC WS server started");
534 }
535 handle
536 });
537
538 let launch_auth = auth_module.clone().start_server(auth_config).map_ok(|handle| {
539 let addr = handle.local_addr();
540 if let Some(ipc_endpoint) = handle.ipc_endpoint() {
541 info!(target: "reth::cli", url=%addr, ipc_endpoint=%ipc_endpoint, "RPC auth server started");
542 } else {
543 info!(target: "reth::cli", url=%addr, "RPC auth server started");
544 }
545 handle
546 });
547
548 let (rpc, auth) = futures::future::try_join(launch_rpc, launch_auth).await?;
550
551 let handles = RethRpcServerHandles { rpc, auth };
552
553 let ctx = RpcContext {
554 node: node.clone(),
555 config,
556 registry: &mut registry,
557 modules: &mut modules,
558 auth_module: &mut auth_module,
559 };
560
561 on_rpc_started.on_rpc_started(ctx, handles.clone())?;
562
563 Ok(RpcHandle {
564 rpc_server_handles: handles,
565 rpc_registry: registry,
566 engine_events,
567 beacon_engine_handle,
568 })
569 }
570}
571
572impl<N, EthB, EV, EB> NodeAddOns<N> for RpcAddOns<N, EthB, EV, EB>
573where
574 N: FullNodeComponents,
575 <N as FullNodeTypes>::Provider: ChainSpecProvider<ChainSpec: EthereumHardforks>,
576 EthB: EthApiBuilder<N>,
577 EV: EngineValidatorBuilder<N>,
578 EB: EngineApiBuilder<N>,
579{
580 type Handle = RpcHandle<N, EthB::EthApi>;
581
582 async fn launch_add_ons(self, ctx: AddOnsContext<'_, N>) -> eyre::Result<Self::Handle> {
583 self.launch_add_ons_with(ctx, |_, _, _| Ok(())).await
584 }
585}
586
587pub trait RethRpcAddOns<N: FullNodeComponents>:
590 NodeAddOns<N, Handle = RpcHandle<N, Self::EthApi>>
591{
592 type EthApi: EthApiTypes;
594
595 fn hooks_mut(&mut self) -> &mut RpcHooks<N, Self::EthApi>;
597}
598
599impl<N: FullNodeComponents, EthB, EV, EB> RethRpcAddOns<N> for RpcAddOns<N, EthB, EV, EB>
600where
601 Self: NodeAddOns<N, Handle = RpcHandle<N, EthB::EthApi>>,
602 EthB: EthApiBuilder<N>,
603{
604 type EthApi = EthB::EthApi;
605
606 fn hooks_mut(&mut self) -> &mut RpcHooks<N, Self::EthApi> {
607 &mut self.hooks
608 }
609}
610
611#[derive(Debug)]
614pub struct EthApiCtx<'a, N: FullNodeTypes> {
615 pub components: &'a N,
617 pub config: EthConfig,
619 pub cache: EthStateCache<BlockTy<N::Types>, ReceiptTy<N::Types>>,
621}
622
623pub trait EthApiBuilder<N: FullNodeComponents>: Default + Send + 'static {
625 type EthApi: EthApiTypes
627 + FullEthApiServer<Provider = N::Provider, Pool = N::Pool>
628 + AddDevSigners
629 + Unpin
630 + 'static;
631
632 fn build_eth_api(
634 self,
635 ctx: EthApiCtx<'_, N>,
636 ) -> impl Future<Output = eyre::Result<Self::EthApi>> + Send;
637}
638
639pub trait EngineValidatorAddOn<Node: FullNodeComponents>: Send {
641 type Validator: EngineValidator<<Node::Types as NodeTypes>::Payload, Block = BlockTy<Node::Types>>
643 + Clone;
644
645 fn engine_validator(
647 &self,
648 ctx: &AddOnsContext<'_, Node>,
649 ) -> impl Future<Output = eyre::Result<Self::Validator>>;
650}
651
652impl<N, EthB, EV, EB> EngineValidatorAddOn<N> for RpcAddOns<N, EthB, EV, EB>
653where
654 N: FullNodeComponents,
655 EthB: EthApiBuilder<N>,
656 EV: EngineValidatorBuilder<N>,
657 EB: EngineApiBuilder<N>,
658{
659 type Validator = EV::Validator;
660
661 async fn engine_validator(&self, ctx: &AddOnsContext<'_, N>) -> eyre::Result<Self::Validator> {
662 self.engine_validator_builder.clone().build(ctx).await
663 }
664}
665
666pub trait EngineValidatorBuilder<Node: FullNodeComponents>: Send + Sync + Clone {
668 type Validator: EngineValidator<<Node::Types as NodeTypes>::Payload, Block = BlockTy<Node::Types>>
670 + Clone;
671
672 fn build(
674 self,
675 ctx: &AddOnsContext<'_, Node>,
676 ) -> impl Future<Output = eyre::Result<Self::Validator>> + Send;
677}
678
679impl<Node, F, Fut, Validator> EngineValidatorBuilder<Node> for F
680where
681 Node: FullNodeComponents,
682 Validator: EngineValidator<<Node::Types as NodeTypes>::Payload, Block = BlockTy<Node::Types>>
683 + Clone
684 + Unpin
685 + 'static,
686 F: FnOnce(&AddOnsContext<'_, Node>) -> Fut + Send + Sync + Clone,
687 Fut: Future<Output = eyre::Result<Validator>> + Send,
688{
689 type Validator = Validator;
690
691 fn build(
692 self,
693 ctx: &AddOnsContext<'_, Node>,
694 ) -> impl Future<Output = eyre::Result<Self::Validator>> {
695 self(ctx)
696 }
697}
698
699pub trait EngineApiBuilder<Node: FullNodeComponents>: Send + Sync {
706 type EngineApi: IntoEngineApiRpcModule + Send + Sync;
708
709 fn build_engine_api(
714 self,
715 ctx: &AddOnsContext<'_, Node>,
716 ) -> impl Future<Output = eyre::Result<Self::EngineApi>> + Send;
717}
718
719#[derive(Debug, Default)]
725pub struct BasicEngineApiBuilder<EV> {
726 engine_validator_builder: EV,
727}
728
729impl<N, EV> EngineApiBuilder<N> for BasicEngineApiBuilder<EV>
730where
731 N: FullNodeComponents<
732 Types: NodeTypes<
733 ChainSpec: EthereumHardforks,
734 Payload: PayloadTypes<ExecutionData = ExecutionData> + EngineTypes,
735 >,
736 >,
737 EV: EngineValidatorBuilder<N>,
738{
739 type EngineApi = EngineApi<
740 N::Provider,
741 <N::Types as NodeTypes>::Payload,
742 N::Pool,
743 EV::Validator,
744 <N::Types as NodeTypes>::ChainSpec,
745 >;
746
747 async fn build_engine_api(self, ctx: &AddOnsContext<'_, N>) -> eyre::Result<Self::EngineApi> {
748 let Self { engine_validator_builder } = self;
749
750 let engine_validator = engine_validator_builder.build(ctx).await?;
751 let client = ClientVersionV1 {
752 code: CLIENT_CODE,
753 name: NAME_CLIENT.to_string(),
754 version: CARGO_PKG_VERSION.to_string(),
755 commit: VERGEN_GIT_SHA.to_string(),
756 };
757 Ok(EngineApi::new(
758 ctx.node.provider().clone(),
759 ctx.config.chain.clone(),
760 ctx.beacon_engine_handle.clone(),
761 PayloadStore::new(ctx.node.payload_builder_handle().clone()),
762 ctx.node.pool().clone(),
763 Box::new(ctx.node.task_executor().clone()),
764 client,
765 EngineCapabilities::default(),
766 engine_validator,
767 ctx.config.engine.accept_execution_requests_hash,
768 ))
769 }
770}
771
772#[derive(Debug, Clone, Default)]
778#[non_exhaustive]
779pub struct NoopEngineApiBuilder;
780
781impl<N: FullNodeComponents> EngineApiBuilder<N> for NoopEngineApiBuilder {
782 type EngineApi = NoopEngineApi;
783
784 async fn build_engine_api(self, _ctx: &AddOnsContext<'_, N>) -> eyre::Result<Self::EngineApi> {
785 Ok(NoopEngineApi::default())
786 }
787}
788
789#[derive(Debug, Clone, Default)]
794#[non_exhaustive]
795pub struct NoopEngineApi;
796
797impl IntoEngineApiRpcModule for NoopEngineApi {
798 fn into_rpc_module(self) -> RpcModule<()> {
799 RpcModule::new(())
800 }
801}