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, doc_auto_cfg))]
21
22use crate::{auth::AuthRpcModule, error::WsHttpSamePortError, metrics::RpcRequestMetrics};
23use alloy_network::{Ethereum, IntoWallet};
24use alloy_provider::{fillers::RecommendedFillers, Provider, ProviderBuilder};
25use core::marker::PhantomData;
26use error::{ConflictingModules, RpcError, ServerKind};
27use http::{header::AUTHORIZATION, HeaderMap};
28use jsonrpsee::{
29 core::RegisterMethodError,
30 server::{middleware::rpc::RpcServiceBuilder, AlreadyStoppedError, IdProvider, ServerHandle},
31 Methods, RpcModule,
32};
33use reth_chainspec::{ChainSpecProvider, EthereumHardforks};
34use reth_consensus::{ConsensusError, FullConsensus};
35use reth_evm::ConfigureEvm;
36use reth_network_api::{noop::NoopNetwork, NetworkInfo, Peers};
37use reth_primitives_traits::NodePrimitives;
38use reth_rpc::{
39 AdminApi, DebugApi, EngineEthApi, EthApi, EthApiBuilder, EthBundle, MinerApi, NetApi,
40 OtterscanApi, RPCApi, RethApi, TraceApi, TxPoolApi, ValidationApiConfig, Web3Api,
41};
42use reth_rpc_api::servers::*;
43use reth_rpc_eth_api::{
44 helpers::{
45 pending_block::PendingEnvBuilder, Call, EthApiSpec, EthTransactions, LoadPendingBlock,
46 TraceExt,
47 },
48 node::RpcNodeCoreAdapter,
49 EthApiServer, EthApiTypes, FullEthApiServer, RpcBlock, RpcConvert, RpcConverter, RpcHeader,
50 RpcNodeCore, RpcReceipt, RpcTransaction, RpcTxReq,
51};
52use reth_rpc_eth_types::{receipt::EthReceiptConverter, EthConfig, EthSubscriptionIdProvider};
53use reth_rpc_layer::{AuthLayer, Claims, CompressionLayer, JwtAuthValidator, JwtSecret};
54use reth_storage_api::{
55 AccountReader, BlockReader, ChangeSetReader, FullRpcProvider, ProviderBlock,
56 StateProviderFactory,
57};
58use reth_tasks::{pool::BlockingTaskGuard, TaskSpawner, TokioTaskExecutor};
59use reth_transaction_pool::{noop::NoopTransactionPool, TransactionPool};
60use serde::{Deserialize, Serialize};
61use std::{
62 collections::HashMap,
63 fmt::Debug,
64 net::{Ipv4Addr, SocketAddr, SocketAddrV4},
65 time::{Duration, SystemTime, UNIX_EPOCH},
66};
67use tower_http::cors::CorsLayer;
68
69pub use cors::CorsDomainError;
70
71pub use jsonrpsee::server::ServerBuilder;
73use jsonrpsee::server::ServerConfigBuilder;
74pub use reth_ipc::server::{
75 Builder as IpcServerBuilder, RpcServiceBuilder as IpcRpcServiceBuilder,
76};
77pub use reth_rpc_server_types::{constants, RethRpcModule, RpcModuleSelection};
78pub use tower::layer::util::{Identity, Stack};
79
80pub mod auth;
82
83pub mod config;
85
86pub mod middleware;
88
89mod cors;
91
92pub mod error;
94
95pub mod eth;
97pub use eth::EthHandlers;
98
99mod metrics;
101use crate::middleware::RethRpcMiddleware;
102pub use metrics::{MeteredRequestFuture, RpcRequestMetricsService};
103use reth_chain_state::CanonStateSubscriptions;
104use reth_rpc::eth::sim_bundle::EthSimBundle;
105
106pub mod rate_limiter;
108
109#[derive(Debug, Clone)]
113pub struct RpcModuleBuilder<N, Provider, Pool, Network, EvmConfig, Consensus> {
114 provider: Provider,
116 pool: Pool,
118 network: Network,
120 executor: Box<dyn TaskSpawner + 'static>,
122 evm_config: EvmConfig,
124 consensus: Consensus,
126 _primitives: PhantomData<N>,
128}
129
130impl<N, Provider, Pool, Network, EvmConfig, Consensus>
133 RpcModuleBuilder<N, Provider, Pool, Network, EvmConfig, Consensus>
134{
135 pub const fn new(
137 provider: Provider,
138 pool: Pool,
139 network: Network,
140 executor: Box<dyn TaskSpawner + 'static>,
141 evm_config: EvmConfig,
142 consensus: Consensus,
143 ) -> Self {
144 Self { provider, pool, network, executor, evm_config, consensus, _primitives: PhantomData }
145 }
146
147 pub fn with_provider<P>(
149 self,
150 provider: P,
151 ) -> RpcModuleBuilder<N, P, Pool, Network, EvmConfig, Consensus> {
152 let Self { pool, network, executor, evm_config, consensus, _primitives, .. } = self;
153 RpcModuleBuilder { provider, network, pool, executor, evm_config, consensus, _primitives }
154 }
155
156 pub fn with_pool<P>(
158 self,
159 pool: P,
160 ) -> RpcModuleBuilder<N, Provider, P, Network, EvmConfig, Consensus> {
161 let Self { provider, network, executor, evm_config, consensus, _primitives, .. } = self;
162 RpcModuleBuilder { provider, network, pool, executor, evm_config, consensus, _primitives }
163 }
164
165 pub fn with_noop_pool(
171 self,
172 ) -> RpcModuleBuilder<N, Provider, NoopTransactionPool, Network, EvmConfig, Consensus> {
173 let Self { provider, executor, network, evm_config, consensus, _primitives, .. } = self;
174 RpcModuleBuilder {
175 provider,
176 executor,
177 network,
178 evm_config,
179 pool: NoopTransactionPool::default(),
180 consensus,
181 _primitives,
182 }
183 }
184
185 pub fn with_network<Net>(
187 self,
188 network: Net,
189 ) -> RpcModuleBuilder<N, Provider, Pool, Net, EvmConfig, Consensus> {
190 let Self { provider, pool, executor, evm_config, consensus, _primitives, .. } = self;
191 RpcModuleBuilder { provider, network, pool, executor, evm_config, consensus, _primitives }
192 }
193
194 pub fn with_noop_network(
200 self,
201 ) -> RpcModuleBuilder<N, Provider, Pool, NoopNetwork, EvmConfig, Consensus> {
202 let Self { provider, pool, executor, evm_config, consensus, _primitives, .. } = self;
203 RpcModuleBuilder {
204 provider,
205 pool,
206 executor,
207 network: NoopNetwork::default(),
208 evm_config,
209 consensus,
210 _primitives,
211 }
212 }
213
214 pub fn with_executor(self, executor: Box<dyn TaskSpawner + 'static>) -> Self {
216 let Self { pool, network, provider, evm_config, consensus, _primitives, .. } = self;
217 Self { provider, network, pool, executor, evm_config, consensus, _primitives }
218 }
219
220 pub fn with_tokio_executor(self) -> Self {
225 let Self { pool, network, provider, evm_config, consensus, _primitives, .. } = self;
226 Self {
227 provider,
228 network,
229 pool,
230 executor: Box::new(TokioTaskExecutor::default()),
231 evm_config,
232 consensus,
233 _primitives,
234 }
235 }
236
237 pub fn with_evm_config<E>(
239 self,
240 evm_config: E,
241 ) -> RpcModuleBuilder<N, Provider, Pool, Network, E, Consensus> {
242 let Self { provider, pool, executor, network, consensus, _primitives, .. } = self;
243 RpcModuleBuilder { provider, network, pool, executor, evm_config, consensus, _primitives }
244 }
245
246 pub fn with_consensus<C>(
248 self,
249 consensus: C,
250 ) -> RpcModuleBuilder<N, Provider, Pool, Network, EvmConfig, C> {
251 let Self { provider, network, pool, executor, evm_config, _primitives, .. } = self;
252 RpcModuleBuilder { provider, network, pool, executor, evm_config, consensus, _primitives }
253 }
254
255 #[expect(clippy::type_complexity)]
257 pub fn eth_api_builder<ChainSpec>(
258 &self,
259 ) -> EthApiBuilder<
260 RpcNodeCoreAdapter<Provider, Pool, Network, EvmConfig>,
261 RpcConverter<Ethereum, EvmConfig, EthReceiptConverter<ChainSpec>>,
262 >
263 where
264 Provider: Clone,
265 Pool: Clone,
266 Network: Clone,
267 EvmConfig: Clone,
268 RpcNodeCoreAdapter<Provider, Pool, Network, EvmConfig>:
269 RpcNodeCore<Provider: ChainSpecProvider<ChainSpec = ChainSpec>, Evm = EvmConfig>,
270 {
271 EthApiBuilder::new(
272 self.provider.clone(),
273 self.pool.clone(),
274 self.network.clone(),
275 self.evm_config.clone(),
276 )
277 }
278
279 #[expect(clippy::type_complexity)]
285 pub fn bootstrap_eth_api<ChainSpec>(
286 &self,
287 ) -> EthApi<
288 RpcNodeCoreAdapter<Provider, Pool, Network, EvmConfig>,
289 RpcConverter<Ethereum, EvmConfig, EthReceiptConverter<ChainSpec>>,
290 >
291 where
292 Provider: Clone,
293 Pool: Clone,
294 Network: Clone,
295 EvmConfig: ConfigureEvm + Clone,
296 RpcNodeCoreAdapter<Provider, Pool, Network, EvmConfig>:
297 RpcNodeCore<Provider: ChainSpecProvider<ChainSpec = ChainSpec>, Evm = EvmConfig>,
298 RpcConverter<Ethereum, EvmConfig, EthReceiptConverter<ChainSpec>>: RpcConvert,
299 (): PendingEnvBuilder<EvmConfig>,
300 {
301 self.eth_api_builder().build()
302 }
303}
304
305impl<N, Provider, Pool, Network, EvmConfig, Consensus>
306 RpcModuleBuilder<N, Provider, Pool, Network, EvmConfig, Consensus>
307where
308 N: NodePrimitives,
309 Provider: FullRpcProvider<Block = N::Block, Receipt = N::Receipt, Header = N::BlockHeader>
310 + CanonStateSubscriptions<Primitives = N>
311 + AccountReader
312 + ChangeSetReader,
313 Pool: TransactionPool + 'static,
314 Network: NetworkInfo + Peers + Clone + 'static,
315 EvmConfig: ConfigureEvm<Primitives = N> + 'static,
316 Consensus: FullConsensus<N, Error = ConsensusError> + Clone + 'static,
317{
318 pub fn build_with_auth_server<EthApi>(
325 self,
326 module_config: TransportRpcModuleConfig,
327 engine: impl IntoEngineApiRpcModule,
328 eth: EthApi,
329 ) -> (
330 TransportRpcModules,
331 AuthRpcModule,
332 RpcRegistryInner<Provider, Pool, Network, EthApi, EvmConfig, Consensus>,
333 )
334 where
335 EthApi: FullEthApiServer<Provider = Provider, Pool = Pool>,
336 {
337 let Self { provider, pool, network, executor, consensus, evm_config, .. } = self;
338
339 let config = module_config.config.clone().unwrap_or_default();
340
341 let mut registry = RpcRegistryInner::new(
342 provider, pool, network, executor, consensus, config, evm_config, eth,
343 );
344
345 let modules = registry.create_transport_rpc_modules(module_config);
346
347 let auth_module = registry.create_auth_module(engine);
348
349 (modules, auth_module, registry)
350 }
351
352 pub fn into_registry<EthApi>(
357 self,
358 config: RpcModuleConfig,
359 eth: EthApi,
360 ) -> RpcRegistryInner<Provider, Pool, Network, EthApi, EvmConfig, Consensus>
361 where
362 EthApi: EthApiTypes + 'static,
363 {
364 let Self { provider, pool, network, executor, consensus, evm_config, .. } = self;
365 RpcRegistryInner::new(provider, pool, network, executor, consensus, config, evm_config, eth)
366 }
367
368 pub fn build<EthApi>(
371 self,
372 module_config: TransportRpcModuleConfig,
373 eth: EthApi,
374 ) -> TransportRpcModules<()>
375 where
376 EthApi: FullEthApiServer<Provider = Provider, Pool = Pool>,
377 {
378 let mut modules = TransportRpcModules::default();
379
380 let Self { provider, pool, network, executor, consensus, evm_config, .. } = self;
381
382 if !module_config.is_empty() {
383 let TransportRpcModuleConfig { http, ws, ipc, config } = module_config.clone();
384
385 let mut registry = RpcRegistryInner::new(
386 provider,
387 pool,
388 network,
389 executor,
390 consensus,
391 config.unwrap_or_default(),
392 evm_config,
393 eth,
394 );
395
396 modules.config = module_config;
397 modules.http = registry.maybe_module(http.as_ref());
398 modules.ws = registry.maybe_module(ws.as_ref());
399 modules.ipc = registry.maybe_module(ipc.as_ref());
400 }
401
402 modules
403 }
404}
405
406impl<N: NodePrimitives> Default for RpcModuleBuilder<N, (), (), (), (), ()> {
407 fn default() -> Self {
408 Self::new((), (), (), Box::new(TokioTaskExecutor::default()), (), ())
409 }
410}
411
412#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize)]
414pub struct RpcModuleConfig {
415 eth: EthConfig,
417 flashbots: ValidationApiConfig,
419}
420
421impl RpcModuleConfig {
424 pub fn builder() -> RpcModuleConfigBuilder {
426 RpcModuleConfigBuilder::default()
427 }
428
429 pub const fn new(eth: EthConfig, flashbots: ValidationApiConfig) -> Self {
431 Self { eth, flashbots }
432 }
433
434 pub const fn eth(&self) -> &EthConfig {
436 &self.eth
437 }
438
439 pub const fn eth_mut(&mut self) -> &mut EthConfig {
441 &mut self.eth
442 }
443}
444
445#[derive(Clone, Debug, Default)]
447pub struct RpcModuleConfigBuilder {
448 eth: Option<EthConfig>,
449 flashbots: Option<ValidationApiConfig>,
450}
451
452impl RpcModuleConfigBuilder {
455 pub fn eth(mut self, eth: EthConfig) -> Self {
457 self.eth = Some(eth);
458 self
459 }
460
461 pub fn flashbots(mut self, flashbots: ValidationApiConfig) -> Self {
463 self.flashbots = Some(flashbots);
464 self
465 }
466
467 pub fn build(self) -> RpcModuleConfig {
469 let Self { eth, flashbots } = self;
470 RpcModuleConfig { eth: eth.unwrap_or_default(), flashbots: flashbots.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, Clone)]
491#[expect(dead_code)] pub struct RpcRegistryInner<
493 Provider: BlockReader,
494 Pool,
495 Network,
496 EthApi: EthApiTypes,
497 EvmConfig,
498 Consensus,
499> {
500 provider: Provider,
501 pool: Pool,
502 network: Network,
503 executor: Box<dyn TaskSpawner + 'static>,
504 evm_config: EvmConfig,
505 consensus: Consensus,
506 eth: EthHandlers<EthApi>,
508 blocking_pool_guard: BlockingTaskGuard,
510 modules: HashMap<RethRpcModule, Methods>,
512 eth_config: EthConfig,
514}
515
516impl<N, Provider, Pool, Network, EthApi, EvmConfig, Consensus>
519 RpcRegistryInner<Provider, Pool, Network, EthApi, EvmConfig, Consensus>
520where
521 N: NodePrimitives,
522 Provider: StateProviderFactory
523 + CanonStateSubscriptions<Primitives = N>
524 + BlockReader<Block = N::Block, Receipt = N::Receipt>
525 + Clone
526 + Unpin
527 + 'static,
528 Pool: Send + Sync + Clone + 'static,
529 Network: Clone + 'static,
530 EthApi: EthApiTypes + 'static,
531 EvmConfig: ConfigureEvm<Primitives = N>,
532{
533 #[expect(clippy::too_many_arguments)]
535 pub fn new(
536 provider: Provider,
537 pool: Pool,
538 network: Network,
539 executor: Box<dyn TaskSpawner + 'static>,
540 consensus: Consensus,
541 config: RpcModuleConfig,
542 evm_config: EvmConfig,
543 eth_api: EthApi,
544 ) -> Self
545 where
546 EvmConfig: ConfigureEvm<Primitives = N>,
547 {
548 let blocking_pool_guard = BlockingTaskGuard::new(config.eth.max_tracing_requests);
549
550 let eth = EthHandlers::bootstrap(config.eth.clone(), executor.clone(), eth_api);
551
552 Self {
553 provider,
554 pool,
555 network,
556 eth,
557 executor,
558 consensus,
559 modules: Default::default(),
560 blocking_pool_guard,
561 eth_config: config.eth,
562 evm_config,
563 }
564 }
565}
566
567impl<Provider, Pool, Network, EthApi, BlockExecutor, Consensus>
568 RpcRegistryInner<Provider, Pool, Network, EthApi, BlockExecutor, Consensus>
569where
570 Provider: BlockReader,
571 EthApi: EthApiTypes,
572{
573 pub const fn eth_api(&self) -> &EthApi {
575 &self.eth.api
576 }
577
578 pub const fn eth_handlers(&self) -> &EthHandlers<EthApi> {
580 &self.eth
581 }
582
583 pub const fn pool(&self) -> &Pool {
585 &self.pool
586 }
587
588 pub const fn tasks(&self) -> &(dyn TaskSpawner + 'static) {
590 &*self.executor
591 }
592
593 pub const fn provider(&self) -> &Provider {
595 &self.provider
596 }
597
598 pub fn methods(&self) -> Vec<Methods> {
600 self.modules.values().cloned().collect()
601 }
602
603 pub fn module(&self) -> RpcModule<()> {
605 let mut module = RpcModule::new(());
606 for methods in self.modules.values().cloned() {
607 module.merge(methods).expect("No conflicts");
608 }
609 module
610 }
611}
612
613impl<Provider, Pool, Network, EthApi, EvmConfig, Consensus>
614 RpcRegistryInner<Provider, Pool, Network, EthApi, EvmConfig, Consensus>
615where
616 Network: NetworkInfo + Clone + 'static,
617 EthApi: EthApiTypes,
618 Provider: BlockReader + ChainSpecProvider<ChainSpec: EthereumHardforks>,
619 EvmConfig: ConfigureEvm,
620{
621 pub fn admin_api(&self) -> AdminApi<Network, Provider::ChainSpec>
623 where
624 Network: Peers,
625 {
626 AdminApi::new(self.network.clone(), self.provider.chain_spec())
627 }
628
629 pub fn web3_api(&self) -> Web3Api<Network> {
631 Web3Api::new(self.network.clone())
632 }
633
634 pub fn register_admin(&mut self) -> &mut Self
636 where
637 Network: Peers,
638 {
639 let adminapi = self.admin_api();
640 self.modules.insert(RethRpcModule::Admin, adminapi.into_rpc().into());
641 self
642 }
643
644 pub fn register_web3(&mut self) -> &mut Self {
646 let web3api = self.web3_api();
647 self.modules.insert(RethRpcModule::Web3, web3api.into_rpc().into());
648 self
649 }
650}
651
652impl<N, Provider, Pool, Network, EthApi, EvmConfig, Consensus>
653 RpcRegistryInner<Provider, Pool, Network, EthApi, EvmConfig, Consensus>
654where
655 N: NodePrimitives,
656 Provider: FullRpcProvider<
657 Header = N::BlockHeader,
658 Block = N::Block,
659 Receipt = N::Receipt,
660 Transaction = N::SignedTx,
661 > + AccountReader
662 + ChangeSetReader
663 + CanonStateSubscriptions,
664 Network: NetworkInfo + Peers + Clone + 'static,
665 EthApi: EthApiServer<
666 RpcTxReq<EthApi::NetworkTypes>,
667 RpcTransaction<EthApi::NetworkTypes>,
668 RpcBlock<EthApi::NetworkTypes>,
669 RpcReceipt<EthApi::NetworkTypes>,
670 RpcHeader<EthApi::NetworkTypes>,
671 > + EthApiTypes,
672 EvmConfig: ConfigureEvm<Primitives = N> + 'static,
673{
674 pub fn register_eth(&mut self) -> &mut Self {
680 let eth_api = self.eth_api().clone();
681 self.modules.insert(RethRpcModule::Eth, eth_api.into_rpc().into());
682 self
683 }
684
685 pub fn register_ots(&mut self) -> &mut Self
691 where
692 EthApi: TraceExt + EthTransactions,
693 {
694 let otterscan_api = self.otterscan_api();
695 self.modules.insert(RethRpcModule::Ots, otterscan_api.into_rpc().into());
696 self
697 }
698
699 pub fn register_debug(&mut self) -> &mut Self
705 where
706 EthApi: EthApiSpec + EthTransactions + TraceExt,
707 EvmConfig::Primitives: NodePrimitives<Block = ProviderBlock<EthApi::Provider>>,
708 {
709 let debug_api = self.debug_api();
710 self.modules.insert(RethRpcModule::Debug, debug_api.into_rpc().into());
711 self
712 }
713
714 pub fn register_trace(&mut self) -> &mut Self
720 where
721 EthApi: TraceExt,
722 {
723 let trace_api = self.trace_api();
724 self.modules.insert(RethRpcModule::Trace, trace_api.into_rpc().into());
725 self
726 }
727
728 pub fn register_net(&mut self) -> &mut Self
736 where
737 EthApi: EthApiSpec + 'static,
738 {
739 let netapi = self.net_api();
740 self.modules.insert(RethRpcModule::Net, netapi.into_rpc().into());
741 self
742 }
743
744 pub fn register_reth(&mut self) -> &mut Self {
752 let rethapi = self.reth_api();
753 self.modules.insert(RethRpcModule::Reth, rethapi.into_rpc().into());
754 self
755 }
756
757 pub fn otterscan_api(&self) -> OtterscanApi<EthApi> {
763 let eth_api = self.eth_api().clone();
764 OtterscanApi::new(eth_api)
765 }
766}
767
768impl<N, Provider, Pool, Network, EthApi, EvmConfig, Consensus>
769 RpcRegistryInner<Provider, Pool, Network, EthApi, EvmConfig, Consensus>
770where
771 N: NodePrimitives,
772 Provider: FullRpcProvider<
773 Block = N::Block,
774 Header = N::BlockHeader,
775 Transaction = N::SignedTx,
776 Receipt = N::Receipt,
777 > + AccountReader
778 + ChangeSetReader,
779 Network: NetworkInfo + Peers + Clone + 'static,
780 EthApi: EthApiTypes,
781 EvmConfig: ConfigureEvm<Primitives = N>,
782{
783 pub fn trace_api(&self) -> TraceApi<EthApi> {
789 TraceApi::new(
790 self.eth_api().clone(),
791 self.blocking_pool_guard.clone(),
792 self.eth_config.clone(),
793 )
794 }
795
796 pub fn bundle_api(&self) -> EthBundle<EthApi>
802 where
803 EthApi: EthTransactions + LoadPendingBlock + Call,
804 {
805 let eth_api = self.eth_api().clone();
806 EthBundle::new(eth_api, self.blocking_pool_guard.clone())
807 }
808
809 pub fn debug_api(&self) -> DebugApi<EthApi> {
815 DebugApi::new(self.eth_api().clone(), self.blocking_pool_guard.clone())
816 }
817
818 pub fn net_api(&self) -> NetApi<Network, EthApi>
824 where
825 EthApi: EthApiSpec + 'static,
826 {
827 let eth_api = self.eth_api().clone();
828 NetApi::new(self.network.clone(), eth_api)
829 }
830
831 pub fn reth_api(&self) -> RethApi<Provider> {
833 RethApi::new(self.provider.clone(), self.executor.clone())
834 }
835}
836
837impl<N, Provider, Pool, Network, EthApi, EvmConfig, Consensus>
838 RpcRegistryInner<Provider, Pool, Network, EthApi, EvmConfig, Consensus>
839where
840 N: NodePrimitives,
841 Provider: FullRpcProvider<Block = N::Block>
842 + CanonStateSubscriptions<Primitives = N>
843 + AccountReader
844 + ChangeSetReader,
845 Pool: TransactionPool + 'static,
846 Network: NetworkInfo + Peers + Clone + 'static,
847 EthApi: FullEthApiServer,
848 EvmConfig: ConfigureEvm<Primitives = N> + 'static,
849 Consensus: FullConsensus<N, Error = ConsensusError> + Clone + 'static,
850{
851 pub fn create_auth_module(&self, engine_api: impl IntoEngineApiRpcModule) -> AuthRpcModule {
857 let mut module = engine_api.into_rpc_module();
858
859 let eth_handlers = self.eth_handlers();
861 let engine_eth = EngineEthApi::new(eth_handlers.api.clone(), eth_handlers.filter.clone());
862
863 module.merge(engine_eth.into_rpc()).expect("No conflicting methods");
864
865 AuthRpcModule { inner: module }
866 }
867
868 fn maybe_module(&mut self, config: Option<&RpcModuleSelection>) -> Option<RpcModule<()>> {
870 config.map(|config| self.module_for(config))
871 }
872
873 pub fn create_transport_rpc_modules(
877 &mut self,
878 config: TransportRpcModuleConfig,
879 ) -> TransportRpcModules<()> {
880 let mut modules = TransportRpcModules::default();
881 let http = self.maybe_module(config.http.as_ref());
882 let ws = self.maybe_module(config.ws.as_ref());
883 let ipc = self.maybe_module(config.ipc.as_ref());
884
885 modules.config = config;
886 modules.http = http;
887 modules.ws = ws;
888 modules.ipc = ipc;
889 modules
890 }
891
892 pub fn module_for(&mut self, config: &RpcModuleSelection) -> RpcModule<()> {
895 let mut module = RpcModule::new(());
896 let all_methods = self.reth_methods(config.iter_selection());
897 for methods in all_methods {
898 module.merge(methods).expect("No conflicts");
899 }
900 module
901 }
902
903 pub fn reth_methods(
912 &mut self,
913 namespaces: impl Iterator<Item = RethRpcModule>,
914 ) -> Vec<Methods> {
915 let EthHandlers { api: eth_api, filter: eth_filter, pubsub: eth_pubsub, .. } =
916 self.eth_handlers().clone();
917
918 let namespaces: Vec<_> = namespaces.collect();
920 namespaces
921 .iter()
922 .copied()
923 .map(|namespace| {
924 self.modules
925 .entry(namespace)
926 .or_insert_with(|| match namespace {
927 RethRpcModule::Admin => {
928 AdminApi::new(self.network.clone(), self.provider.chain_spec())
929 .into_rpc()
930 .into()
931 }
932 RethRpcModule::Debug => {
933 DebugApi::new(eth_api.clone(), self.blocking_pool_guard.clone())
934 .into_rpc()
935 .into()
936 }
937 RethRpcModule::Eth => {
938 let mut module = eth_api.clone().into_rpc();
940 module.merge(eth_filter.clone().into_rpc()).expect("No conflicts");
941 module.merge(eth_pubsub.clone().into_rpc()).expect("No conflicts");
942 module
943 .merge(
944 EthBundle::new(
945 eth_api.clone(),
946 self.blocking_pool_guard.clone(),
947 )
948 .into_rpc(),
949 )
950 .expect("No conflicts");
951
952 module.into()
953 }
954 RethRpcModule::Net => {
955 NetApi::new(self.network.clone(), eth_api.clone()).into_rpc().into()
956 }
957 RethRpcModule::Trace => TraceApi::new(
958 eth_api.clone(),
959 self.blocking_pool_guard.clone(),
960 self.eth_config.clone(),
961 )
962 .into_rpc()
963 .into(),
964 RethRpcModule::Web3 => Web3Api::new(self.network.clone()).into_rpc().into(),
965 RethRpcModule::Txpool => TxPoolApi::new(
966 self.eth.api.pool().clone(),
967 self.eth.api.tx_resp_builder().clone(),
968 )
969 .into_rpc()
970 .into(),
971 RethRpcModule::Rpc => RPCApi::new(
972 namespaces
973 .iter()
974 .map(|module| (module.to_string(), "1.0".to_string()))
975 .collect(),
976 )
977 .into_rpc()
978 .into(),
979 RethRpcModule::Ots => OtterscanApi::new(eth_api.clone()).into_rpc().into(),
980 RethRpcModule::Reth => {
981 RethApi::new(self.provider.clone(), self.executor.clone())
982 .into_rpc()
983 .into()
984 }
985 RethRpcModule::Flashbots => Default::default(),
989 RethRpcModule::Miner => MinerApi::default().into_rpc().into(),
990 RethRpcModule::Mev => {
991 EthSimBundle::new(eth_api.clone(), self.blocking_pool_guard.clone())
992 .into_rpc()
993 .into()
994 }
995 })
996 .clone()
997 })
998 .collect::<Vec<_>>()
999 }
1000}
1001
1002#[derive(Debug)]
1014pub struct RpcServerConfig<RpcMiddleware = Identity> {
1015 http_server_config: Option<ServerConfigBuilder>,
1017 http_cors_domains: Option<String>,
1019 http_addr: Option<SocketAddr>,
1021 http_disable_compression: bool,
1023 ws_server_config: Option<ServerConfigBuilder>,
1025 ws_cors_domains: Option<String>,
1027 ws_addr: Option<SocketAddr>,
1029 ipc_server_config: Option<IpcServerBuilder<Identity, Identity>>,
1031 ipc_endpoint: Option<String>,
1033 jwt_secret: Option<JwtSecret>,
1035 rpc_middleware: RpcMiddleware,
1037}
1038
1039impl Default for RpcServerConfig<Identity> {
1042 fn default() -> Self {
1044 Self {
1045 http_server_config: None,
1046 http_cors_domains: None,
1047 http_addr: None,
1048 http_disable_compression: false,
1049 ws_server_config: None,
1050 ws_cors_domains: None,
1051 ws_addr: None,
1052 ipc_server_config: None,
1053 ipc_endpoint: None,
1054 jwt_secret: None,
1055 rpc_middleware: Default::default(),
1056 }
1057 }
1058}
1059
1060impl RpcServerConfig {
1061 pub fn http(config: ServerConfigBuilder) -> Self {
1063 Self::default().with_http(config)
1064 }
1065
1066 pub fn ws(config: ServerConfigBuilder) -> Self {
1068 Self::default().with_ws(config)
1069 }
1070
1071 pub fn ipc(config: IpcServerBuilder<Identity, Identity>) -> Self {
1073 Self::default().with_ipc(config)
1074 }
1075
1076 pub fn with_http(mut self, config: ServerConfigBuilder) -> Self {
1081 self.http_server_config =
1082 Some(config.set_id_provider(EthSubscriptionIdProvider::default()));
1083 self
1084 }
1085
1086 pub fn with_ws(mut self, config: ServerConfigBuilder) -> Self {
1091 self.ws_server_config = Some(config.set_id_provider(EthSubscriptionIdProvider::default()));
1092 self
1093 }
1094
1095 pub fn with_ipc(mut self, config: IpcServerBuilder<Identity, Identity>) -> Self {
1100 self.ipc_server_config = Some(config.set_id_provider(EthSubscriptionIdProvider::default()));
1101 self
1102 }
1103}
1104
1105impl<RpcMiddleware> RpcServerConfig<RpcMiddleware> {
1106 pub fn set_rpc_middleware<T>(self, rpc_middleware: T) -> RpcServerConfig<T> {
1108 RpcServerConfig {
1109 http_server_config: self.http_server_config,
1110 http_cors_domains: self.http_cors_domains,
1111 http_addr: self.http_addr,
1112 http_disable_compression: self.http_disable_compression,
1113 ws_server_config: self.ws_server_config,
1114 ws_cors_domains: self.ws_cors_domains,
1115 ws_addr: self.ws_addr,
1116 ipc_server_config: self.ipc_server_config,
1117 ipc_endpoint: self.ipc_endpoint,
1118 jwt_secret: self.jwt_secret,
1119 rpc_middleware,
1120 }
1121 }
1122
1123 pub fn with_cors(self, cors_domain: Option<String>) -> Self {
1125 self.with_http_cors(cors_domain.clone()).with_ws_cors(cors_domain)
1126 }
1127
1128 pub fn with_ws_cors(mut self, cors_domain: Option<String>) -> Self {
1130 self.ws_cors_domains = cors_domain;
1131 self
1132 }
1133
1134 pub const fn with_http_disable_compression(mut self, http_disable_compression: bool) -> Self {
1136 self.http_disable_compression = http_disable_compression;
1137 self
1138 }
1139
1140 pub fn with_http_cors(mut self, cors_domain: Option<String>) -> Self {
1142 self.http_cors_domains = cors_domain;
1143 self
1144 }
1145
1146 pub const fn with_http_address(mut self, addr: SocketAddr) -> Self {
1151 self.http_addr = Some(addr);
1152 self
1153 }
1154
1155 pub const fn with_ws_address(mut self, addr: SocketAddr) -> Self {
1160 self.ws_addr = Some(addr);
1161 self
1162 }
1163
1164 pub fn with_id_provider<I>(mut self, id_provider: I) -> Self
1168 where
1169 I: IdProvider + Clone + 'static,
1170 {
1171 if let Some(config) = self.http_server_config {
1172 self.http_server_config = Some(config.set_id_provider(id_provider.clone()));
1173 }
1174 if let Some(config) = self.ws_server_config {
1175 self.ws_server_config = Some(config.set_id_provider(id_provider.clone()));
1176 }
1177 if let Some(ipc) = self.ipc_server_config {
1178 self.ipc_server_config = Some(ipc.set_id_provider(id_provider));
1179 }
1180
1181 self
1182 }
1183
1184 pub fn with_ipc_endpoint(mut self, path: impl Into<String>) -> Self {
1188 self.ipc_endpoint = Some(path.into());
1189 self
1190 }
1191
1192 pub const fn with_jwt_secret(mut self, secret: Option<JwtSecret>) -> Self {
1194 self.jwt_secret = secret;
1195 self
1196 }
1197
1198 pub fn with_tokio_runtime(mut self, tokio_runtime: Option<tokio::runtime::Handle>) -> Self {
1200 let Some(tokio_runtime) = tokio_runtime else { return self };
1201 if let Some(http_server_config) = self.http_server_config {
1202 self.http_server_config =
1203 Some(http_server_config.custom_tokio_runtime(tokio_runtime.clone()));
1204 }
1205 if let Some(ws_server_config) = self.ws_server_config {
1206 self.ws_server_config =
1207 Some(ws_server_config.custom_tokio_runtime(tokio_runtime.clone()));
1208 }
1209 if let Some(ipc_server_config) = self.ipc_server_config {
1210 self.ipc_server_config = Some(ipc_server_config.custom_tokio_runtime(tokio_runtime));
1211 }
1212 self
1213 }
1214
1215 pub const fn has_server(&self) -> bool {
1219 self.http_server_config.is_some() ||
1220 self.ws_server_config.is_some() ||
1221 self.ipc_server_config.is_some()
1222 }
1223
1224 pub const fn http_address(&self) -> Option<SocketAddr> {
1226 self.http_addr
1227 }
1228
1229 pub const fn ws_address(&self) -> Option<SocketAddr> {
1231 self.ws_addr
1232 }
1233
1234 pub fn ipc_endpoint(&self) -> Option<String> {
1236 self.ipc_endpoint.clone()
1237 }
1238
1239 fn maybe_cors_layer(cors: Option<String>) -> Result<Option<CorsLayer>, CorsDomainError> {
1241 cors.as_deref().map(cors::create_cors_layer).transpose()
1242 }
1243
1244 fn maybe_jwt_layer(jwt_secret: Option<JwtSecret>) -> Option<AuthLayer<JwtAuthValidator>> {
1246 jwt_secret.map(|secret| AuthLayer::new(JwtAuthValidator::new(secret)))
1247 }
1248
1249 fn maybe_compression_layer(disable_compression: bool) -> Option<CompressionLayer> {
1252 if disable_compression {
1253 None
1254 } else {
1255 Some(CompressionLayer::new())
1256 }
1257 }
1258
1259 pub async fn start(self, modules: &TransportRpcModules) -> Result<RpcServerHandle, RpcError>
1265 where
1266 RpcMiddleware: RethRpcMiddleware,
1267 {
1268 let mut http_handle = None;
1269 let mut ws_handle = None;
1270 let mut ipc_handle = None;
1271
1272 let http_socket_addr = self.http_addr.unwrap_or(SocketAddr::V4(SocketAddrV4::new(
1273 Ipv4Addr::LOCALHOST,
1274 constants::DEFAULT_HTTP_RPC_PORT,
1275 )));
1276
1277 let ws_socket_addr = self.ws_addr.unwrap_or(SocketAddr::V4(SocketAddrV4::new(
1278 Ipv4Addr::LOCALHOST,
1279 constants::DEFAULT_WS_RPC_PORT,
1280 )));
1281
1282 let metrics = modules.ipc.as_ref().map(RpcRequestMetrics::ipc).unwrap_or_default();
1283 let ipc_path =
1284 self.ipc_endpoint.clone().unwrap_or_else(|| constants::DEFAULT_IPC_ENDPOINT.into());
1285
1286 if let Some(builder) = self.ipc_server_config {
1287 let ipc = builder
1288 .set_rpc_middleware(IpcRpcServiceBuilder::new().layer(metrics))
1289 .build(ipc_path);
1290 ipc_handle = Some(ipc.start(modules.ipc.clone().expect("ipc server error")).await?);
1291 }
1292
1293 if self.http_addr == self.ws_addr &&
1295 self.http_server_config.is_some() &&
1296 self.ws_server_config.is_some()
1297 {
1298 let cors = match (self.ws_cors_domains.as_ref(), self.http_cors_domains.as_ref()) {
1299 (Some(ws_cors), Some(http_cors)) => {
1300 if ws_cors.trim() != http_cors.trim() {
1301 return Err(WsHttpSamePortError::ConflictingCorsDomains {
1302 http_cors_domains: Some(http_cors.clone()),
1303 ws_cors_domains: Some(ws_cors.clone()),
1304 }
1305 .into());
1306 }
1307 Some(ws_cors)
1308 }
1309 (a, b) => a.or(b),
1310 }
1311 .cloned();
1312
1313 modules.config.ensure_ws_http_identical()?;
1315
1316 if let Some(config) = self.http_server_config {
1317 let server = ServerBuilder::new()
1318 .set_http_middleware(
1319 tower::ServiceBuilder::new()
1320 .option_layer(Self::maybe_cors_layer(cors)?)
1321 .option_layer(Self::maybe_jwt_layer(self.jwt_secret))
1322 .option_layer(Self::maybe_compression_layer(
1323 self.http_disable_compression,
1324 )),
1325 )
1326 .set_rpc_middleware(
1327 RpcServiceBuilder::default()
1328 .layer(
1329 modules
1330 .http
1331 .as_ref()
1332 .or(modules.ws.as_ref())
1333 .map(RpcRequestMetrics::same_port)
1334 .unwrap_or_default(),
1335 )
1336 .layer(self.rpc_middleware.clone()),
1337 )
1338 .set_config(config.build())
1339 .build(http_socket_addr)
1340 .await
1341 .map_err(|err| {
1342 RpcError::server_error(err, ServerKind::WsHttp(http_socket_addr))
1343 })?;
1344 let addr = server.local_addr().map_err(|err| {
1345 RpcError::server_error(err, ServerKind::WsHttp(http_socket_addr))
1346 })?;
1347 if let Some(module) = modules.http.as_ref().or(modules.ws.as_ref()) {
1348 let handle = server.start(module.clone());
1349 http_handle = Some(handle.clone());
1350 ws_handle = Some(handle);
1351 }
1352 return Ok(RpcServerHandle {
1353 http_local_addr: Some(addr),
1354 ws_local_addr: Some(addr),
1355 http: http_handle,
1356 ws: ws_handle,
1357 ipc_endpoint: self.ipc_endpoint.clone(),
1358 ipc: ipc_handle,
1359 jwt_secret: self.jwt_secret,
1360 });
1361 }
1362 }
1363
1364 let mut ws_local_addr = None;
1365 let mut ws_server = None;
1366 let mut http_local_addr = None;
1367 let mut http_server = None;
1368
1369 if let Some(config) = self.ws_server_config {
1370 let server = ServerBuilder::new()
1371 .set_config(config.ws_only().build())
1372 .set_http_middleware(
1373 tower::ServiceBuilder::new()
1374 .option_layer(Self::maybe_cors_layer(self.ws_cors_domains.clone())?)
1375 .option_layer(Self::maybe_jwt_layer(self.jwt_secret)),
1376 )
1377 .set_rpc_middleware(
1378 RpcServiceBuilder::default()
1379 .layer(modules.ws.as_ref().map(RpcRequestMetrics::ws).unwrap_or_default())
1380 .layer(self.rpc_middleware.clone()),
1381 )
1382 .build(ws_socket_addr)
1383 .await
1384 .map_err(|err| RpcError::server_error(err, ServerKind::WS(ws_socket_addr)))?;
1385
1386 let addr = server
1387 .local_addr()
1388 .map_err(|err| RpcError::server_error(err, ServerKind::WS(ws_socket_addr)))?;
1389
1390 ws_local_addr = Some(addr);
1391 ws_server = Some(server);
1392 }
1393
1394 if let Some(config) = self.http_server_config {
1395 let server = ServerBuilder::new()
1396 .set_config(config.http_only().build())
1397 .set_http_middleware(
1398 tower::ServiceBuilder::new()
1399 .option_layer(Self::maybe_cors_layer(self.http_cors_domains.clone())?)
1400 .option_layer(Self::maybe_jwt_layer(self.jwt_secret))
1401 .option_layer(Self::maybe_compression_layer(self.http_disable_compression)),
1402 )
1403 .set_rpc_middleware(
1404 RpcServiceBuilder::default()
1405 .layer(
1406 modules.http.as_ref().map(RpcRequestMetrics::http).unwrap_or_default(),
1407 )
1408 .layer(self.rpc_middleware.clone()),
1409 )
1410 .build(http_socket_addr)
1411 .await
1412 .map_err(|err| RpcError::server_error(err, ServerKind::Http(http_socket_addr)))?;
1413 let local_addr = server
1414 .local_addr()
1415 .map_err(|err| RpcError::server_error(err, ServerKind::Http(http_socket_addr)))?;
1416 http_local_addr = Some(local_addr);
1417 http_server = Some(server);
1418 }
1419
1420 http_handle = http_server
1421 .map(|http_server| http_server.start(modules.http.clone().expect("http server error")));
1422 ws_handle = ws_server
1423 .map(|ws_server| ws_server.start(modules.ws.clone().expect("ws server error")));
1424 Ok(RpcServerHandle {
1425 http_local_addr,
1426 ws_local_addr,
1427 http: http_handle,
1428 ws: ws_handle,
1429 ipc_endpoint: self.ipc_endpoint.clone(),
1430 ipc: ipc_handle,
1431 jwt_secret: self.jwt_secret,
1432 })
1433 }
1434}
1435
1436#[derive(Debug, Clone, Default, Eq, PartialEq)]
1448pub struct TransportRpcModuleConfig {
1449 http: Option<RpcModuleSelection>,
1451 ws: Option<RpcModuleSelection>,
1453 ipc: Option<RpcModuleSelection>,
1455 config: Option<RpcModuleConfig>,
1457}
1458
1459impl TransportRpcModuleConfig {
1462 pub fn set_http(http: impl Into<RpcModuleSelection>) -> Self {
1464 Self::default().with_http(http)
1465 }
1466
1467 pub fn set_ws(ws: impl Into<RpcModuleSelection>) -> Self {
1469 Self::default().with_ws(ws)
1470 }
1471
1472 pub fn set_ipc(ipc: impl Into<RpcModuleSelection>) -> Self {
1474 Self::default().with_ipc(ipc)
1475 }
1476
1477 pub fn with_http(mut self, http: impl Into<RpcModuleSelection>) -> Self {
1479 self.http = Some(http.into());
1480 self
1481 }
1482
1483 pub fn with_ws(mut self, ws: impl Into<RpcModuleSelection>) -> Self {
1485 self.ws = Some(ws.into());
1486 self
1487 }
1488
1489 pub fn with_ipc(mut self, ipc: impl Into<RpcModuleSelection>) -> Self {
1491 self.ipc = Some(ipc.into());
1492 self
1493 }
1494
1495 pub fn with_config(mut self, config: RpcModuleConfig) -> Self {
1497 self.config = Some(config);
1498 self
1499 }
1500
1501 pub const fn http_mut(&mut self) -> &mut Option<RpcModuleSelection> {
1503 &mut self.http
1504 }
1505
1506 pub const fn ws_mut(&mut self) -> &mut Option<RpcModuleSelection> {
1508 &mut self.ws
1509 }
1510
1511 pub const fn ipc_mut(&mut self) -> &mut Option<RpcModuleSelection> {
1513 &mut self.ipc
1514 }
1515
1516 pub const fn config_mut(&mut self) -> &mut Option<RpcModuleConfig> {
1518 &mut self.config
1519 }
1520
1521 pub const fn is_empty(&self) -> bool {
1523 self.http.is_none() && self.ws.is_none() && self.ipc.is_none()
1524 }
1525
1526 pub const fn http(&self) -> Option<&RpcModuleSelection> {
1528 self.http.as_ref()
1529 }
1530
1531 pub const fn ws(&self) -> Option<&RpcModuleSelection> {
1533 self.ws.as_ref()
1534 }
1535
1536 pub const fn ipc(&self) -> Option<&RpcModuleSelection> {
1538 self.ipc.as_ref()
1539 }
1540
1541 pub const fn config(&self) -> Option<&RpcModuleConfig> {
1543 self.config.as_ref()
1544 }
1545
1546 pub fn contains_any(&self, module: &RethRpcModule) -> bool {
1548 self.contains_http(module) || self.contains_ws(module) || self.contains_ipc(module)
1549 }
1550
1551 pub fn contains_http(&self, module: &RethRpcModule) -> bool {
1553 self.http.as_ref().is_some_and(|http| http.contains(module))
1554 }
1555
1556 pub fn contains_ws(&self, module: &RethRpcModule) -> bool {
1558 self.ws.as_ref().is_some_and(|ws| ws.contains(module))
1559 }
1560
1561 pub fn contains_ipc(&self, module: &RethRpcModule) -> bool {
1563 self.ipc.as_ref().is_some_and(|ipc| ipc.contains(module))
1564 }
1565
1566 fn ensure_ws_http_identical(&self) -> Result<(), WsHttpSamePortError> {
1569 if RpcModuleSelection::are_identical(self.http.as_ref(), self.ws.as_ref()) {
1570 Ok(())
1571 } else {
1572 let http_modules =
1573 self.http.as_ref().map(RpcModuleSelection::to_selection).unwrap_or_default();
1574 let ws_modules =
1575 self.ws.as_ref().map(RpcModuleSelection::to_selection).unwrap_or_default();
1576
1577 let http_not_ws = http_modules.difference(&ws_modules).copied().collect();
1578 let ws_not_http = ws_modules.difference(&http_modules).copied().collect();
1579 let overlap = http_modules.intersection(&ws_modules).copied().collect();
1580
1581 Err(WsHttpSamePortError::ConflictingModules(Box::new(ConflictingModules {
1582 overlap,
1583 http_not_ws,
1584 ws_not_http,
1585 })))
1586 }
1587 }
1588}
1589
1590#[derive(Debug, Clone, Default)]
1592pub struct TransportRpcModules<Context = ()> {
1593 config: TransportRpcModuleConfig,
1595 http: Option<RpcModule<Context>>,
1597 ws: Option<RpcModule<Context>>,
1599 ipc: Option<RpcModule<Context>>,
1601}
1602
1603impl TransportRpcModules {
1606 pub fn with_config(mut self, config: TransportRpcModuleConfig) -> Self {
1609 self.config = config;
1610 self
1611 }
1612
1613 pub fn with_http(mut self, http: RpcModule<()>) -> Self {
1616 self.http = Some(http);
1617 self
1618 }
1619
1620 pub fn with_ws(mut self, ws: RpcModule<()>) -> Self {
1623 self.ws = Some(ws);
1624 self
1625 }
1626
1627 pub fn with_ipc(mut self, ipc: RpcModule<()>) -> Self {
1630 self.ipc = Some(ipc);
1631 self
1632 }
1633
1634 pub const fn module_config(&self) -> &TransportRpcModuleConfig {
1636 &self.config
1637 }
1638
1639 pub fn merge_if_module_configured(
1644 &mut self,
1645 module: RethRpcModule,
1646 other: impl Into<Methods>,
1647 ) -> Result<(), RegisterMethodError> {
1648 let other = other.into();
1649 if self.module_config().contains_http(&module) {
1650 self.merge_http(other.clone())?;
1651 }
1652 if self.module_config().contains_ws(&module) {
1653 self.merge_ws(other.clone())?;
1654 }
1655 if self.module_config().contains_ipc(&module) {
1656 self.merge_ipc(other)?;
1657 }
1658
1659 Ok(())
1660 }
1661
1662 pub fn merge_http(&mut self, other: impl Into<Methods>) -> Result<bool, RegisterMethodError> {
1668 if let Some(ref mut http) = self.http {
1669 return http.merge(other.into()).map(|_| true)
1670 }
1671 Ok(false)
1672 }
1673
1674 pub fn merge_ws(&mut self, other: impl Into<Methods>) -> Result<bool, RegisterMethodError> {
1680 if let Some(ref mut ws) = self.ws {
1681 return ws.merge(other.into()).map(|_| true)
1682 }
1683 Ok(false)
1684 }
1685
1686 pub fn merge_ipc(&mut self, other: impl Into<Methods>) -> Result<bool, RegisterMethodError> {
1692 if let Some(ref mut ipc) = self.ipc {
1693 return ipc.merge(other.into()).map(|_| true)
1694 }
1695 Ok(false)
1696 }
1697
1698 pub fn merge_configured(
1702 &mut self,
1703 other: impl Into<Methods>,
1704 ) -> Result<(), RegisterMethodError> {
1705 let other = other.into();
1706 self.merge_http(other.clone())?;
1707 self.merge_ws(other.clone())?;
1708 self.merge_ipc(other)?;
1709 Ok(())
1710 }
1711
1712 pub fn methods_by_module<F>(&self, module: RethRpcModule) -> Methods {
1716 self.methods_by(|name| name.starts_with(module.as_str()))
1717 }
1718
1719 pub fn methods_by<F>(&self, mut filter: F) -> Methods
1723 where
1724 F: FnMut(&str) -> bool,
1725 {
1726 let mut methods = Methods::new();
1727
1728 let mut f =
1730 |name: &str, mm: &Methods| filter(name) && !mm.method_names().any(|m| m == name);
1731
1732 if let Some(m) = self.http_methods(|name| f(name, &methods)) {
1733 let _ = methods.merge(m);
1734 }
1735 if let Some(m) = self.ws_methods(|name| f(name, &methods)) {
1736 let _ = methods.merge(m);
1737 }
1738 if let Some(m) = self.ipc_methods(|name| f(name, &methods)) {
1739 let _ = methods.merge(m);
1740 }
1741 methods
1742 }
1743
1744 pub fn http_methods<F>(&self, filter: F) -> Option<Methods>
1748 where
1749 F: FnMut(&str) -> bool,
1750 {
1751 self.http.as_ref().map(|module| methods_by(module, filter))
1752 }
1753
1754 pub fn ws_methods<F>(&self, filter: F) -> Option<Methods>
1758 where
1759 F: FnMut(&str) -> bool,
1760 {
1761 self.ws.as_ref().map(|module| methods_by(module, filter))
1762 }
1763
1764 pub fn ipc_methods<F>(&self, filter: F) -> Option<Methods>
1768 where
1769 F: FnMut(&str) -> bool,
1770 {
1771 self.ipc.as_ref().map(|module| methods_by(module, filter))
1772 }
1773
1774 pub fn remove_http_method(&mut self, method_name: &'static str) -> bool {
1782 if let Some(http_module) = &mut self.http {
1783 http_module.remove_method(method_name).is_some()
1784 } else {
1785 false
1786 }
1787 }
1788
1789 pub fn remove_http_methods(&mut self, methods: impl IntoIterator<Item = &'static str>) {
1791 for name in methods {
1792 self.remove_http_method(name);
1793 }
1794 }
1795
1796 pub fn remove_ws_method(&mut self, method_name: &'static str) -> bool {
1804 if let Some(ws_module) = &mut self.ws {
1805 ws_module.remove_method(method_name).is_some()
1806 } else {
1807 false
1808 }
1809 }
1810
1811 pub fn remove_ws_methods(&mut self, methods: impl IntoIterator<Item = &'static str>) {
1813 for name in methods {
1814 self.remove_ws_method(name);
1815 }
1816 }
1817
1818 pub fn remove_ipc_method(&mut self, method_name: &'static str) -> bool {
1826 if let Some(ipc_module) = &mut self.ipc {
1827 ipc_module.remove_method(method_name).is_some()
1828 } else {
1829 false
1830 }
1831 }
1832
1833 pub fn remove_ipc_methods(&mut self, methods: impl IntoIterator<Item = &'static str>) {
1835 for name in methods {
1836 self.remove_ipc_method(name);
1837 }
1838 }
1839
1840 pub fn remove_method_from_configured(&mut self, method_name: &'static str) -> bool {
1844 let http_removed = self.remove_http_method(method_name);
1845 let ws_removed = self.remove_ws_method(method_name);
1846 let ipc_removed = self.remove_ipc_method(method_name);
1847
1848 http_removed || ws_removed || ipc_removed
1849 }
1850
1851 pub fn rename(
1855 &mut self,
1856 old_name: &'static str,
1857 new_method: impl Into<Methods>,
1858 ) -> Result<(), RegisterMethodError> {
1859 self.remove_method_from_configured(old_name);
1861
1862 self.merge_configured(new_method)
1864 }
1865
1866 pub fn replace_http(&mut self, other: impl Into<Methods>) -> Result<bool, RegisterMethodError> {
1873 let other = other.into();
1874 self.remove_http_methods(other.method_names());
1875 self.merge_http(other)
1876 }
1877
1878 pub fn replace_ipc(&mut self, other: impl Into<Methods>) -> Result<bool, RegisterMethodError> {
1885 let other = other.into();
1886 self.remove_ipc_methods(other.method_names());
1887 self.merge_ipc(other)
1888 }
1889
1890 pub fn replace_ws(&mut self, other: impl Into<Methods>) -> Result<bool, RegisterMethodError> {
1897 let other = other.into();
1898 self.remove_ws_methods(other.method_names());
1899 self.merge_ws(other)
1900 }
1901
1902 pub fn replace_configured(
1906 &mut self,
1907 other: impl Into<Methods>,
1908 ) -> Result<bool, RegisterMethodError> {
1909 let other = other.into();
1910 self.replace_http(other.clone())?;
1911 self.replace_ws(other.clone())?;
1912 self.replace_ipc(other)?;
1913 Ok(true)
1914 }
1915
1916 pub fn add_or_replace_http(
1920 &mut self,
1921 other: impl Into<Methods>,
1922 ) -> Result<bool, RegisterMethodError> {
1923 let other = other.into();
1924 self.remove_http_methods(other.method_names());
1925 self.merge_http(other)
1926 }
1927
1928 pub fn add_or_replace_ws(
1932 &mut self,
1933 other: impl Into<Methods>,
1934 ) -> Result<bool, RegisterMethodError> {
1935 let other = other.into();
1936 self.remove_ws_methods(other.method_names());
1937 self.merge_ws(other)
1938 }
1939
1940 pub fn add_or_replace_ipc(
1944 &mut self,
1945 other: impl Into<Methods>,
1946 ) -> Result<bool, RegisterMethodError> {
1947 let other = other.into();
1948 self.remove_ipc_methods(other.method_names());
1949 self.merge_ipc(other)
1950 }
1951
1952 pub fn add_or_replace_configured(
1954 &mut self,
1955 other: impl Into<Methods>,
1956 ) -> Result<(), RegisterMethodError> {
1957 let other = other.into();
1958 self.add_or_replace_http(other.clone())?;
1959 self.add_or_replace_ws(other.clone())?;
1960 self.add_or_replace_ipc(other)?;
1961 Ok(())
1962 }
1963}
1964
1965fn methods_by<T, F>(module: &RpcModule<T>, mut filter: F) -> Methods
1967where
1968 F: FnMut(&str) -> bool,
1969{
1970 let mut methods = Methods::new();
1971 let method_names = module.method_names().filter(|name| filter(name));
1972
1973 for name in method_names {
1974 if let Some(matched_method) = module.method(name).cloned() {
1975 let _ = methods.verify_and_insert(name, matched_method);
1976 }
1977 }
1978
1979 methods
1980}
1981
1982#[derive(Clone, Debug)]
1987#[must_use = "Server stops if dropped"]
1988pub struct RpcServerHandle {
1989 http_local_addr: Option<SocketAddr>,
1991 ws_local_addr: Option<SocketAddr>,
1992 http: Option<ServerHandle>,
1993 ws: Option<ServerHandle>,
1994 ipc_endpoint: Option<String>,
1995 ipc: Option<jsonrpsee::server::ServerHandle>,
1996 jwt_secret: Option<JwtSecret>,
1997}
1998
1999impl RpcServerHandle {
2002 fn bearer_token(&self) -> Option<String> {
2004 self.jwt_secret.as_ref().map(|secret| {
2005 format!(
2006 "Bearer {}",
2007 secret
2008 .encode(&Claims {
2009 iat: (SystemTime::now().duration_since(UNIX_EPOCH).unwrap() +
2010 Duration::from_secs(60))
2011 .as_secs(),
2012 exp: None,
2013 })
2014 .unwrap()
2015 )
2016 })
2017 }
2018 pub const fn http_local_addr(&self) -> Option<SocketAddr> {
2020 self.http_local_addr
2021 }
2022
2023 pub const fn ws_local_addr(&self) -> Option<SocketAddr> {
2025 self.ws_local_addr
2026 }
2027
2028 pub fn stop(self) -> Result<(), AlreadyStoppedError> {
2030 if let Some(handle) = self.http {
2031 handle.stop()?
2032 }
2033
2034 if let Some(handle) = self.ws {
2035 handle.stop()?
2036 }
2037
2038 if let Some(handle) = self.ipc {
2039 handle.stop()?
2040 }
2041
2042 Ok(())
2043 }
2044
2045 pub fn ipc_endpoint(&self) -> Option<String> {
2047 self.ipc_endpoint.clone()
2048 }
2049
2050 pub fn http_url(&self) -> Option<String> {
2052 self.http_local_addr.map(|addr| format!("http://{addr}"))
2053 }
2054
2055 pub fn ws_url(&self) -> Option<String> {
2057 self.ws_local_addr.map(|addr| format!("ws://{addr}"))
2058 }
2059
2060 pub fn http_client(&self) -> Option<jsonrpsee::http_client::HttpClient> {
2062 let url = self.http_url()?;
2063
2064 let client = if let Some(token) = self.bearer_token() {
2065 jsonrpsee::http_client::HttpClientBuilder::default()
2066 .set_headers(HeaderMap::from_iter([(AUTHORIZATION, token.parse().unwrap())]))
2067 .build(url)
2068 } else {
2069 jsonrpsee::http_client::HttpClientBuilder::default().build(url)
2070 };
2071
2072 client.expect("failed to create http client").into()
2073 }
2074
2075 pub async fn ws_client(&self) -> Option<jsonrpsee::ws_client::WsClient> {
2077 let url = self.ws_url()?;
2078 let mut builder = jsonrpsee::ws_client::WsClientBuilder::default();
2079
2080 if let Some(token) = self.bearer_token() {
2081 let headers = HeaderMap::from_iter([(AUTHORIZATION, token.parse().unwrap())]);
2082 builder = builder.set_headers(headers);
2083 }
2084
2085 let client = builder.build(url).await.expect("failed to create ws client");
2086 Some(client)
2087 }
2088
2089 pub fn eth_http_provider(
2091 &self,
2092 ) -> Option<impl Provider<alloy_network::Ethereum> + Clone + Unpin + 'static> {
2093 self.new_http_provider_for()
2094 }
2095
2096 pub fn eth_http_provider_with_wallet<W>(
2099 &self,
2100 wallet: W,
2101 ) -> Option<impl Provider<alloy_network::Ethereum> + Clone + Unpin + 'static>
2102 where
2103 W: IntoWallet<alloy_network::Ethereum, NetworkWallet: Clone + Unpin + 'static>,
2104 {
2105 let rpc_url = self.http_url()?;
2106 let provider =
2107 ProviderBuilder::new().wallet(wallet).connect_http(rpc_url.parse().expect("valid url"));
2108 Some(provider)
2109 }
2110
2111 pub fn new_http_provider_for<N>(&self) -> Option<impl Provider<N> + Clone + Unpin + 'static>
2116 where
2117 N: RecommendedFillers<RecommendedFillers: Unpin>,
2118 {
2119 let rpc_url = self.http_url()?;
2120 let provider = ProviderBuilder::default()
2121 .with_recommended_fillers()
2122 .connect_http(rpc_url.parse().expect("valid url"));
2123 Some(provider)
2124 }
2125
2126 pub async fn eth_ws_provider(
2128 &self,
2129 ) -> Option<impl Provider<alloy_network::Ethereum> + Clone + Unpin + 'static> {
2130 self.new_ws_provider_for().await
2131 }
2132
2133 pub async fn eth_ws_provider_with_wallet<W>(
2136 &self,
2137 wallet: W,
2138 ) -> Option<impl Provider<alloy_network::Ethereum> + Clone + Unpin + 'static>
2139 where
2140 W: IntoWallet<alloy_network::Ethereum, NetworkWallet: Clone + Unpin + 'static>,
2141 {
2142 let rpc_url = self.ws_url()?;
2143 let provider = ProviderBuilder::new()
2144 .wallet(wallet)
2145 .connect(&rpc_url)
2146 .await
2147 .expect("failed to create ws client");
2148 Some(provider)
2149 }
2150
2151 pub async fn new_ws_provider_for<N>(&self) -> Option<impl Provider<N> + Clone + Unpin + 'static>
2156 where
2157 N: RecommendedFillers<RecommendedFillers: Unpin>,
2158 {
2159 let rpc_url = self.ws_url()?;
2160 let provider = ProviderBuilder::default()
2161 .with_recommended_fillers()
2162 .connect(&rpc_url)
2163 .await
2164 .expect("failed to create ws client");
2165 Some(provider)
2166 }
2167
2168 pub async fn eth_ipc_provider(
2170 &self,
2171 ) -> Option<impl Provider<alloy_network::Ethereum> + Clone + Unpin + 'static> {
2172 self.new_ipc_provider_for().await
2173 }
2174
2175 pub async fn new_ipc_provider_for<N>(
2180 &self,
2181 ) -> Option<impl Provider<N> + Clone + Unpin + 'static>
2182 where
2183 N: RecommendedFillers<RecommendedFillers: Unpin>,
2184 {
2185 let rpc_url = self.ipc_endpoint()?;
2186 let provider = ProviderBuilder::default()
2187 .with_recommended_fillers()
2188 .connect(&rpc_url)
2189 .await
2190 .expect("failed to create ipc client");
2191 Some(provider)
2192 }
2193}
2194
2195#[cfg(test)]
2196mod tests {
2197 use super::*;
2198
2199 #[test]
2200 fn parse_eth_call_bundle_selection() {
2201 let selection = "eth,admin,debug".parse::<RpcModuleSelection>().unwrap();
2202 assert_eq!(
2203 selection,
2204 RpcModuleSelection::Selection(
2205 [RethRpcModule::Eth, RethRpcModule::Admin, RethRpcModule::Debug,].into()
2206 )
2207 );
2208 }
2209
2210 #[test]
2211 fn parse_rpc_module_selection() {
2212 let selection = "all".parse::<RpcModuleSelection>().unwrap();
2213 assert_eq!(selection, RpcModuleSelection::All);
2214 }
2215
2216 #[test]
2217 fn parse_rpc_module_selection_none() {
2218 let selection = "none".parse::<RpcModuleSelection>().unwrap();
2219 assert_eq!(selection, RpcModuleSelection::Selection(Default::default()));
2220 }
2221
2222 #[test]
2223 fn parse_rpc_unique_module_selection() {
2224 let selection = "eth,admin,eth,net".parse::<RpcModuleSelection>().unwrap();
2225 assert_eq!(
2226 selection,
2227 RpcModuleSelection::Selection(
2228 [RethRpcModule::Eth, RethRpcModule::Admin, RethRpcModule::Net,].into()
2229 )
2230 );
2231 }
2232
2233 #[test]
2234 fn identical_selection() {
2235 assert!(RpcModuleSelection::are_identical(
2236 Some(&RpcModuleSelection::All),
2237 Some(&RpcModuleSelection::All),
2238 ));
2239 assert!(!RpcModuleSelection::are_identical(
2240 Some(&RpcModuleSelection::All),
2241 Some(&RpcModuleSelection::Standard),
2242 ));
2243 assert!(RpcModuleSelection::are_identical(
2244 Some(&RpcModuleSelection::Selection(RpcModuleSelection::Standard.to_selection())),
2245 Some(&RpcModuleSelection::Standard),
2246 ));
2247 assert!(RpcModuleSelection::are_identical(
2248 Some(&RpcModuleSelection::Selection([RethRpcModule::Eth].into())),
2249 Some(&RpcModuleSelection::Selection([RethRpcModule::Eth].into())),
2250 ));
2251 assert!(RpcModuleSelection::are_identical(
2252 None,
2253 Some(&RpcModuleSelection::Selection(Default::default())),
2254 ));
2255 assert!(RpcModuleSelection::are_identical(
2256 Some(&RpcModuleSelection::Selection(Default::default())),
2257 None,
2258 ));
2259 assert!(RpcModuleSelection::are_identical(None, None));
2260 }
2261
2262 #[test]
2263 fn test_rpc_module_str() {
2264 macro_rules! assert_rpc_module {
2265 ($($s:expr => $v:expr,)*) => {
2266 $(
2267 let val: RethRpcModule = $s.parse().unwrap();
2268 assert_eq!(val, $v);
2269 assert_eq!(val.to_string().as_str(), $s);
2270 )*
2271 };
2272 }
2273 assert_rpc_module!
2274 (
2275 "admin" => RethRpcModule::Admin,
2276 "debug" => RethRpcModule::Debug,
2277 "eth" => RethRpcModule::Eth,
2278 "net" => RethRpcModule::Net,
2279 "trace" => RethRpcModule::Trace,
2280 "web3" => RethRpcModule::Web3,
2281 "rpc" => RethRpcModule::Rpc,
2282 "ots" => RethRpcModule::Ots,
2283 "reth" => RethRpcModule::Reth,
2284 );
2285 }
2286
2287 #[test]
2288 fn test_default_selection() {
2289 let selection = RpcModuleSelection::Standard.to_selection();
2290 assert_eq!(selection, [RethRpcModule::Eth, RethRpcModule::Net, RethRpcModule::Web3].into())
2291 }
2292
2293 #[test]
2294 fn test_create_rpc_module_config() {
2295 let selection = vec!["eth", "admin"];
2296 let config = RpcModuleSelection::try_from_selection(selection).unwrap();
2297 assert_eq!(
2298 config,
2299 RpcModuleSelection::Selection([RethRpcModule::Eth, RethRpcModule::Admin].into())
2300 );
2301 }
2302
2303 #[test]
2304 fn test_configure_transport_config() {
2305 let config = TransportRpcModuleConfig::default()
2306 .with_http([RethRpcModule::Eth, RethRpcModule::Admin]);
2307 assert_eq!(
2308 config,
2309 TransportRpcModuleConfig {
2310 http: Some(RpcModuleSelection::Selection(
2311 [RethRpcModule::Eth, RethRpcModule::Admin].into()
2312 )),
2313 ws: None,
2314 ipc: None,
2315 config: None,
2316 }
2317 )
2318 }
2319
2320 #[test]
2321 fn test_configure_transport_config_none() {
2322 let config = TransportRpcModuleConfig::default().with_http(Vec::<RethRpcModule>::new());
2323 assert_eq!(
2324 config,
2325 TransportRpcModuleConfig {
2326 http: Some(RpcModuleSelection::Selection(Default::default())),
2327 ws: None,
2328 ipc: None,
2329 config: None,
2330 }
2331 )
2332 }
2333
2334 fn create_test_module() -> RpcModule<()> {
2335 let mut module = RpcModule::new(());
2336 module.register_method("anything", |_, _, _| "succeed").unwrap();
2337 module
2338 }
2339
2340 #[test]
2341 fn test_remove_http_method() {
2342 let mut modules =
2343 TransportRpcModules { http: Some(create_test_module()), ..Default::default() };
2344 assert!(modules.remove_http_method("anything"));
2346
2347 assert!(!modules.remove_http_method("non_existent_method"));
2349
2350 assert!(modules.http.as_ref().unwrap().method("anything").is_none());
2352 }
2353
2354 #[test]
2355 fn test_remove_ws_method() {
2356 let mut modules =
2357 TransportRpcModules { ws: Some(create_test_module()), ..Default::default() };
2358
2359 assert!(modules.remove_ws_method("anything"));
2361
2362 assert!(!modules.remove_ws_method("non_existent_method"));
2364
2365 assert!(modules.ws.as_ref().unwrap().method("anything").is_none());
2367 }
2368
2369 #[test]
2370 fn test_remove_ipc_method() {
2371 let mut modules =
2372 TransportRpcModules { ipc: Some(create_test_module()), ..Default::default() };
2373
2374 assert!(modules.remove_ipc_method("anything"));
2376
2377 assert!(!modules.remove_ipc_method("non_existent_method"));
2379
2380 assert!(modules.ipc.as_ref().unwrap().method("anything").is_none());
2382 }
2383
2384 #[test]
2385 fn test_remove_method_from_configured() {
2386 let mut modules = TransportRpcModules {
2387 http: Some(create_test_module()),
2388 ws: Some(create_test_module()),
2389 ipc: Some(create_test_module()),
2390 ..Default::default()
2391 };
2392
2393 assert!(modules.remove_method_from_configured("anything"));
2395
2396 assert!(!modules.remove_method_from_configured("anything"));
2398
2399 assert!(!modules.remove_method_from_configured("non_existent_method"));
2401
2402 assert!(modules.http.as_ref().unwrap().method("anything").is_none());
2404 assert!(modules.ws.as_ref().unwrap().method("anything").is_none());
2405 assert!(modules.ipc.as_ref().unwrap().method("anything").is_none());
2406 }
2407
2408 #[test]
2409 fn test_transport_rpc_module_rename() {
2410 let mut modules = TransportRpcModules {
2411 http: Some(create_test_module()),
2412 ws: Some(create_test_module()),
2413 ipc: Some(create_test_module()),
2414 ..Default::default()
2415 };
2416
2417 assert!(modules.http.as_ref().unwrap().method("anything").is_some());
2419 assert!(modules.ws.as_ref().unwrap().method("anything").is_some());
2420 assert!(modules.ipc.as_ref().unwrap().method("anything").is_some());
2421
2422 assert!(modules.http.as_ref().unwrap().method("something").is_none());
2424 assert!(modules.ws.as_ref().unwrap().method("something").is_none());
2425 assert!(modules.ipc.as_ref().unwrap().method("something").is_none());
2426
2427 let mut other_module = RpcModule::new(());
2429 other_module.register_method("something", |_, _, _| "fails").unwrap();
2430
2431 modules.rename("anything", other_module).expect("rename failed");
2433
2434 assert!(modules.http.as_ref().unwrap().method("anything").is_none());
2436 assert!(modules.ws.as_ref().unwrap().method("anything").is_none());
2437 assert!(modules.ipc.as_ref().unwrap().method("anything").is_none());
2438
2439 assert!(modules.http.as_ref().unwrap().method("something").is_some());
2441 assert!(modules.ws.as_ref().unwrap().method("something").is_some());
2442 assert!(modules.ipc.as_ref().unwrap().method("something").is_some());
2443 }
2444
2445 #[test]
2446 fn test_replace_http_method() {
2447 let mut modules =
2448 TransportRpcModules { http: Some(create_test_module()), ..Default::default() };
2449
2450 let mut other_module = RpcModule::new(());
2451 other_module.register_method("something", |_, _, _| "fails").unwrap();
2452
2453 assert!(modules.replace_http(other_module.clone()).unwrap());
2454
2455 assert!(modules.http.as_ref().unwrap().method("something").is_some());
2456
2457 other_module.register_method("anything", |_, _, _| "fails").unwrap();
2458 assert!(modules.replace_http(other_module.clone()).unwrap());
2459
2460 assert!(modules.http.as_ref().unwrap().method("anything").is_some());
2461 }
2462 #[test]
2463 fn test_replace_ipc_method() {
2464 let mut modules =
2465 TransportRpcModules { ipc: Some(create_test_module()), ..Default::default() };
2466
2467 let mut other_module = RpcModule::new(());
2468 other_module.register_method("something", |_, _, _| "fails").unwrap();
2469
2470 assert!(modules.replace_ipc(other_module.clone()).unwrap());
2471
2472 assert!(modules.ipc.as_ref().unwrap().method("something").is_some());
2473
2474 other_module.register_method("anything", |_, _, _| "fails").unwrap();
2475 assert!(modules.replace_ipc(other_module.clone()).unwrap());
2476
2477 assert!(modules.ipc.as_ref().unwrap().method("anything").is_some());
2478 }
2479 #[test]
2480 fn test_replace_ws_method() {
2481 let mut modules =
2482 TransportRpcModules { ws: Some(create_test_module()), ..Default::default() };
2483
2484 let mut other_module = RpcModule::new(());
2485 other_module.register_method("something", |_, _, _| "fails").unwrap();
2486
2487 assert!(modules.replace_ws(other_module.clone()).unwrap());
2488
2489 assert!(modules.ws.as_ref().unwrap().method("something").is_some());
2490
2491 other_module.register_method("anything", |_, _, _| "fails").unwrap();
2492 assert!(modules.replace_ws(other_module.clone()).unwrap());
2493
2494 assert!(modules.ws.as_ref().unwrap().method("anything").is_some());
2495 }
2496
2497 #[test]
2498 fn test_replace_configured() {
2499 let mut modules = TransportRpcModules {
2500 http: Some(create_test_module()),
2501 ws: Some(create_test_module()),
2502 ipc: Some(create_test_module()),
2503 ..Default::default()
2504 };
2505 let mut other_module = RpcModule::new(());
2506 other_module.register_method("something", |_, _, _| "fails").unwrap();
2507
2508 assert!(modules.replace_configured(other_module).unwrap());
2509
2510 assert!(modules.http.as_ref().unwrap().method("something").is_some());
2512 assert!(modules.ipc.as_ref().unwrap().method("something").is_some());
2513 assert!(modules.ws.as_ref().unwrap().method("something").is_some());
2514
2515 assert!(modules.http.as_ref().unwrap().method("anything").is_some());
2516 assert!(modules.ipc.as_ref().unwrap().method("anything").is_some());
2517 assert!(modules.ws.as_ref().unwrap().method("anything").is_some());
2518 }
2519}