reth_network/
config.rs

1//! Network config support
2
3use crate::{
4    error::NetworkError,
5    import::{BlockImport, ProofOfStakeBlockImport},
6    transactions::TransactionsManagerConfig,
7    NetworkHandle, NetworkManager,
8};
9use reth_chainspec::{ChainSpecProvider, EthChainSpec, Hardforks};
10use reth_discv4::{Discv4Config, Discv4ConfigBuilder, NatResolver, DEFAULT_DISCOVERY_ADDRESS};
11use reth_discv5::NetworkStackId;
12use reth_dns_discovery::DnsDiscoveryConfig;
13use reth_eth_wire::{
14    handshake::{EthHandshake, EthRlpxHandshake},
15    EthNetworkPrimitives, HelloMessage, HelloMessageWithProtocols, NetworkPrimitives, Status,
16};
17use reth_ethereum_forks::{ForkFilter, Head};
18use reth_network_peers::{mainnet_nodes, pk2id, sepolia_nodes, PeerId, TrustedPeer};
19use reth_network_types::{PeersConfig, SessionsConfig};
20use reth_storage_api::{noop::NoopProvider, BlockNumReader, BlockReader, HeaderProvider};
21use reth_tasks::{TaskSpawner, TokioTaskExecutor};
22use secp256k1::SECP256K1;
23use std::{collections::HashSet, net::SocketAddr, sync::Arc};
24
25// re-export for convenience
26use crate::protocol::{IntoRlpxSubProtocol, RlpxSubProtocols};
27pub use secp256k1::SecretKey;
28
29/// Convenience function to create a new random [`SecretKey`]
30pub fn rng_secret_key() -> SecretKey {
31    SecretKey::new(&mut rand::thread_rng())
32}
33
34/// All network related initialization settings.
35#[derive(Debug)]
36pub struct NetworkConfig<C, N: NetworkPrimitives = EthNetworkPrimitives> {
37    /// The client type that can interact with the chain.
38    ///
39    /// This type is used to fetch the block number after we established a session and received the
40    /// [Status] block hash.
41    pub client: C,
42    /// The node's secret key, from which the node's identity is derived.
43    pub secret_key: SecretKey,
44    /// All boot nodes to start network discovery with.
45    pub boot_nodes: HashSet<TrustedPeer>,
46    /// How to set up discovery over DNS.
47    pub dns_discovery_config: Option<DnsDiscoveryConfig>,
48    /// Address to use for discovery v4.
49    pub discovery_v4_addr: SocketAddr,
50    /// How to set up discovery.
51    pub discovery_v4_config: Option<Discv4Config>,
52    /// How to set up discovery version 5.
53    pub discovery_v5_config: Option<reth_discv5::Config>,
54    /// Address to listen for incoming connections
55    pub listener_addr: SocketAddr,
56    /// How to instantiate peer manager.
57    pub peers_config: PeersConfig,
58    /// How to configure the [`SessionManager`](crate::session::SessionManager).
59    pub sessions_config: SessionsConfig,
60    /// The chain id
61    pub chain_id: u64,
62    /// The [`ForkFilter`] to use at launch for authenticating sessions.
63    ///
64    /// See also <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2124.md#stale-software-examples>
65    ///
66    /// For sync from block `0`, this should be the default chain [`ForkFilter`] beginning at the
67    /// first hardfork, `Frontier` for mainnet.
68    pub fork_filter: ForkFilter,
69    /// The block importer type.
70    pub block_import: Box<dyn BlockImport<N::Block>>,
71    /// The default mode of the network.
72    pub network_mode: NetworkMode,
73    /// The executor to use for spawning tasks.
74    pub executor: Box<dyn TaskSpawner>,
75    /// The `Status` message to send to peers at the beginning.
76    pub status: Status,
77    /// Sets the hello message for the p2p handshake in `RLPx`
78    pub hello_message: HelloMessageWithProtocols,
79    /// Additional protocols to announce and handle in `RLPx`
80    pub extra_protocols: RlpxSubProtocols,
81    /// Whether to disable transaction gossip
82    pub tx_gossip_disabled: bool,
83    /// How to instantiate transactions manager.
84    pub transactions_manager_config: TransactionsManagerConfig,
85    /// The NAT resolver for external IP
86    pub nat: Option<NatResolver>,
87    /// The Ethereum P2P handshake, see also:
88    /// <https://github.com/ethereum/devp2p/blob/master/rlpx.md#initial-handshake>.
89    /// This can be overridden to support custom handshake logic via the
90    /// [`NetworkConfigBuilder`].
91    pub handshake: Arc<dyn EthRlpxHandshake>,
92}
93
94// === impl NetworkConfig ===
95
96impl<N: NetworkPrimitives> NetworkConfig<(), N> {
97    /// Convenience method for creating the corresponding builder type
98    pub fn builder(secret_key: SecretKey) -> NetworkConfigBuilder<N> {
99        NetworkConfigBuilder::new(secret_key)
100    }
101
102    /// Convenience method for creating the corresponding builder type with a random secret key.
103    pub fn builder_with_rng_secret_key() -> NetworkConfigBuilder<N> {
104        NetworkConfigBuilder::with_rng_secret_key()
105    }
106}
107
108impl<C, N: NetworkPrimitives> NetworkConfig<C, N> {
109    /// Create a new instance with all mandatory fields set, rest is field with defaults.
110    pub fn new(client: C, secret_key: SecretKey) -> Self
111    where
112        C: ChainSpecProvider<ChainSpec: Hardforks>,
113    {
114        NetworkConfig::builder(secret_key).build(client)
115    }
116
117    /// Apply a function to the config.
118    pub fn apply<F>(self, f: F) -> Self
119    where
120        F: FnOnce(Self) -> Self,
121    {
122        f(self)
123    }
124
125    /// Sets the config to use for the discovery v4 protocol.
126    pub fn set_discovery_v4(mut self, discovery_config: Discv4Config) -> Self {
127        self.discovery_v4_config = Some(discovery_config);
128        self
129    }
130
131    /// Sets the address for the incoming `RLPx` connection listener.
132    pub const fn set_listener_addr(mut self, listener_addr: SocketAddr) -> Self {
133        self.listener_addr = listener_addr;
134        self
135    }
136
137    /// Returns the address for the incoming `RLPx` connection listener.
138    pub const fn listener_addr(&self) -> &SocketAddr {
139        &self.listener_addr
140    }
141}
142
143impl<C, N> NetworkConfig<C, N>
144where
145    C: BlockNumReader + 'static,
146    N: NetworkPrimitives,
147{
148    /// Convenience method for calling [`NetworkManager::new`].
149    pub async fn manager(self) -> Result<NetworkManager<N>, NetworkError> {
150        NetworkManager::new(self).await
151    }
152}
153
154impl<C, N> NetworkConfig<C, N>
155where
156    N: NetworkPrimitives,
157    C: BlockReader<Block = N::Block, Receipt = N::Receipt, Header = N::BlockHeader>
158        + HeaderProvider
159        + Clone
160        + Unpin
161        + 'static,
162{
163    /// Starts the networking stack given a [`NetworkConfig`] and returns a handle to the network.
164    pub async fn start_network(self) -> Result<NetworkHandle<N>, NetworkError> {
165        let client = self.client.clone();
166        let (handle, network, _txpool, eth) = NetworkManager::builder::<C>(self)
167            .await?
168            .request_handler::<C>(client)
169            .split_with_handle();
170
171        tokio::task::spawn(network);
172        tokio::task::spawn(eth);
173        Ok(handle)
174    }
175}
176
177/// Builder for [`NetworkConfig`](struct.NetworkConfig.html).
178#[derive(Debug)]
179pub struct NetworkConfigBuilder<N: NetworkPrimitives = EthNetworkPrimitives> {
180    /// The node's secret key, from which the node's identity is derived.
181    secret_key: SecretKey,
182    /// How to configure discovery over DNS.
183    dns_discovery_config: Option<DnsDiscoveryConfig>,
184    /// How to set up discovery version 4.
185    discovery_v4_builder: Option<Discv4ConfigBuilder>,
186    /// How to set up discovery version 5.
187    discovery_v5_builder: Option<reth_discv5::ConfigBuilder>,
188    /// All boot nodes to start network discovery with.
189    boot_nodes: HashSet<TrustedPeer>,
190    /// Address to use for discovery
191    discovery_addr: Option<SocketAddr>,
192    /// Listener for incoming connections
193    listener_addr: Option<SocketAddr>,
194    /// How to instantiate peer manager.
195    peers_config: Option<PeersConfig>,
196    /// How to configure the sessions manager
197    sessions_config: Option<SessionsConfig>,
198    /// The default mode of the network.
199    network_mode: NetworkMode,
200    /// The executor to use for spawning tasks.
201    executor: Option<Box<dyn TaskSpawner>>,
202    /// Sets the hello message for the p2p handshake in `RLPx`
203    hello_message: Option<HelloMessageWithProtocols>,
204    /// The executor to use for spawning tasks.
205    extra_protocols: RlpxSubProtocols,
206    /// Head used to start set for the fork filter and status.
207    head: Option<Head>,
208    /// Whether tx gossip is disabled
209    tx_gossip_disabled: bool,
210    /// The block importer type
211    block_import: Option<Box<dyn BlockImport<N::Block>>>,
212    /// How to instantiate transactions manager.
213    transactions_manager_config: TransactionsManagerConfig,
214    /// The NAT resolver for external IP
215    nat: Option<NatResolver>,
216    /// The Ethereum P2P handshake, see also:
217    /// <https://github.com/ethereum/devp2p/blob/master/rlpx.md#initial-handshake>.
218    handshake: Arc<dyn EthRlpxHandshake>,
219}
220
221impl NetworkConfigBuilder<EthNetworkPrimitives> {
222    /// Creates the `NetworkConfigBuilder` with [`EthNetworkPrimitives`] types.
223    pub fn eth(secret_key: SecretKey) -> Self {
224        Self::new(secret_key)
225    }
226}
227
228// === impl NetworkConfigBuilder ===
229
230#[allow(missing_docs)]
231impl<N: NetworkPrimitives> NetworkConfigBuilder<N> {
232    /// Create a new builder instance with a random secret key.
233    pub fn with_rng_secret_key() -> Self {
234        Self::new(rng_secret_key())
235    }
236
237    /// Create a new builder instance with the given secret key.
238    pub fn new(secret_key: SecretKey) -> Self {
239        Self {
240            secret_key,
241            dns_discovery_config: Some(Default::default()),
242            discovery_v4_builder: Some(Default::default()),
243            discovery_v5_builder: None,
244            boot_nodes: Default::default(),
245            discovery_addr: None,
246            listener_addr: None,
247            peers_config: None,
248            sessions_config: None,
249            network_mode: Default::default(),
250            executor: None,
251            hello_message: None,
252            extra_protocols: Default::default(),
253            head: None,
254            tx_gossip_disabled: false,
255            block_import: None,
256            transactions_manager_config: Default::default(),
257            nat: None,
258            handshake: Arc::new(EthHandshake::default()),
259        }
260    }
261
262    /// Apply a function to the builder.
263    pub fn apply<F>(self, f: F) -> Self
264    where
265        F: FnOnce(Self) -> Self,
266    {
267        f(self)
268    }
269
270    /// Returns the configured [`PeerId`]
271    pub fn get_peer_id(&self) -> PeerId {
272        pk2id(&self.secret_key.public_key(SECP256K1))
273    }
274
275    /// Returns the configured [`SecretKey`], from which the node's identity is derived.
276    pub const fn secret_key(&self) -> &SecretKey {
277        &self.secret_key
278    }
279
280    /// Sets the [`NetworkMode`].
281    pub const fn network_mode(mut self, network_mode: NetworkMode) -> Self {
282        self.network_mode = network_mode;
283        self
284    }
285
286    /// Configures the network to use proof-of-work.
287    ///
288    /// This effectively allows block propagation in the `eth` sub-protocol, which has been
289    /// soft-deprecated with ethereum `PoS` after the merge. Even if block propagation is
290    /// technically allowed, according to the eth protocol, it is not expected to be used in `PoS`
291    /// networks and peers are supposed to terminate the connection if they receive a `NewBlock`
292    /// message.
293    pub const fn with_pow(self) -> Self {
294        self.network_mode(NetworkMode::Work)
295    }
296
297    /// Sets the highest synced block.
298    ///
299    /// This is used to construct the appropriate [`ForkFilter`] and [`Status`] message.
300    ///
301    /// If not set, this defaults to the genesis specified by the current chain specification.
302    pub const fn set_head(mut self, head: Head) -> Self {
303        self.head = Some(head);
304        self
305    }
306
307    /// Sets the `HelloMessage` to send when connecting to peers.
308    ///
309    /// ```
310    /// # use reth_eth_wire::HelloMessage;
311    /// # use reth_network::NetworkConfigBuilder;
312    /// # fn builder(builder: NetworkConfigBuilder) {
313    /// let peer_id = builder.get_peer_id();
314    /// builder.hello_message(HelloMessage::builder(peer_id).build());
315    /// # }
316    /// ```
317    pub fn hello_message(mut self, hello_message: HelloMessageWithProtocols) -> Self {
318        self.hello_message = Some(hello_message);
319        self
320    }
321
322    /// Set a custom peer config for how peers are handled
323    pub fn peer_config(mut self, config: PeersConfig) -> Self {
324        self.peers_config = Some(config);
325        self
326    }
327
328    /// Sets the executor to use for spawning tasks.
329    ///
330    /// If `None`, then [`tokio::spawn`] is used for spawning tasks.
331    pub fn with_task_executor(mut self, executor: Box<dyn TaskSpawner>) -> Self {
332        self.executor = Some(executor);
333        self
334    }
335
336    /// Sets a custom config for how sessions are handled.
337    pub const fn sessions_config(mut self, config: SessionsConfig) -> Self {
338        self.sessions_config = Some(config);
339        self
340    }
341
342    /// Configures the transactions manager with the given config.
343    pub const fn transactions_manager_config(mut self, config: TransactionsManagerConfig) -> Self {
344        self.transactions_manager_config = config;
345        self
346    }
347
348    /// Sets the discovery and listener address
349    ///
350    /// This is a convenience function for both [`NetworkConfigBuilder::listener_addr`] and
351    /// [`NetworkConfigBuilder::discovery_addr`].
352    ///
353    /// By default, both are on the same port:
354    /// [`DEFAULT_DISCOVERY_PORT`](reth_discv4::DEFAULT_DISCOVERY_PORT)
355    pub const fn set_addrs(self, addr: SocketAddr) -> Self {
356        self.listener_addr(addr).discovery_addr(addr)
357    }
358
359    /// Sets the socket address the network will listen on.
360    ///
361    /// By default, this is [`DEFAULT_DISCOVERY_ADDRESS`]
362    pub const fn listener_addr(mut self, listener_addr: SocketAddr) -> Self {
363        self.listener_addr = Some(listener_addr);
364        self
365    }
366
367    /// Sets the port of the address the network will listen on.
368    ///
369    /// By default, this is [`DEFAULT_DISCOVERY_PORT`](reth_discv4::DEFAULT_DISCOVERY_PORT)
370    pub fn listener_port(mut self, port: u16) -> Self {
371        self.listener_addr.get_or_insert(DEFAULT_DISCOVERY_ADDRESS).set_port(port);
372        self
373    }
374
375    /// Sets the socket address the discovery network will listen on
376    pub const fn discovery_addr(mut self, discovery_addr: SocketAddr) -> Self {
377        self.discovery_addr = Some(discovery_addr);
378        self
379    }
380
381    /// Sets the port of the address the discovery network will listen on.
382    ///
383    /// By default, this is [`DEFAULT_DISCOVERY_PORT`](reth_discv4::DEFAULT_DISCOVERY_PORT)
384    pub fn discovery_port(mut self, port: u16) -> Self {
385        self.discovery_addr.get_or_insert(DEFAULT_DISCOVERY_ADDRESS).set_port(port);
386        self
387    }
388
389    /// Launches the network with an unused network and discovery port
390    /// This is useful for testing.
391    pub fn with_unused_ports(self) -> Self {
392        self.with_unused_discovery_port().with_unused_listener_port()
393    }
394
395    /// Sets the discovery port to an unused port.
396    /// This is useful for testing.
397    pub fn with_unused_discovery_port(self) -> Self {
398        self.discovery_port(0)
399    }
400
401    /// Sets the listener port to an unused port.
402    /// This is useful for testing.
403    pub fn with_unused_listener_port(self) -> Self {
404        self.listener_port(0)
405    }
406
407    /// Sets the external ip resolver to use for discovery v4.
408    ///
409    /// If no [`Discv4ConfigBuilder`] is set via [`Self::discovery`], this will create a new one.
410    ///
411    /// This is a convenience function for setting the external ip resolver on the default
412    /// [`Discv4Config`] config.
413    pub fn external_ip_resolver(mut self, resolver: NatResolver) -> Self {
414        self.discovery_v4_builder
415            .get_or_insert_with(Discv4Config::builder)
416            .external_ip_resolver(Some(resolver));
417        self.nat = Some(resolver);
418        self
419    }
420
421    /// Sets the discv4 config to use.
422    pub fn discovery(mut self, builder: Discv4ConfigBuilder) -> Self {
423        self.discovery_v4_builder = Some(builder);
424        self
425    }
426
427    /// Sets the discv5 config to use.
428    pub fn discovery_v5(mut self, builder: reth_discv5::ConfigBuilder) -> Self {
429        self.discovery_v5_builder = Some(builder);
430        self
431    }
432
433    /// Sets the dns discovery config to use.
434    pub fn dns_discovery(mut self, config: DnsDiscoveryConfig) -> Self {
435        self.dns_discovery_config = Some(config);
436        self
437    }
438
439    /// Convenience function for setting [`Self::boot_nodes`] to the mainnet boot nodes.
440    pub fn mainnet_boot_nodes(self) -> Self {
441        self.boot_nodes(mainnet_nodes())
442    }
443
444    /// Convenience function for setting [`Self::boot_nodes`] to the sepolia boot nodes.
445    pub fn sepolia_boot_nodes(self) -> Self {
446        self.boot_nodes(sepolia_nodes())
447    }
448
449    /// Sets the boot nodes to use to bootstrap the configured discovery services (discv4 + discv5).
450    pub fn boot_nodes<T: Into<TrustedPeer>>(mut self, nodes: impl IntoIterator<Item = T>) -> Self {
451        self.boot_nodes = nodes.into_iter().map(Into::into).collect();
452        self
453    }
454
455    /// Returns an iterator over all configured boot nodes.
456    pub fn boot_nodes_iter(&self) -> impl Iterator<Item = &TrustedPeer> + '_ {
457        self.boot_nodes.iter()
458    }
459
460    /// Disable the DNS discovery.
461    pub fn disable_dns_discovery(mut self) -> Self {
462        self.dns_discovery_config = None;
463        self
464    }
465
466    // Disable nat
467    pub const fn disable_nat(mut self) -> Self {
468        self.nat = None;
469        self
470    }
471
472    /// Disables all discovery.
473    pub fn disable_discovery(self) -> Self {
474        self.disable_discv4_discovery().disable_discv5_discovery().disable_dns_discovery()
475    }
476
477    /// Disables all discovery if the given condition is true.
478    pub fn disable_discovery_if(self, disable: bool) -> Self {
479        if disable {
480            self.disable_discovery()
481        } else {
482            self
483        }
484    }
485
486    /// Disable the Discv4 discovery.
487    pub fn disable_discv4_discovery(mut self) -> Self {
488        self.discovery_v4_builder = None;
489        self
490    }
491
492    /// Disable the Discv5 discovery.
493    pub fn disable_discv5_discovery(mut self) -> Self {
494        self.discovery_v5_builder = None;
495        self
496    }
497
498    /// Disable the DNS discovery if the given condition is true.
499    pub fn disable_dns_discovery_if(self, disable: bool) -> Self {
500        if disable {
501            self.disable_dns_discovery()
502        } else {
503            self
504        }
505    }
506
507    /// Disable the Discv4 discovery if the given condition is true.
508    pub fn disable_discv4_discovery_if(self, disable: bool) -> Self {
509        if disable {
510            self.disable_discv4_discovery()
511        } else {
512            self
513        }
514    }
515
516    /// Disable the Discv5 discovery if the given condition is true.
517    pub fn disable_discv5_discovery_if(self, disable: bool) -> Self {
518        if disable {
519            self.disable_discv5_discovery()
520        } else {
521            self
522        }
523    }
524
525    /// Adds a new additional protocol to the `RLPx` sub-protocol list.
526    pub fn add_rlpx_sub_protocol(mut self, protocol: impl IntoRlpxSubProtocol) -> Self {
527        self.extra_protocols.push(protocol);
528        self
529    }
530
531    /// Sets whether tx gossip is disabled.
532    pub const fn disable_tx_gossip(mut self, disable_tx_gossip: bool) -> Self {
533        self.tx_gossip_disabled = disable_tx_gossip;
534        self
535    }
536
537    /// Sets the block import type.
538    pub fn block_import(mut self, block_import: Box<dyn BlockImport<N::Block>>) -> Self {
539        self.block_import = Some(block_import);
540        self
541    }
542
543    /// Convenience function for creating a [`NetworkConfig`] with a noop provider that does
544    /// nothing.
545    pub fn build_with_noop_provider<ChainSpec>(
546        self,
547        chain_spec: Arc<ChainSpec>,
548    ) -> NetworkConfig<NoopProvider<ChainSpec>, N>
549    where
550        ChainSpec: EthChainSpec + Hardforks + 'static,
551    {
552        self.build(NoopProvider::eth(chain_spec))
553    }
554
555    /// Sets the NAT resolver for external IP.
556    pub const fn add_nat(mut self, nat: Option<NatResolver>) -> Self {
557        self.nat = nat;
558        self
559    }
560
561    /// Overrides the default Eth `RLPx` handshake.
562    pub fn eth_rlpx_handshake(mut self, handshake: Arc<dyn EthRlpxHandshake>) -> Self {
563        self.handshake = handshake;
564        self
565    }
566
567    /// Consumes the type and creates the actual [`NetworkConfig`]
568    /// for the given client type that can interact with the chain.
569    ///
570    /// The given client is to be used for interacting with the chain, for example fetching the
571    /// corresponding block for a given block hash we receive from a peer in the status message when
572    /// establishing a connection.
573    pub fn build<C>(self, client: C) -> NetworkConfig<C, N>
574    where
575        C: ChainSpecProvider<ChainSpec: Hardforks>,
576    {
577        let peer_id = self.get_peer_id();
578        let chain_spec = client.chain_spec();
579        let Self {
580            secret_key,
581            mut dns_discovery_config,
582            discovery_v4_builder,
583            mut discovery_v5_builder,
584            boot_nodes,
585            discovery_addr,
586            listener_addr,
587            peers_config,
588            sessions_config,
589            network_mode,
590            executor,
591            hello_message,
592            extra_protocols,
593            head,
594            tx_gossip_disabled,
595            block_import,
596            transactions_manager_config,
597            nat,
598            handshake,
599        } = self;
600
601        discovery_v5_builder = discovery_v5_builder.map(|mut builder| {
602            if let Some(network_stack_id) = NetworkStackId::id(&chain_spec) {
603                let fork_id = chain_spec.latest_fork_id();
604                builder = builder.fork(network_stack_id, fork_id)
605            }
606
607            builder
608        });
609
610        let listener_addr = listener_addr.unwrap_or(DEFAULT_DISCOVERY_ADDRESS);
611
612        let mut hello_message =
613            hello_message.unwrap_or_else(|| HelloMessage::builder(peer_id).build());
614        hello_message.port = listener_addr.port();
615
616        let head = head.unwrap_or_else(|| Head {
617            hash: chain_spec.genesis_hash(),
618            number: 0,
619            timestamp: chain_spec.genesis().timestamp,
620            difficulty: chain_spec.genesis().difficulty,
621            total_difficulty: chain_spec.genesis().difficulty,
622        });
623
624        // set the status
625        let status = Status::spec_builder(&chain_spec, &head).build();
626
627        // set a fork filter based on the chain spec and head
628        let fork_filter = chain_spec.fork_filter(head);
629
630        // get the chain id
631        let chain_id = chain_spec.chain().id();
632
633        // If default DNS config is used then we add the known dns network to bootstrap from
634        if let Some(dns_networks) =
635            dns_discovery_config.as_mut().and_then(|c| c.bootstrap_dns_networks.as_mut())
636        {
637            if dns_networks.is_empty() {
638                if let Some(link) = chain_spec.chain().public_dns_network_protocol() {
639                    dns_networks.insert(link.parse().expect("is valid DNS link entry"));
640                }
641            }
642        }
643
644        NetworkConfig {
645            client,
646            secret_key,
647            boot_nodes,
648            dns_discovery_config,
649            discovery_v4_config: discovery_v4_builder.map(|builder| builder.build()),
650            discovery_v5_config: discovery_v5_builder.map(|builder| builder.build()),
651            discovery_v4_addr: discovery_addr.unwrap_or(DEFAULT_DISCOVERY_ADDRESS),
652            listener_addr,
653            peers_config: peers_config.unwrap_or_default(),
654            sessions_config: sessions_config.unwrap_or_default(),
655            chain_id,
656            block_import: block_import.unwrap_or_else(|| Box::<ProofOfStakeBlockImport>::default()),
657            network_mode,
658            executor: executor.unwrap_or_else(|| Box::<TokioTaskExecutor>::default()),
659            status,
660            hello_message,
661            extra_protocols,
662            fork_filter,
663            tx_gossip_disabled,
664            transactions_manager_config,
665            nat,
666            handshake,
667        }
668    }
669}
670
671/// Describes the mode of the network wrt. POS or POW.
672///
673/// This affects block propagation in the `eth` sub-protocol [EIP-3675](https://eips.ethereum.org/EIPS/eip-3675#devp2p)
674///
675/// In POS `NewBlockHashes` and `NewBlock` messages become invalid.
676#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
677#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
678pub enum NetworkMode {
679    /// Network is in proof-of-work mode.
680    Work,
681    /// Network is in proof-of-stake mode
682    #[default]
683    Stake,
684}
685
686// === impl NetworkMode ===
687
688impl NetworkMode {
689    /// Returns true if network has entered proof-of-stake
690    pub const fn is_stake(&self) -> bool {
691        matches!(self, Self::Stake)
692    }
693}
694
695#[cfg(test)]
696mod tests {
697    use super::*;
698    use rand::thread_rng;
699    use reth_chainspec::{Chain, MAINNET};
700    use reth_dns_discovery::tree::LinkEntry;
701    use reth_primitives::ForkHash;
702    use reth_storage_api::noop::NoopProvider;
703    use std::sync::Arc;
704
705    fn builder() -> NetworkConfigBuilder {
706        let secret_key = SecretKey::new(&mut thread_rng());
707        NetworkConfigBuilder::new(secret_key)
708    }
709
710    #[test]
711    fn test_network_dns_defaults() {
712        let config = builder().build(NoopProvider::default());
713
714        let dns = config.dns_discovery_config.unwrap();
715        let bootstrap_nodes = dns.bootstrap_dns_networks.unwrap();
716        let mainnet_dns: LinkEntry =
717            Chain::mainnet().public_dns_network_protocol().unwrap().parse().unwrap();
718        assert!(bootstrap_nodes.contains(&mainnet_dns));
719        assert_eq!(bootstrap_nodes.len(), 1);
720    }
721
722    #[test]
723    fn test_network_fork_filter_default() {
724        let mut chain_spec = Arc::clone(&MAINNET);
725
726        // remove any `next` fields we would have by removing all hardforks
727        Arc::make_mut(&mut chain_spec).hardforks = Default::default();
728
729        // check that the forkid is initialized with the genesis and no other forks
730        let genesis_fork_hash = ForkHash::from(chain_spec.genesis_hash());
731
732        // enforce that the fork_id set in the status is consistent with the generated fork filter
733        let config = builder().build_with_noop_provider(chain_spec);
734
735        let status = config.status;
736        let fork_filter = config.fork_filter;
737
738        // assert that there are no other forks
739        assert_eq!(status.forkid.next, 0);
740
741        // assert the same thing for the fork_filter
742        assert_eq!(fork_filter.current().next, 0);
743
744        // check status and fork_filter forkhash
745        assert_eq!(status.forkid.hash, genesis_fork_hash);
746        assert_eq!(fork_filter.current().hash, genesis_fork_hash);
747    }
748}