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