1#![doc(
15 html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
16 html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
17 issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
18)]
19#![cfg_attr(not(test), warn(unused_crate_dependencies))]
20#![cfg_attr(docsrs, feature(doc_cfg))]
21
22use crate::{auth::AuthRpcModule, error::WsHttpSamePortError, metrics::RpcRequestMetrics};
23use alloy_network::{Ethereum, IntoWallet};
24use alloy_provider::{fillers::RecommendedFillers, Provider, ProviderBuilder, WsConnect};
25use alloy_rpc_client::{ClientBuilder, RpcClient};
26use alloy_transport::Authorization;
27use core::marker::PhantomData;
28use error::{ConflictingModules, RpcError, ServerKind};
29use jsonrpsee::{
30 core::RegisterMethodError,
31 server::{middleware::rpc::RpcServiceBuilder, AlreadyStoppedError, IdProvider, ServerHandle},
32 Methods, RpcModule,
33};
34use reth_chainspec::{ChainSpecProvider, EthereumHardforks};
35use reth_consensus::FullConsensus;
36use reth_engine_primitives::{ConsensusEngineEvent, ConsensusEngineHandle};
37use reth_evm::ConfigureEvm;
38use reth_network_api::{noop::NoopNetwork, NetworkInfo, Peers};
39use reth_payload_primitives::PayloadTypes;
40use reth_primitives_traits::{NodePrimitives, TxTy};
41use reth_rpc::{
42 AdminApi, DebugApi, EngineEthApi, EthApi, EthApiBuilder, EthBundle, MinerApi, NetApi,
43 OtterscanApi, RPCApi, RethApi, TraceApi, TxPoolApi, Web3Api,
44};
45use reth_rpc_api::servers::*;
46use reth_rpc_engine_api::RethEngineApi;
47use reth_rpc_eth_api::{
48 helpers::{pending_block::PendingEnvBuilder, Call, EthApiSpec, EthTransactions, TraceExt},
49 node::RpcNodeCoreAdapter,
50 EthApiServer, EthApiTypes, FullEthApiServer, FullEthApiTypes, RpcBlock, RpcConvert,
51 RpcConverter, RpcHeader, RpcNodeCore, RpcReceipt, RpcTransaction, RpcTxReq,
52};
53use reth_rpc_eth_types::{receipt::EthReceiptConverter, EthConfig, EthSubscriptionIdProvider};
54use reth_rpc_layer::{
55 AuthClientLayer, AuthLayer, Claims, CompressionLayer, DecompressionLayer, JwtAuthValidator,
56 JwtSecret,
57};
58pub use reth_rpc_server_types::RethRpcModule;
59use reth_storage_api::{
60 BlockReader, ChangeSetReader, FullRpcProvider, NodePrimitivesProvider, StateProviderFactory,
61};
62use reth_tasks::{pool::BlockingTaskGuard, Runtime};
63use reth_tokio_util::EventSender;
64use reth_transaction_pool::{noop::NoopTransactionPool, TransactionPool};
65use serde::{Deserialize, Serialize};
66use std::{
67 collections::HashMap,
68 fmt::Debug,
69 net::{Ipv4Addr, SocketAddr, SocketAddrV4},
70 time::{Duration, SystemTime, UNIX_EPOCH},
71};
72use tower_http::{cors::CorsLayer, validate_request::ValidateRequestHeaderLayer};
73
74pub use cors::CorsDomainError;
75
76pub use jsonrpsee::server::ServerBuilder;
78use jsonrpsee::server::ServerConfigBuilder;
79pub use reth_ipc::server::{
80 Builder as IpcServerBuilder, RpcServiceBuilder as IpcRpcServiceBuilder,
81};
82pub use reth_rpc_server_types::{constants, RpcModuleSelection};
83pub use tower::layer::util::{Identity, Stack};
84
85pub mod auth;
87
88pub mod config;
90
91pub mod middleware;
93
94mod cors;
96
97pub mod error;
99
100pub mod eth;
102pub use eth::EthHandlers;
103
104mod metrics;
106use crate::middleware::RethRpcMiddleware;
107pub use metrics::{MeteredBatchRequestsFuture, MeteredRequestFuture, RpcRequestMetricsService};
108use reth_chain_state::{
109 CanonStateSubscriptions, ForkChoiceSubscriptions, PersistedBlockSubscriptions,
110};
111use reth_rpc::eth::sim_bundle::EthSimBundle;
112
113pub mod rate_limiter;
115
116#[derive(Debug, Clone)]
120pub struct RpcModuleBuilder<N, Provider, Pool, Network, EvmConfig, Consensus> {
121 provider: Provider,
123 pool: Pool,
125 network: Network,
127 executor: Option<Runtime>,
129 evm_config: EvmConfig,
131 consensus: Consensus,
133 _primitives: PhantomData<N>,
135}
136
137impl<N, Provider, Pool, Network, EvmConfig, Consensus>
140 RpcModuleBuilder<N, Provider, Pool, Network, EvmConfig, Consensus>
141{
142 pub const fn new(
144 provider: Provider,
145 pool: Pool,
146 network: Network,
147 executor: Runtime,
148 evm_config: EvmConfig,
149 consensus: Consensus,
150 ) -> Self {
151 Self {
152 provider,
153 pool,
154 network,
155 executor: Some(executor),
156 evm_config,
157 consensus,
158 _primitives: PhantomData,
159 }
160 }
161
162 pub fn with_provider<P>(
164 self,
165 provider: P,
166 ) -> RpcModuleBuilder<N, P, Pool, Network, EvmConfig, Consensus> {
167 let Self { pool, network, executor, evm_config, consensus, _primitives, .. } = self;
168 RpcModuleBuilder { provider, network, pool, executor, evm_config, consensus, _primitives }
169 }
170
171 pub fn with_pool<P>(
173 self,
174 pool: P,
175 ) -> RpcModuleBuilder<N, Provider, P, Network, EvmConfig, Consensus> {
176 let Self { provider, network, executor, evm_config, consensus, _primitives, .. } = self;
177 RpcModuleBuilder { provider, network, pool, executor, evm_config, consensus, _primitives }
178 }
179
180 pub fn with_noop_pool(
186 self,
187 ) -> RpcModuleBuilder<N, Provider, NoopTransactionPool, Network, EvmConfig, Consensus> {
188 let Self { provider, executor, network, evm_config, consensus, _primitives, .. } = self;
189 RpcModuleBuilder {
190 provider,
191 executor,
192 network,
193 evm_config,
194 pool: NoopTransactionPool::default(),
195 consensus,
196 _primitives,
197 }
198 }
199
200 pub fn with_network<Net>(
202 self,
203 network: Net,
204 ) -> RpcModuleBuilder<N, Provider, Pool, Net, EvmConfig, Consensus> {
205 let Self { provider, pool, executor, evm_config, consensus, _primitives, .. } = self;
206 RpcModuleBuilder { provider, network, pool, executor, evm_config, consensus, _primitives }
207 }
208
209 pub fn with_noop_network(
215 self,
216 ) -> RpcModuleBuilder<N, Provider, Pool, NoopNetwork, EvmConfig, Consensus> {
217 let Self { provider, pool, executor, evm_config, consensus, _primitives, .. } = self;
218 RpcModuleBuilder {
219 provider,
220 pool,
221 executor,
222 network: NoopNetwork::default(),
223 evm_config,
224 consensus,
225 _primitives,
226 }
227 }
228
229 pub fn with_executor(self, executor: Runtime) -> Self {
231 let Self { pool, network, provider, evm_config, consensus, _primitives, .. } = self;
232 Self {
233 provider,
234 network,
235 pool,
236 executor: Some(executor),
237 evm_config,
238 consensus,
239 _primitives,
240 }
241 }
242
243 pub fn with_evm_config<E>(
245 self,
246 evm_config: E,
247 ) -> RpcModuleBuilder<N, Provider, Pool, Network, E, Consensus> {
248 let Self { provider, pool, executor, network, consensus, _primitives, .. } = self;
249 RpcModuleBuilder { provider, network, pool, executor, evm_config, consensus, _primitives }
250 }
251
252 pub fn with_consensus<C>(
254 self,
255 consensus: C,
256 ) -> RpcModuleBuilder<N, Provider, Pool, Network, EvmConfig, C> {
257 let Self { provider, network, pool, executor, evm_config, _primitives, .. } = self;
258 RpcModuleBuilder { provider, network, pool, executor, evm_config, consensus, _primitives }
259 }
260
261 #[expect(clippy::type_complexity)]
263 pub fn eth_api_builder<ChainSpec>(
264 &self,
265 ) -> EthApiBuilder<
266 RpcNodeCoreAdapter<Provider, Pool, Network, EvmConfig>,
267 RpcConverter<Ethereum, EvmConfig, EthReceiptConverter<ChainSpec>>,
268 >
269 where
270 Provider: Clone,
271 Pool: Clone,
272 Network: Clone,
273 EvmConfig: Clone,
274 RpcNodeCoreAdapter<Provider, Pool, Network, EvmConfig>:
275 RpcNodeCore<Provider: ChainSpecProvider<ChainSpec = ChainSpec>, Evm = EvmConfig>,
276 {
277 EthApiBuilder::new(
278 self.provider.clone(),
279 self.pool.clone(),
280 self.network.clone(),
281 self.evm_config.clone(),
282 )
283 }
284
285 #[expect(clippy::type_complexity)]
291 pub fn bootstrap_eth_api<ChainSpec>(
292 &self,
293 ) -> EthApi<
294 RpcNodeCoreAdapter<Provider, Pool, Network, EvmConfig>,
295 RpcConverter<Ethereum, EvmConfig, EthReceiptConverter<ChainSpec>>,
296 >
297 where
298 Provider: Clone,
299 Pool: Clone,
300 Network: Clone,
301 EvmConfig: ConfigureEvm,
302 RpcNodeCoreAdapter<Provider, Pool, Network, EvmConfig>:
303 RpcNodeCore<Provider: ChainSpecProvider<ChainSpec = ChainSpec>, Evm = EvmConfig>,
304 RpcConverter<Ethereum, EvmConfig, EthReceiptConverter<ChainSpec>>: RpcConvert,
305 (): PendingEnvBuilder<EvmConfig>,
306 {
307 self.eth_api_builder().build()
308 }
309}
310
311impl<N, Provider, Pool, Network, EvmConfig, Consensus>
312 RpcModuleBuilder<N, Provider, Pool, Network, EvmConfig, Consensus>
313where
314 N: NodePrimitives,
315 Provider: FullRpcProvider<Block = N::Block, Receipt = N::Receipt, Header = N::BlockHeader>
316 + CanonStateSubscriptions<Primitives = N>
317 + ForkChoiceSubscriptions<Header = N::BlockHeader>
318 + PersistedBlockSubscriptions
319 + ChangeSetReader
320 + NodePrimitivesProvider<Primitives = N>,
321 Pool: TransactionPool + 'static,
322 Network: NetworkInfo + Peers + Clone + 'static,
323 EvmConfig: ConfigureEvm<Primitives = N> + 'static,
324 Consensus: FullConsensus<N> + Clone + 'static,
325{
326 pub fn build_with_auth_server<EthApi, Payload>(
333 self,
334 module_config: TransportRpcModuleConfig,
335 engine: impl IntoEngineApiRpcModule,
336 eth: EthApi,
337 engine_events: EventSender<ConsensusEngineEvent<N>>,
338 beacon_engine_handle: ConsensusEngineHandle<Payload>,
339 ) -> (
340 TransportRpcModules,
341 AuthRpcModule,
342 RpcRegistryInner<Provider, Pool, Network, EthApi, EvmConfig, Consensus>,
343 )
344 where
345 EthApi: FullEthApiServer<Provider = Provider, Pool = Pool>,
346 Payload: PayloadTypes,
347 {
348 let config = module_config.config.clone().unwrap_or_default();
349
350 let mut registry = self.into_registry(config, eth, engine_events);
351 let modules = registry.create_transport_rpc_modules(module_config);
352 let auth_module = registry.create_auth_module(engine, beacon_engine_handle);
353
354 (modules, auth_module, registry)
355 }
356
357 pub fn into_registry<EthApi>(
362 self,
363 config: RpcModuleConfig,
364 eth: EthApi,
365 engine_events: EventSender<ConsensusEngineEvent<N>>,
366 ) -> RpcRegistryInner<Provider, Pool, Network, EthApi, EvmConfig, Consensus>
367 where
368 EthApi: FullEthApiServer<Provider = Provider, Pool = Pool>,
369 {
370 let Self { provider, pool, network, executor, consensus, evm_config, .. } = self;
371 let executor =
372 executor.expect("RpcModuleBuilder requires a Runtime to be set via `with_executor`");
373 RpcRegistryInner::new(
374 provider,
375 pool,
376 network,
377 executor,
378 consensus,
379 config,
380 evm_config,
381 eth,
382 engine_events,
383 )
384 }
385
386 pub fn build<EthApi>(
389 self,
390 module_config: TransportRpcModuleConfig,
391 eth: EthApi,
392 engine_events: EventSender<ConsensusEngineEvent<N>>,
393 ) -> TransportRpcModules<()>
394 where
395 EthApi: FullEthApiServer<Provider = Provider, Pool = Pool>,
396 {
397 if module_config.is_empty() {
398 TransportRpcModules::default()
399 } else {
400 let config = module_config.config.clone().unwrap_or_default();
401 let mut registry = self.into_registry(config, eth, engine_events);
402 registry.create_transport_rpc_modules(module_config)
403 }
404 }
405}
406
407impl<N: NodePrimitives> Default for RpcModuleBuilder<N, (), (), (), (), ()> {
408 fn default() -> Self {
409 Self {
410 provider: (),
411 pool: (),
412 network: (),
413 executor: None,
414 evm_config: (),
415 consensus: (),
416 _primitives: PhantomData,
417 }
418 }
419}
420
421#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize)]
423pub struct RpcModuleConfig {
424 eth: EthConfig,
426}
427
428impl RpcModuleConfig {
431 pub fn builder() -> RpcModuleConfigBuilder {
433 RpcModuleConfigBuilder::default()
434 }
435
436 pub const fn new(eth: EthConfig) -> Self {
438 Self { eth }
439 }
440
441 pub const fn eth(&self) -> &EthConfig {
443 &self.eth
444 }
445
446 pub const fn eth_mut(&mut self) -> &mut EthConfig {
448 &mut self.eth
449 }
450}
451
452#[derive(Clone, Debug, Default)]
454pub struct RpcModuleConfigBuilder {
455 eth: Option<EthConfig>,
456}
457
458impl RpcModuleConfigBuilder {
461 pub fn eth(mut self, eth: EthConfig) -> Self {
463 self.eth = Some(eth);
464 self
465 }
466
467 pub fn build(self) -> RpcModuleConfig {
469 let Self { eth } = self;
470 RpcModuleConfig { eth: eth.unwrap_or_default() }
471 }
472
473 pub const fn get_eth(&self) -> Option<&EthConfig> {
475 self.eth.as_ref()
476 }
477
478 pub const fn eth_mut(&mut self) -> &mut Option<EthConfig> {
480 &mut self.eth
481 }
482
483 pub fn eth_mut_or_default(&mut self) -> &mut EthConfig {
485 self.eth.get_or_insert_with(EthConfig::default)
486 }
487}
488
489#[derive(Debug)]
491pub struct RpcRegistryInner<Provider, Pool, Network, EthApi: EthApiTypes, EvmConfig, Consensus> {
492 provider: Provider,
493 pool: Pool,
494 network: Network,
495 executor: Runtime,
496 evm_config: EvmConfig,
497 consensus: Consensus,
498 eth: EthHandlers<EthApi>,
500 blocking_pool_guard: BlockingTaskGuard,
502 modules: HashMap<RethRpcModule, Methods>,
504 eth_config: EthConfig,
506 engine_events:
508 EventSender<ConsensusEngineEvent<<EthApi::RpcConvert as RpcConvert>::Primitives>>,
509}
510
511impl<N, Provider, Pool, Network, EthApi, EvmConfig, Consensus>
514 RpcRegistryInner<Provider, Pool, Network, EthApi, EvmConfig, Consensus>
515where
516 N: NodePrimitives,
517 Provider: StateProviderFactory
518 + CanonStateSubscriptions<Primitives = N>
519 + BlockReader<Block = N::Block, Receipt = N::Receipt>
520 + Clone
521 + Unpin
522 + 'static,
523 Pool: Send + Sync + Clone + 'static,
524 Network: Clone + 'static,
525 EthApi: FullEthApiTypes,
526 EvmConfig: ConfigureEvm<Primitives = N>,
527{
528 #[expect(clippy::too_many_arguments)]
530 pub fn new(
531 provider: Provider,
532 pool: Pool,
533 network: Network,
534 executor: Runtime,
535 consensus: Consensus,
536 config: RpcModuleConfig,
537 evm_config: EvmConfig,
538 eth_api: EthApi,
539 engine_events: EventSender<
540 ConsensusEngineEvent<<EthApi::Provider as NodePrimitivesProvider>::Primitives>,
541 >,
542 ) -> Self
543 where
544 EvmConfig: ConfigureEvm<Primitives = N>,
545 {
546 let blocking_pool_guard = BlockingTaskGuard::new(config.eth.max_tracing_requests);
547
548 let eth = EthHandlers::bootstrap(config.eth.clone(), executor.clone(), eth_api);
549
550 Self {
551 provider,
552 pool,
553 network,
554 eth,
555 executor,
556 consensus,
557 modules: Default::default(),
558 blocking_pool_guard,
559 eth_config: config.eth,
560 evm_config,
561 engine_events,
562 }
563 }
564}
565
566impl<Provider, Pool, Network, EthApi, Evm, Consensus>
567 RpcRegistryInner<Provider, Pool, Network, EthApi, Evm, Consensus>
568where
569 EthApi: EthApiTypes,
570{
571 pub const fn eth_api(&self) -> &EthApi {
573 &self.eth.api
574 }
575
576 pub const fn eth_handlers(&self) -> &EthHandlers<EthApi> {
578 &self.eth
579 }
580
581 pub const fn pool(&self) -> &Pool {
583 &self.pool
584 }
585
586 pub const fn tasks(&self) -> &Runtime {
588 &self.executor
589 }
590
591 pub const fn provider(&self) -> &Provider {
593 &self.provider
594 }
595
596 pub const fn evm_config(&self) -> &Evm {
598 &self.evm_config
599 }
600
601 pub fn methods(&self) -> Vec<Methods> {
603 self.modules.values().cloned().collect()
604 }
605
606 pub fn module(&self) -> RpcModule<()> {
608 let mut module = RpcModule::new(());
609 for methods in self.modules.values().cloned() {
610 module.merge(methods).expect("No conflicts");
611 }
612 module
613 }
614}
615
616impl<Provider, Pool, Network, EthApi, EvmConfig, Consensus>
617 RpcRegistryInner<Provider, Pool, Network, EthApi, EvmConfig, Consensus>
618where
619 Network: NetworkInfo + Clone + 'static,
620 EthApi: EthApiTypes,
621 Provider: BlockReader + ChainSpecProvider<ChainSpec: EthereumHardforks>,
622 EvmConfig: ConfigureEvm,
623{
624 pub fn admin_api(&self) -> AdminApi<Network, Provider::ChainSpec, Pool>
626 where
627 Network: Peers,
628 Pool: TransactionPool + 'static,
629 {
630 AdminApi::new(self.network.clone(), self.provider.chain_spec(), self.pool.clone())
631 }
632
633 pub fn web3_api(&self) -> Web3Api<Network> {
635 Web3Api::new(self.network.clone())
636 }
637
638 pub fn register_admin(&mut self) -> &mut Self
640 where
641 Network: Peers,
642 Pool: TransactionPool + 'static,
643 {
644 let adminapi = self.admin_api();
645 self.modules.insert(RethRpcModule::Admin, adminapi.into_rpc().into());
646 self
647 }
648
649 pub fn register_web3(&mut self) -> &mut Self {
651 let web3api = self.web3_api();
652 self.modules.insert(RethRpcModule::Web3, web3api.into_rpc().into());
653 self
654 }
655}
656
657impl<N, Provider, Pool, Network, EthApi, EvmConfig, Consensus>
658 RpcRegistryInner<Provider, Pool, Network, EthApi, EvmConfig, Consensus>
659where
660 N: NodePrimitives,
661 Provider: FullRpcProvider<
662 Header = N::BlockHeader,
663 Block = N::Block,
664 Receipt = N::Receipt,
665 Transaction = N::SignedTx,
666 > + ChangeSetReader
667 + CanonStateSubscriptions<Primitives = N>
668 + ForkChoiceSubscriptions<Header = N::BlockHeader>
669 + PersistedBlockSubscriptions,
670 Network: NetworkInfo + Peers + Clone + 'static,
671 EthApi: EthApiServer<
672 RpcTxReq<EthApi::NetworkTypes>,
673 RpcTransaction<EthApi::NetworkTypes>,
674 RpcBlock<EthApi::NetworkTypes>,
675 RpcReceipt<EthApi::NetworkTypes>,
676 RpcHeader<EthApi::NetworkTypes>,
677 TxTy<N>,
678 > + EthApiTypes,
679 EvmConfig: ConfigureEvm<Primitives = N> + 'static,
680{
681 pub fn register_eth(&mut self) -> &mut Self {
687 let eth_api = self.eth_api().clone();
688 self.modules.insert(RethRpcModule::Eth, eth_api.into_rpc().into());
689 self
690 }
691
692 pub fn register_ots(&mut self) -> &mut Self
698 where
699 EthApi: TraceExt + EthTransactions<Primitives = N>,
700 {
701 let otterscan_api = self.otterscan_api();
702 self.modules.insert(RethRpcModule::Ots, otterscan_api.into_rpc().into());
703 self
704 }
705
706 pub fn register_debug(&mut self) -> &mut Self
712 where
713 EthApi: EthTransactions + TraceExt,
714 {
715 let debug_api = self.debug_api();
716 self.modules.insert(RethRpcModule::Debug, debug_api.into_rpc().into());
717 self
718 }
719
720 pub fn register_trace(&mut self) -> &mut Self
726 where
727 EthApi: TraceExt,
728 {
729 let trace_api = self.trace_api();
730 self.modules.insert(RethRpcModule::Trace, trace_api.into_rpc().into());
731 self
732 }
733
734 pub fn register_net(&mut self) -> &mut Self
742 where
743 EthApi: EthApiSpec,
744 {
745 let netapi = self.net_api();
746 self.modules.insert(RethRpcModule::Net, netapi.into_rpc().into());
747 self
748 }
749
750 pub fn register_reth(&mut self) -> &mut Self {
758 let rethapi = self.reth_api();
759 self.modules.insert(RethRpcModule::Reth, rethapi.into_rpc().into());
760 self
761 }
762
763 pub fn otterscan_api(&self) -> OtterscanApi<EthApi> {
769 let eth_api = self.eth_api().clone();
770 OtterscanApi::new(eth_api)
771 }
772}
773
774impl<N, Provider, Pool, Network, EthApi, EvmConfig, Consensus>
775 RpcRegistryInner<Provider, Pool, Network, EthApi, EvmConfig, Consensus>
776where
777 N: NodePrimitives,
778 Provider: FullRpcProvider<
779 Block = N::Block,
780 Header = N::BlockHeader,
781 Transaction = N::SignedTx,
782 Receipt = N::Receipt,
783 > + ChangeSetReader,
784 Network: NetworkInfo + Peers + Clone + 'static,
785 EthApi: EthApiTypes,
786 EvmConfig: ConfigureEvm<Primitives = N>,
787{
788 pub fn trace_api(&self) -> TraceApi<EthApi> {
794 TraceApi::new(
795 self.eth_api().clone(),
796 self.blocking_pool_guard.clone(),
797 self.eth_config.clone(),
798 )
799 }
800
801 pub fn bundle_api(&self) -> EthBundle<EthApi>
807 where
808 EthApi: EthTransactions + Call,
809 {
810 let eth_api = self.eth_api().clone();
811 EthBundle::new(eth_api, self.blocking_pool_guard.clone())
812 }
813
814 pub fn debug_api(&self) -> DebugApi<EthApi>
820 where
821 EthApi: FullEthApiTypes,
822 {
823 DebugApi::new(
824 self.eth_api().clone(),
825 self.blocking_pool_guard.clone(),
826 self.tasks(),
827 self.engine_events.new_listener(),
828 )
829 }
830
831 pub fn net_api(&self) -> NetApi<Network, EthApi>
837 where
838 EthApi: EthApiSpec,
839 {
840 let eth_api = self.eth_api().clone();
841 NetApi::new(self.network.clone(), eth_api)
842 }
843
844 pub fn reth_api(&self) -> RethApi<Provider, EvmConfig> {
846 RethApi::new(
847 self.provider.clone(),
848 self.evm_config.clone(),
849 self.blocking_pool_guard.clone(),
850 self.executor.clone(),
851 )
852 }
853}
854
855impl<N, Provider, Pool, Network, EthApi, EvmConfig, Consensus>
856 RpcRegistryInner<Provider, Pool, Network, EthApi, EvmConfig, Consensus>
857where
858 N: NodePrimitives,
859 Provider: FullRpcProvider<Block = N::Block>
860 + CanonStateSubscriptions<Primitives = N>
861 + ForkChoiceSubscriptions<Header = N::BlockHeader>
862 + PersistedBlockSubscriptions
863 + ChangeSetReader,
864 Pool: TransactionPool + 'static,
865 Network: NetworkInfo + Peers + Clone + 'static,
866 EthApi: FullEthApiServer,
867 EvmConfig: ConfigureEvm<Primitives = N> + 'static,
868 Consensus: FullConsensus<N> + Clone + 'static,
869{
870 pub fn create_auth_module<Payload>(
877 &self,
878 engine_api: impl IntoEngineApiRpcModule,
879 beacon_engine_handle: ConsensusEngineHandle<Payload>,
880 ) -> AuthRpcModule
881 where
882 Payload: PayloadTypes,
883 {
884 let mut module = engine_api.into_rpc_module();
885
886 let reth_engine_api = RethEngineApi::new(beacon_engine_handle);
888 module
889 .merge(RethEngineApiServer::into_rpc(reth_engine_api).remove_context())
890 .expect("No conflicting methods");
891
892 let eth_handlers = self.eth_handlers();
894 let engine_eth = EngineEthApi::new(eth_handlers.api.clone(), eth_handlers.filter.clone());
895
896 module.merge(engine_eth.into_rpc()).expect("No conflicting methods");
897
898 AuthRpcModule { inner: module }
899 }
900
901 fn maybe_module(&mut self, config: Option<&RpcModuleSelection>) -> Option<RpcModule<()>> {
903 config.map(|config| self.module_for(config))
904 }
905
906 pub fn create_transport_rpc_modules(
910 &mut self,
911 config: TransportRpcModuleConfig,
912 ) -> TransportRpcModules<()> {
913 let mut modules = TransportRpcModules::default();
914 let http = self.maybe_module(config.http.as_ref());
915 let ws = self.maybe_module(config.ws.as_ref());
916 let ipc = self.maybe_module(config.ipc.as_ref());
917
918 modules.config = config;
919 modules.http = http;
920 modules.ws = ws;
921 modules.ipc = ipc;
922 modules
923 }
924
925 pub fn module_for(&mut self, config: &RpcModuleSelection) -> RpcModule<()> {
928 let mut module = RpcModule::new(());
929 let all_methods = self.reth_methods(config.iter_selection());
930 for methods in all_methods {
931 module.merge(methods).expect("No conflicts");
932 }
933 module
934 }
935
936 pub fn reth_methods(
945 &mut self,
946 namespaces: impl Iterator<Item = RethRpcModule>,
947 ) -> Vec<Methods> {
948 let EthHandlers { api: eth_api, filter: eth_filter, pubsub: eth_pubsub, .. } =
949 self.eth_handlers().clone();
950
951 let namespaces: Vec<_> = namespaces.collect();
953 namespaces
954 .iter()
955 .map(|namespace| {
956 if matches!(namespace, RethRpcModule::Rpc) {
959 return RPCApi::new(
960 namespaces
961 .iter()
962 .map(|module| (module.to_string(), "1.0".to_string()))
963 .collect(),
964 )
965 .into_rpc()
966 .into();
967 }
968
969 self.modules
970 .entry(namespace.clone())
971 .or_insert_with(|| match namespace.clone() {
972 RethRpcModule::Admin => AdminApi::new(
973 self.network.clone(),
974 self.provider.chain_spec(),
975 self.pool.clone(),
976 )
977 .into_rpc()
978 .into(),
979 RethRpcModule::Debug => DebugApi::new(
980 eth_api.clone(),
981 self.blocking_pool_guard.clone(),
982 &self.executor,
983 self.engine_events.new_listener(),
984 )
985 .into_rpc()
986 .into(),
987 RethRpcModule::Eth => {
988 let mut module = eth_api.clone().into_rpc();
990 module.merge(eth_filter.clone().into_rpc()).expect("No conflicts");
991 module.merge(eth_pubsub.clone().into_rpc()).expect("No conflicts");
992 module
993 .merge(
994 EthBundle::new(
995 eth_api.clone(),
996 self.blocking_pool_guard.clone(),
997 )
998 .into_rpc(),
999 )
1000 .expect("No conflicts");
1001
1002 module.into()
1003 }
1004 RethRpcModule::Net => {
1005 NetApi::new(self.network.clone(), eth_api.clone()).into_rpc().into()
1006 }
1007 RethRpcModule::Trace => TraceApi::new(
1008 eth_api.clone(),
1009 self.blocking_pool_guard.clone(),
1010 self.eth_config.clone(),
1011 )
1012 .into_rpc()
1013 .into(),
1014 RethRpcModule::Web3 => Web3Api::new(self.network.clone()).into_rpc().into(),
1015 RethRpcModule::Txpool => TxPoolApi::new(
1016 self.eth.api.pool().clone(),
1017 dyn_clone::clone(self.eth.api.converter()),
1018 )
1019 .into_rpc()
1020 .into(),
1021 RethRpcModule::Ots => OtterscanApi::new(eth_api.clone()).into_rpc().into(),
1022 RethRpcModule::Reth => RethApi::new(
1023 self.provider.clone(),
1024 self.evm_config.clone(),
1025 self.blocking_pool_guard.clone(),
1026 self.executor.clone(),
1027 )
1028 .into_rpc()
1029 .into(),
1030 RethRpcModule::Miner => MinerApi::default().into_rpc().into(),
1031 RethRpcModule::Mev => {
1032 EthSimBundle::new(eth_api.clone(), self.blocking_pool_guard.clone())
1033 .into_rpc()
1034 .into()
1035 }
1036 RethRpcModule::Rpc |
1040 RethRpcModule::Flashbots |
1041 RethRpcModule::Testing |
1042 RethRpcModule::Other(_) => Default::default(),
1043 })
1044 .clone()
1045 })
1046 .collect::<Vec<_>>()
1047 }
1048}
1049
1050impl<Provider, Pool, Network, EthApi, EvmConfig, Consensus> Clone
1051 for RpcRegistryInner<Provider, Pool, Network, EthApi, EvmConfig, Consensus>
1052where
1053 EthApi: EthApiTypes,
1054 Provider: Clone,
1055 Pool: Clone,
1056 Network: Clone,
1057 EvmConfig: Clone,
1058 Consensus: Clone,
1059{
1060 fn clone(&self) -> Self {
1061 Self {
1062 provider: self.provider.clone(),
1063 pool: self.pool.clone(),
1064 network: self.network.clone(),
1065 executor: self.executor.clone(),
1066 evm_config: self.evm_config.clone(),
1067 consensus: self.consensus.clone(),
1068 eth: self.eth.clone(),
1069 blocking_pool_guard: self.blocking_pool_guard.clone(),
1070 modules: self.modules.clone(),
1071 eth_config: self.eth_config.clone(),
1072 engine_events: self.engine_events.clone(),
1073 }
1074 }
1075}
1076
1077#[derive(Debug)]
1089pub struct RpcServerConfig<RpcMiddleware = Identity> {
1090 http_server_config: Option<ServerConfigBuilder>,
1092 http_cors_domains: Option<String>,
1094 http_addr: Option<SocketAddr>,
1096 http_disable_compression: bool,
1098 http_compression_algorithms: Option<Vec<String>>,
1102 http_decompression_algorithms: Option<Vec<String>>,
1106 http_max_request_body_size: Option<u32>,
1108 ws_server_config: Option<ServerConfigBuilder>,
1110 ws_cors_domains: Option<String>,
1112 ws_addr: Option<SocketAddr>,
1114 ipc_server_config: Option<IpcServerBuilder<Identity>>,
1116 ipc_endpoint: Option<String>,
1118 jwt_secret: Option<JwtSecret>,
1120 rpc_metrics_enabled: bool,
1122 rpc_middleware: RpcMiddleware,
1124}
1125
1126impl Default for RpcServerConfig<Identity> {
1129 fn default() -> Self {
1131 Self {
1132 http_server_config: None,
1133 http_cors_domains: None,
1134 http_addr: None,
1135 http_disable_compression: false,
1136 http_compression_algorithms: None,
1137 http_decompression_algorithms: None,
1138 http_max_request_body_size: None,
1139 ws_server_config: None,
1140 ws_cors_domains: None,
1141 ws_addr: None,
1142 ipc_server_config: None,
1143 ipc_endpoint: None,
1144 jwt_secret: None,
1145 rpc_metrics_enabled: true,
1146 rpc_middleware: Default::default(),
1147 }
1148 }
1149}
1150
1151impl RpcServerConfig {
1152 pub fn http(config: ServerConfigBuilder) -> Self {
1154 Self::default().with_http(config)
1155 }
1156
1157 pub fn ws(config: ServerConfigBuilder) -> Self {
1159 Self::default().with_ws(config)
1160 }
1161
1162 pub fn ipc(config: IpcServerBuilder<Identity>) -> Self {
1164 Self::default().with_ipc(config)
1165 }
1166
1167 pub fn with_http(mut self, config: ServerConfigBuilder) -> Self {
1172 self.http_server_config =
1173 Some(config.set_id_provider(EthSubscriptionIdProvider::default()));
1174 self
1175 }
1176
1177 pub fn with_ws(mut self, config: ServerConfigBuilder) -> Self {
1182 self.ws_server_config = Some(config.set_id_provider(EthSubscriptionIdProvider::default()));
1183 self
1184 }
1185
1186 pub fn with_ipc(mut self, config: IpcServerBuilder<Identity>) -> Self {
1191 self.ipc_server_config = Some(config.set_id_provider(EthSubscriptionIdProvider::default()));
1192 self
1193 }
1194}
1195
1196impl<RpcMiddleware> RpcServerConfig<RpcMiddleware> {
1197 pub fn set_rpc_middleware<T>(self, rpc_middleware: T) -> RpcServerConfig<T> {
1199 RpcServerConfig {
1200 http_server_config: self.http_server_config,
1201 http_cors_domains: self.http_cors_domains,
1202 http_addr: self.http_addr,
1203 http_disable_compression: self.http_disable_compression,
1204 http_compression_algorithms: self.http_compression_algorithms,
1205 http_decompression_algorithms: self.http_decompression_algorithms,
1206 http_max_request_body_size: self.http_max_request_body_size,
1207 ws_server_config: self.ws_server_config,
1208 ws_cors_domains: self.ws_cors_domains,
1209 ws_addr: self.ws_addr,
1210 ipc_server_config: self.ipc_server_config,
1211 ipc_endpoint: self.ipc_endpoint,
1212 jwt_secret: self.jwt_secret,
1213 rpc_metrics_enabled: self.rpc_metrics_enabled,
1214 rpc_middleware,
1215 }
1216 }
1217
1218 pub const fn with_rpc_metrics_enabled(mut self, enabled: bool) -> Self {
1220 self.rpc_metrics_enabled = enabled;
1221 self
1222 }
1223
1224 pub fn with_cors(self, cors_domain: Option<String>) -> Self {
1226 self.with_http_cors(cors_domain.clone()).with_ws_cors(cors_domain)
1227 }
1228
1229 pub fn with_ws_cors(mut self, cors_domain: Option<String>) -> Self {
1231 self.ws_cors_domains = cors_domain;
1232 self
1233 }
1234
1235 pub fn with_http_cors(mut self, cors_domain: Option<String>) -> Self {
1237 self.http_cors_domains = cors_domain;
1238 self
1239 }
1240
1241 pub const fn with_http_disable_compression(mut self, http_disable_compression: bool) -> Self {
1243 self.http_disable_compression = http_disable_compression;
1244 self
1245 }
1246
1247 pub fn with_http_compression_algorithms(mut self, algos: Option<Vec<String>>) -> Self {
1252 self.http_compression_algorithms = algos;
1253 self
1254 }
1255
1256 pub fn with_http_decompression(mut self, algos: Option<Vec<String>>, max_size: u32) -> Self {
1260 self.http_decompression_algorithms = algos;
1261 self.http_max_request_body_size = Some(max_size);
1262 self
1263 }
1264
1265 pub const fn with_http_address(mut self, addr: SocketAddr) -> Self {
1270 self.http_addr = Some(addr);
1271 self
1272 }
1273
1274 pub const fn with_ws_address(mut self, addr: SocketAddr) -> Self {
1279 self.ws_addr = Some(addr);
1280 self
1281 }
1282
1283 pub fn with_id_provider<I>(mut self, id_provider: I) -> Self
1287 where
1288 I: IdProvider + Clone + 'static,
1289 {
1290 if let Some(config) = self.http_server_config {
1291 self.http_server_config = Some(config.set_id_provider(id_provider.clone()));
1292 }
1293 if let Some(config) = self.ws_server_config {
1294 self.ws_server_config = Some(config.set_id_provider(id_provider.clone()));
1295 }
1296 if let Some(ipc) = self.ipc_server_config {
1297 self.ipc_server_config = Some(ipc.set_id_provider(id_provider));
1298 }
1299
1300 self
1301 }
1302
1303 pub fn with_ipc_endpoint(mut self, path: impl Into<String>) -> Self {
1307 self.ipc_endpoint = Some(path.into());
1308 self
1309 }
1310
1311 pub const fn with_jwt_secret(mut self, secret: Option<JwtSecret>) -> Self {
1313 self.jwt_secret = secret;
1314 self
1315 }
1316
1317 pub fn with_tokio_runtime(mut self, tokio_runtime: Option<tokio::runtime::Handle>) -> Self {
1319 let Some(tokio_runtime) = tokio_runtime else { return self };
1320 if let Some(http_server_config) = self.http_server_config {
1321 self.http_server_config =
1322 Some(http_server_config.custom_tokio_runtime(tokio_runtime.clone()));
1323 }
1324 if let Some(ws_server_config) = self.ws_server_config {
1325 self.ws_server_config =
1326 Some(ws_server_config.custom_tokio_runtime(tokio_runtime.clone()));
1327 }
1328 if let Some(ipc_server_config) = self.ipc_server_config {
1329 self.ipc_server_config = Some(ipc_server_config.custom_tokio_runtime(tokio_runtime));
1330 }
1331 self
1332 }
1333
1334 pub const fn has_server(&self) -> bool {
1338 self.http_server_config.is_some() ||
1339 self.ws_server_config.is_some() ||
1340 self.ipc_server_config.is_some()
1341 }
1342
1343 pub const fn http_address(&self) -> Option<SocketAddr> {
1345 self.http_addr
1346 }
1347
1348 pub const fn ws_address(&self) -> Option<SocketAddr> {
1350 self.ws_addr
1351 }
1352
1353 pub fn ipc_endpoint(&self) -> Option<String> {
1355 self.ipc_endpoint.clone()
1356 }
1357
1358 pub const fn rpc_metrics_enabled(&self) -> bool {
1360 self.rpc_metrics_enabled
1361 }
1362
1363 fn maybe_cors_layer(cors: Option<String>) -> Result<Option<CorsLayer>, CorsDomainError> {
1365 cors.as_deref().map(cors::create_cors_layer).transpose()
1366 }
1367
1368 fn maybe_jwt_layer(jwt_secret: Option<JwtSecret>) -> Option<AuthLayer<JwtAuthValidator>> {
1370 jwt_secret.map(|secret| AuthLayer::new(JwtAuthValidator::new(secret)))
1371 }
1372
1373 fn maybe_compression_layer(
1379 addr: SocketAddr,
1380 disable_compression: bool,
1381 algos: Option<&[String]>,
1382 ) -> Option<CompressionLayer> {
1383 if disable_compression || addr.ip().to_canonical().is_loopback() {
1384 None
1385 } else {
1386 match algos {
1387 None => Some(CompressionLayer::new()),
1389 Some(algos) => Some(CompressionLayer::with_algorithms(algos)),
1390 }
1391 }
1392 }
1393
1394 fn maybe_decompression_layer(
1400 max_request_body_size: Option<u32>,
1401 algos: Option<&[String]>,
1402 ) -> Option<DecompressionLayer> {
1403 let algos = algos?;
1404 if algos.is_empty() {
1405 return None;
1406 }
1407
1408 let max = max_request_body_size?;
1409 Some(DecompressionLayer::new(algos, max as usize))
1410 }
1411
1412 pub async fn start(self, modules: &TransportRpcModules) -> Result<RpcServerHandle, RpcError>
1418 where
1419 RpcMiddleware: RethRpcMiddleware,
1420 {
1421 let mut http_handle = None;
1422 let mut ws_handle = None;
1423 let mut ipc_handle = None;
1424
1425 let http_socket_addr = self.http_addr.unwrap_or(SocketAddr::V4(SocketAddrV4::new(
1426 Ipv4Addr::LOCALHOST,
1427 constants::DEFAULT_HTTP_RPC_PORT,
1428 )));
1429
1430 let ws_socket_addr = self.ws_addr.unwrap_or(SocketAddr::V4(SocketAddrV4::new(
1431 Ipv4Addr::LOCALHOST,
1432 constants::DEFAULT_WS_RPC_PORT,
1433 )));
1434
1435 let rpc_metrics_enabled = self.rpc_metrics_enabled;
1436 let ipc_path =
1437 self.ipc_endpoint.clone().unwrap_or_else(|| constants::DEFAULT_IPC_ENDPOINT.into());
1438
1439 if let Some(builder) = self.ipc_server_config {
1440 let ipc = builder
1441 .set_rpc_middleware(
1442 IpcRpcServiceBuilder::new().option_layer(
1443 rpc_metrics_enabled
1444 .then(|| modules.ipc.as_ref().map(RpcRequestMetrics::ipc))
1445 .flatten(),
1446 ),
1447 )
1448 .build(ipc_path);
1449 ipc_handle = Some(ipc.start(modules.ipc.clone().expect("ipc server error")).await?);
1450 }
1451
1452 if self.http_addr == self.ws_addr &&
1454 self.http_server_config.is_some() &&
1455 self.ws_server_config.is_some()
1456 {
1457 let cors = match (self.ws_cors_domains.as_ref(), self.http_cors_domains.as_ref()) {
1458 (Some(ws_cors), Some(http_cors)) => {
1459 if ws_cors.trim() != http_cors.trim() {
1460 return Err(WsHttpSamePortError::ConflictingCorsDomains {
1461 http_cors_domains: Some(http_cors.clone()),
1462 ws_cors_domains: Some(ws_cors.clone()),
1463 }
1464 .into());
1465 }
1466 Some(ws_cors)
1467 }
1468 (a, b) => a.or(b),
1469 }
1470 .cloned();
1471
1472 modules.config.ensure_ws_http_identical()?;
1474
1475 if let Some(config) = self.http_server_config {
1476 let server = ServerBuilder::new()
1477 .set_http_middleware(
1478 tower::ServiceBuilder::new()
1479 .option_layer(Self::maybe_cors_layer(cors.clone())?)
1480 .option_layer(Self::maybe_jwt_layer(self.jwt_secret))
1481 .option_layer(Self::maybe_decompression_layer(
1482 self.http_max_request_body_size,
1483 self.http_decompression_algorithms.as_deref(),
1484 ))
1485 .option_layer(Self::maybe_compression_layer(
1486 http_socket_addr,
1487 self.http_disable_compression,
1488 self.http_compression_algorithms.as_deref(),
1489 ))
1490 .layer(ValidateRequestHeaderLayer::custom(
1491 cors::WsOriginValidator::new(cors.as_deref())?,
1492 )),
1493 )
1494 .set_rpc_middleware(
1495 RpcServiceBuilder::default()
1496 .option_layer(
1497 rpc_metrics_enabled
1498 .then(|| {
1499 modules
1500 .http
1501 .as_ref()
1502 .or(modules.ws.as_ref())
1503 .map(RpcRequestMetrics::same_port)
1504 })
1505 .flatten(),
1506 )
1507 .layer(self.rpc_middleware.clone()),
1508 )
1509 .set_config(config.build())
1510 .build(http_socket_addr)
1511 .await
1512 .map_err(|err| {
1513 RpcError::server_error(err, ServerKind::WsHttp(http_socket_addr))
1514 })?;
1515 let addr = server.local_addr().map_err(|err| {
1516 RpcError::server_error(err, ServerKind::WsHttp(http_socket_addr))
1517 })?;
1518 if let Some(module) = modules.http.as_ref().or(modules.ws.as_ref()) {
1519 http_handle = Some(server.start(module.clone()));
1521 }
1522 return Ok(RpcServerHandle {
1523 http_local_addr: Some(addr),
1524 ws_local_addr: Some(addr),
1525 http: http_handle,
1526 ws: ws_handle,
1527 ipc_endpoint: self.ipc_endpoint.clone(),
1528 ipc: ipc_handle,
1529 jwt_secret: self.jwt_secret,
1530 });
1531 }
1532 }
1533
1534 let mut ws_local_addr = None;
1535 let mut ws_server = None;
1536 let mut http_local_addr = None;
1537 let mut http_server = None;
1538
1539 if let Some(config) = self.ws_server_config {
1540 let server = ServerBuilder::new()
1541 .set_config(config.ws_only().build())
1542 .set_http_middleware(
1543 tower::ServiceBuilder::new()
1544 .option_layer(Self::maybe_cors_layer(self.ws_cors_domains.clone())?)
1545 .option_layer(Self::maybe_jwt_layer(self.jwt_secret))
1546 .layer(ValidateRequestHeaderLayer::custom(cors::WsOriginValidator::new(
1547 self.ws_cors_domains.as_deref(),
1548 )?)),
1549 )
1550 .set_rpc_middleware(
1551 RpcServiceBuilder::default()
1552 .option_layer(
1553 rpc_metrics_enabled
1554 .then(|| modules.ws.as_ref().map(RpcRequestMetrics::ws))
1555 .flatten(),
1556 )
1557 .layer(self.rpc_middleware.clone()),
1558 )
1559 .build(ws_socket_addr)
1560 .await
1561 .map_err(|err| RpcError::server_error(err, ServerKind::WS(ws_socket_addr)))?;
1562
1563 let addr = server
1564 .local_addr()
1565 .map_err(|err| RpcError::server_error(err, ServerKind::WS(ws_socket_addr)))?;
1566
1567 ws_local_addr = Some(addr);
1568 ws_server = Some(server);
1569 }
1570
1571 if let Some(config) = self.http_server_config {
1572 let server = ServerBuilder::new()
1573 .set_config(config.http_only().build())
1574 .set_http_middleware(
1575 tower::ServiceBuilder::new()
1576 .option_layer(Self::maybe_cors_layer(self.http_cors_domains.clone())?)
1577 .option_layer(Self::maybe_jwt_layer(self.jwt_secret))
1578 .option_layer(Self::maybe_decompression_layer(
1579 self.http_max_request_body_size,
1580 self.http_decompression_algorithms.as_deref(),
1581 ))
1582 .option_layer(Self::maybe_compression_layer(
1583 http_socket_addr,
1584 self.http_disable_compression,
1585 self.http_compression_algorithms.as_deref(),
1586 )),
1587 )
1588 .set_rpc_middleware(
1589 RpcServiceBuilder::default()
1590 .option_layer(
1591 rpc_metrics_enabled
1592 .then(|| modules.http.as_ref().map(RpcRequestMetrics::http))
1593 .flatten(),
1594 )
1595 .layer(self.rpc_middleware.clone()),
1596 )
1597 .build(http_socket_addr)
1598 .await
1599 .map_err(|err| RpcError::server_error(err, ServerKind::Http(http_socket_addr)))?;
1600 let local_addr = server
1601 .local_addr()
1602 .map_err(|err| RpcError::server_error(err, ServerKind::Http(http_socket_addr)))?;
1603 http_local_addr = Some(local_addr);
1604 http_server = Some(server);
1605 }
1606
1607 http_handle = http_server
1608 .map(|http_server| http_server.start(modules.http.clone().expect("http server error")));
1609 ws_handle = ws_server
1610 .map(|ws_server| ws_server.start(modules.ws.clone().expect("ws server error")));
1611 Ok(RpcServerHandle {
1612 http_local_addr,
1613 ws_local_addr,
1614 http: http_handle,
1615 ws: ws_handle,
1616 ipc_endpoint: self.ipc_endpoint.clone(),
1617 ipc: ipc_handle,
1618 jwt_secret: self.jwt_secret,
1619 })
1620 }
1621}
1622
1623#[derive(Debug, Clone, Default, Eq, PartialEq)]
1635pub struct TransportRpcModuleConfig {
1636 http: Option<RpcModuleSelection>,
1638 ws: Option<RpcModuleSelection>,
1640 ipc: Option<RpcModuleSelection>,
1642 config: Option<RpcModuleConfig>,
1644}
1645
1646impl TransportRpcModuleConfig {
1649 pub fn set_http(http: impl Into<RpcModuleSelection>) -> Self {
1651 Self::default().with_http(http)
1652 }
1653
1654 pub fn set_ws(ws: impl Into<RpcModuleSelection>) -> Self {
1656 Self::default().with_ws(ws)
1657 }
1658
1659 pub fn set_ipc(ipc: impl Into<RpcModuleSelection>) -> Self {
1661 Self::default().with_ipc(ipc)
1662 }
1663
1664 pub fn with_http(mut self, http: impl Into<RpcModuleSelection>) -> Self {
1666 self.http = Some(http.into());
1667 self
1668 }
1669
1670 pub fn with_ws(mut self, ws: impl Into<RpcModuleSelection>) -> Self {
1672 self.ws = Some(ws.into());
1673 self
1674 }
1675
1676 pub fn with_ipc(mut self, ipc: impl Into<RpcModuleSelection>) -> Self {
1678 self.ipc = Some(ipc.into());
1679 self
1680 }
1681
1682 pub fn with_config(mut self, config: RpcModuleConfig) -> Self {
1684 self.config = Some(config);
1685 self
1686 }
1687
1688 pub const fn http_mut(&mut self) -> &mut Option<RpcModuleSelection> {
1690 &mut self.http
1691 }
1692
1693 pub const fn ws_mut(&mut self) -> &mut Option<RpcModuleSelection> {
1695 &mut self.ws
1696 }
1697
1698 pub const fn ipc_mut(&mut self) -> &mut Option<RpcModuleSelection> {
1700 &mut self.ipc
1701 }
1702
1703 pub const fn config_mut(&mut self) -> &mut Option<RpcModuleConfig> {
1705 &mut self.config
1706 }
1707
1708 pub const fn is_empty(&self) -> bool {
1710 self.http.is_none() && self.ws.is_none() && self.ipc.is_none()
1711 }
1712
1713 pub const fn http(&self) -> Option<&RpcModuleSelection> {
1715 self.http.as_ref()
1716 }
1717
1718 pub const fn ws(&self) -> Option<&RpcModuleSelection> {
1720 self.ws.as_ref()
1721 }
1722
1723 pub const fn ipc(&self) -> Option<&RpcModuleSelection> {
1725 self.ipc.as_ref()
1726 }
1727
1728 pub const fn config(&self) -> Option<&RpcModuleConfig> {
1730 self.config.as_ref()
1731 }
1732
1733 pub fn contains_any(&self, module: &RethRpcModule) -> bool {
1735 self.contains_http(module) || self.contains_ws(module) || self.contains_ipc(module)
1736 }
1737
1738 pub fn contains_http(&self, module: &RethRpcModule) -> bool {
1740 self.http.as_ref().is_some_and(|http| http.contains(module))
1741 }
1742
1743 pub fn contains_ws(&self, module: &RethRpcModule) -> bool {
1745 self.ws.as_ref().is_some_and(|ws| ws.contains(module))
1746 }
1747
1748 pub fn contains_ipc(&self, module: &RethRpcModule) -> bool {
1750 self.ipc.as_ref().is_some_and(|ipc| ipc.contains(module))
1751 }
1752
1753 fn ensure_ws_http_identical(&self) -> Result<(), WsHttpSamePortError> {
1756 if RpcModuleSelection::are_identical(self.http.as_ref(), self.ws.as_ref()) {
1757 Ok(())
1758 } else {
1759 let http_modules =
1760 self.http.as_ref().map(RpcModuleSelection::to_selection).unwrap_or_default();
1761 let ws_modules =
1762 self.ws.as_ref().map(RpcModuleSelection::to_selection).unwrap_or_default();
1763
1764 let http_not_ws = http_modules.difference(&ws_modules).cloned().collect();
1765 let ws_not_http = ws_modules.difference(&http_modules).cloned().collect();
1766 let overlap = http_modules.intersection(&ws_modules).cloned().collect();
1767
1768 Err(WsHttpSamePortError::ConflictingModules(Box::new(ConflictingModules {
1769 overlap,
1770 http_not_ws,
1771 ws_not_http,
1772 })))
1773 }
1774 }
1775}
1776
1777#[derive(Debug, Clone, Default)]
1779pub struct TransportRpcModules<Context = ()> {
1780 config: TransportRpcModuleConfig,
1782 http: Option<RpcModule<Context>>,
1784 ws: Option<RpcModule<Context>>,
1786 ipc: Option<RpcModule<Context>>,
1788}
1789
1790impl TransportRpcModules {
1793 pub fn with_config(mut self, config: TransportRpcModuleConfig) -> Self {
1796 self.config = config;
1797 self
1798 }
1799
1800 pub fn with_http(mut self, http: RpcModule<()>) -> Self {
1803 self.http = Some(http);
1804 self
1805 }
1806
1807 pub fn with_ws(mut self, ws: RpcModule<()>) -> Self {
1810 self.ws = Some(ws);
1811 self
1812 }
1813
1814 pub fn with_ipc(mut self, ipc: RpcModule<()>) -> Self {
1817 self.ipc = Some(ipc);
1818 self
1819 }
1820
1821 pub const fn module_config(&self) -> &TransportRpcModuleConfig {
1823 &self.config
1824 }
1825
1826 pub fn merge_if_module_configured(
1831 &mut self,
1832 module: RethRpcModule,
1833 other: impl Into<Methods>,
1834 ) -> Result<(), RegisterMethodError> {
1835 let other = other.into();
1836 if self.module_config().contains_http(&module) {
1837 self.merge_http(other.clone())?;
1838 }
1839 if self.module_config().contains_ws(&module) {
1840 self.merge_ws(other.clone())?;
1841 }
1842 if self.module_config().contains_ipc(&module) {
1843 self.merge_ipc(other)?;
1844 }
1845
1846 Ok(())
1847 }
1848
1849 pub fn merge_if_module_configured_with<F>(
1856 &mut self,
1857 module: RethRpcModule,
1858 f: F,
1859 ) -> Result<(), RegisterMethodError>
1860 where
1861 F: FnOnce() -> Methods,
1862 {
1863 if !self.module_config().contains_any(&module) {
1865 return Ok(());
1866 }
1867 self.merge_if_module_configured(module, f())
1868 }
1869
1870 pub fn merge_http(&mut self, other: impl Into<Methods>) -> Result<bool, RegisterMethodError> {
1876 if let Some(ref mut http) = self.http {
1877 return http.merge(other.into()).map(|_| true)
1878 }
1879 Ok(false)
1880 }
1881
1882 pub fn merge_ws(&mut self, other: impl Into<Methods>) -> Result<bool, RegisterMethodError> {
1888 if let Some(ref mut ws) = self.ws {
1889 return ws.merge(other.into()).map(|_| true)
1890 }
1891 Ok(false)
1892 }
1893
1894 pub fn merge_ipc(&mut self, other: impl Into<Methods>) -> Result<bool, RegisterMethodError> {
1900 if let Some(ref mut ipc) = self.ipc {
1901 return ipc.merge(other.into()).map(|_| true)
1902 }
1903 Ok(false)
1904 }
1905
1906 pub fn merge_configured(
1910 &mut self,
1911 other: impl Into<Methods>,
1912 ) -> Result<(), RegisterMethodError> {
1913 let other = other.into();
1914 self.merge_http(other.clone())?;
1915 self.merge_ws(other.clone())?;
1916 self.merge_ipc(other)?;
1917 Ok(())
1918 }
1919
1920 pub fn methods_by_module(&self, module: RethRpcModule) -> Methods {
1924 self.methods_by(|name| name.starts_with(module.as_str()))
1925 }
1926
1927 pub fn methods_by<F>(&self, mut filter: F) -> Methods
1931 where
1932 F: FnMut(&str) -> bool,
1933 {
1934 let mut methods = Methods::new();
1935
1936 let mut f =
1938 |name: &str, mm: &Methods| filter(name) && !mm.method_names().any(|m| m == name);
1939
1940 if let Some(m) = self.http_methods(|name| f(name, &methods)) {
1941 let _ = methods.merge(m);
1942 }
1943 if let Some(m) = self.ws_methods(|name| f(name, &methods)) {
1944 let _ = methods.merge(m);
1945 }
1946 if let Some(m) = self.ipc_methods(|name| f(name, &methods)) {
1947 let _ = methods.merge(m);
1948 }
1949 methods
1950 }
1951
1952 pub fn http_methods<F>(&self, filter: F) -> Option<Methods>
1956 where
1957 F: FnMut(&str) -> bool,
1958 {
1959 self.http.as_ref().map(|module| methods_by(module, filter))
1960 }
1961
1962 pub fn ws_methods<F>(&self, filter: F) -> Option<Methods>
1966 where
1967 F: FnMut(&str) -> bool,
1968 {
1969 self.ws.as_ref().map(|module| methods_by(module, filter))
1970 }
1971
1972 pub fn ipc_methods<F>(&self, filter: F) -> Option<Methods>
1976 where
1977 F: FnMut(&str) -> bool,
1978 {
1979 self.ipc.as_ref().map(|module| methods_by(module, filter))
1980 }
1981
1982 pub fn remove_http_method(&mut self, method_name: &'static str) -> bool {
1990 self.http.as_mut().is_some_and(|module| module.remove_method(method_name).is_some())
1991 }
1992
1993 pub fn remove_http_methods(&mut self, methods: impl IntoIterator<Item = &'static str>) {
1995 for name in methods {
1996 self.remove_http_method(name);
1997 }
1998 }
1999
2000 pub fn remove_ws_method(&mut self, method_name: &'static str) -> bool {
2008 self.ws.as_mut().is_some_and(|module| module.remove_method(method_name).is_some())
2009 }
2010
2011 pub fn remove_ws_methods(&mut self, methods: impl IntoIterator<Item = &'static str>) {
2013 for name in methods {
2014 self.remove_ws_method(name);
2015 }
2016 }
2017
2018 pub fn remove_ipc_method(&mut self, method_name: &'static str) -> bool {
2026 self.ipc.as_mut().is_some_and(|module| module.remove_method(method_name).is_some())
2027 }
2028
2029 pub fn remove_ipc_methods(&mut self, methods: impl IntoIterator<Item = &'static str>) {
2031 for name in methods {
2032 self.remove_ipc_method(name);
2033 }
2034 }
2035
2036 pub fn remove_method_from_configured(&mut self, method_name: &'static str) -> bool {
2040 let http_removed = self.remove_http_method(method_name);
2041 let ws_removed = self.remove_ws_method(method_name);
2042 let ipc_removed = self.remove_ipc_method(method_name);
2043
2044 http_removed || ws_removed || ipc_removed
2045 }
2046
2047 pub fn rename(
2051 &mut self,
2052 old_name: &'static str,
2053 new_method: impl Into<Methods>,
2054 ) -> Result<(), RegisterMethodError> {
2055 self.remove_method_from_configured(old_name);
2057
2058 self.merge_configured(new_method)
2060 }
2061
2062 pub fn replace_http(&mut self, other: impl Into<Methods>) -> Result<bool, RegisterMethodError> {
2069 let other = other.into();
2070 self.remove_http_methods(other.method_names());
2071 self.merge_http(other)
2072 }
2073
2074 pub fn replace_ipc(&mut self, other: impl Into<Methods>) -> Result<bool, RegisterMethodError> {
2081 let other = other.into();
2082 self.remove_ipc_methods(other.method_names());
2083 self.merge_ipc(other)
2084 }
2085
2086 pub fn replace_ws(&mut self, other: impl Into<Methods>) -> Result<bool, RegisterMethodError> {
2093 let other = other.into();
2094 self.remove_ws_methods(other.method_names());
2095 self.merge_ws(other)
2096 }
2097
2098 pub fn replace_configured(
2102 &mut self,
2103 other: impl Into<Methods>,
2104 ) -> Result<bool, RegisterMethodError> {
2105 let other = other.into();
2106 self.replace_http(other.clone())?;
2107 self.replace_ws(other.clone())?;
2108 self.replace_ipc(other)?;
2109 Ok(true)
2110 }
2111
2112 pub fn add_or_replace_http(
2116 &mut self,
2117 other: impl Into<Methods>,
2118 ) -> Result<bool, RegisterMethodError> {
2119 let other = other.into();
2120 self.remove_http_methods(other.method_names());
2121 self.merge_http(other)
2122 }
2123
2124 pub fn add_or_replace_ws(
2128 &mut self,
2129 other: impl Into<Methods>,
2130 ) -> Result<bool, RegisterMethodError> {
2131 let other = other.into();
2132 self.remove_ws_methods(other.method_names());
2133 self.merge_ws(other)
2134 }
2135
2136 pub fn add_or_replace_ipc(
2140 &mut self,
2141 other: impl Into<Methods>,
2142 ) -> Result<bool, RegisterMethodError> {
2143 let other = other.into();
2144 self.remove_ipc_methods(other.method_names());
2145 self.merge_ipc(other)
2146 }
2147
2148 pub fn add_or_replace_configured(
2150 &mut self,
2151 other: impl Into<Methods>,
2152 ) -> Result<(), RegisterMethodError> {
2153 let other = other.into();
2154 self.add_or_replace_http(other.clone())?;
2155 self.add_or_replace_ws(other.clone())?;
2156 self.add_or_replace_ipc(other)?;
2157 Ok(())
2158 }
2159 pub fn add_or_replace_if_module_configured(
2162 &mut self,
2163 module: RethRpcModule,
2164 other: impl Into<Methods>,
2165 ) -> Result<(), RegisterMethodError> {
2166 let other = other.into();
2167 if self.module_config().contains_http(&module) {
2168 self.add_or_replace_http(other.clone())?;
2169 }
2170 if self.module_config().contains_ws(&module) {
2171 self.add_or_replace_ws(other.clone())?;
2172 }
2173 if self.module_config().contains_ipc(&module) {
2174 self.add_or_replace_ipc(other)?;
2175 }
2176 Ok(())
2177 }
2178}
2179
2180fn methods_by<T, F>(module: &RpcModule<T>, mut filter: F) -> Methods
2182where
2183 F: FnMut(&str) -> bool,
2184{
2185 let mut methods = Methods::new();
2186 let method_names = module.method_names().filter(|name| filter(name));
2187
2188 for name in method_names {
2189 if let Some(matched_method) = module.method(name).cloned() {
2190 let _ = methods.verify_and_insert(name, matched_method);
2191 }
2192 }
2193
2194 methods
2195}
2196
2197#[derive(Clone, Debug)]
2202#[must_use = "Server stops if dropped"]
2203pub struct RpcServerHandle {
2204 http_local_addr: Option<SocketAddr>,
2206 ws_local_addr: Option<SocketAddr>,
2207 http: Option<ServerHandle>,
2208 ws: Option<ServerHandle>,
2209 ipc_endpoint: Option<String>,
2210 ipc: Option<jsonrpsee::server::ServerHandle>,
2211 jwt_secret: Option<JwtSecret>,
2212}
2213
2214impl RpcServerHandle {
2217 fn bearer_token(&self) -> Option<String> {
2219 self.jwt_secret.as_ref().map(|secret| {
2220 format!(
2221 "Bearer {}",
2222 secret
2223 .encode(&Claims {
2224 iat: (SystemTime::now().duration_since(UNIX_EPOCH).unwrap() +
2225 Duration::from_secs(60))
2226 .as_secs(),
2227 exp: None,
2228 })
2229 .unwrap()
2230 )
2231 })
2232 }
2233 pub const fn http_local_addr(&self) -> Option<SocketAddr> {
2235 self.http_local_addr
2236 }
2237
2238 pub const fn ws_local_addr(&self) -> Option<SocketAddr> {
2240 self.ws_local_addr
2241 }
2242
2243 pub fn stop(self) -> Result<(), AlreadyStoppedError> {
2245 let shared_server =
2248 self.http_local_addr.is_some() && self.http_local_addr == self.ws_local_addr;
2249
2250 if let Some(handle) = self.http {
2251 handle.stop()?
2252 }
2253
2254 if let Some(handle) = self.ws.filter(|_| !shared_server) {
2255 handle.stop()?
2256 }
2257
2258 if let Some(handle) = self.ipc {
2259 handle.stop()?
2260 }
2261
2262 Ok(())
2263 }
2264
2265 pub fn ipc_endpoint(&self) -> Option<String> {
2267 self.ipc_endpoint.clone()
2268 }
2269
2270 pub fn http_url(&self) -> Option<String> {
2272 self.http_local_addr.map(|addr| format!("http://{addr}"))
2273 }
2274
2275 pub fn ws_url(&self) -> Option<String> {
2277 self.ws_local_addr.map(|addr| format!("ws://{addr}"))
2278 }
2279
2280 pub fn http_client(&self) -> Option<RpcClient> {
2284 let url = self.http_url()?.parse().expect("valid http url");
2285 let client = match self.jwt_secret {
2286 Some(secret) => ClientBuilder::default().layer(AuthClientLayer::new(secret)).http(url),
2287 None => ClientBuilder::default().http(url),
2288 };
2289 Some(client)
2290 }
2291
2292 pub async fn ws_client(&self) -> Option<RpcClient> {
2294 let mut connect = WsConnect::new(self.ws_url()?);
2295 if let Some(token) = self.bearer_token() {
2296 connect = connect.with_auth(Authorization::raw(token));
2297 }
2298 let client =
2299 ClientBuilder::default().ws(connect).await.expect("failed to create ws client");
2300 Some(client)
2301 }
2302
2303 pub fn eth_http_provider(
2305 &self,
2306 ) -> Option<impl Provider<alloy_network::Ethereum> + Clone + Unpin + 'static> {
2307 self.new_http_provider_for()
2308 }
2309
2310 pub fn eth_http_provider_with_wallet<W>(
2313 &self,
2314 wallet: W,
2315 ) -> Option<impl Provider<alloy_network::Ethereum> + Clone + Unpin + 'static>
2316 where
2317 W: IntoWallet<alloy_network::Ethereum, NetworkWallet: Clone + Unpin + 'static>,
2318 {
2319 let rpc_url = self.http_url()?;
2320 let provider =
2321 ProviderBuilder::new().wallet(wallet).connect_http(rpc_url.parse().expect("valid url"));
2322 Some(provider)
2323 }
2324
2325 pub fn new_http_provider_for<N>(&self) -> Option<impl Provider<N> + Clone + Unpin + 'static>
2330 where
2331 N: RecommendedFillers<RecommendedFillers: Unpin>,
2332 {
2333 let rpc_url = self.http_url()?;
2334 let provider = ProviderBuilder::default()
2335 .with_recommended_fillers()
2336 .connect_http(rpc_url.parse().expect("valid url"));
2337 Some(provider)
2338 }
2339
2340 pub async fn eth_ws_provider(
2342 &self,
2343 ) -> Option<impl Provider<alloy_network::Ethereum> + Clone + Unpin + 'static> {
2344 self.new_ws_provider_for().await
2345 }
2346
2347 pub async fn eth_ws_provider_with_wallet<W>(
2350 &self,
2351 wallet: W,
2352 ) -> Option<impl Provider<alloy_network::Ethereum> + Clone + Unpin + 'static>
2353 where
2354 W: IntoWallet<alloy_network::Ethereum, NetworkWallet: Clone + Unpin + 'static>,
2355 {
2356 let rpc_url = self.ws_url()?;
2357 let provider = ProviderBuilder::new()
2358 .wallet(wallet)
2359 .connect(&rpc_url)
2360 .await
2361 .expect("failed to create ws client");
2362 Some(provider)
2363 }
2364
2365 pub async fn new_ws_provider_for<N>(&self) -> Option<impl Provider<N> + Clone + Unpin + 'static>
2370 where
2371 N: RecommendedFillers<RecommendedFillers: Unpin>,
2372 {
2373 let rpc_url = self.ws_url()?;
2374 let provider = ProviderBuilder::default()
2375 .with_recommended_fillers()
2376 .connect(&rpc_url)
2377 .await
2378 .expect("failed to create ws client");
2379 Some(provider)
2380 }
2381
2382 pub async fn eth_ipc_provider(
2384 &self,
2385 ) -> Option<impl Provider<alloy_network::Ethereum> + Clone + Unpin + 'static> {
2386 self.new_ipc_provider_for().await
2387 }
2388
2389 pub async fn new_ipc_provider_for<N>(
2394 &self,
2395 ) -> Option<impl Provider<N> + Clone + Unpin + 'static>
2396 where
2397 N: RecommendedFillers<RecommendedFillers: Unpin>,
2398 {
2399 let rpc_url = self.ipc_endpoint()?;
2400 let provider = ProviderBuilder::default()
2401 .with_recommended_fillers()
2402 .connect(&rpc_url)
2403 .await
2404 .expect("failed to create ipc client");
2405 Some(provider)
2406 }
2407}
2408
2409#[cfg(test)]
2410mod tests {
2411 use super::*;
2412
2413 #[test]
2414 fn parse_eth_call_bundle_selection() {
2415 let selection = "eth,admin,debug".parse::<RpcModuleSelection>().unwrap();
2416 assert_eq!(
2417 selection,
2418 RpcModuleSelection::Selection(
2419 [RethRpcModule::Eth, RethRpcModule::Admin, RethRpcModule::Debug,].into()
2420 )
2421 );
2422 }
2423
2424 #[test]
2425 fn parse_rpc_module_selection() {
2426 let selection = "all".parse::<RpcModuleSelection>().unwrap();
2427 assert_eq!(selection, RpcModuleSelection::All);
2428 }
2429
2430 #[test]
2431 fn parse_rpc_module_selection_none() {
2432 let selection = "none".parse::<RpcModuleSelection>().unwrap();
2433 assert_eq!(selection, RpcModuleSelection::Selection(Default::default()));
2434 }
2435
2436 #[test]
2437 fn parse_rpc_unique_module_selection() {
2438 let selection = "eth,admin,eth,net".parse::<RpcModuleSelection>().unwrap();
2439 assert_eq!(
2440 selection,
2441 RpcModuleSelection::Selection(
2442 [RethRpcModule::Eth, RethRpcModule::Admin, RethRpcModule::Net,].into()
2443 )
2444 );
2445 }
2446
2447 #[test]
2448 fn identical_selection() {
2449 assert!(RpcModuleSelection::are_identical(
2450 Some(&RpcModuleSelection::All),
2451 Some(&RpcModuleSelection::All),
2452 ));
2453 assert!(!RpcModuleSelection::are_identical(
2454 Some(&RpcModuleSelection::All),
2455 Some(&RpcModuleSelection::Standard),
2456 ));
2457 assert!(RpcModuleSelection::are_identical(
2458 Some(&RpcModuleSelection::Selection(RpcModuleSelection::Standard.to_selection())),
2459 Some(&RpcModuleSelection::Standard),
2460 ));
2461 assert!(RpcModuleSelection::are_identical(
2462 Some(&RpcModuleSelection::Selection([RethRpcModule::Eth].into())),
2463 Some(&RpcModuleSelection::Selection([RethRpcModule::Eth].into())),
2464 ));
2465 assert!(RpcModuleSelection::are_identical(
2466 None,
2467 Some(&RpcModuleSelection::Selection(Default::default())),
2468 ));
2469 assert!(RpcModuleSelection::are_identical(
2470 Some(&RpcModuleSelection::Selection(Default::default())),
2471 None,
2472 ));
2473 assert!(RpcModuleSelection::are_identical(None, None));
2474 }
2475
2476 #[test]
2477 fn test_rpc_module_str() {
2478 macro_rules! assert_rpc_module {
2479 ($($s:expr => $v:expr,)*) => {
2480 $(
2481 let val: RethRpcModule = $s.parse().unwrap();
2482 assert_eq!(val, $v);
2483 assert_eq!(val.to_string(), $s);
2484 )*
2485 };
2486 }
2487 assert_rpc_module!
2488 (
2489 "admin" => RethRpcModule::Admin,
2490 "debug" => RethRpcModule::Debug,
2491 "eth" => RethRpcModule::Eth,
2492 "net" => RethRpcModule::Net,
2493 "trace" => RethRpcModule::Trace,
2494 "web3" => RethRpcModule::Web3,
2495 "rpc" => RethRpcModule::Rpc,
2496 "ots" => RethRpcModule::Ots,
2497 "reth" => RethRpcModule::Reth,
2498 );
2499 }
2500
2501 #[test]
2502 fn test_default_selection() {
2503 let selection = RpcModuleSelection::Standard.to_selection();
2504 assert_eq!(selection, [RethRpcModule::Eth, RethRpcModule::Net, RethRpcModule::Web3].into())
2505 }
2506
2507 #[test]
2508 fn test_create_rpc_module_config() {
2509 let selection = vec!["eth", "admin"];
2510 let config = RpcModuleSelection::try_from_selection(selection).unwrap();
2511 assert_eq!(
2512 config,
2513 RpcModuleSelection::Selection([RethRpcModule::Eth, RethRpcModule::Admin].into())
2514 );
2515 }
2516
2517 #[test]
2518 fn test_configure_transport_config() {
2519 let config = TransportRpcModuleConfig::default()
2520 .with_http([RethRpcModule::Eth, RethRpcModule::Admin]);
2521 assert_eq!(
2522 config,
2523 TransportRpcModuleConfig {
2524 http: Some(RpcModuleSelection::Selection(
2525 [RethRpcModule::Eth, RethRpcModule::Admin].into()
2526 )),
2527 ws: None,
2528 ipc: None,
2529 config: None,
2530 }
2531 )
2532 }
2533
2534 #[test]
2535 fn test_configure_transport_config_none() {
2536 let config = TransportRpcModuleConfig::default().with_http(Vec::<RethRpcModule>::new());
2537 assert_eq!(
2538 config,
2539 TransportRpcModuleConfig {
2540 http: Some(RpcModuleSelection::Selection(Default::default())),
2541 ws: None,
2542 ipc: None,
2543 config: None,
2544 }
2545 )
2546 }
2547
2548 fn create_test_module() -> RpcModule<()> {
2549 let mut module = RpcModule::new(());
2550 module.register_method("anything", |_, _| "succeed").unwrap();
2551 module
2552 }
2553
2554 #[test]
2555 fn test_remove_http_method() {
2556 let mut modules =
2557 TransportRpcModules { http: Some(create_test_module()), ..Default::default() };
2558 assert!(modules.remove_http_method("anything"));
2560
2561 assert!(!modules.remove_http_method("non_existent_method"));
2563
2564 assert!(modules.http.as_ref().unwrap().method("anything").is_none());
2566 }
2567
2568 #[test]
2569 fn test_remove_ws_method() {
2570 let mut modules =
2571 TransportRpcModules { ws: Some(create_test_module()), ..Default::default() };
2572
2573 assert!(modules.remove_ws_method("anything"));
2575
2576 assert!(!modules.remove_ws_method("non_existent_method"));
2578
2579 assert!(modules.ws.as_ref().unwrap().method("anything").is_none());
2581 }
2582
2583 #[test]
2584 fn test_remove_ipc_method() {
2585 let mut modules =
2586 TransportRpcModules { ipc: Some(create_test_module()), ..Default::default() };
2587
2588 assert!(modules.remove_ipc_method("anything"));
2590
2591 assert!(!modules.remove_ipc_method("non_existent_method"));
2593
2594 assert!(modules.ipc.as_ref().unwrap().method("anything").is_none());
2596 }
2597
2598 #[test]
2599 fn test_remove_method_from_configured() {
2600 let mut modules = TransportRpcModules {
2601 http: Some(create_test_module()),
2602 ws: Some(create_test_module()),
2603 ipc: Some(create_test_module()),
2604 ..Default::default()
2605 };
2606
2607 assert!(modules.remove_method_from_configured("anything"));
2609
2610 assert!(!modules.remove_method_from_configured("anything"));
2612
2613 assert!(!modules.remove_method_from_configured("non_existent_method"));
2615
2616 assert!(modules.http.as_ref().unwrap().method("anything").is_none());
2618 assert!(modules.ws.as_ref().unwrap().method("anything").is_none());
2619 assert!(modules.ipc.as_ref().unwrap().method("anything").is_none());
2620 }
2621
2622 #[test]
2623 fn test_transport_rpc_module_rename() {
2624 let mut modules = TransportRpcModules {
2625 http: Some(create_test_module()),
2626 ws: Some(create_test_module()),
2627 ipc: Some(create_test_module()),
2628 ..Default::default()
2629 };
2630
2631 assert!(modules.http.as_ref().unwrap().method("anything").is_some());
2633 assert!(modules.ws.as_ref().unwrap().method("anything").is_some());
2634 assert!(modules.ipc.as_ref().unwrap().method("anything").is_some());
2635
2636 assert!(modules.http.as_ref().unwrap().method("something").is_none());
2638 assert!(modules.ws.as_ref().unwrap().method("something").is_none());
2639 assert!(modules.ipc.as_ref().unwrap().method("something").is_none());
2640
2641 let mut other_module = RpcModule::new(());
2643 other_module.register_method("something", |_, _| "fails").unwrap();
2644
2645 modules.rename("anything", other_module).expect("rename failed");
2647
2648 assert!(modules.http.as_ref().unwrap().method("anything").is_none());
2650 assert!(modules.ws.as_ref().unwrap().method("anything").is_none());
2651 assert!(modules.ipc.as_ref().unwrap().method("anything").is_none());
2652
2653 assert!(modules.http.as_ref().unwrap().method("something").is_some());
2655 assert!(modules.ws.as_ref().unwrap().method("something").is_some());
2656 assert!(modules.ipc.as_ref().unwrap().method("something").is_some());
2657 }
2658
2659 #[test]
2660 fn test_replace_http_method() {
2661 let mut modules =
2662 TransportRpcModules { http: Some(create_test_module()), ..Default::default() };
2663
2664 let mut other_module = RpcModule::new(());
2665 other_module.register_method("something", |_, _| "fails").unwrap();
2666
2667 assert!(modules.replace_http(other_module.clone()).unwrap());
2668
2669 assert!(modules.http.as_ref().unwrap().method("something").is_some());
2670
2671 other_module.register_method("anything", |_, _| "fails").unwrap();
2672 assert!(modules.replace_http(other_module.clone()).unwrap());
2673
2674 assert!(modules.http.as_ref().unwrap().method("anything").is_some());
2675 }
2676 #[test]
2677 fn test_replace_ipc_method() {
2678 let mut modules =
2679 TransportRpcModules { ipc: Some(create_test_module()), ..Default::default() };
2680
2681 let mut other_module = RpcModule::new(());
2682 other_module.register_method("something", |_, _| "fails").unwrap();
2683
2684 assert!(modules.replace_ipc(other_module.clone()).unwrap());
2685
2686 assert!(modules.ipc.as_ref().unwrap().method("something").is_some());
2687
2688 other_module.register_method("anything", |_, _| "fails").unwrap();
2689 assert!(modules.replace_ipc(other_module.clone()).unwrap());
2690
2691 assert!(modules.ipc.as_ref().unwrap().method("anything").is_some());
2692 }
2693 #[test]
2694 fn test_replace_ws_method() {
2695 let mut modules =
2696 TransportRpcModules { ws: Some(create_test_module()), ..Default::default() };
2697
2698 let mut other_module = RpcModule::new(());
2699 other_module.register_method("something", |_, _| "fails").unwrap();
2700
2701 assert!(modules.replace_ws(other_module.clone()).unwrap());
2702
2703 assert!(modules.ws.as_ref().unwrap().method("something").is_some());
2704
2705 other_module.register_method("anything", |_, _| "fails").unwrap();
2706 assert!(modules.replace_ws(other_module.clone()).unwrap());
2707
2708 assert!(modules.ws.as_ref().unwrap().method("anything").is_some());
2709 }
2710
2711 #[test]
2712 fn test_replace_configured() {
2713 let mut modules = TransportRpcModules {
2714 http: Some(create_test_module()),
2715 ws: Some(create_test_module()),
2716 ipc: Some(create_test_module()),
2717 ..Default::default()
2718 };
2719 let mut other_module = RpcModule::new(());
2720 other_module.register_method("something", |_, _| "fails").unwrap();
2721
2722 assert!(modules.replace_configured(other_module).unwrap());
2723
2724 assert!(modules.http.as_ref().unwrap().method("something").is_some());
2726 assert!(modules.ipc.as_ref().unwrap().method("something").is_some());
2727 assert!(modules.ws.as_ref().unwrap().method("something").is_some());
2728
2729 assert!(modules.http.as_ref().unwrap().method("anything").is_some());
2730 assert!(modules.ipc.as_ref().unwrap().method("anything").is_some());
2731 assert!(modules.ws.as_ref().unwrap().method("anything").is_some());
2732 }
2733
2734 #[test]
2735 fn test_add_or_replace_if_module_configured() {
2736 let config = TransportRpcModuleConfig::default()
2738 .with_http([RethRpcModule::Eth])
2739 .with_ws([RethRpcModule::Eth]);
2740
2741 let mut http_module = RpcModule::new(());
2743 http_module.register_method("eth_existing", |_, _| "original").unwrap();
2744
2745 let mut ws_module = RpcModule::new(());
2747 ws_module.register_method("eth_existing", |_, _| "original").unwrap();
2748
2749 let ipc_module = RpcModule::new(());
2751
2752 let mut modules = TransportRpcModules {
2754 config,
2755 http: Some(http_module),
2756 ws: Some(ws_module),
2757 ipc: Some(ipc_module),
2758 };
2759
2760 let mut new_module = RpcModule::new(());
2762 new_module.register_method("eth_existing", |_, _| "replaced").unwrap(); new_module.register_method("eth_new", |_, _| "added").unwrap(); let new_methods: Methods = new_module.into();
2765
2766 let result = modules.add_or_replace_if_module_configured(RethRpcModule::Eth, new_methods);
2768 assert!(result.is_ok(), "Function should succeed");
2769
2770 let http = modules.http.as_ref().unwrap();
2772 assert!(http.method("eth_existing").is_some());
2773 assert!(http.method("eth_new").is_some());
2774
2775 let ws = modules.ws.as_ref().unwrap();
2777 assert!(ws.method("eth_existing").is_some());
2778 assert!(ws.method("eth_new").is_some());
2779
2780 let ipc = modules.ipc.as_ref().unwrap();
2782 assert!(ipc.method("eth_existing").is_none());
2783 assert!(ipc.method("eth_new").is_none());
2784 }
2785
2786 #[test]
2787 fn test_merge_if_module_configured_with_lazy_evaluation() {
2788 let config = TransportRpcModuleConfig::default().with_http([RethRpcModule::Eth]);
2790
2791 let mut modules =
2792 TransportRpcModules { config, http: Some(RpcModule::new(())), ws: None, ipc: None };
2793
2794 let mut closure_called = false;
2796
2797 let result = modules.merge_if_module_configured_with(RethRpcModule::Eth, || {
2799 closure_called = true;
2800 let mut methods = RpcModule::new(());
2801 methods.register_method("eth_test", |_, _| "test").unwrap();
2802 methods.into()
2803 });
2804
2805 assert!(result.is_ok());
2806 assert!(closure_called, "Closure should be called when module is configured");
2807 assert!(modules.http.as_ref().unwrap().method("eth_test").is_some());
2808
2809 closure_called = false;
2811 let result = modules.merge_if_module_configured_with(RethRpcModule::Debug, || {
2812 closure_called = true;
2813 RpcModule::new(()).into()
2814 });
2815
2816 assert!(result.is_ok());
2817 assert!(!closure_called, "Closure should NOT be called when module is not configured");
2818 }
2819
2820 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
2821 async fn test_stop_rpc_servers() {
2822 let modules = TransportRpcModules::default()
2823 .with_http(create_test_module())
2824 .with_ws(create_test_module());
2825 for config in [
2826 RpcServerConfig::http(Default::default()),
2827 RpcServerConfig::ws(Default::default()),
2828 RpcServerConfig::http(Default::default()).with_ws(Default::default()),
2829 ] {
2830 let addr = SocketAddr::from((Ipv4Addr::LOCALHOST, 0));
2831 let handle =
2832 config.with_http_address(addr).with_ws_address(addr).start(&modules).await.unwrap();
2833 let servers = handle.http.iter().chain(handle.ws.iter()).cloned().collect::<Vec<_>>();
2834 let cloned = handle.clone();
2835
2836 handle.stop().unwrap();
2837 for server in servers {
2838 tokio::time::timeout(Duration::from_secs(5), server.stopped()).await.unwrap();
2839 }
2840 assert!(matches!(cloned.stop(), Err(AlreadyStoppedError)));
2841 }
2842 }
2843}