zebra_network/
config.rs

1//! Configuration for Zebra's network communication.
2
3use std::{
4    collections::HashSet,
5    io::{self, ErrorKind},
6    net::{IpAddr, SocketAddr},
7    sync::Arc,
8    time::Duration,
9};
10
11use indexmap::IndexSet;
12use serde::{de, Deserialize, Deserializer};
13use tokio::fs;
14
15use tracing::Span;
16use zebra_chain::{
17    common::atomic_write,
18    parameters::{
19        testnet::{self, ConfiguredActivationHeights, ConfiguredFundingStreams},
20        Magic, Network, NetworkKind,
21    },
22    work::difficulty::U256,
23};
24
25use crate::{
26    constants::{
27        DEFAULT_CRAWL_NEW_PEER_INTERVAL, DEFAULT_MAX_CONNS_PER_IP,
28        DEFAULT_PEERSET_INITIAL_TARGET_SIZE, DNS_LOOKUP_TIMEOUT, INBOUND_PEER_LIMIT_MULTIPLIER,
29        MAX_PEER_DISK_CACHE_SIZE, OUTBOUND_PEER_LIMIT_MULTIPLIER,
30    },
31    protocol::external::{canonical_peer_addr, canonical_socket_addr},
32    BoxError, PeerSocketAddr,
33};
34
35mod cache_dir;
36
37#[cfg(test)]
38mod tests;
39
40pub use cache_dir::CacheDir;
41
42/// The number of times Zebra will retry each initial peer's DNS resolution,
43/// before checking if any other initial peers have returned addresses.
44///
45/// After doing this number of retries of a failed single peer, Zebra will
46/// check if it has enough peer addresses from other seed peers. If it has
47/// enough addresses, it won't retry this peer again.
48///
49/// If the number of retries is `0`, other peers are checked after every successful
50/// or failed DNS attempt.
51const MAX_SINGLE_SEED_PEER_DNS_RETRIES: usize = 0;
52
53/// Configuration for networking code.
54#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
55#[serde(deny_unknown_fields, default, into = "DConfig")]
56pub struct Config {
57    /// The address on which this node should listen for connections.
58    ///
59    /// Can be `address:port` or just `address`. If there is no configured
60    /// port, Zebra will use the default port for the configured `network`.
61    ///
62    /// `address` can be an IP address or a DNS name. DNS names are
63    /// only resolved once, when Zebra starts up.
64    ///
65    /// If a specific listener address is configured, Zebra will advertise
66    /// it to other nodes. But by default, Zebra uses an unspecified address
67    /// ("0.0.0.0" or "\[::\]"), which is not advertised to other nodes.
68    ///
69    /// Zebra does not currently support:
70    /// - [Advertising a different external IP address #1890](https://github.com/ZcashFoundation/zebra/issues/1890), or
71    /// - [Auto-discovering its own external IP address #1893](https://github.com/ZcashFoundation/zebra/issues/1893).
72    ///
73    /// However, other Zebra instances compensate for unspecified or incorrect
74    /// listener addresses by adding the external IP addresses of peers to
75    /// their address books.
76    pub listen_addr: SocketAddr,
77
78    /// The external address of this node if any.
79    ///
80    /// Zebra bind to `listen_addr` but this can be an internal address if the node
81    /// is behind a firewall, load balancer or NAT. This field can be used to
82    /// advertise a different address to peers making it possible to receive inbound
83    /// connections and contribute to the P2P network from behind a firewall, load balancer, or NAT.
84    pub external_addr: Option<SocketAddr>,
85
86    /// The network to connect to.
87    pub network: Network,
88
89    /// A list of initial peers for the peerset when operating on
90    /// mainnet.
91    pub initial_mainnet_peers: IndexSet<String>,
92
93    /// A list of initial peers for the peerset when operating on
94    /// testnet.
95    pub initial_testnet_peers: IndexSet<String>,
96
97    /// An optional root directory for storing cached peer address data.
98    ///
99    /// # Configuration
100    ///
101    /// Set to:
102    /// - `true` to read and write peer addresses to disk using the default cache path,
103    /// - `false` to disable reading and writing peer addresses to disk,
104    /// - `'/custom/cache/directory'` to read and write peer addresses to a custom directory.
105    ///
106    /// By default, all Zebra instances run by the same user will share a single peer cache.
107    /// If you use a custom cache path, you might also want to change `state.cache_dir`.
108    ///
109    /// # Functionality
110    ///
111    /// The peer cache is a list of the addresses of some recently useful peers.
112    ///
113    /// For privacy reasons, the cache does *not* include any other information about peers,
114    /// such as when they were connected to the node.
115    ///
116    /// Deleting or modifying the peer cache can impact your node's:
117    /// - reliability: if DNS or the Zcash DNS seeders are unavailable or broken
118    /// - security: if DNS is compromised with malicious peers
119    ///
120    /// If you delete it, Zebra will replace it with a fresh set of peers from the DNS seeders.
121    ///
122    /// # Defaults
123    ///
124    /// The default directory is platform dependent, based on
125    /// [`dirs::cache_dir()`](https://docs.rs/dirs/3.0.1/dirs/fn.cache_dir.html):
126    ///
127    /// |Platform | Value                                           | Example                              |
128    /// | ------- | ----------------------------------------------- | ------------------------------------ |
129    /// | Linux   | `$XDG_CACHE_HOME/zebra` or `$HOME/.cache/zebra` | `/home/alice/.cache/zebra`           |
130    /// | macOS   | `$HOME/Library/Caches/zebra`                    | `/Users/Alice/Library/Caches/zebra`  |
131    /// | Windows | `{FOLDERID_LocalAppData}\zebra`                 | `C:\Users\Alice\AppData\Local\zebra` |
132    /// | Other   | `std::env::current_dir()/cache/zebra`           | `/cache/zebra`                       |
133    ///
134    /// # Security
135    ///
136    /// If you are running Zebra with elevated permissions ("root"), create the
137    /// directory for this file before running Zebra, and make sure the Zebra user
138    /// account has exclusive access to that directory, and other users can't modify
139    /// its parent directories.
140    ///
141    /// # Implementation Details
142    ///
143    /// Each network has a separate peer list, which is updated regularly from the current
144    /// address book. These lists are stored in `network/mainnet.peers` and
145    /// `network/testnet.peers` files, underneath the `cache_dir` path.
146    ///
147    /// Previous peer lists are automatically loaded at startup, and used to populate the
148    /// initial peer set and address book.
149    pub cache_dir: CacheDir,
150
151    /// The initial target size for the peer set.
152    ///
153    /// Also used to limit the number of inbound and outbound connections made by Zebra,
154    /// and the size of the cached peer list.
155    ///
156    /// If you have a slow network connection, and Zebra is having trouble
157    /// syncing, try reducing the peer set size. You can also reduce the peer
158    /// set size to reduce Zebra's bandwidth usage.
159    pub peerset_initial_target_size: usize,
160
161    /// How frequently we attempt to crawl the network to discover new peer
162    /// addresses.
163    ///
164    /// Zebra asks its connected peers for more peer addresses:
165    /// - regularly, every time `crawl_new_peer_interval` elapses, and
166    /// - if the peer set is busy, and there aren't any peer addresses for the
167    ///   next connection attempt.
168    #[serde(with = "humantime_serde")]
169    pub crawl_new_peer_interval: Duration,
170
171    /// The maximum number of peer connections Zebra will keep for a given IP address
172    /// before it drops any additional peer connections with that IP.
173    ///
174    /// The default and minimum value are 1.
175    ///
176    /// # Security
177    ///
178    /// Increasing this config above 1 reduces Zebra's network security.
179    ///
180    /// If this config is greater than 1, Zebra can initiate multiple outbound handshakes to the same
181    /// IP address.
182    ///
183    /// This config does not currently limit the number of inbound connections that Zebra will accept
184    /// from the same IP address.
185    ///
186    /// If Zebra makes multiple inbound or outbound connections to the same IP, they will be dropped
187    /// after the handshake, but before adding them to the peer set. The total numbers of inbound and
188    /// outbound connections are also limited to a multiple of `peerset_initial_target_size`.
189    pub max_connections_per_ip: usize,
190}
191
192impl Config {
193    /// The maximum number of outbound connections that Zebra will open at the same time.
194    /// When this limit is reached, Zebra stops opening outbound connections.
195    ///
196    /// # Security
197    ///
198    /// See the note at [`INBOUND_PEER_LIMIT_MULTIPLIER`].
199    ///
200    /// # Performance
201    ///
202    /// Zebra's peer set should be limited to a reasonable size,
203    /// to avoid queueing too many in-flight block downloads.
204    /// A large queue of in-flight block downloads can choke a
205    /// constrained local network connection.
206    ///
207    /// We assume that Zebra nodes have at least 10 Mbps bandwidth.
208    /// Therefore, a maximum-sized block can take up to 2 seconds to
209    /// download. So the initial outbound peer set adds up to 100 seconds worth
210    /// of blocks to the queue. If Zebra has reached its outbound peer limit,
211    /// that adds an extra 200 seconds of queued blocks.
212    ///
213    /// But the peer set for slow nodes is typically much smaller, due to
214    /// the handshake RTT timeout. And Zebra responds to inbound request
215    /// overloads by dropping peer connections.
216    pub fn peerset_outbound_connection_limit(&self) -> usize {
217        self.peerset_initial_target_size * OUTBOUND_PEER_LIMIT_MULTIPLIER
218    }
219
220    /// The maximum number of inbound connections that Zebra will accept at the same time.
221    /// When this limit is reached, Zebra drops new inbound connections,
222    /// without handshaking on them.
223    ///
224    /// # Security
225    ///
226    /// See the note at [`INBOUND_PEER_LIMIT_MULTIPLIER`].
227    pub fn peerset_inbound_connection_limit(&self) -> usize {
228        self.peerset_initial_target_size * INBOUND_PEER_LIMIT_MULTIPLIER
229    }
230
231    /// The maximum number of inbound and outbound connections that Zebra will have
232    /// at the same time.
233    pub fn peerset_total_connection_limit(&self) -> usize {
234        self.peerset_outbound_connection_limit() + self.peerset_inbound_connection_limit()
235    }
236
237    /// Returns the initial seed peer hostnames for the configured network.
238    pub fn initial_peer_hostnames(&self) -> IndexSet<String> {
239        match &self.network {
240            Network::Mainnet => self.initial_mainnet_peers.clone(),
241            Network::Testnet(_params) => self.initial_testnet_peers.clone(),
242        }
243    }
244
245    /// Resolve initial seed peer IP addresses, based on the configured network,
246    /// and load cached peers from disk, if available.
247    ///
248    /// # Panics
249    ///
250    /// If a configured address is an invalid [`SocketAddr`] or DNS name.
251    pub async fn initial_peers(&self) -> HashSet<PeerSocketAddr> {
252        // TODO: do DNS and disk in parallel if startup speed becomes important
253        let dns_peers =
254            Config::resolve_peers(&self.initial_peer_hostnames().iter().cloned().collect()).await;
255
256        if self.network.is_regtest() {
257            // Only return local peer addresses and skip loading the peer cache on Regtest.
258            dns_peers
259                .into_iter()
260                .filter(PeerSocketAddr::is_localhost)
261                .collect()
262        } else {
263            // Ignore disk errors because the cache is optional and the method already logs them.
264            let disk_peers = self.load_peer_cache().await.unwrap_or_default();
265
266            dns_peers.into_iter().chain(disk_peers).collect()
267        }
268    }
269
270    /// Concurrently resolves `peers` into zero or more IP addresses, with a
271    /// timeout of a few seconds on each DNS request.
272    ///
273    /// If DNS resolution fails or times out for all peers, continues retrying
274    /// until at least one peer is found.
275    async fn resolve_peers(peers: &HashSet<String>) -> HashSet<PeerSocketAddr> {
276        use futures::stream::StreamExt;
277
278        if peers.is_empty() {
279            warn!(
280                "no initial peers in the network config. \
281                 Hint: you must configure at least one peer IP or DNS seeder to run Zebra, \
282                 give it some previously cached peer IP addresses on disk, \
283                 or make sure Zebra's listener port gets inbound connections."
284            );
285            return HashSet::new();
286        }
287
288        loop {
289            // We retry each peer individually, as well as retrying if there are
290            // no peers in the combined list. DNS failures are correlated, so all
291            // peers can fail DNS, leaving Zebra with a small list of custom IP
292            // address peers. Individual retries avoid this issue.
293            let peer_addresses = peers
294                .iter()
295                .map(|s| Config::resolve_host(s, MAX_SINGLE_SEED_PEER_DNS_RETRIES))
296                .collect::<futures::stream::FuturesUnordered<_>>()
297                .concat()
298                .await;
299
300            if peer_addresses.is_empty() {
301                tracing::info!(
302                    ?peers,
303                    ?peer_addresses,
304                    "empty peer list after DNS resolution, retrying after {} seconds",
305                    DNS_LOOKUP_TIMEOUT.as_secs(),
306                );
307                tokio::time::sleep(DNS_LOOKUP_TIMEOUT).await;
308            } else {
309                return peer_addresses;
310            }
311        }
312    }
313
314    /// Resolves `host` into zero or more IP addresses, retrying up to
315    /// `max_retries` times.
316    ///
317    /// If DNS continues to fail, returns an empty list of addresses.
318    ///
319    /// # Panics
320    ///
321    /// If a configured address is an invalid [`SocketAddr`] or DNS name.
322    async fn resolve_host(host: &str, max_retries: usize) -> HashSet<PeerSocketAddr> {
323        for retries in 0..=max_retries {
324            if let Ok(addresses) = Config::resolve_host_once(host).await {
325                return addresses;
326            }
327
328            if retries < max_retries {
329                tracing::info!(
330                    ?host,
331                    previous_attempts = ?(retries + 1),
332                    "Waiting {DNS_LOOKUP_TIMEOUT:?} to retry seed peer DNS resolution",
333                );
334                tokio::time::sleep(DNS_LOOKUP_TIMEOUT).await;
335            } else {
336                tracing::info!(
337                    ?host,
338                    attempts = ?(retries + 1),
339                    "Seed peer DNS resolution failed, checking for addresses from other seed peers",
340                );
341            }
342        }
343
344        HashSet::new()
345    }
346
347    /// Resolves `host` into zero or more IP addresses.
348    ///
349    /// If `host` is a DNS name, performs DNS resolution with a timeout of a few seconds.
350    /// If DNS resolution fails or times out, returns an error.
351    ///
352    /// # Panics
353    ///
354    /// If a configured address is an invalid [`SocketAddr`] or DNS name.
355    async fn resolve_host_once(host: &str) -> Result<HashSet<PeerSocketAddr>, BoxError> {
356        let fut = tokio::net::lookup_host(host);
357        let fut = tokio::time::timeout(DNS_LOOKUP_TIMEOUT, fut);
358
359        match fut.await {
360            Ok(Ok(ip_addrs)) => {
361                let ip_addrs: Vec<PeerSocketAddr> = ip_addrs.map(canonical_peer_addr).collect();
362
363                // This log is needed for user debugging, but it's annoying during tests.
364                #[cfg(not(test))]
365                info!(seed = ?host, remote_ip_count = ?ip_addrs.len(), "resolved seed peer IP addresses");
366                #[cfg(test)]
367                debug!(seed = ?host, remote_ip_count = ?ip_addrs.len(), "resolved seed peer IP addresses");
368
369                for ip in &ip_addrs {
370                    // Count each initial peer, recording the seed config and resolved IP address.
371                    //
372                    // If an IP is returned by multiple seeds,
373                    // each duplicate adds 1 to the initial peer count.
374                    // (But we only make one initial connection attempt to each IP.)
375                    metrics::counter!(
376                        "zcash.net.peers.initial",
377                        "seed" => host.to_string(),
378                        "remote_ip" => ip.to_string()
379                    )
380                    .increment(1);
381                }
382
383                Ok(ip_addrs.into_iter().collect())
384            }
385            Ok(Err(e)) if e.kind() == ErrorKind::InvalidInput => {
386                // TODO: add testnet/mainnet ports, like we do with the listener address
387                panic!(
388                    "Invalid peer IP address in Zebra config: addresses must have ports:\n\
389                     resolving {host:?} returned {e:?}"
390                );
391            }
392            Ok(Err(e)) => {
393                tracing::info!(?host, ?e, "DNS error resolving peer IP addresses");
394                Err(e.into())
395            }
396            Err(e) => {
397                tracing::info!(?host, ?e, "DNS timeout resolving peer IP addresses");
398                Err(e.into())
399            }
400        }
401    }
402
403    /// Returns the addresses in the peer list cache file, if available.
404    pub async fn load_peer_cache(&self) -> io::Result<HashSet<PeerSocketAddr>> {
405        let Some(peer_cache_file) = self.cache_dir.peer_cache_file_path(&self.network) else {
406            return Ok(HashSet::new());
407        };
408
409        let peer_list = match fs::read_to_string(&peer_cache_file).await {
410            Ok(peer_list) => peer_list,
411            Err(peer_list_error) => {
412                // We expect that the cache will be missing for new Zebra installs
413                if peer_list_error.kind() == ErrorKind::NotFound {
414                    return Ok(HashSet::new());
415                } else {
416                    info!(
417                        ?peer_list_error,
418                        "could not load cached peer list, using default seed peers"
419                    );
420                    return Err(peer_list_error);
421                }
422            }
423        };
424
425        // Skip and log addresses that don't parse, and automatically deduplicate using the HashSet.
426        // (These issues shouldn't happen unless users modify the file.)
427        let peer_list: HashSet<PeerSocketAddr> = peer_list
428            .lines()
429            .filter_map(|peer| {
430                peer.parse()
431                    .map_err(|peer_parse_error| {
432                        info!(
433                            ?peer_parse_error,
434                            "invalid peer address in cached peer list, skipping"
435                        );
436                        peer_parse_error
437                    })
438                    .ok()
439            })
440            .collect();
441
442        // This log is needed for user debugging, but it's annoying during tests.
443        #[cfg(not(test))]
444        info!(
445            cached_ip_count = ?peer_list.len(),
446            ?peer_cache_file,
447            "loaded cached peer IP addresses"
448        );
449        #[cfg(test)]
450        debug!(
451            cached_ip_count = ?peer_list.len(),
452            ?peer_cache_file,
453            "loaded cached peer IP addresses"
454        );
455
456        for ip in &peer_list {
457            // Count each initial peer, recording the cache file and loaded IP address.
458            //
459            // If an IP is returned by DNS seeders and the cache,
460            // each duplicate adds 1 to the initial peer count.
461            // (But we only make one initial connection attempt to each IP.)
462            metrics::counter!(
463                "zcash.net.peers.initial",
464                "cache" => peer_cache_file.display().to_string(),
465                "remote_ip" => ip.to_string()
466            )
467            .increment(1);
468        }
469
470        Ok(peer_list)
471    }
472
473    /// Atomically writes a new `peer_list` to the peer list cache file, if configured.
474    /// If the list is empty, keeps the previous cache file.
475    ///
476    /// Also creates the peer cache directory, if it doesn't already exist.
477    ///
478    /// Atomic writes avoid corrupting the cache if Zebra panics or crashes, or if multiple Zebra
479    /// instances try to read and write the same cache file.
480    pub async fn update_peer_cache(&self, peer_list: HashSet<PeerSocketAddr>) -> io::Result<()> {
481        let Some(peer_cache_file) = self.cache_dir.peer_cache_file_path(&self.network) else {
482            return Ok(());
483        };
484
485        if peer_list.is_empty() {
486            info!(
487                ?peer_cache_file,
488                "cacheable peer list was empty, keeping previous cache"
489            );
490            return Ok(());
491        }
492
493        // Turn IP addresses into strings
494        let mut peer_list: Vec<String> = peer_list
495            .iter()
496            .take(MAX_PEER_DISK_CACHE_SIZE)
497            .map(|redacted_peer| redacted_peer.remove_socket_addr_privacy().to_string())
498            .collect();
499        // # Privacy
500        //
501        // Sort to destroy any peer order, which could leak peer connection times.
502        // (Currently the HashSet argument does this as well.)
503        peer_list.sort();
504        // Make a newline-separated list
505        let peer_data = peer_list.join("\n");
506
507        // Write the peer cache file atomically so the cache is not corrupted if Zebra shuts down
508        // or crashes.
509        let span = Span::current();
510        let write_result = tokio::task::spawn_blocking(move || {
511            span.in_scope(move || atomic_write(peer_cache_file, peer_data.as_bytes()))
512        })
513        .await
514        .expect("could not write the peer cache file")?;
515
516        match write_result {
517            Ok(peer_cache_file) => {
518                info!(
519                    cached_ip_count = ?peer_list.len(),
520                    ?peer_cache_file,
521                    "updated cached peer IP addresses"
522                );
523
524                for ip in &peer_list {
525                    metrics::counter!(
526                        "zcash.net.peers.cache",
527                        "cache" => peer_cache_file.display().to_string(),
528                        "remote_ip" => ip.to_string()
529                    )
530                    .increment(1);
531                }
532
533                Ok(())
534            }
535            Err(error) => Err(error.error),
536        }
537    }
538}
539
540impl Default for Config {
541    fn default() -> Config {
542        let mainnet_peers = [
543            "dnsseed.z.cash:8233",
544            "dnsseed.str4d.xyz:8233",
545            "mainnet.seeder.zfnd.org:8233",
546            "mainnet.is.yolo.money:8233",
547        ]
548        .iter()
549        .map(|&s| String::from(s))
550        .collect();
551
552        let testnet_peers = [
553            "dnsseed.testnet.z.cash:18233",
554            "testnet.seeder.zfnd.org:18233",
555            "testnet.is.yolo.money:18233",
556        ]
557        .iter()
558        .map(|&s| String::from(s))
559        .collect();
560
561        Config {
562            listen_addr: "0.0.0.0:8233"
563                .parse()
564                .expect("Hardcoded address should be parseable"),
565            external_addr: None,
566            network: Network::Mainnet,
567            initial_mainnet_peers: mainnet_peers,
568            initial_testnet_peers: testnet_peers,
569            cache_dir: CacheDir::default(),
570            crawl_new_peer_interval: DEFAULT_CRAWL_NEW_PEER_INTERVAL,
571
572            // # Security
573            //
574            // The default peerset target size should be large enough to ensure
575            // nodes have a reliable set of peers.
576            //
577            // But Zebra should only make a small number of initial outbound connections,
578            // so that idle peers don't use too many connection slots.
579            peerset_initial_target_size: DEFAULT_PEERSET_INITIAL_TARGET_SIZE,
580            max_connections_per_ip: DEFAULT_MAX_CONNS_PER_IP,
581        }
582    }
583}
584
585#[derive(Serialize, Deserialize)]
586#[serde(deny_unknown_fields)]
587struct DTestnetParameters {
588    network_name: Option<String>,
589    network_magic: Option<[u8; 4]>,
590    slow_start_interval: Option<u32>,
591    target_difficulty_limit: Option<String>,
592    disable_pow: Option<bool>,
593    genesis_hash: Option<String>,
594    activation_heights: Option<ConfiguredActivationHeights>,
595    pre_nu6_funding_streams: Option<ConfiguredFundingStreams>,
596    post_nu6_funding_streams: Option<ConfiguredFundingStreams>,
597    pre_blossom_halving_interval: Option<u32>,
598}
599
600#[derive(Serialize, Deserialize)]
601#[serde(deny_unknown_fields, default)]
602struct DConfig {
603    listen_addr: String,
604    external_addr: Option<String>,
605    network: NetworkKind,
606    testnet_parameters: Option<DTestnetParameters>,
607    initial_mainnet_peers: IndexSet<String>,
608    initial_testnet_peers: IndexSet<String>,
609    cache_dir: CacheDir,
610    peerset_initial_target_size: usize,
611    #[serde(alias = "new_peer_interval", with = "humantime_serde")]
612    crawl_new_peer_interval: Duration,
613    max_connections_per_ip: Option<usize>,
614}
615
616impl Default for DConfig {
617    fn default() -> Self {
618        let config = Config::default();
619        Self {
620            listen_addr: "0.0.0.0".to_string(),
621            external_addr: None,
622            network: Default::default(),
623            testnet_parameters: None,
624            initial_mainnet_peers: config.initial_mainnet_peers,
625            initial_testnet_peers: config.initial_testnet_peers,
626            cache_dir: config.cache_dir,
627            peerset_initial_target_size: config.peerset_initial_target_size,
628            crawl_new_peer_interval: config.crawl_new_peer_interval,
629            max_connections_per_ip: Some(config.max_connections_per_ip),
630        }
631    }
632}
633
634impl From<Arc<testnet::Parameters>> for DTestnetParameters {
635    fn from(params: Arc<testnet::Parameters>) -> Self {
636        Self {
637            network_name: Some(params.network_name().to_string()),
638            network_magic: Some(params.network_magic().0),
639            slow_start_interval: Some(params.slow_start_interval().0),
640            target_difficulty_limit: Some(params.target_difficulty_limit().to_string()),
641            disable_pow: Some(params.disable_pow()),
642            genesis_hash: Some(params.genesis_hash().to_string()),
643            activation_heights: Some(params.activation_heights().into()),
644            pre_nu6_funding_streams: Some(params.pre_nu6_funding_streams().into()),
645            post_nu6_funding_streams: Some(params.post_nu6_funding_streams().into()),
646            pre_blossom_halving_interval: Some(
647                params
648                    .pre_blossom_halving_interval()
649                    .try_into()
650                    .expect("should convert"),
651            ),
652        }
653    }
654}
655
656impl From<Config> for DConfig {
657    fn from(
658        Config {
659            listen_addr,
660            external_addr,
661            network,
662            initial_mainnet_peers,
663            initial_testnet_peers,
664            cache_dir,
665            peerset_initial_target_size,
666            crawl_new_peer_interval,
667            max_connections_per_ip,
668        }: Config,
669    ) -> Self {
670        let testnet_parameters = network
671            .parameters()
672            .filter(|params| !params.is_default_testnet() && !params.is_regtest())
673            .map(Into::into);
674
675        DConfig {
676            listen_addr: listen_addr.to_string(),
677            external_addr: external_addr.map(|addr| addr.to_string()),
678            network: network.into(),
679            testnet_parameters,
680            initial_mainnet_peers,
681            initial_testnet_peers,
682            cache_dir,
683            peerset_initial_target_size,
684            crawl_new_peer_interval,
685            max_connections_per_ip: Some(max_connections_per_ip),
686        }
687    }
688}
689
690impl<'de> Deserialize<'de> for Config {
691    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
692    where
693        D: Deserializer<'de>,
694    {
695        let DConfig {
696            listen_addr,
697            external_addr,
698            network: network_kind,
699            testnet_parameters,
700            initial_mainnet_peers,
701            initial_testnet_peers,
702            cache_dir,
703            peerset_initial_target_size,
704            crawl_new_peer_interval,
705            max_connections_per_ip,
706        } = DConfig::deserialize(deserializer)?;
707
708        /// Accepts an [`IndexSet`] of initial peers,
709        ///
710        /// Returns true if any of them are the default Testnet or Mainnet initial peers.
711        fn contains_default_initial_peers(initial_peers: &IndexSet<String>) -> bool {
712            let Config {
713                initial_mainnet_peers: mut default_initial_peers,
714                initial_testnet_peers: default_initial_testnet_peers,
715                ..
716            } = Config::default();
717            default_initial_peers.extend(default_initial_testnet_peers);
718
719            initial_peers
720                .intersection(&default_initial_peers)
721                .next()
722                .is_some()
723        }
724
725        let network = match (network_kind, testnet_parameters) {
726            (NetworkKind::Mainnet, _) => Network::Mainnet,
727            (NetworkKind::Testnet, None) => Network::new_default_testnet(),
728            (NetworkKind::Regtest, testnet_parameters) => {
729                let configured_activation_heights = testnet_parameters
730                    .and_then(|params| params.activation_heights)
731                    .unwrap_or_default();
732
733                Network::new_regtest(configured_activation_heights)
734            }
735            (
736                NetworkKind::Testnet,
737                Some(DTestnetParameters {
738                    network_name,
739                    network_magic,
740                    slow_start_interval,
741                    target_difficulty_limit,
742                    disable_pow,
743                    genesis_hash,
744                    activation_heights,
745                    pre_nu6_funding_streams,
746                    post_nu6_funding_streams,
747                    pre_blossom_halving_interval,
748                }),
749            ) => {
750                let mut params_builder = testnet::Parameters::build();
751
752                if let Some(network_name) = network_name.clone() {
753                    params_builder = params_builder.with_network_name(network_name)
754                }
755
756                if let Some(network_magic) = network_magic {
757                    params_builder = params_builder.with_network_magic(Magic(network_magic));
758                }
759
760                if let Some(genesis_hash) = genesis_hash {
761                    params_builder = params_builder.with_genesis_hash(genesis_hash);
762                }
763
764                if let Some(slow_start_interval) = slow_start_interval {
765                    params_builder = params_builder.with_slow_start_interval(
766                        slow_start_interval.try_into().map_err(de::Error::custom)?,
767                    );
768                }
769
770                if let Some(target_difficulty_limit) = target_difficulty_limit.clone() {
771                    params_builder = params_builder.with_target_difficulty_limit(
772                        target_difficulty_limit
773                            .parse::<U256>()
774                            .map_err(de::Error::custom)?,
775                    );
776                }
777
778                if let Some(disable_pow) = disable_pow {
779                    params_builder = params_builder.with_disable_pow(disable_pow);
780                }
781
782                // Retain default Testnet activation heights unless there's an empty [testnet_parameters.activation_heights] section.
783                if let Some(activation_heights) = activation_heights.clone() {
784                    params_builder = params_builder.with_activation_heights(activation_heights)
785                }
786
787                if let Some(halving_interval) = pre_blossom_halving_interval {
788                    params_builder = params_builder.with_halving_interval(halving_interval.into())
789                }
790
791                // Set configured funding streams after setting any parameters that affect the funding stream address period.
792
793                if let Some(funding_streams) = pre_nu6_funding_streams {
794                    params_builder = params_builder.with_pre_nu6_funding_streams(funding_streams);
795                }
796
797                if let Some(funding_streams) = post_nu6_funding_streams {
798                    params_builder = params_builder.with_post_nu6_funding_streams(funding_streams);
799                }
800
801                // Return an error if the initial testnet peers includes any of the default initial Mainnet or Testnet
802                // peers and the configured network parameters are incompatible with the default public Testnet.
803                if !params_builder.is_compatible_with_default_parameters()
804                    && contains_default_initial_peers(&initial_testnet_peers)
805                {
806                    return Err(de::Error::custom(
807                        "cannot use default initials peers with incompatible testnet",
808                    ));
809                };
810
811                // Return the default Testnet if no network name was configured and all parameters match the default Testnet
812                if network_name.is_none() && params_builder == testnet::Parameters::build() {
813                    Network::new_default_testnet()
814                } else {
815                    params_builder.to_network()
816                }
817            }
818        };
819
820        let listen_addr = match listen_addr.parse::<SocketAddr>().or_else(|_| format!("{listen_addr}:{}", network.default_port()).parse()) {
821            Ok(socket) => Ok(socket),
822            Err(_) => match listen_addr.parse::<IpAddr>() {
823                Ok(ip) => Ok(SocketAddr::new(ip, network.default_port())),
824                Err(err) => Err(de::Error::custom(format!(
825                    "{err}; Hint: addresses can be a IPv4, IPv6 (with brackets), or a DNS name, the port is optional"
826                ))),
827            },
828        }?;
829
830        let external_socket_addr = if let Some(address) = &external_addr {
831            match address.parse::<SocketAddr>().or_else(|_| format!("{address}:{}", network.default_port()).parse()) {
832                Ok(socket) => Ok(Some(socket)),
833                Err(_) => match address.parse::<IpAddr>() {
834                    Ok(ip) => Ok(Some(SocketAddr::new(ip, network.default_port()))),
835                    Err(err) => Err(de::Error::custom(format!(
836                        "{err}; Hint: addresses can be a IPv4, IPv6 (with brackets), or a DNS name, the port is optional"
837                    ))),
838                },
839            }?
840        } else {
841            None
842        };
843
844        let [max_connections_per_ip, peerset_initial_target_size] = [
845            ("max_connections_per_ip", max_connections_per_ip, DEFAULT_MAX_CONNS_PER_IP), 
846            // If we want Zebra to operate with no network,
847            // we should implement a `zebrad` command that doesn't use `zebra-network`.
848            ("peerset_initial_target_size", Some(peerset_initial_target_size), DEFAULT_PEERSET_INITIAL_TARGET_SIZE)
849        ].map(|(field_name, non_zero_config_field, default_config_value)| {
850            if non_zero_config_field == Some(0) {
851                warn!(
852                    ?field_name,
853                    ?non_zero_config_field,
854                    "{field_name} should be greater than 0, using default value of {default_config_value} instead"
855                );
856            }
857
858            non_zero_config_field.filter(|config_value| config_value > &0).unwrap_or(default_config_value)
859        });
860
861        Ok(Config {
862            listen_addr: canonical_socket_addr(listen_addr),
863            external_addr: external_socket_addr,
864            network,
865            initial_mainnet_peers,
866            initial_testnet_peers,
867            cache_dir,
868            peerset_initial_target_size,
869            crawl_new_peer_interval,
870            max_connections_per_ip,
871        })
872    }
873}