Skip to main content

reth_rpc_builder/
lib.rs

1//! Configure reth RPC.
2//!
3//! This crate contains several builder and config types that allow to configure the selection of
4//! [`RethRpcModule`] specific to transports (ws, http, ipc).
5//!
6//! The [`RpcModuleBuilder`] is the main entrypoint for configuring all reth modules. It takes
7//! instances of components required to start the servers, such as provider impls, network and
8//! transaction pool. [`RpcModuleBuilder::build`] returns a [`TransportRpcModules`] which contains
9//! the transport specific config (what APIs are available via this transport).
10//!
11//! The [`RpcServerConfig`] is used to assemble and start the http server, ws server, ipc servers,
12//! it requires the [`TransportRpcModules`] so it can start the servers with the configured modules.
13
14#![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};
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::FullConsensus;
35use reth_engine_primitives::{ConsensusEngineEvent, ConsensusEngineHandle};
36use reth_evm::ConfigureEvm;
37use reth_network_api::{noop::NoopNetwork, NetworkInfo, Peers};
38use reth_payload_primitives::PayloadTypes;
39use reth_primitives_traits::{NodePrimitives, TxTy};
40use reth_rpc::{
41    AdminApi, DebugApi, EngineEthApi, EthApi, EthApiBuilder, EthBundle, MinerApi, NetApi,
42    OtterscanApi, RPCApi, RethApi, TraceApi, TxPoolApi, Web3Api,
43};
44use reth_rpc_api::servers::*;
45use reth_rpc_engine_api::RethEngineApi;
46use reth_rpc_eth_api::{
47    helpers::{
48        pending_block::PendingEnvBuilder, Call, EthApiSpec, EthTransactions, LoadPendingBlock,
49        TraceExt,
50    },
51    node::RpcNodeCoreAdapter,
52    EthApiServer, EthApiTypes, FullEthApiServer, FullEthApiTypes, RpcBlock, RpcConvert,
53    RpcConverter, RpcHeader, RpcNodeCore, RpcReceipt, RpcTransaction, RpcTxReq,
54};
55use reth_rpc_eth_types::{receipt::EthReceiptConverter, EthConfig, EthSubscriptionIdProvider};
56use reth_rpc_layer::{AuthLayer, Claims, CompressionLayer, JwtAuthValidator, JwtSecret};
57pub use reth_rpc_server_types::RethRpcModule;
58use reth_storage_api::{
59    AccountReader, BlockReader, ChangeSetReader, FullRpcProvider, NodePrimitivesProvider,
60    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;
73
74pub use cors::CorsDomainError;
75
76// re-export for convenience
77pub 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
85/// Auth server utilities.
86pub mod auth;
87
88/// RPC server utilities.
89pub mod config;
90
91/// Utils for installing Rpc middleware
92pub mod middleware;
93
94/// Cors utilities.
95mod cors;
96
97/// Rpc error utilities.
98pub mod error;
99
100/// Eth utils
101pub mod eth;
102pub use eth::EthHandlers;
103
104// Rpc server metrics
105mod 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
113// Rpc rate limiter
114pub mod rate_limiter;
115
116/// A builder type to configure the RPC module: See [`RpcModule`]
117///
118/// This is the main entrypoint and the easiest way to configure an RPC server.
119#[derive(Debug, Clone)]
120pub struct RpcModuleBuilder<N, Provider, Pool, Network, EvmConfig, Consensus> {
121    /// The Provider type to when creating all rpc handlers
122    provider: Provider,
123    /// The Pool type to when creating all rpc handlers
124    pool: Pool,
125    /// The Network type to when creating all rpc handlers
126    network: Network,
127    /// How additional tasks are spawned, for example in the eth pubsub namespace
128    executor: Option<Runtime>,
129    /// Defines how the EVM should be configured before execution.
130    evm_config: EvmConfig,
131    /// The consensus implementation.
132    consensus: Consensus,
133    /// Node data primitives.
134    _primitives: PhantomData<N>,
135}
136
137// === impl RpcBuilder ===
138
139impl<N, Provider, Pool, Network, EvmConfig, Consensus>
140    RpcModuleBuilder<N, Provider, Pool, Network, EvmConfig, Consensus>
141{
142    /// Create a new instance of the builder
143    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    /// Configure the provider instance.
163    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    /// Configure the transaction pool instance.
172    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    /// Configure a [`NoopTransactionPool`] instance.
181    ///
182    /// Caution: This will configure a pool API that does absolutely nothing.
183    /// This is only intended for allow easier setup of namespaces that depend on the
184    /// [`EthApi`] which requires a [`TransactionPool`] implementation.
185    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    /// Configure the network instance.
201    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    /// Configure a [`NoopNetwork`] instance.
210    ///
211    /// Caution: This will configure a network API that does absolutely nothing.
212    /// This is only intended for allow easier setup of namespaces that depend on the
213    /// [`EthApi`] which requires a [`NetworkInfo`] implementation.
214    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    /// Configure the task executor to use for additional tasks.
230    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    /// Configure the evm configuration type
244    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    /// Configure the consensus implementation.
253    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    /// Instantiates a new [`EthApiBuilder`] from the configured components.
262    #[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    /// Initializes a new [`EthApiServer`] with the configured components and default settings.
286    ///
287    /// Note: This spawns all necessary tasks.
288    ///
289    /// See also [`EthApiBuilder`].
290    #[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 + Clone,
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        + AccountReader
320        + ChangeSetReader,
321    Pool: TransactionPool + Clone + 'static,
322    Network: NetworkInfo + Peers + Clone + 'static,
323    EvmConfig: ConfigureEvm<Primitives = N> + 'static,
324    Consensus: FullConsensus<N> + Clone + 'static,
325{
326    /// Configures all [`RpcModule`]s specific to the given [`TransportRpcModuleConfig`] which can
327    /// be used to start the transport server(s).
328    ///
329    /// This behaves exactly as [`RpcModuleBuilder::build`] for the [`TransportRpcModules`], but
330    /// also configures the auth (engine api) server, which exposes a subset of the `eth_`
331    /// namespace.
332    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    /// Converts the builder into a [`RpcRegistryInner`] which can be used to create all
358    /// components.
359    ///
360    /// This is useful for getting access to API handlers directly
361    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    /// Configures all [`RpcModule`]s specific to the given [`TransportRpcModuleConfig`] which can
387    /// be used to start the transport server(s).
388    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/// Bundles settings for modules
422#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize)]
423pub struct RpcModuleConfig {
424    /// `eth` namespace settings
425    eth: EthConfig,
426}
427
428// === impl RpcModuleConfig ===
429
430impl RpcModuleConfig {
431    /// Convenience method to create a new [`RpcModuleConfigBuilder`]
432    pub fn builder() -> RpcModuleConfigBuilder {
433        RpcModuleConfigBuilder::default()
434    }
435
436    /// Returns a new RPC module config given the eth namespace config
437    pub const fn new(eth: EthConfig) -> Self {
438        Self { eth }
439    }
440
441    /// Get a reference to the eth namespace config
442    pub const fn eth(&self) -> &EthConfig {
443        &self.eth
444    }
445
446    /// Get a mutable reference to the eth namespace config
447    pub const fn eth_mut(&mut self) -> &mut EthConfig {
448        &mut self.eth
449    }
450}
451
452/// Configures [`RpcModuleConfig`]
453#[derive(Clone, Debug, Default)]
454pub struct RpcModuleConfigBuilder {
455    eth: Option<EthConfig>,
456}
457
458// === impl RpcModuleConfigBuilder ===
459
460impl RpcModuleConfigBuilder {
461    /// Configures a custom eth namespace config
462    pub fn eth(mut self, eth: EthConfig) -> Self {
463        self.eth = Some(eth);
464        self
465    }
466
467    /// Consumes the type and creates the [`RpcModuleConfig`]
468    pub fn build(self) -> RpcModuleConfig {
469        let Self { eth } = self;
470        RpcModuleConfig { eth: eth.unwrap_or_default() }
471    }
472
473    /// Get a reference to the eth namespace config, if any
474    pub const fn get_eth(&self) -> Option<&EthConfig> {
475        self.eth.as_ref()
476    }
477
478    /// Get a mutable reference to the eth namespace config, if any
479    pub const fn eth_mut(&mut self) -> &mut Option<EthConfig> {
480        &mut self.eth
481    }
482
483    /// Get the eth namespace config, creating a default if none is set
484    pub fn eth_mut_or_default(&mut self) -> &mut EthConfig {
485        self.eth.get_or_insert_with(EthConfig::default)
486    }
487}
488
489/// A Helper type the holds instances of the configured modules.
490#[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    /// Holds all `eth_` namespace handlers
499    eth: EthHandlers<EthApi>,
500    /// to put trace calls behind semaphore
501    blocking_pool_guard: BlockingTaskGuard,
502    /// Contains the [Methods] of a module
503    modules: HashMap<RethRpcModule, Methods>,
504    /// eth config settings
505    eth_config: EthConfig,
506    /// Notification channel for engine API events
507    engine_events:
508        EventSender<ConsensusEngineEvent<<EthApi::RpcConvert as RpcConvert>::Primitives>>,
509}
510
511// === impl RpcRegistryInner ===
512
513impl<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 + 'static,
526    EvmConfig: ConfigureEvm<Primitives = N>,
527{
528    /// Creates a new, empty instance.
529    #[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    /// Returns a reference to the installed [`EthApi`].
572    pub const fn eth_api(&self) -> &EthApi {
573        &self.eth.api
574    }
575
576    /// Returns a reference to the installed [`EthHandlers`].
577    pub const fn eth_handlers(&self) -> &EthHandlers<EthApi> {
578        &self.eth
579    }
580
581    /// Returns a reference to the pool
582    pub const fn pool(&self) -> &Pool {
583        &self.pool
584    }
585
586    /// Returns a reference to the tasks type
587    pub const fn tasks(&self) -> &Runtime {
588        &self.executor
589    }
590
591    /// Returns a reference to the provider
592    pub const fn provider(&self) -> &Provider {
593        &self.provider
594    }
595
596    /// Returns a reference to the evm config
597    pub const fn evm_config(&self) -> &Evm {
598        &self.evm_config
599    }
600
601    /// Returns all installed methods
602    pub fn methods(&self) -> Vec<Methods> {
603        self.modules.values().cloned().collect()
604    }
605
606    /// Returns a merged `RpcModule`
607    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    /// Instantiates `AdminApi`
625    pub fn admin_api(&self) -> AdminApi<Network, Provider::ChainSpec, Pool>
626    where
627        Network: Peers,
628        Pool: TransactionPool + Clone + 'static,
629    {
630        AdminApi::new(self.network.clone(), self.provider.chain_spec(), self.pool.clone())
631    }
632
633    /// Instantiates `Web3Api`
634    pub fn web3_api(&self) -> Web3Api<Network> {
635        Web3Api::new(self.network.clone())
636    }
637
638    /// Register Admin Namespace
639    pub fn register_admin(&mut self) -> &mut Self
640    where
641        Network: Peers,
642        Pool: TransactionPool + Clone + 'static,
643    {
644        let adminapi = self.admin_api();
645        self.modules.insert(RethRpcModule::Admin, adminapi.into_rpc().into());
646        self
647    }
648
649    /// Register Web3 Namespace
650    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        > + AccountReader
667        + ChangeSetReader
668        + CanonStateSubscriptions<Primitives = N>
669        + ForkChoiceSubscriptions<Header = N::BlockHeader>
670        + PersistedBlockSubscriptions,
671    Network: NetworkInfo + Peers + Clone + 'static,
672    EthApi: EthApiServer<
673            RpcTxReq<EthApi::NetworkTypes>,
674            RpcTransaction<EthApi::NetworkTypes>,
675            RpcBlock<EthApi::NetworkTypes>,
676            RpcReceipt<EthApi::NetworkTypes>,
677            RpcHeader<EthApi::NetworkTypes>,
678            TxTy<N>,
679        > + EthApiTypes,
680    EvmConfig: ConfigureEvm<Primitives = N> + 'static,
681{
682    /// Register Eth Namespace
683    ///
684    /// # Panics
685    ///
686    /// If called outside of the tokio runtime. See also [`Self::eth_api`]
687    pub fn register_eth(&mut self) -> &mut Self {
688        let eth_api = self.eth_api().clone();
689        self.modules.insert(RethRpcModule::Eth, eth_api.into_rpc().into());
690        self
691    }
692
693    /// Register Otterscan Namespace
694    ///
695    /// # Panics
696    ///
697    /// If called outside of the tokio runtime. See also [`Self::eth_api`]
698    pub fn register_ots(&mut self) -> &mut Self
699    where
700        EthApi: TraceExt + EthTransactions<Primitives = N>,
701    {
702        let otterscan_api = self.otterscan_api();
703        self.modules.insert(RethRpcModule::Ots, otterscan_api.into_rpc().into());
704        self
705    }
706
707    /// Register Debug Namespace
708    ///
709    /// # Panics
710    ///
711    /// If called outside of the tokio runtime. See also [`Self::eth_api`]
712    pub fn register_debug(&mut self) -> &mut Self
713    where
714        EthApi: EthTransactions + TraceExt,
715    {
716        let debug_api = self.debug_api();
717        self.modules.insert(RethRpcModule::Debug, debug_api.into_rpc().into());
718        self
719    }
720
721    /// Register Trace Namespace
722    ///
723    /// # Panics
724    ///
725    /// If called outside of the tokio runtime. See also [`Self::eth_api`]
726    pub fn register_trace(&mut self) -> &mut Self
727    where
728        EthApi: TraceExt,
729    {
730        let trace_api = self.trace_api();
731        self.modules.insert(RethRpcModule::Trace, trace_api.into_rpc().into());
732        self
733    }
734
735    /// Register Net Namespace
736    ///
737    /// See also [`Self::eth_api`]
738    ///
739    /// # Panics
740    ///
741    /// If called outside of the tokio runtime.
742    pub fn register_net(&mut self) -> &mut Self
743    where
744        EthApi: EthApiSpec + 'static,
745    {
746        let netapi = self.net_api();
747        self.modules.insert(RethRpcModule::Net, netapi.into_rpc().into());
748        self
749    }
750
751    /// Register Reth namespace
752    ///
753    /// See also [`Self::eth_api`]
754    ///
755    /// # Panics
756    ///
757    /// If called outside of the tokio runtime.
758    pub fn register_reth(&mut self) -> &mut Self {
759        let rethapi = self.reth_api();
760        self.modules.insert(RethRpcModule::Reth, rethapi.into_rpc().into());
761        self
762    }
763
764    /// Instantiates `OtterscanApi`
765    ///
766    /// # Panics
767    ///
768    /// If called outside of the tokio runtime. See also [`Self::eth_api`]
769    pub fn otterscan_api(&self) -> OtterscanApi<EthApi> {
770        let eth_api = self.eth_api().clone();
771        OtterscanApi::new(eth_api)
772    }
773}
774
775impl<N, Provider, Pool, Network, EthApi, EvmConfig, Consensus>
776    RpcRegistryInner<Provider, Pool, Network, EthApi, EvmConfig, Consensus>
777where
778    N: NodePrimitives,
779    Provider: FullRpcProvider<
780            Block = N::Block,
781            Header = N::BlockHeader,
782            Transaction = N::SignedTx,
783            Receipt = N::Receipt,
784        > + AccountReader
785        + ChangeSetReader,
786    Network: NetworkInfo + Peers + Clone + 'static,
787    EthApi: EthApiTypes,
788    EvmConfig: ConfigureEvm<Primitives = N>,
789{
790    /// Instantiates `TraceApi`
791    ///
792    /// # Panics
793    ///
794    /// If called outside of the tokio runtime. See also [`Self::eth_api`]
795    pub fn trace_api(&self) -> TraceApi<EthApi> {
796        TraceApi::new(
797            self.eth_api().clone(),
798            self.blocking_pool_guard.clone(),
799            self.eth_config.clone(),
800        )
801    }
802
803    /// Instantiates [`EthBundle`] Api
804    ///
805    /// # Panics
806    ///
807    /// If called outside of the tokio runtime. See also [`Self::eth_api`]
808    pub fn bundle_api(&self) -> EthBundle<EthApi>
809    where
810        EthApi: EthTransactions + LoadPendingBlock + Call,
811    {
812        let eth_api = self.eth_api().clone();
813        EthBundle::new(eth_api, self.blocking_pool_guard.clone())
814    }
815
816    /// Instantiates `DebugApi`
817    ///
818    /// # Panics
819    ///
820    /// If called outside of the tokio runtime. See also [`Self::eth_api`]
821    pub fn debug_api(&self) -> DebugApi<EthApi>
822    where
823        EthApi: FullEthApiTypes,
824    {
825        DebugApi::new(
826            self.eth_api().clone(),
827            self.blocking_pool_guard.clone(),
828            self.tasks(),
829            self.engine_events.new_listener(),
830        )
831    }
832
833    /// Instantiates `NetApi`
834    ///
835    /// # Panics
836    ///
837    /// If called outside of the tokio runtime. See also [`Self::eth_api`]
838    pub fn net_api(&self) -> NetApi<Network, EthApi>
839    where
840        EthApi: EthApiSpec + 'static,
841    {
842        let eth_api = self.eth_api().clone();
843        NetApi::new(self.network.clone(), eth_api)
844    }
845
846    /// Instantiates `RethApi`
847    pub fn reth_api(&self) -> RethApi<Provider, EvmConfig> {
848        RethApi::new(
849            self.provider.clone(),
850            self.evm_config.clone(),
851            self.blocking_pool_guard.clone(),
852            self.executor.clone(),
853        )
854    }
855}
856
857impl<N, Provider, Pool, Network, EthApi, EvmConfig, Consensus>
858    RpcRegistryInner<Provider, Pool, Network, EthApi, EvmConfig, Consensus>
859where
860    N: NodePrimitives,
861    Provider: FullRpcProvider<Block = N::Block>
862        + CanonStateSubscriptions<Primitives = N>
863        + ForkChoiceSubscriptions<Header = N::BlockHeader>
864        + PersistedBlockSubscriptions
865        + AccountReader
866        + ChangeSetReader,
867    Pool: TransactionPool + Clone + 'static,
868    Network: NetworkInfo + Peers + Clone + 'static,
869    EthApi: FullEthApiServer,
870    EvmConfig: ConfigureEvm<Primitives = N> + 'static,
871    Consensus: FullConsensus<N> + Clone + 'static,
872{
873    /// Configures the auth module that includes the
874    ///   * `engine_` namespace
875    ///   * `reth_` namespace
876    ///   * `api_` namespace
877    ///
878    /// Note: This does _not_ register the `engine_` in this registry.
879    pub fn create_auth_module<Payload>(
880        &self,
881        engine_api: impl IntoEngineApiRpcModule,
882        beacon_engine_handle: ConsensusEngineHandle<Payload>,
883    ) -> AuthRpcModule
884    where
885        Payload: PayloadTypes,
886    {
887        let mut module = engine_api.into_rpc_module();
888
889        // Merge reth_* endpoints
890        let reth_engine_api = RethEngineApi::new(beacon_engine_handle);
891        module
892            .merge(RethEngineApiServer::into_rpc(reth_engine_api).remove_context())
893            .expect("No conflicting methods");
894
895        // also merge a subset of `eth_` handlers
896        let eth_handlers = self.eth_handlers();
897        let engine_eth = EngineEthApi::new(eth_handlers.api.clone(), eth_handlers.filter.clone());
898
899        module.merge(engine_eth.into_rpc()).expect("No conflicting methods");
900
901        AuthRpcModule { inner: module }
902    }
903
904    /// Helper function to create a [`RpcModule`] if it's not `None`
905    fn maybe_module(&mut self, config: Option<&RpcModuleSelection>) -> Option<RpcModule<()>> {
906        config.map(|config| self.module_for(config))
907    }
908
909    /// Configure a [`TransportRpcModules`] using the current registry. This
910    /// creates [`RpcModule`] instances for the modules selected by the
911    /// `config`.
912    pub fn create_transport_rpc_modules(
913        &mut self,
914        config: TransportRpcModuleConfig,
915    ) -> TransportRpcModules<()> {
916        let mut modules = TransportRpcModules::default();
917        let http = self.maybe_module(config.http.as_ref());
918        let ws = self.maybe_module(config.ws.as_ref());
919        let ipc = self.maybe_module(config.ipc.as_ref());
920
921        modules.config = config;
922        modules.http = http;
923        modules.ws = ws;
924        modules.ipc = ipc;
925        modules
926    }
927
928    /// Populates a new [`RpcModule`] based on the selected [`RethRpcModule`]s in the given
929    /// [`RpcModuleSelection`]
930    pub fn module_for(&mut self, config: &RpcModuleSelection) -> RpcModule<()> {
931        let mut module = RpcModule::new(());
932        let all_methods = self.reth_methods(config.iter_selection());
933        for methods in all_methods {
934            module.merge(methods).expect("No conflicts");
935        }
936        module
937    }
938
939    /// Returns the [Methods] for the given [`RethRpcModule`]
940    ///
941    /// If this is the first time the namespace is requested, a new instance of API implementation
942    /// will be created.
943    ///
944    /// # Panics
945    ///
946    /// If called outside of the tokio runtime. See also [`Self::eth_api`]
947    pub fn reth_methods(
948        &mut self,
949        namespaces: impl Iterator<Item = RethRpcModule>,
950    ) -> Vec<Methods> {
951        let EthHandlers { api: eth_api, filter: eth_filter, pubsub: eth_pubsub, .. } =
952            self.eth_handlers().clone();
953
954        // Create a copy, so we can list out all the methods for rpc_ api
955        let namespaces: Vec<_> = namespaces.collect();
956        namespaces
957            .iter()
958            .map(|namespace| {
959                self.modules
960                    .entry(namespace.clone())
961                    .or_insert_with(|| match namespace.clone() {
962                        RethRpcModule::Admin => AdminApi::new(
963                            self.network.clone(),
964                            self.provider.chain_spec(),
965                            self.pool.clone(),
966                        )
967                        .into_rpc()
968                        .into(),
969                        RethRpcModule::Debug => DebugApi::new(
970                            eth_api.clone(),
971                            self.blocking_pool_guard.clone(),
972                            &self.executor,
973                            self.engine_events.new_listener(),
974                        )
975                        .into_rpc()
976                        .into(),
977                        RethRpcModule::Eth => {
978                            // merge all eth handlers
979                            let mut module = eth_api.clone().into_rpc();
980                            module.merge(eth_filter.clone().into_rpc()).expect("No conflicts");
981                            module.merge(eth_pubsub.clone().into_rpc()).expect("No conflicts");
982                            module
983                                .merge(
984                                    EthBundle::new(
985                                        eth_api.clone(),
986                                        self.blocking_pool_guard.clone(),
987                                    )
988                                    .into_rpc(),
989                                )
990                                .expect("No conflicts");
991
992                            module.into()
993                        }
994                        RethRpcModule::Net => {
995                            NetApi::new(self.network.clone(), eth_api.clone()).into_rpc().into()
996                        }
997                        RethRpcModule::Trace => TraceApi::new(
998                            eth_api.clone(),
999                            self.blocking_pool_guard.clone(),
1000                            self.eth_config.clone(),
1001                        )
1002                        .into_rpc()
1003                        .into(),
1004                        RethRpcModule::Web3 => Web3Api::new(self.network.clone()).into_rpc().into(),
1005                        RethRpcModule::Txpool => TxPoolApi::new(
1006                            self.eth.api.pool().clone(),
1007                            dyn_clone::clone(self.eth.api.converter()),
1008                        )
1009                        .into_rpc()
1010                        .into(),
1011                        RethRpcModule::Rpc => RPCApi::new(
1012                            namespaces
1013                                .iter()
1014                                .map(|module| (module.to_string(), "1.0".to_string()))
1015                                .collect(),
1016                        )
1017                        .into_rpc()
1018                        .into(),
1019                        RethRpcModule::Ots => OtterscanApi::new(eth_api.clone()).into_rpc().into(),
1020                        RethRpcModule::Reth => RethApi::new(
1021                            self.provider.clone(),
1022                            self.evm_config.clone(),
1023                            self.blocking_pool_guard.clone(),
1024                            self.executor.clone(),
1025                        )
1026                        .into_rpc()
1027                        .into(),
1028                        RethRpcModule::Miner => MinerApi::default().into_rpc().into(),
1029                        RethRpcModule::Mev => {
1030                            EthSimBundle::new(eth_api.clone(), self.blocking_pool_guard.clone())
1031                                .into_rpc()
1032                                .into()
1033                        }
1034                        // these are implementation specific and need to be handled during
1035                        // initialization and should be registered via extend_rpc_modules in the
1036                        // nodebuilder rpc addon stack
1037                        RethRpcModule::Flashbots |
1038                        RethRpcModule::Testing |
1039                        RethRpcModule::Other(_) => Default::default(),
1040                    })
1041                    .clone()
1042            })
1043            .collect::<Vec<_>>()
1044    }
1045}
1046
1047impl<Provider, Pool, Network, EthApi, EvmConfig, Consensus> Clone
1048    for RpcRegistryInner<Provider, Pool, Network, EthApi, EvmConfig, Consensus>
1049where
1050    EthApi: EthApiTypes,
1051    Provider: Clone,
1052    Pool: Clone,
1053    Network: Clone,
1054    EvmConfig: Clone,
1055    Consensus: Clone,
1056{
1057    fn clone(&self) -> Self {
1058        Self {
1059            provider: self.provider.clone(),
1060            pool: self.pool.clone(),
1061            network: self.network.clone(),
1062            executor: self.executor.clone(),
1063            evm_config: self.evm_config.clone(),
1064            consensus: self.consensus.clone(),
1065            eth: self.eth.clone(),
1066            blocking_pool_guard: self.blocking_pool_guard.clone(),
1067            modules: self.modules.clone(),
1068            eth_config: self.eth_config.clone(),
1069            engine_events: self.engine_events.clone(),
1070        }
1071    }
1072}
1073
1074/// A builder type for configuring and launching the servers that will handle RPC requests.
1075///
1076/// Supported server transports are:
1077///    - http
1078///    - ws
1079///    - ipc
1080///
1081/// Http and WS share the same settings: [`ServerBuilder`].
1082///
1083/// Once the [`RpcModule`] is built via [`RpcModuleBuilder`] the servers can be started, See also
1084/// [`ServerBuilder::build`] and [`Server::start`](jsonrpsee::server::Server::start).
1085#[derive(Debug)]
1086pub struct RpcServerConfig<RpcMiddleware = Identity> {
1087    /// Configs for JSON-RPC Http.
1088    http_server_config: Option<ServerConfigBuilder>,
1089    /// Allowed CORS Domains for http
1090    http_cors_domains: Option<String>,
1091    /// Address where to bind the http server to
1092    http_addr: Option<SocketAddr>,
1093    /// Control whether http responses should be compressed
1094    http_disable_compression: bool,
1095    /// Configs for WS server
1096    ws_server_config: Option<ServerConfigBuilder>,
1097    /// Allowed CORS Domains for ws.
1098    ws_cors_domains: Option<String>,
1099    /// Address where to bind the ws server to
1100    ws_addr: Option<SocketAddr>,
1101    /// Configs for JSON-RPC IPC server
1102    ipc_server_config: Option<IpcServerBuilder<Identity, Identity>>,
1103    /// The Endpoint where to launch the ipc server
1104    ipc_endpoint: Option<String>,
1105    /// JWT secret for authentication
1106    jwt_secret: Option<JwtSecret>,
1107    /// Configurable RPC middleware
1108    rpc_middleware: RpcMiddleware,
1109}
1110
1111// === impl RpcServerConfig ===
1112
1113impl Default for RpcServerConfig<Identity> {
1114    /// Create a new config instance
1115    fn default() -> Self {
1116        Self {
1117            http_server_config: None,
1118            http_cors_domains: None,
1119            http_addr: None,
1120            http_disable_compression: false,
1121            ws_server_config: None,
1122            ws_cors_domains: None,
1123            ws_addr: None,
1124            ipc_server_config: None,
1125            ipc_endpoint: None,
1126            jwt_secret: None,
1127            rpc_middleware: Default::default(),
1128        }
1129    }
1130}
1131
1132impl RpcServerConfig {
1133    /// Creates a new config with only http set
1134    pub fn http(config: ServerConfigBuilder) -> Self {
1135        Self::default().with_http(config)
1136    }
1137
1138    /// Creates a new config with only ws set
1139    pub fn ws(config: ServerConfigBuilder) -> Self {
1140        Self::default().with_ws(config)
1141    }
1142
1143    /// Creates a new config with only ipc set
1144    pub fn ipc(config: IpcServerBuilder<Identity, Identity>) -> Self {
1145        Self::default().with_ipc(config)
1146    }
1147
1148    /// Configures the http server
1149    ///
1150    /// Note: this always configures an [`EthSubscriptionIdProvider`] [`IdProvider`] for
1151    /// convenience. To set a custom [`IdProvider`], please use [`Self::with_id_provider`].
1152    pub fn with_http(mut self, config: ServerConfigBuilder) -> Self {
1153        self.http_server_config =
1154            Some(config.set_id_provider(EthSubscriptionIdProvider::default()));
1155        self
1156    }
1157
1158    /// Configures the ws server
1159    ///
1160    /// Note: this always configures an [`EthSubscriptionIdProvider`] [`IdProvider`] for
1161    /// convenience. To set a custom [`IdProvider`], please use [`Self::with_id_provider`].
1162    pub fn with_ws(mut self, config: ServerConfigBuilder) -> Self {
1163        self.ws_server_config = Some(config.set_id_provider(EthSubscriptionIdProvider::default()));
1164        self
1165    }
1166
1167    /// Configures the ipc server
1168    ///
1169    /// Note: this always configures an [`EthSubscriptionIdProvider`] [`IdProvider`] for
1170    /// convenience. To set a custom [`IdProvider`], please use [`Self::with_id_provider`].
1171    pub fn with_ipc(mut self, config: IpcServerBuilder<Identity, Identity>) -> Self {
1172        self.ipc_server_config = Some(config.set_id_provider(EthSubscriptionIdProvider::default()));
1173        self
1174    }
1175}
1176
1177impl<RpcMiddleware> RpcServerConfig<RpcMiddleware> {
1178    /// Configure rpc middleware
1179    pub fn set_rpc_middleware<T>(self, rpc_middleware: T) -> RpcServerConfig<T> {
1180        RpcServerConfig {
1181            http_server_config: self.http_server_config,
1182            http_cors_domains: self.http_cors_domains,
1183            http_addr: self.http_addr,
1184            http_disable_compression: self.http_disable_compression,
1185            ws_server_config: self.ws_server_config,
1186            ws_cors_domains: self.ws_cors_domains,
1187            ws_addr: self.ws_addr,
1188            ipc_server_config: self.ipc_server_config,
1189            ipc_endpoint: self.ipc_endpoint,
1190            jwt_secret: self.jwt_secret,
1191            rpc_middleware,
1192        }
1193    }
1194
1195    /// Configure the cors domains for http _and_ ws
1196    pub fn with_cors(self, cors_domain: Option<String>) -> Self {
1197        self.with_http_cors(cors_domain.clone()).with_ws_cors(cors_domain)
1198    }
1199
1200    /// Configure the cors domains for WS
1201    pub fn with_ws_cors(mut self, cors_domain: Option<String>) -> Self {
1202        self.ws_cors_domains = cors_domain;
1203        self
1204    }
1205
1206    /// Configure whether HTTP responses should be compressed
1207    pub const fn with_http_disable_compression(mut self, http_disable_compression: bool) -> Self {
1208        self.http_disable_compression = http_disable_compression;
1209        self
1210    }
1211
1212    /// Configure the cors domains for HTTP
1213    pub fn with_http_cors(mut self, cors_domain: Option<String>) -> Self {
1214        self.http_cors_domains = cors_domain;
1215        self
1216    }
1217
1218    /// Configures the [`SocketAddr`] of the http server
1219    ///
1220    /// Default is [`Ipv4Addr::LOCALHOST`] and
1221    /// [`reth_rpc_server_types::constants::DEFAULT_HTTP_RPC_PORT`]
1222    pub const fn with_http_address(mut self, addr: SocketAddr) -> Self {
1223        self.http_addr = Some(addr);
1224        self
1225    }
1226
1227    /// Configures the [`SocketAddr`] of the ws server
1228    ///
1229    /// Default is [`Ipv4Addr::LOCALHOST`] and
1230    /// [`reth_rpc_server_types::constants::DEFAULT_WS_RPC_PORT`]
1231    pub const fn with_ws_address(mut self, addr: SocketAddr) -> Self {
1232        self.ws_addr = Some(addr);
1233        self
1234    }
1235
1236    /// Sets a custom [`IdProvider`] for all configured transports.
1237    ///
1238    /// By default all transports use [`EthSubscriptionIdProvider`]
1239    pub fn with_id_provider<I>(mut self, id_provider: I) -> Self
1240    where
1241        I: IdProvider + Clone + 'static,
1242    {
1243        if let Some(config) = self.http_server_config {
1244            self.http_server_config = Some(config.set_id_provider(id_provider.clone()));
1245        }
1246        if let Some(config) = self.ws_server_config {
1247            self.ws_server_config = Some(config.set_id_provider(id_provider.clone()));
1248        }
1249        if let Some(ipc) = self.ipc_server_config {
1250            self.ipc_server_config = Some(ipc.set_id_provider(id_provider));
1251        }
1252
1253        self
1254    }
1255
1256    /// Configures the endpoint of the ipc server
1257    ///
1258    /// Default is [`reth_rpc_server_types::constants::DEFAULT_IPC_ENDPOINT`]
1259    pub fn with_ipc_endpoint(mut self, path: impl Into<String>) -> Self {
1260        self.ipc_endpoint = Some(path.into());
1261        self
1262    }
1263
1264    /// Configures the JWT secret for authentication.
1265    pub const fn with_jwt_secret(mut self, secret: Option<JwtSecret>) -> Self {
1266        self.jwt_secret = secret;
1267        self
1268    }
1269
1270    /// Configures a custom tokio runtime for the rpc server.
1271    pub fn with_tokio_runtime(mut self, tokio_runtime: Option<tokio::runtime::Handle>) -> Self {
1272        let Some(tokio_runtime) = tokio_runtime else { return self };
1273        if let Some(http_server_config) = self.http_server_config {
1274            self.http_server_config =
1275                Some(http_server_config.custom_tokio_runtime(tokio_runtime.clone()));
1276        }
1277        if let Some(ws_server_config) = self.ws_server_config {
1278            self.ws_server_config =
1279                Some(ws_server_config.custom_tokio_runtime(tokio_runtime.clone()));
1280        }
1281        if let Some(ipc_server_config) = self.ipc_server_config {
1282            self.ipc_server_config = Some(ipc_server_config.custom_tokio_runtime(tokio_runtime));
1283        }
1284        self
1285    }
1286
1287    /// Returns true if any server is configured.
1288    ///
1289    /// If no server is configured, no server will be launched on [`RpcServerConfig::start`].
1290    pub const fn has_server(&self) -> bool {
1291        self.http_server_config.is_some() ||
1292            self.ws_server_config.is_some() ||
1293            self.ipc_server_config.is_some()
1294    }
1295
1296    /// Returns the [`SocketAddr`] of the http server
1297    pub const fn http_address(&self) -> Option<SocketAddr> {
1298        self.http_addr
1299    }
1300
1301    /// Returns the [`SocketAddr`] of the ws server
1302    pub const fn ws_address(&self) -> Option<SocketAddr> {
1303        self.ws_addr
1304    }
1305
1306    /// Returns the endpoint of the ipc server
1307    pub fn ipc_endpoint(&self) -> Option<String> {
1308        self.ipc_endpoint.clone()
1309    }
1310
1311    /// Creates the [`CorsLayer`] if any
1312    fn maybe_cors_layer(cors: Option<String>) -> Result<Option<CorsLayer>, CorsDomainError> {
1313        cors.as_deref().map(cors::create_cors_layer).transpose()
1314    }
1315
1316    /// Creates the [`AuthLayer`] if any
1317    fn maybe_jwt_layer(jwt_secret: Option<JwtSecret>) -> Option<AuthLayer<JwtAuthValidator>> {
1318        jwt_secret.map(|secret| AuthLayer::new(JwtAuthValidator::new(secret)))
1319    }
1320
1321    /// Returns a [`CompressionLayer`] that adds compression support (gzip, deflate, brotli, zstd)
1322    /// based on the client's `Accept-Encoding` header
1323    fn maybe_compression_layer(disable_compression: bool) -> Option<CompressionLayer> {
1324        if disable_compression {
1325            None
1326        } else {
1327            Some(CompressionLayer::new())
1328        }
1329    }
1330
1331    /// Builds and starts the configured server(s): http, ws, ipc.
1332    ///
1333    /// If both http and ws are on the same port, they are combined into one server.
1334    ///
1335    /// Returns the [`RpcServerHandle`] with the handle to the started servers.
1336    pub async fn start(self, modules: &TransportRpcModules) -> Result<RpcServerHandle, RpcError>
1337    where
1338        RpcMiddleware: RethRpcMiddleware,
1339    {
1340        let mut http_handle = None;
1341        let mut ws_handle = None;
1342        let mut ipc_handle = None;
1343
1344        let http_socket_addr = self.http_addr.unwrap_or(SocketAddr::V4(SocketAddrV4::new(
1345            Ipv4Addr::LOCALHOST,
1346            constants::DEFAULT_HTTP_RPC_PORT,
1347        )));
1348
1349        let ws_socket_addr = self.ws_addr.unwrap_or(SocketAddr::V4(SocketAddrV4::new(
1350            Ipv4Addr::LOCALHOST,
1351            constants::DEFAULT_WS_RPC_PORT,
1352        )));
1353
1354        let metrics = modules.ipc.as_ref().map(RpcRequestMetrics::ipc).unwrap_or_default();
1355        let ipc_path =
1356            self.ipc_endpoint.clone().unwrap_or_else(|| constants::DEFAULT_IPC_ENDPOINT.into());
1357
1358        if let Some(builder) = self.ipc_server_config {
1359            let ipc = builder
1360                .set_rpc_middleware(IpcRpcServiceBuilder::new().layer(metrics))
1361                .build(ipc_path);
1362            ipc_handle = Some(ipc.start(modules.ipc.clone().expect("ipc server error")).await?);
1363        }
1364
1365        // If both are configured on the same port, we combine them into one server.
1366        if self.http_addr == self.ws_addr &&
1367            self.http_server_config.is_some() &&
1368            self.ws_server_config.is_some()
1369        {
1370            let cors = match (self.ws_cors_domains.as_ref(), self.http_cors_domains.as_ref()) {
1371                (Some(ws_cors), Some(http_cors)) => {
1372                    if ws_cors.trim() != http_cors.trim() {
1373                        return Err(WsHttpSamePortError::ConflictingCorsDomains {
1374                            http_cors_domains: Some(http_cors.clone()),
1375                            ws_cors_domains: Some(ws_cors.clone()),
1376                        }
1377                        .into());
1378                    }
1379                    Some(ws_cors)
1380                }
1381                (a, b) => a.or(b),
1382            }
1383            .cloned();
1384
1385            // we merge this into one server using the http setup
1386            modules.config.ensure_ws_http_identical()?;
1387
1388            if let Some(config) = self.http_server_config {
1389                let server = ServerBuilder::new()
1390                    .set_http_middleware(
1391                        tower::ServiceBuilder::new()
1392                            .option_layer(Self::maybe_cors_layer(cors)?)
1393                            .option_layer(Self::maybe_jwt_layer(self.jwt_secret))
1394                            .option_layer(Self::maybe_compression_layer(
1395                                self.http_disable_compression,
1396                            )),
1397                    )
1398                    .set_rpc_middleware(
1399                        RpcServiceBuilder::default()
1400                            .layer(
1401                                modules
1402                                    .http
1403                                    .as_ref()
1404                                    .or(modules.ws.as_ref())
1405                                    .map(RpcRequestMetrics::same_port)
1406                                    .unwrap_or_default(),
1407                            )
1408                            .layer(self.rpc_middleware.clone()),
1409                    )
1410                    .set_config(config.build())
1411                    .build(http_socket_addr)
1412                    .await
1413                    .map_err(|err| {
1414                        RpcError::server_error(err, ServerKind::WsHttp(http_socket_addr))
1415                    })?;
1416                let addr = server.local_addr().map_err(|err| {
1417                    RpcError::server_error(err, ServerKind::WsHttp(http_socket_addr))
1418                })?;
1419                if let Some(module) = modules.http.as_ref().or(modules.ws.as_ref()) {
1420                    let handle = server.start(module.clone());
1421                    http_handle = Some(handle.clone());
1422                    ws_handle = Some(handle);
1423                }
1424                return Ok(RpcServerHandle {
1425                    http_local_addr: Some(addr),
1426                    ws_local_addr: Some(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        let mut ws_local_addr = None;
1437        let mut ws_server = None;
1438        let mut http_local_addr = None;
1439        let mut http_server = None;
1440
1441        if let Some(config) = self.ws_server_config {
1442            let server = ServerBuilder::new()
1443                .set_config(config.ws_only().build())
1444                .set_http_middleware(
1445                    tower::ServiceBuilder::new()
1446                        .option_layer(Self::maybe_cors_layer(self.ws_cors_domains.clone())?)
1447                        .option_layer(Self::maybe_jwt_layer(self.jwt_secret)),
1448                )
1449                .set_rpc_middleware(
1450                    RpcServiceBuilder::default()
1451                        .layer(modules.ws.as_ref().map(RpcRequestMetrics::ws).unwrap_or_default())
1452                        .layer(self.rpc_middleware.clone()),
1453                )
1454                .build(ws_socket_addr)
1455                .await
1456                .map_err(|err| RpcError::server_error(err, ServerKind::WS(ws_socket_addr)))?;
1457
1458            let addr = server
1459                .local_addr()
1460                .map_err(|err| RpcError::server_error(err, ServerKind::WS(ws_socket_addr)))?;
1461
1462            ws_local_addr = Some(addr);
1463            ws_server = Some(server);
1464        }
1465
1466        if let Some(config) = self.http_server_config {
1467            let server = ServerBuilder::new()
1468                .set_config(config.http_only().build())
1469                .set_http_middleware(
1470                    tower::ServiceBuilder::new()
1471                        .option_layer(Self::maybe_cors_layer(self.http_cors_domains.clone())?)
1472                        .option_layer(Self::maybe_jwt_layer(self.jwt_secret))
1473                        .option_layer(Self::maybe_compression_layer(self.http_disable_compression)),
1474                )
1475                .set_rpc_middleware(
1476                    RpcServiceBuilder::default()
1477                        .layer(
1478                            modules.http.as_ref().map(RpcRequestMetrics::http).unwrap_or_default(),
1479                        )
1480                        .layer(self.rpc_middleware.clone()),
1481                )
1482                .build(http_socket_addr)
1483                .await
1484                .map_err(|err| RpcError::server_error(err, ServerKind::Http(http_socket_addr)))?;
1485            let local_addr = server
1486                .local_addr()
1487                .map_err(|err| RpcError::server_error(err, ServerKind::Http(http_socket_addr)))?;
1488            http_local_addr = Some(local_addr);
1489            http_server = Some(server);
1490        }
1491
1492        http_handle = http_server
1493            .map(|http_server| http_server.start(modules.http.clone().expect("http server error")));
1494        ws_handle = ws_server
1495            .map(|ws_server| ws_server.start(modules.ws.clone().expect("ws server error")));
1496        Ok(RpcServerHandle {
1497            http_local_addr,
1498            ws_local_addr,
1499            http: http_handle,
1500            ws: ws_handle,
1501            ipc_endpoint: self.ipc_endpoint.clone(),
1502            ipc: ipc_handle,
1503            jwt_secret: self.jwt_secret,
1504        })
1505    }
1506}
1507
1508/// Holds modules to be installed per transport type
1509///
1510/// # Example
1511///
1512/// Configure a http transport only
1513///
1514/// ```
1515/// use reth_rpc_builder::{RethRpcModule, TransportRpcModuleConfig};
1516/// let config =
1517///     TransportRpcModuleConfig::default().with_http([RethRpcModule::Eth, RethRpcModule::Admin]);
1518/// ```
1519#[derive(Debug, Clone, Default, Eq, PartialEq)]
1520pub struct TransportRpcModuleConfig {
1521    /// http module configuration
1522    http: Option<RpcModuleSelection>,
1523    /// ws module configuration
1524    ws: Option<RpcModuleSelection>,
1525    /// ipc module configuration
1526    ipc: Option<RpcModuleSelection>,
1527    /// Config for the modules
1528    config: Option<RpcModuleConfig>,
1529}
1530
1531// === impl TransportRpcModuleConfig ===
1532
1533impl TransportRpcModuleConfig {
1534    /// Creates a new config with only http set
1535    pub fn set_http(http: impl Into<RpcModuleSelection>) -> Self {
1536        Self::default().with_http(http)
1537    }
1538
1539    /// Creates a new config with only ws set
1540    pub fn set_ws(ws: impl Into<RpcModuleSelection>) -> Self {
1541        Self::default().with_ws(ws)
1542    }
1543
1544    /// Creates a new config with only ipc set
1545    pub fn set_ipc(ipc: impl Into<RpcModuleSelection>) -> Self {
1546        Self::default().with_ipc(ipc)
1547    }
1548
1549    /// Sets the [`RpcModuleSelection`] for the http transport.
1550    pub fn with_http(mut self, http: impl Into<RpcModuleSelection>) -> Self {
1551        self.http = Some(http.into());
1552        self
1553    }
1554
1555    /// Sets the [`RpcModuleSelection`] for the ws transport.
1556    pub fn with_ws(mut self, ws: impl Into<RpcModuleSelection>) -> Self {
1557        self.ws = Some(ws.into());
1558        self
1559    }
1560
1561    /// Sets the [`RpcModuleSelection`] for the ipc transport.
1562    pub fn with_ipc(mut self, ipc: impl Into<RpcModuleSelection>) -> Self {
1563        self.ipc = Some(ipc.into());
1564        self
1565    }
1566
1567    /// Sets a custom [`RpcModuleConfig`] for the configured modules.
1568    pub fn with_config(mut self, config: RpcModuleConfig) -> Self {
1569        self.config = Some(config);
1570        self
1571    }
1572
1573    /// Get a mutable reference to the http module configuration.
1574    pub const fn http_mut(&mut self) -> &mut Option<RpcModuleSelection> {
1575        &mut self.http
1576    }
1577
1578    /// Get a mutable reference to the ws module configuration.
1579    pub const fn ws_mut(&mut self) -> &mut Option<RpcModuleSelection> {
1580        &mut self.ws
1581    }
1582
1583    /// Get a mutable reference to the ipc module configuration.
1584    pub const fn ipc_mut(&mut self) -> &mut Option<RpcModuleSelection> {
1585        &mut self.ipc
1586    }
1587
1588    /// Get a mutable reference to the rpc module configuration.
1589    pub const fn config_mut(&mut self) -> &mut Option<RpcModuleConfig> {
1590        &mut self.config
1591    }
1592
1593    /// Returns true if no transports are configured
1594    pub const fn is_empty(&self) -> bool {
1595        self.http.is_none() && self.ws.is_none() && self.ipc.is_none()
1596    }
1597
1598    /// Returns the [`RpcModuleSelection`] for the http transport
1599    pub const fn http(&self) -> Option<&RpcModuleSelection> {
1600        self.http.as_ref()
1601    }
1602
1603    /// Returns the [`RpcModuleSelection`] for the ws transport
1604    pub const fn ws(&self) -> Option<&RpcModuleSelection> {
1605        self.ws.as_ref()
1606    }
1607
1608    /// Returns the [`RpcModuleSelection`] for the ipc transport
1609    pub const fn ipc(&self) -> Option<&RpcModuleSelection> {
1610        self.ipc.as_ref()
1611    }
1612
1613    /// Returns the [`RpcModuleConfig`] for the configured modules
1614    pub const fn config(&self) -> Option<&RpcModuleConfig> {
1615        self.config.as_ref()
1616    }
1617
1618    /// Returns true if the given module is configured for any transport.
1619    pub fn contains_any(&self, module: &RethRpcModule) -> bool {
1620        self.contains_http(module) || self.contains_ws(module) || self.contains_ipc(module)
1621    }
1622
1623    /// Returns true if the given module is configured for the http transport.
1624    pub fn contains_http(&self, module: &RethRpcModule) -> bool {
1625        self.http.as_ref().is_some_and(|http| http.contains(module))
1626    }
1627
1628    /// Returns true if the given module is configured for the ws transport.
1629    pub fn contains_ws(&self, module: &RethRpcModule) -> bool {
1630        self.ws.as_ref().is_some_and(|ws| ws.contains(module))
1631    }
1632
1633    /// Returns true if the given module is configured for the ipc transport.
1634    pub fn contains_ipc(&self, module: &RethRpcModule) -> bool {
1635        self.ipc.as_ref().is_some_and(|ipc| ipc.contains(module))
1636    }
1637
1638    /// Ensures that both http and ws are configured and that they are configured to use the same
1639    /// port.
1640    fn ensure_ws_http_identical(&self) -> Result<(), WsHttpSamePortError> {
1641        if RpcModuleSelection::are_identical(self.http.as_ref(), self.ws.as_ref()) {
1642            Ok(())
1643        } else {
1644            let http_modules =
1645                self.http.as_ref().map(RpcModuleSelection::to_selection).unwrap_or_default();
1646            let ws_modules =
1647                self.ws.as_ref().map(RpcModuleSelection::to_selection).unwrap_or_default();
1648
1649            let http_not_ws = http_modules.difference(&ws_modules).cloned().collect();
1650            let ws_not_http = ws_modules.difference(&http_modules).cloned().collect();
1651            let overlap = http_modules.intersection(&ws_modules).cloned().collect();
1652
1653            Err(WsHttpSamePortError::ConflictingModules(Box::new(ConflictingModules {
1654                overlap,
1655                http_not_ws,
1656                ws_not_http,
1657            })))
1658        }
1659    }
1660}
1661
1662/// Holds installed modules per transport type.
1663#[derive(Debug, Clone, Default)]
1664pub struct TransportRpcModules<Context = ()> {
1665    /// The original config
1666    config: TransportRpcModuleConfig,
1667    /// rpcs module for http
1668    http: Option<RpcModule<Context>>,
1669    /// rpcs module for ws
1670    ws: Option<RpcModule<Context>>,
1671    /// rpcs module for ipc
1672    ipc: Option<RpcModule<Context>>,
1673}
1674
1675// === impl TransportRpcModules ===
1676
1677impl TransportRpcModules {
1678    /// Sets a custom [`TransportRpcModuleConfig`] for the configured modules.
1679    /// This will overwrite current configuration, if any.
1680    pub fn with_config(mut self, config: TransportRpcModuleConfig) -> Self {
1681        self.config = config;
1682        self
1683    }
1684
1685    /// Sets the [`RpcModule`] for the http transport.
1686    /// This will overwrite current module, if any.
1687    pub fn with_http(mut self, http: RpcModule<()>) -> Self {
1688        self.http = Some(http);
1689        self
1690    }
1691
1692    /// Sets the [`RpcModule`] for the ws transport.
1693    /// This will overwrite current module, if any.
1694    pub fn with_ws(mut self, ws: RpcModule<()>) -> Self {
1695        self.ws = Some(ws);
1696        self
1697    }
1698
1699    /// Sets the [`RpcModule`] for the ipc transport.
1700    /// This will overwrite current module, if any.
1701    pub fn with_ipc(mut self, ipc: RpcModule<()>) -> Self {
1702        self.ipc = Some(ipc);
1703        self
1704    }
1705
1706    /// Returns the [`TransportRpcModuleConfig`] used to configure this instance.
1707    pub const fn module_config(&self) -> &TransportRpcModuleConfig {
1708        &self.config
1709    }
1710
1711    /// Merge the given [`Methods`] in all configured transport modules if the given
1712    /// [`RethRpcModule`] is configured for the transport.
1713    ///
1714    /// Fails if any of the methods in other is present already.
1715    pub fn merge_if_module_configured(
1716        &mut self,
1717        module: RethRpcModule,
1718        other: impl Into<Methods>,
1719    ) -> Result<(), RegisterMethodError> {
1720        let other = other.into();
1721        if self.module_config().contains_http(&module) {
1722            self.merge_http(other.clone())?;
1723        }
1724        if self.module_config().contains_ws(&module) {
1725            self.merge_ws(other.clone())?;
1726        }
1727        if self.module_config().contains_ipc(&module) {
1728            self.merge_ipc(other)?;
1729        }
1730
1731        Ok(())
1732    }
1733
1734    /// Merge the given [`Methods`] in all configured transport modules if the given
1735    /// [`RethRpcModule`] is configured for the transport, using a closure to lazily
1736    /// create the methods only when needed.
1737    ///
1738    /// The closure is only called if at least one transport has the module configured.
1739    /// Fails if any of the methods in the closure result is present already.
1740    pub fn merge_if_module_configured_with<F>(
1741        &mut self,
1742        module: RethRpcModule,
1743        f: F,
1744    ) -> Result<(), RegisterMethodError>
1745    where
1746        F: FnOnce() -> Methods,
1747    {
1748        // Early return if module not configured for any transport
1749        if !self.module_config().contains_any(&module) {
1750            return Ok(());
1751        }
1752        self.merge_if_module_configured(module, f())
1753    }
1754
1755    /// Merge the given [Methods] in the configured http methods.
1756    ///
1757    /// Fails if any of the methods in other is present already.
1758    ///
1759    /// Returns [Ok(false)] if no http transport is configured.
1760    pub fn merge_http(&mut self, other: impl Into<Methods>) -> Result<bool, RegisterMethodError> {
1761        if let Some(ref mut http) = self.http {
1762            return http.merge(other.into()).map(|_| true)
1763        }
1764        Ok(false)
1765    }
1766
1767    /// Merge the given [Methods] in the configured ws methods.
1768    ///
1769    /// Fails if any of the methods in other is present already.
1770    ///
1771    /// Returns [Ok(false)] if no ws transport is configured.
1772    pub fn merge_ws(&mut self, other: impl Into<Methods>) -> Result<bool, RegisterMethodError> {
1773        if let Some(ref mut ws) = self.ws {
1774            return ws.merge(other.into()).map(|_| true)
1775        }
1776        Ok(false)
1777    }
1778
1779    /// Merge the given [Methods] in the configured ipc methods.
1780    ///
1781    /// Fails if any of the methods in other is present already.
1782    ///
1783    /// Returns [Ok(false)] if no ipc transport is configured.
1784    pub fn merge_ipc(&mut self, other: impl Into<Methods>) -> Result<bool, RegisterMethodError> {
1785        if let Some(ref mut ipc) = self.ipc {
1786            return ipc.merge(other.into()).map(|_| true)
1787        }
1788        Ok(false)
1789    }
1790
1791    /// Merge the given [`Methods`] in all configured methods.
1792    ///
1793    /// Fails if any of the methods in other is present already.
1794    pub fn merge_configured(
1795        &mut self,
1796        other: impl Into<Methods>,
1797    ) -> Result<(), RegisterMethodError> {
1798        let other = other.into();
1799        self.merge_http(other.clone())?;
1800        self.merge_ws(other.clone())?;
1801        self.merge_ipc(other)?;
1802        Ok(())
1803    }
1804
1805    /// Returns all unique endpoints installed for the given module.
1806    ///
1807    /// Note: In case of duplicate method names this only record the first occurrence.
1808    pub fn methods_by_module(&self, module: RethRpcModule) -> Methods {
1809        self.methods_by(|name| name.starts_with(module.as_str()))
1810    }
1811
1812    /// Returns all unique endpoints installed in any of the configured modules.
1813    ///
1814    /// Note: In case of duplicate method names this only record the first occurrence.
1815    pub fn methods_by<F>(&self, mut filter: F) -> Methods
1816    where
1817        F: FnMut(&str) -> bool,
1818    {
1819        let mut methods = Methods::new();
1820
1821        // filter that matches the given filter and also removes duplicates we already have
1822        let mut f =
1823            |name: &str, mm: &Methods| filter(name) && !mm.method_names().any(|m| m == name);
1824
1825        if let Some(m) = self.http_methods(|name| f(name, &methods)) {
1826            let _ = methods.merge(m);
1827        }
1828        if let Some(m) = self.ws_methods(|name| f(name, &methods)) {
1829            let _ = methods.merge(m);
1830        }
1831        if let Some(m) = self.ipc_methods(|name| f(name, &methods)) {
1832            let _ = methods.merge(m);
1833        }
1834        methods
1835    }
1836
1837    /// Returns all [`Methods`] installed for the http server based in the given closure.
1838    ///
1839    /// Returns `None` if no http support is configured.
1840    pub fn http_methods<F>(&self, filter: F) -> Option<Methods>
1841    where
1842        F: FnMut(&str) -> bool,
1843    {
1844        self.http.as_ref().map(|module| methods_by(module, filter))
1845    }
1846
1847    /// Returns all [`Methods`] installed for the ws server based in the given closure.
1848    ///
1849    /// Returns `None` if no ws support is configured.
1850    pub fn ws_methods<F>(&self, filter: F) -> Option<Methods>
1851    where
1852        F: FnMut(&str) -> bool,
1853    {
1854        self.ws.as_ref().map(|module| methods_by(module, filter))
1855    }
1856
1857    /// Returns all [`Methods`] installed for the ipc server based in the given closure.
1858    ///
1859    /// Returns `None` if no ipc support is configured.
1860    pub fn ipc_methods<F>(&self, filter: F) -> Option<Methods>
1861    where
1862        F: FnMut(&str) -> bool,
1863    {
1864        self.ipc.as_ref().map(|module| methods_by(module, filter))
1865    }
1866
1867    /// Removes the method with the given name from the configured http methods.
1868    ///
1869    /// Returns `true` if the method was found and removed, `false` otherwise.
1870    ///
1871    /// Be aware that a subscription consist of two methods, `subscribe` and `unsubscribe` and
1872    /// it's the caller responsibility to remove both `subscribe` and `unsubscribe` methods for
1873    /// subscriptions.
1874    pub fn remove_http_method(&mut self, method_name: &'static str) -> bool {
1875        if let Some(http_module) = &mut self.http {
1876            http_module.remove_method(method_name).is_some()
1877        } else {
1878            false
1879        }
1880    }
1881
1882    /// Removes the given methods from the configured http methods.
1883    pub fn remove_http_methods(&mut self, methods: impl IntoIterator<Item = &'static str>) {
1884        for name in methods {
1885            self.remove_http_method(name);
1886        }
1887    }
1888
1889    /// Removes the method with the given name from the configured ws methods.
1890    ///
1891    /// Returns `true` if the method was found and removed, `false` otherwise.
1892    ///
1893    /// Be aware that a subscription consist of two methods, `subscribe` and `unsubscribe` and
1894    /// it's the caller responsibility to remove both `subscribe` and `unsubscribe` methods for
1895    /// subscriptions.
1896    pub fn remove_ws_method(&mut self, method_name: &'static str) -> bool {
1897        if let Some(ws_module) = &mut self.ws {
1898            ws_module.remove_method(method_name).is_some()
1899        } else {
1900            false
1901        }
1902    }
1903
1904    /// Removes the given methods from the configured ws methods.
1905    pub fn remove_ws_methods(&mut self, methods: impl IntoIterator<Item = &'static str>) {
1906        for name in methods {
1907            self.remove_ws_method(name);
1908        }
1909    }
1910
1911    /// Removes the method with the given name from the configured ipc methods.
1912    ///
1913    /// Returns `true` if the method was found and removed, `false` otherwise.
1914    ///
1915    /// Be aware that a subscription consist of two methods, `subscribe` and `unsubscribe` and
1916    /// it's the caller responsibility to remove both `subscribe` and `unsubscribe` methods for
1917    /// subscriptions.
1918    pub fn remove_ipc_method(&mut self, method_name: &'static str) -> bool {
1919        if let Some(ipc_module) = &mut self.ipc {
1920            ipc_module.remove_method(method_name).is_some()
1921        } else {
1922            false
1923        }
1924    }
1925
1926    /// Removes the given methods from the configured ipc methods.
1927    pub fn remove_ipc_methods(&mut self, methods: impl IntoIterator<Item = &'static str>) {
1928        for name in methods {
1929            self.remove_ipc_method(name);
1930        }
1931    }
1932
1933    /// Removes the method with the given name from all configured transports.
1934    ///
1935    /// Returns `true` if the method was found and removed, `false` otherwise.
1936    pub fn remove_method_from_configured(&mut self, method_name: &'static str) -> bool {
1937        let http_removed = self.remove_http_method(method_name);
1938        let ws_removed = self.remove_ws_method(method_name);
1939        let ipc_removed = self.remove_ipc_method(method_name);
1940
1941        http_removed || ws_removed || ipc_removed
1942    }
1943
1944    /// Renames a method in all configured transports by:
1945    /// 1. Removing the old method name.
1946    /// 2. Adding the new method.
1947    pub fn rename(
1948        &mut self,
1949        old_name: &'static str,
1950        new_method: impl Into<Methods>,
1951    ) -> Result<(), RegisterMethodError> {
1952        // Remove the old method from all configured transports
1953        self.remove_method_from_configured(old_name);
1954
1955        // Merge the new method into the configured transports
1956        self.merge_configured(new_method)
1957    }
1958
1959    /// Replace the given [`Methods`] in the configured http methods.
1960    ///
1961    /// Fails if any of the methods in other is present already or if the method being removed is
1962    /// not present
1963    ///
1964    /// Returns [Ok(false)] if no http transport is configured.
1965    pub fn replace_http(&mut self, other: impl Into<Methods>) -> Result<bool, RegisterMethodError> {
1966        let other = other.into();
1967        self.remove_http_methods(other.method_names());
1968        self.merge_http(other)
1969    }
1970
1971    /// Replace the given [Methods] in the configured ipc methods.
1972    ///
1973    /// Fails if any of the methods in other is present already or if the method being removed is
1974    /// not present
1975    ///
1976    /// Returns [Ok(false)] if no ipc transport is configured.
1977    pub fn replace_ipc(&mut self, other: impl Into<Methods>) -> Result<bool, RegisterMethodError> {
1978        let other = other.into();
1979        self.remove_ipc_methods(other.method_names());
1980        self.merge_ipc(other)
1981    }
1982
1983    /// Replace the given [Methods] in the configured ws methods.
1984    ///
1985    /// Fails if any of the methods in other is present already or if the method being removed is
1986    /// not present
1987    ///
1988    /// Returns [Ok(false)] if no ws transport is configured.
1989    pub fn replace_ws(&mut self, other: impl Into<Methods>) -> Result<bool, RegisterMethodError> {
1990        let other = other.into();
1991        self.remove_ws_methods(other.method_names());
1992        self.merge_ws(other)
1993    }
1994
1995    /// Replaces the method with the given name from all configured transports.
1996    ///
1997    /// Returns `true` if the method was found and replaced, `false` otherwise
1998    pub fn replace_configured(
1999        &mut self,
2000        other: impl Into<Methods>,
2001    ) -> Result<bool, RegisterMethodError> {
2002        let other = other.into();
2003        self.replace_http(other.clone())?;
2004        self.replace_ws(other.clone())?;
2005        self.replace_ipc(other)?;
2006        Ok(true)
2007    }
2008
2009    /// Adds or replaces given [`Methods`] in http module.
2010    ///
2011    /// Returns `true` if the methods were replaced or added, `false` otherwise.
2012    pub fn add_or_replace_http(
2013        &mut self,
2014        other: impl Into<Methods>,
2015    ) -> Result<bool, RegisterMethodError> {
2016        let other = other.into();
2017        self.remove_http_methods(other.method_names());
2018        self.merge_http(other)
2019    }
2020
2021    /// Adds or replaces given [`Methods`] in ws module.
2022    ///
2023    /// Returns `true` if the methods were replaced or added, `false` otherwise.
2024    pub fn add_or_replace_ws(
2025        &mut self,
2026        other: impl Into<Methods>,
2027    ) -> Result<bool, RegisterMethodError> {
2028        let other = other.into();
2029        self.remove_ws_methods(other.method_names());
2030        self.merge_ws(other)
2031    }
2032
2033    /// Adds or replaces given [`Methods`] in ipc module.
2034    ///
2035    /// Returns `true` if the methods were replaced or added, `false` otherwise.
2036    pub fn add_or_replace_ipc(
2037        &mut self,
2038        other: impl Into<Methods>,
2039    ) -> Result<bool, RegisterMethodError> {
2040        let other = other.into();
2041        self.remove_ipc_methods(other.method_names());
2042        self.merge_ipc(other)
2043    }
2044
2045    /// Adds or replaces given [`Methods`] in all configured network modules.
2046    pub fn add_or_replace_configured(
2047        &mut self,
2048        other: impl Into<Methods>,
2049    ) -> Result<(), RegisterMethodError> {
2050        let other = other.into();
2051        self.add_or_replace_http(other.clone())?;
2052        self.add_or_replace_ws(other.clone())?;
2053        self.add_or_replace_ipc(other)?;
2054        Ok(())
2055    }
2056    /// Adds or replaces the given [`Methods`] in the transport modules where the specified
2057    /// [`RethRpcModule`] is configured.
2058    pub fn add_or_replace_if_module_configured(
2059        &mut self,
2060        module: RethRpcModule,
2061        other: impl Into<Methods>,
2062    ) -> Result<(), RegisterMethodError> {
2063        let other = other.into();
2064        if self.module_config().contains_http(&module) {
2065            self.add_or_replace_http(other.clone())?;
2066        }
2067        if self.module_config().contains_ws(&module) {
2068            self.add_or_replace_ws(other.clone())?;
2069        }
2070        if self.module_config().contains_ipc(&module) {
2071            self.add_or_replace_ipc(other)?;
2072        }
2073        Ok(())
2074    }
2075}
2076
2077/// Returns the methods installed in the given module that match the given filter.
2078fn methods_by<T, F>(module: &RpcModule<T>, mut filter: F) -> Methods
2079where
2080    F: FnMut(&str) -> bool,
2081{
2082    let mut methods = Methods::new();
2083    let method_names = module.method_names().filter(|name| filter(name));
2084
2085    for name in method_names {
2086        if let Some(matched_method) = module.method(name).cloned() {
2087            let _ = methods.verify_and_insert(name, matched_method);
2088        }
2089    }
2090
2091    methods
2092}
2093
2094/// A handle to the spawned servers.
2095///
2096/// When this type is dropped or [`RpcServerHandle::stop`] has been called the server will be
2097/// stopped.
2098#[derive(Clone, Debug)]
2099#[must_use = "Server stops if dropped"]
2100pub struct RpcServerHandle {
2101    /// The address of the http/ws server
2102    http_local_addr: Option<SocketAddr>,
2103    ws_local_addr: Option<SocketAddr>,
2104    http: Option<ServerHandle>,
2105    ws: Option<ServerHandle>,
2106    ipc_endpoint: Option<String>,
2107    ipc: Option<jsonrpsee::server::ServerHandle>,
2108    jwt_secret: Option<JwtSecret>,
2109}
2110
2111// === impl RpcServerHandle ===
2112
2113impl RpcServerHandle {
2114    /// Configures the JWT secret for authentication.
2115    fn bearer_token(&self) -> Option<String> {
2116        self.jwt_secret.as_ref().map(|secret| {
2117            format!(
2118                "Bearer {}",
2119                secret
2120                    .encode(&Claims {
2121                        iat: (SystemTime::now().duration_since(UNIX_EPOCH).unwrap() +
2122                            Duration::from_secs(60))
2123                        .as_secs(),
2124                        exp: None,
2125                    })
2126                    .unwrap()
2127            )
2128        })
2129    }
2130    /// Returns the [`SocketAddr`] of the http server if started.
2131    pub const fn http_local_addr(&self) -> Option<SocketAddr> {
2132        self.http_local_addr
2133    }
2134
2135    /// Returns the [`SocketAddr`] of the ws server if started.
2136    pub const fn ws_local_addr(&self) -> Option<SocketAddr> {
2137        self.ws_local_addr
2138    }
2139
2140    /// Tell the server to stop without waiting for the server to stop.
2141    pub fn stop(self) -> Result<(), AlreadyStoppedError> {
2142        if let Some(handle) = self.http {
2143            handle.stop()?
2144        }
2145
2146        if let Some(handle) = self.ws {
2147            handle.stop()?
2148        }
2149
2150        if let Some(handle) = self.ipc {
2151            handle.stop()?
2152        }
2153
2154        Ok(())
2155    }
2156
2157    /// Returns the endpoint of the launched IPC server, if any
2158    pub fn ipc_endpoint(&self) -> Option<String> {
2159        self.ipc_endpoint.clone()
2160    }
2161
2162    /// Returns the url to the http server
2163    pub fn http_url(&self) -> Option<String> {
2164        self.http_local_addr.map(|addr| format!("http://{addr}"))
2165    }
2166
2167    /// Returns the url to the ws server
2168    pub fn ws_url(&self) -> Option<String> {
2169        self.ws_local_addr.map(|addr| format!("ws://{addr}"))
2170    }
2171
2172    /// Returns a http client connected to the server.
2173    pub fn http_client(&self) -> Option<jsonrpsee::http_client::HttpClient> {
2174        let url = self.http_url()?;
2175
2176        let client = if let Some(token) = self.bearer_token() {
2177            jsonrpsee::http_client::HttpClientBuilder::default()
2178                .set_headers(HeaderMap::from_iter([(AUTHORIZATION, token.parse().unwrap())]))
2179                .build(url)
2180        } else {
2181            jsonrpsee::http_client::HttpClientBuilder::default().build(url)
2182        };
2183
2184        client.expect("failed to create http client").into()
2185    }
2186
2187    /// Returns a ws client connected to the server.
2188    pub async fn ws_client(&self) -> Option<jsonrpsee::ws_client::WsClient> {
2189        let url = self.ws_url()?;
2190        let mut builder = jsonrpsee::ws_client::WsClientBuilder::default();
2191
2192        if let Some(token) = self.bearer_token() {
2193            let headers = HeaderMap::from_iter([(AUTHORIZATION, token.parse().unwrap())]);
2194            builder = builder.set_headers(headers);
2195        }
2196
2197        let client = builder.build(url).await.expect("failed to create ws client");
2198        Some(client)
2199    }
2200
2201    /// Returns a new [`alloy_network::Ethereum`] http provider with its recommended fillers.
2202    pub fn eth_http_provider(
2203        &self,
2204    ) -> Option<impl Provider<alloy_network::Ethereum> + Clone + Unpin + 'static> {
2205        self.new_http_provider_for()
2206    }
2207
2208    /// Returns a new [`alloy_network::Ethereum`] http provider with its recommended fillers and
2209    /// installed wallet.
2210    pub fn eth_http_provider_with_wallet<W>(
2211        &self,
2212        wallet: W,
2213    ) -> Option<impl Provider<alloy_network::Ethereum> + Clone + Unpin + 'static>
2214    where
2215        W: IntoWallet<alloy_network::Ethereum, NetworkWallet: Clone + Unpin + 'static>,
2216    {
2217        let rpc_url = self.http_url()?;
2218        let provider =
2219            ProviderBuilder::new().wallet(wallet).connect_http(rpc_url.parse().expect("valid url"));
2220        Some(provider)
2221    }
2222
2223    /// Returns an http provider from the rpc server handle for the
2224    /// specified [`alloy_network::Network`].
2225    ///
2226    /// This installs the recommended fillers: [`RecommendedFillers`]
2227    pub fn new_http_provider_for<N>(&self) -> Option<impl Provider<N> + Clone + Unpin + 'static>
2228    where
2229        N: RecommendedFillers<RecommendedFillers: Unpin>,
2230    {
2231        let rpc_url = self.http_url()?;
2232        let provider = ProviderBuilder::default()
2233            .with_recommended_fillers()
2234            .connect_http(rpc_url.parse().expect("valid url"));
2235        Some(provider)
2236    }
2237
2238    /// Returns a new [`alloy_network::Ethereum`] websocket provider with its recommended fillers.
2239    pub async fn eth_ws_provider(
2240        &self,
2241    ) -> Option<impl Provider<alloy_network::Ethereum> + Clone + Unpin + 'static> {
2242        self.new_ws_provider_for().await
2243    }
2244
2245    /// Returns a new [`alloy_network::Ethereum`] ws provider with its recommended fillers and
2246    /// installed wallet.
2247    pub async fn eth_ws_provider_with_wallet<W>(
2248        &self,
2249        wallet: W,
2250    ) -> Option<impl Provider<alloy_network::Ethereum> + Clone + Unpin + 'static>
2251    where
2252        W: IntoWallet<alloy_network::Ethereum, NetworkWallet: Clone + Unpin + 'static>,
2253    {
2254        let rpc_url = self.ws_url()?;
2255        let provider = ProviderBuilder::new()
2256            .wallet(wallet)
2257            .connect(&rpc_url)
2258            .await
2259            .expect("failed to create ws client");
2260        Some(provider)
2261    }
2262
2263    /// Returns an ws provider from the rpc server handle for the
2264    /// specified [`alloy_network::Network`].
2265    ///
2266    /// This installs the recommended fillers: [`RecommendedFillers`]
2267    pub async fn new_ws_provider_for<N>(&self) -> Option<impl Provider<N> + Clone + Unpin + 'static>
2268    where
2269        N: RecommendedFillers<RecommendedFillers: Unpin>,
2270    {
2271        let rpc_url = self.ws_url()?;
2272        let provider = ProviderBuilder::default()
2273            .with_recommended_fillers()
2274            .connect(&rpc_url)
2275            .await
2276            .expect("failed to create ws client");
2277        Some(provider)
2278    }
2279
2280    /// Returns a new [`alloy_network::Ethereum`] ipc provider with its recommended fillers.
2281    pub async fn eth_ipc_provider(
2282        &self,
2283    ) -> Option<impl Provider<alloy_network::Ethereum> + Clone + Unpin + 'static> {
2284        self.new_ipc_provider_for().await
2285    }
2286
2287    /// Returns an ipc provider from the rpc server handle for the
2288    /// specified [`alloy_network::Network`].
2289    ///
2290    /// This installs the recommended fillers: [`RecommendedFillers`]
2291    pub async fn new_ipc_provider_for<N>(
2292        &self,
2293    ) -> Option<impl Provider<N> + Clone + Unpin + 'static>
2294    where
2295        N: RecommendedFillers<RecommendedFillers: Unpin>,
2296    {
2297        let rpc_url = self.ipc_endpoint()?;
2298        let provider = ProviderBuilder::default()
2299            .with_recommended_fillers()
2300            .connect(&rpc_url)
2301            .await
2302            .expect("failed to create ipc client");
2303        Some(provider)
2304    }
2305}
2306
2307#[cfg(test)]
2308mod tests {
2309    use super::*;
2310
2311    #[test]
2312    fn parse_eth_call_bundle_selection() {
2313        let selection = "eth,admin,debug".parse::<RpcModuleSelection>().unwrap();
2314        assert_eq!(
2315            selection,
2316            RpcModuleSelection::Selection(
2317                [RethRpcModule::Eth, RethRpcModule::Admin, RethRpcModule::Debug,].into()
2318            )
2319        );
2320    }
2321
2322    #[test]
2323    fn parse_rpc_module_selection() {
2324        let selection = "all".parse::<RpcModuleSelection>().unwrap();
2325        assert_eq!(selection, RpcModuleSelection::All);
2326    }
2327
2328    #[test]
2329    fn parse_rpc_module_selection_none() {
2330        let selection = "none".parse::<RpcModuleSelection>().unwrap();
2331        assert_eq!(selection, RpcModuleSelection::Selection(Default::default()));
2332    }
2333
2334    #[test]
2335    fn parse_rpc_unique_module_selection() {
2336        let selection = "eth,admin,eth,net".parse::<RpcModuleSelection>().unwrap();
2337        assert_eq!(
2338            selection,
2339            RpcModuleSelection::Selection(
2340                [RethRpcModule::Eth, RethRpcModule::Admin, RethRpcModule::Net,].into()
2341            )
2342        );
2343    }
2344
2345    #[test]
2346    fn identical_selection() {
2347        assert!(RpcModuleSelection::are_identical(
2348            Some(&RpcModuleSelection::All),
2349            Some(&RpcModuleSelection::All),
2350        ));
2351        assert!(!RpcModuleSelection::are_identical(
2352            Some(&RpcModuleSelection::All),
2353            Some(&RpcModuleSelection::Standard),
2354        ));
2355        assert!(RpcModuleSelection::are_identical(
2356            Some(&RpcModuleSelection::Selection(RpcModuleSelection::Standard.to_selection())),
2357            Some(&RpcModuleSelection::Standard),
2358        ));
2359        assert!(RpcModuleSelection::are_identical(
2360            Some(&RpcModuleSelection::Selection([RethRpcModule::Eth].into())),
2361            Some(&RpcModuleSelection::Selection([RethRpcModule::Eth].into())),
2362        ));
2363        assert!(RpcModuleSelection::are_identical(
2364            None,
2365            Some(&RpcModuleSelection::Selection(Default::default())),
2366        ));
2367        assert!(RpcModuleSelection::are_identical(
2368            Some(&RpcModuleSelection::Selection(Default::default())),
2369            None,
2370        ));
2371        assert!(RpcModuleSelection::are_identical(None, None));
2372    }
2373
2374    #[test]
2375    fn test_rpc_module_str() {
2376        macro_rules! assert_rpc_module {
2377            ($($s:expr => $v:expr,)*) => {
2378                $(
2379                    let val: RethRpcModule  = $s.parse().unwrap();
2380                    assert_eq!(val, $v);
2381                    assert_eq!(val.to_string(), $s);
2382                )*
2383            };
2384        }
2385        assert_rpc_module!
2386        (
2387                "admin" =>  RethRpcModule::Admin,
2388                "debug" =>  RethRpcModule::Debug,
2389                "eth" =>  RethRpcModule::Eth,
2390                "net" =>  RethRpcModule::Net,
2391                "trace" =>  RethRpcModule::Trace,
2392                "web3" =>  RethRpcModule::Web3,
2393                "rpc" => RethRpcModule::Rpc,
2394                "ots" => RethRpcModule::Ots,
2395                "reth" => RethRpcModule::Reth,
2396            );
2397    }
2398
2399    #[test]
2400    fn test_default_selection() {
2401        let selection = RpcModuleSelection::Standard.to_selection();
2402        assert_eq!(selection, [RethRpcModule::Eth, RethRpcModule::Net, RethRpcModule::Web3].into())
2403    }
2404
2405    #[test]
2406    fn test_create_rpc_module_config() {
2407        let selection = vec!["eth", "admin"];
2408        let config = RpcModuleSelection::try_from_selection(selection).unwrap();
2409        assert_eq!(
2410            config,
2411            RpcModuleSelection::Selection([RethRpcModule::Eth, RethRpcModule::Admin].into())
2412        );
2413    }
2414
2415    #[test]
2416    fn test_configure_transport_config() {
2417        let config = TransportRpcModuleConfig::default()
2418            .with_http([RethRpcModule::Eth, RethRpcModule::Admin]);
2419        assert_eq!(
2420            config,
2421            TransportRpcModuleConfig {
2422                http: Some(RpcModuleSelection::Selection(
2423                    [RethRpcModule::Eth, RethRpcModule::Admin].into()
2424                )),
2425                ws: None,
2426                ipc: None,
2427                config: None,
2428            }
2429        )
2430    }
2431
2432    #[test]
2433    fn test_configure_transport_config_none() {
2434        let config = TransportRpcModuleConfig::default().with_http(Vec::<RethRpcModule>::new());
2435        assert_eq!(
2436            config,
2437            TransportRpcModuleConfig {
2438                http: Some(RpcModuleSelection::Selection(Default::default())),
2439                ws: None,
2440                ipc: None,
2441                config: None,
2442            }
2443        )
2444    }
2445
2446    fn create_test_module() -> RpcModule<()> {
2447        let mut module = RpcModule::new(());
2448        module.register_method("anything", |_, _, _| "succeed").unwrap();
2449        module
2450    }
2451
2452    #[test]
2453    fn test_remove_http_method() {
2454        let mut modules =
2455            TransportRpcModules { http: Some(create_test_module()), ..Default::default() };
2456        // Remove a method that exists
2457        assert!(modules.remove_http_method("anything"));
2458
2459        // Remove a method that does not exist
2460        assert!(!modules.remove_http_method("non_existent_method"));
2461
2462        // Verify that the method was removed
2463        assert!(modules.http.as_ref().unwrap().method("anything").is_none());
2464    }
2465
2466    #[test]
2467    fn test_remove_ws_method() {
2468        let mut modules =
2469            TransportRpcModules { ws: Some(create_test_module()), ..Default::default() };
2470
2471        // Remove a method that exists
2472        assert!(modules.remove_ws_method("anything"));
2473
2474        // Remove a method that does not exist
2475        assert!(!modules.remove_ws_method("non_existent_method"));
2476
2477        // Verify that the method was removed
2478        assert!(modules.ws.as_ref().unwrap().method("anything").is_none());
2479    }
2480
2481    #[test]
2482    fn test_remove_ipc_method() {
2483        let mut modules =
2484            TransportRpcModules { ipc: Some(create_test_module()), ..Default::default() };
2485
2486        // Remove a method that exists
2487        assert!(modules.remove_ipc_method("anything"));
2488
2489        // Remove a method that does not exist
2490        assert!(!modules.remove_ipc_method("non_existent_method"));
2491
2492        // Verify that the method was removed
2493        assert!(modules.ipc.as_ref().unwrap().method("anything").is_none());
2494    }
2495
2496    #[test]
2497    fn test_remove_method_from_configured() {
2498        let mut modules = TransportRpcModules {
2499            http: Some(create_test_module()),
2500            ws: Some(create_test_module()),
2501            ipc: Some(create_test_module()),
2502            ..Default::default()
2503        };
2504
2505        // Remove a method that exists
2506        assert!(modules.remove_method_from_configured("anything"));
2507
2508        // Remove a method that was just removed (it does not exist anymore)
2509        assert!(!modules.remove_method_from_configured("anything"));
2510
2511        // Remove a method that does not exist
2512        assert!(!modules.remove_method_from_configured("non_existent_method"));
2513
2514        // Verify that the method was removed from all transports
2515        assert!(modules.http.as_ref().unwrap().method("anything").is_none());
2516        assert!(modules.ws.as_ref().unwrap().method("anything").is_none());
2517        assert!(modules.ipc.as_ref().unwrap().method("anything").is_none());
2518    }
2519
2520    #[test]
2521    fn test_transport_rpc_module_rename() {
2522        let mut modules = TransportRpcModules {
2523            http: Some(create_test_module()),
2524            ws: Some(create_test_module()),
2525            ipc: Some(create_test_module()),
2526            ..Default::default()
2527        };
2528
2529        // Verify that the old we want to rename exists at the start
2530        assert!(modules.http.as_ref().unwrap().method("anything").is_some());
2531        assert!(modules.ws.as_ref().unwrap().method("anything").is_some());
2532        assert!(modules.ipc.as_ref().unwrap().method("anything").is_some());
2533
2534        // Verify that the new method does not exist at the start
2535        assert!(modules.http.as_ref().unwrap().method("something").is_none());
2536        assert!(modules.ws.as_ref().unwrap().method("something").is_none());
2537        assert!(modules.ipc.as_ref().unwrap().method("something").is_none());
2538
2539        // Create another module
2540        let mut other_module = RpcModule::new(());
2541        other_module.register_method("something", |_, _, _| "fails").unwrap();
2542
2543        // Rename the method
2544        modules.rename("anything", other_module).expect("rename failed");
2545
2546        // Verify that the old method was removed from all transports
2547        assert!(modules.http.as_ref().unwrap().method("anything").is_none());
2548        assert!(modules.ws.as_ref().unwrap().method("anything").is_none());
2549        assert!(modules.ipc.as_ref().unwrap().method("anything").is_none());
2550
2551        // Verify that the new method was added to all transports
2552        assert!(modules.http.as_ref().unwrap().method("something").is_some());
2553        assert!(modules.ws.as_ref().unwrap().method("something").is_some());
2554        assert!(modules.ipc.as_ref().unwrap().method("something").is_some());
2555    }
2556
2557    #[test]
2558    fn test_replace_http_method() {
2559        let mut modules =
2560            TransportRpcModules { http: Some(create_test_module()), ..Default::default() };
2561
2562        let mut other_module = RpcModule::new(());
2563        other_module.register_method("something", |_, _, _| "fails").unwrap();
2564
2565        assert!(modules.replace_http(other_module.clone()).unwrap());
2566
2567        assert!(modules.http.as_ref().unwrap().method("something").is_some());
2568
2569        other_module.register_method("anything", |_, _, _| "fails").unwrap();
2570        assert!(modules.replace_http(other_module.clone()).unwrap());
2571
2572        assert!(modules.http.as_ref().unwrap().method("anything").is_some());
2573    }
2574    #[test]
2575    fn test_replace_ipc_method() {
2576        let mut modules =
2577            TransportRpcModules { ipc: Some(create_test_module()), ..Default::default() };
2578
2579        let mut other_module = RpcModule::new(());
2580        other_module.register_method("something", |_, _, _| "fails").unwrap();
2581
2582        assert!(modules.replace_ipc(other_module.clone()).unwrap());
2583
2584        assert!(modules.ipc.as_ref().unwrap().method("something").is_some());
2585
2586        other_module.register_method("anything", |_, _, _| "fails").unwrap();
2587        assert!(modules.replace_ipc(other_module.clone()).unwrap());
2588
2589        assert!(modules.ipc.as_ref().unwrap().method("anything").is_some());
2590    }
2591    #[test]
2592    fn test_replace_ws_method() {
2593        let mut modules =
2594            TransportRpcModules { ws: Some(create_test_module()), ..Default::default() };
2595
2596        let mut other_module = RpcModule::new(());
2597        other_module.register_method("something", |_, _, _| "fails").unwrap();
2598
2599        assert!(modules.replace_ws(other_module.clone()).unwrap());
2600
2601        assert!(modules.ws.as_ref().unwrap().method("something").is_some());
2602
2603        other_module.register_method("anything", |_, _, _| "fails").unwrap();
2604        assert!(modules.replace_ws(other_module.clone()).unwrap());
2605
2606        assert!(modules.ws.as_ref().unwrap().method("anything").is_some());
2607    }
2608
2609    #[test]
2610    fn test_replace_configured() {
2611        let mut modules = TransportRpcModules {
2612            http: Some(create_test_module()),
2613            ws: Some(create_test_module()),
2614            ipc: Some(create_test_module()),
2615            ..Default::default()
2616        };
2617        let mut other_module = RpcModule::new(());
2618        other_module.register_method("something", |_, _, _| "fails").unwrap();
2619
2620        assert!(modules.replace_configured(other_module).unwrap());
2621
2622        // Verify that the other_method was added
2623        assert!(modules.http.as_ref().unwrap().method("something").is_some());
2624        assert!(modules.ipc.as_ref().unwrap().method("something").is_some());
2625        assert!(modules.ws.as_ref().unwrap().method("something").is_some());
2626
2627        assert!(modules.http.as_ref().unwrap().method("anything").is_some());
2628        assert!(modules.ipc.as_ref().unwrap().method("anything").is_some());
2629        assert!(modules.ws.as_ref().unwrap().method("anything").is_some());
2630    }
2631
2632    #[test]
2633    fn test_add_or_replace_if_module_configured() {
2634        // Create a config that enables RethRpcModule::Eth for HTTP and WS, but NOT IPC
2635        let config = TransportRpcModuleConfig::default()
2636            .with_http([RethRpcModule::Eth])
2637            .with_ws([RethRpcModule::Eth]);
2638
2639        // Create HTTP module with an existing method (to test "replace")
2640        let mut http_module = RpcModule::new(());
2641        http_module.register_method("eth_existing", |_, _, _| "original").unwrap();
2642
2643        // Create WS module with the same existing method
2644        let mut ws_module = RpcModule::new(());
2645        ws_module.register_method("eth_existing", |_, _, _| "original").unwrap();
2646
2647        // Create IPC module (empty, to ensure no changes)
2648        let ipc_module = RpcModule::new(());
2649
2650        // Set up TransportRpcModules with the config and modules
2651        let mut modules = TransportRpcModules {
2652            config,
2653            http: Some(http_module),
2654            ws: Some(ws_module),
2655            ipc: Some(ipc_module),
2656        };
2657
2658        // Create new methods: one to replace an existing method, one to add a new one
2659        let mut new_module = RpcModule::new(());
2660        new_module.register_method("eth_existing", |_, _, _| "replaced").unwrap(); // Replace
2661        new_module.register_method("eth_new", |_, _, _| "added").unwrap(); // Add
2662        let new_methods: Methods = new_module.into();
2663
2664        // Call the function for RethRpcModule::Eth
2665        let result = modules.add_or_replace_if_module_configured(RethRpcModule::Eth, new_methods);
2666        assert!(result.is_ok(), "Function should succeed");
2667
2668        // Verify HTTP: existing method still exists (replaced), new method added
2669        let http = modules.http.as_ref().unwrap();
2670        assert!(http.method("eth_existing").is_some());
2671        assert!(http.method("eth_new").is_some());
2672
2673        // Verify WS: existing method still exists (replaced), new method added
2674        let ws = modules.ws.as_ref().unwrap();
2675        assert!(ws.method("eth_existing").is_some());
2676        assert!(ws.method("eth_new").is_some());
2677
2678        // Verify IPC: no changes (Eth not configured for IPC)
2679        let ipc = modules.ipc.as_ref().unwrap();
2680        assert!(ipc.method("eth_existing").is_none());
2681        assert!(ipc.method("eth_new").is_none());
2682    }
2683
2684    #[test]
2685    fn test_merge_if_module_configured_with_lazy_evaluation() {
2686        // Create a config that enables RethRpcModule::Eth for HTTP only
2687        let config = TransportRpcModuleConfig::default().with_http([RethRpcModule::Eth]);
2688
2689        let mut modules =
2690            TransportRpcModules { config, http: Some(RpcModule::new(())), ws: None, ipc: None };
2691
2692        // Track whether closure was called
2693        let mut closure_called = false;
2694
2695        // Test with configured module - closure should be called
2696        let result = modules.merge_if_module_configured_with(RethRpcModule::Eth, || {
2697            closure_called = true;
2698            let mut methods = RpcModule::new(());
2699            methods.register_method("eth_test", |_, _, _| "test").unwrap();
2700            methods.into()
2701        });
2702
2703        assert!(result.is_ok());
2704        assert!(closure_called, "Closure should be called when module is configured");
2705        assert!(modules.http.as_ref().unwrap().method("eth_test").is_some());
2706
2707        // Reset and test with unconfigured module - closure should NOT be called
2708        closure_called = false;
2709        let result = modules.merge_if_module_configured_with(RethRpcModule::Debug, || {
2710            closure_called = true;
2711            RpcModule::new(()).into()
2712        });
2713
2714        assert!(result.is_ok());
2715        assert!(!closure_called, "Closure should NOT be called when module is not configured");
2716    }
2717}