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