1use 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::{
20 self, ConfiguredActivationHeights, ConfiguredCheckpoints, ConfiguredFundingStreams,
21 ConfiguredLockboxDisbursement, RegtestParameters,
22 },
23 Magic, Network, NetworkKind,
24 },
25 work::difficulty::U256,
26};
27
28use crate::{
29 constants::{
30 DEFAULT_CRAWL_NEW_PEER_INTERVAL, DEFAULT_MAX_CONNS_PER_IP,
31 DEFAULT_PEERSET_INITIAL_TARGET_SIZE, DNS_LOOKUP_TIMEOUT, INBOUND_PEER_LIMIT_MULTIPLIER,
32 MAX_PEER_DISK_CACHE_SIZE, OUTBOUND_PEER_LIMIT_MULTIPLIER,
33 },
34 protocol::external::{canonical_peer_addr, canonical_socket_addr},
35 BoxError, PeerSocketAddr,
36};
37
38mod cache_dir;
39
40#[cfg(test)]
41mod tests;
42
43pub use cache_dir::CacheDir;
44
45const MAX_SINGLE_SEED_PEER_DNS_RETRIES: usize = 0;
55
56#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
58#[serde(deny_unknown_fields, default, into = "DConfig")]
59pub struct Config {
60 pub listen_addr: SocketAddr,
83
84 pub external_addr: Option<SocketAddr>,
91
92 pub network: Network,
94
95 pub initial_mainnet_peers: IndexSet<String>,
98
99 pub initial_testnet_peers: IndexSet<String>,
102
103 pub cache_dir: CacheDir,
156
157 pub peerset_initial_target_size: usize,
166
167 #[serde(with = "humantime_serde")]
175 pub crawl_new_peer_interval: Duration,
176
177 pub max_connections_per_ip: usize,
196}
197
198impl Config {
199 pub fn peerset_outbound_connection_limit(&self) -> usize {
223 self.peerset_initial_target_size * OUTBOUND_PEER_LIMIT_MULTIPLIER
224 }
225
226 pub fn peerset_inbound_connection_limit(&self) -> usize {
234 self.peerset_initial_target_size * INBOUND_PEER_LIMIT_MULTIPLIER
235 }
236
237 pub fn peerset_total_connection_limit(&self) -> usize {
240 self.peerset_outbound_connection_limit() + self.peerset_inbound_connection_limit()
241 }
242
243 pub fn initial_peer_hostnames(&self) -> IndexSet<String> {
245 match &self.network {
246 Network::Mainnet => self.initial_mainnet_peers.clone(),
247 Network::Testnet(_params) => self.initial_testnet_peers.clone(),
248 }
249 }
250
251 pub async fn initial_peers(&self) -> HashSet<PeerSocketAddr> {
258 let dns_peers =
260 Config::resolve_peers(&self.initial_peer_hostnames().iter().cloned().collect()).await;
261
262 if self.network.is_regtest() {
263 dns_peers
265 .into_iter()
266 .filter(PeerSocketAddr::is_localhost)
267 .collect()
268 } else {
269 let disk_peers = self.load_peer_cache().await.unwrap_or_default();
271
272 dns_peers.into_iter().chain(disk_peers).collect()
273 }
274 }
275
276 async fn resolve_peers(peers: &HashSet<String>) -> HashSet<PeerSocketAddr> {
282 use futures::stream::StreamExt;
283
284 if peers.is_empty() {
285 warn!(
286 "no initial peers in the network config. \
287 Hint: you must configure at least one peer IP or DNS seeder to run Zebra, \
288 give it some previously cached peer IP addresses on disk, \
289 or make sure Zebra's listener port gets inbound connections."
290 );
291 return HashSet::new();
292 }
293
294 loop {
295 let peer_addresses = peers
300 .iter()
301 .map(|s| Config::resolve_host(s, MAX_SINGLE_SEED_PEER_DNS_RETRIES))
302 .collect::<futures::stream::FuturesUnordered<_>>()
303 .concat()
304 .await;
305
306 if peer_addresses.is_empty() {
307 tracing::info!(
308 ?peers,
309 ?peer_addresses,
310 "empty peer list after DNS resolution, retrying after {} seconds",
311 DNS_LOOKUP_TIMEOUT.as_secs(),
312 );
313 tokio::time::sleep(DNS_LOOKUP_TIMEOUT).await;
314 } else {
315 return peer_addresses;
316 }
317 }
318 }
319
320 async fn resolve_host(host: &str, max_retries: usize) -> HashSet<PeerSocketAddr> {
329 for retries in 0..=max_retries {
330 if let Ok(addresses) = Config::resolve_host_once(host).await {
331 return addresses;
332 }
333
334 if retries < max_retries {
335 tracing::info!(
336 ?host,
337 previous_attempts = ?(retries + 1),
338 "Waiting {DNS_LOOKUP_TIMEOUT:?} to retry seed peer DNS resolution",
339 );
340 tokio::time::sleep(DNS_LOOKUP_TIMEOUT).await;
341 } else {
342 tracing::info!(
343 ?host,
344 attempts = ?(retries + 1),
345 "Seed peer DNS resolution failed, checking for addresses from other seed peers",
346 );
347 }
348 }
349
350 HashSet::new()
351 }
352
353 async fn resolve_host_once(host: &str) -> Result<HashSet<PeerSocketAddr>, BoxError> {
362 let fut = tokio::net::lookup_host(host);
363 let fut = tokio::time::timeout(DNS_LOOKUP_TIMEOUT, fut);
364
365 match fut.await {
366 Ok(Ok(ip_addrs)) => {
367 let ip_addrs: Vec<PeerSocketAddr> = ip_addrs.map(canonical_peer_addr).collect();
368
369 #[cfg(not(test))]
371 info!(seed = ?host, remote_ip_count = ?ip_addrs.len(), "resolved seed peer IP addresses");
372 #[cfg(test)]
373 debug!(seed = ?host, remote_ip_count = ?ip_addrs.len(), "resolved seed peer IP addresses");
374
375 for ip in &ip_addrs {
376 metrics::counter!(
382 "zcash.net.peers.initial",
383 "seed" => host.to_string(),
384 "remote_ip" => ip.to_string()
385 )
386 .increment(1);
387 }
388
389 Ok(ip_addrs.into_iter().collect())
390 }
391 Ok(Err(e)) if e.kind() == ErrorKind::InvalidInput => {
392 panic!(
394 "Invalid peer IP address in Zebra config: addresses must have ports:\n\
395 resolving {host:?} returned {e:?}"
396 );
397 }
398 Ok(Err(e)) => {
399 tracing::info!(?host, ?e, "DNS error resolving peer IP addresses");
400 Err(e.into())
401 }
402 Err(e) => {
403 tracing::info!(?host, ?e, "DNS timeout resolving peer IP addresses");
404 Err(e.into())
405 }
406 }
407 }
408
409 pub async fn load_peer_cache(&self) -> io::Result<HashSet<PeerSocketAddr>> {
411 let Some(peer_cache_file) = self.cache_dir.peer_cache_file_path(&self.network) else {
412 return Ok(HashSet::new());
413 };
414
415 let peer_list = match fs::read_to_string(&peer_cache_file).await {
416 Ok(peer_list) => peer_list,
417 Err(peer_list_error) => {
418 if peer_list_error.kind() == ErrorKind::NotFound {
420 return Ok(HashSet::new());
421 } else {
422 info!(
423 ?peer_list_error,
424 "could not load cached peer list, using default seed peers"
425 );
426 return Err(peer_list_error);
427 }
428 }
429 };
430
431 let peer_list: HashSet<PeerSocketAddr> = peer_list
434 .lines()
435 .filter_map(|peer| {
436 peer.parse()
437 .map_err(|peer_parse_error| {
438 info!(
439 ?peer_parse_error,
440 "invalid peer address in cached peer list, skipping"
441 );
442 peer_parse_error
443 })
444 .ok()
445 })
446 .collect();
447
448 #[cfg(not(test))]
450 info!(
451 cached_ip_count = ?peer_list.len(),
452 ?peer_cache_file,
453 "loaded cached peer IP addresses"
454 );
455 #[cfg(test)]
456 debug!(
457 cached_ip_count = ?peer_list.len(),
458 ?peer_cache_file,
459 "loaded cached peer IP addresses"
460 );
461
462 for ip in &peer_list {
463 metrics::counter!(
469 "zcash.net.peers.initial",
470 "cache" => peer_cache_file.display().to_string(),
471 "remote_ip" => ip.to_string()
472 )
473 .increment(1);
474 }
475
476 Ok(peer_list)
477 }
478
479 pub async fn update_peer_cache(&self, peer_list: HashSet<PeerSocketAddr>) -> io::Result<()> {
487 let Some(peer_cache_file) = self.cache_dir.peer_cache_file_path(&self.network) else {
488 return Ok(());
489 };
490
491 if peer_list.is_empty() {
492 info!(
493 ?peer_cache_file,
494 "cacheable peer list was empty, keeping previous cache"
495 );
496 return Ok(());
497 }
498
499 let mut peer_list: Vec<String> = peer_list
501 .iter()
502 .take(MAX_PEER_DISK_CACHE_SIZE)
503 .map(|redacted_peer| redacted_peer.remove_socket_addr_privacy().to_string())
504 .collect();
505 peer_list.sort();
510 let peer_data = peer_list.join("\n");
512
513 let span = Span::current();
516 let write_result = tokio::task::spawn_blocking(move || {
517 span.in_scope(move || atomic_write(peer_cache_file, peer_data.as_bytes()))
518 })
519 .await
520 .expect("could not write the peer cache file")?;
521
522 match write_result {
523 Ok(peer_cache_file) => {
524 info!(
525 cached_ip_count = ?peer_list.len(),
526 ?peer_cache_file,
527 "updated cached peer IP addresses"
528 );
529
530 for ip in &peer_list {
531 metrics::counter!(
532 "zcash.net.peers.cache",
533 "cache" => peer_cache_file.display().to_string(),
534 "remote_ip" => ip.to_string()
535 )
536 .increment(1);
537 }
538
539 Ok(())
540 }
541 Err(error) => Err(error.error),
542 }
543 }
544}
545
546impl Default for Config {
547 fn default() -> Config {
548 let mainnet_peers = [
549 "dnsseed.str4d.xyz:8233",
550 "dnsseed.z.cash:8233",
551 "mainnet.seeder.shieldedinfra.net:8233",
552 "mainnet.seeder.zfnd.org:8233",
553 ]
554 .iter()
555 .map(|&s| String::from(s))
556 .collect();
557
558 let testnet_peers = [
559 "dnsseed.testnet.z.cash:18233",
560 "testnet.seeder.zfnd.org:18233",
561 ]
562 .iter()
563 .map(|&s| String::from(s))
564 .collect();
565
566 Config {
567 listen_addr: "[::]:8233"
568 .parse()
569 .expect("Hardcoded address should be parseable"),
570 external_addr: None,
571 network: Network::Mainnet,
572 initial_mainnet_peers: mainnet_peers,
573 initial_testnet_peers: testnet_peers,
574 cache_dir: CacheDir::default(),
575 crawl_new_peer_interval: DEFAULT_CRAWL_NEW_PEER_INTERVAL,
576
577 peerset_initial_target_size: DEFAULT_PEERSET_INITIAL_TARGET_SIZE,
585 max_connections_per_ip: DEFAULT_MAX_CONNS_PER_IP,
586 }
587 }
588}
589
590#[derive(Serialize, Deserialize)]
591#[serde(deny_unknown_fields)]
592struct DTestnetParameters {
593 network_name: Option<String>,
594 network_magic: Option<[u8; 4]>,
595 slow_start_interval: Option<u32>,
596 target_difficulty_limit: Option<String>,
597 disable_pow: Option<bool>,
598 genesis_hash: Option<String>,
599 activation_heights: Option<ConfiguredActivationHeights>,
600 pre_nu6_funding_streams: Option<ConfiguredFundingStreams>,
601 post_nu6_funding_streams: Option<ConfiguredFundingStreams>,
602 funding_streams: Option<Vec<ConfiguredFundingStreams>>,
603 pre_blossom_halving_interval: Option<u32>,
604 lockbox_disbursements: Option<Vec<ConfiguredLockboxDisbursement>>,
605 #[serde(default)]
606 checkpoints: ConfiguredCheckpoints,
607 extend_funding_stream_addresses_as_required: Option<bool>,
610}
611
612#[derive(Serialize, Deserialize)]
614#[serde(untagged)]
615enum DNetwork {
616 DefaultForKind(NetworkKind),
617 ConfiguredRegtest {
618 params: Box<DTestnetParameters>,
619
620 #[serde(default, skip_serializing)]
621 regtest: Option<bool>,
622 },
623 ConfiguredTestnet(Box<DTestnetParameters>),
624}
625
626impl Default for DNetwork {
627 fn default() -> Self {
628 DNetwork::DefaultForKind(NetworkKind::Mainnet)
629 }
630}
631
632#[derive(Serialize, Deserialize)]
633#[serde(deny_unknown_fields, default)]
634struct DConfig {
635 listen_addr: String,
636 external_addr: Option<String>,
637 network: DNetwork,
638
639 #[serde(default, skip_serializing_if = "Option::is_none")]
641 testnet_parameters: Option<DTestnetParameters>,
642
643 initial_mainnet_peers: IndexSet<String>,
644 initial_testnet_peers: IndexSet<String>,
645 cache_dir: CacheDir,
646 peerset_initial_target_size: usize,
647 #[serde(alias = "new_peer_interval", with = "humantime_serde")]
648 crawl_new_peer_interval: Duration,
649 max_connections_per_ip: Option<usize>,
650}
651
652impl Default for DConfig {
653 fn default() -> Self {
654 let config = Config::default();
655 Self {
656 listen_addr: "[::]".to_string(),
657 external_addr: None,
658 network: Default::default(),
659 testnet_parameters: None,
660 initial_mainnet_peers: config.initial_mainnet_peers,
661 initial_testnet_peers: config.initial_testnet_peers,
662 cache_dir: config.cache_dir,
663 peerset_initial_target_size: config.peerset_initial_target_size,
664 crawl_new_peer_interval: config.crawl_new_peer_interval,
665 max_connections_per_ip: Some(config.max_connections_per_ip),
666 }
667 }
668}
669
670impl From<Arc<testnet::Parameters>> for DTestnetParameters {
671 fn from(params: Arc<testnet::Parameters>) -> Self {
672 Self {
673 network_name: Some(params.network_name().to_string()),
674 network_magic: Some(params.network_magic().0),
675 slow_start_interval: Some(params.slow_start_interval().0),
676 target_difficulty_limit: Some(params.target_difficulty_limit().to_string()),
677 disable_pow: Some(params.disable_pow()),
678 genesis_hash: Some(params.genesis_hash().to_string()),
679 activation_heights: Some(params.activation_heights().into()),
680 pre_nu6_funding_streams: None,
681 post_nu6_funding_streams: None,
682 funding_streams: Some(params.funding_streams().iter().map(Into::into).collect()),
683 pre_blossom_halving_interval: Some(
684 params
685 .pre_blossom_halving_interval()
686 .try_into()
687 .expect("should convert"),
688 ),
689 lockbox_disbursements: Some(
690 params
691 .lockbox_disbursements()
692 .into_iter()
693 .map(Into::into)
694 .collect(),
695 ),
696 checkpoints: if params.checkpoints() == testnet::Parameters::default().checkpoints() {
697 ConfiguredCheckpoints::Default(true)
698 } else {
699 params.checkpoints().into()
700 },
701 extend_funding_stream_addresses_as_required: None,
702 }
703 }
704}
705
706impl From<Config> for DConfig {
707 fn from(
708 Config {
709 listen_addr,
710 external_addr,
711 network,
712 initial_mainnet_peers,
713 initial_testnet_peers,
714 cache_dir,
715 peerset_initial_target_size,
716 crawl_new_peer_interval,
717 max_connections_per_ip,
718 }: Config,
719 ) -> Self {
720 let dnetwork = match network.kind() {
721 NetworkKind::Testnet => match network
722 .parameters()
723 .filter(|params| !params.is_default_testnet())
724 .map(Into::into)
725 {
726 Some(params) => DNetwork::ConfiguredTestnet(Box::new(params)),
727 None => DNetwork::DefaultForKind(NetworkKind::Testnet),
728 },
729
730 NetworkKind::Regtest => match network.parameters().map(Into::into) {
731 Some(params) => DNetwork::ConfiguredRegtest {
732 params: Box::new(params),
733 regtest: Some(true),
734 },
735 None => DNetwork::DefaultForKind(NetworkKind::Regtest),
736 },
737
738 other_kind => DNetwork::DefaultForKind(other_kind),
739 };
740
741 DConfig {
742 listen_addr: listen_addr.to_string(),
743 external_addr: external_addr.map(|addr| addr.to_string()),
744 network: dnetwork,
745 testnet_parameters: None,
746 initial_mainnet_peers,
747 initial_testnet_peers,
748 cache_dir,
749 peerset_initial_target_size,
750 crawl_new_peer_interval,
751 max_connections_per_ip: Some(max_connections_per_ip),
752 }
753 }
754}
755
756impl<'de> Deserialize<'de> for Config {
757 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
758 where
759 D: Deserializer<'de>,
760 {
761 let DConfig {
762 listen_addr,
763 external_addr,
764 network: dnetwork,
765 testnet_parameters,
766 initial_mainnet_peers,
767 initial_testnet_peers,
768 cache_dir,
769 peerset_initial_target_size,
770 crawl_new_peer_interval,
771 max_connections_per_ip,
772 } = DConfig::deserialize(deserializer)?;
773
774 let network = match (dnetwork, testnet_parameters) {
775 (DNetwork::ConfiguredTestnet(params), _) => {
776 build_configured_testnet::<D>(*params, &initial_testnet_peers)?
777 }
778 (DNetwork::ConfiguredRegtest { params, .. }, _) => {
779 Network::new_regtest(build_regtest_params(*params))
780 }
781 (DNetwork::DefaultForKind(NetworkKind::Mainnet), _) => Network::Mainnet,
782 (DNetwork::DefaultForKind(NetworkKind::Testnet), Some(params)) => {
783 build_configured_testnet::<D>(params, &initial_testnet_peers)?
784 }
785 (DNetwork::DefaultForKind(NetworkKind::Testnet), None) => {
786 Network::new_default_testnet()
787 }
788 (DNetwork::DefaultForKind(NetworkKind::Regtest), Some(params)) => {
789 Network::new_regtest(build_regtest_params(params))
790 }
791 (DNetwork::DefaultForKind(NetworkKind::Regtest), None) => {
792 Network::new_regtest(Default::default())
793 }
794 };
795
796 let listen_addr = match listen_addr.parse::<SocketAddr>().or_else(|_| format!("{listen_addr}:{}", network.default_port()).parse()) {
797 Ok(socket) => Ok(socket),
798 Err(_) => match listen_addr.parse::<IpAddr>() {
799 Ok(ip) => Ok(SocketAddr::new(ip, network.default_port())),
800 Err(err) => Err(de::Error::custom(format!(
801 "{err}; Hint: addresses can be a IPv4, IPv6 (with brackets), or a DNS name, the port is optional"
802 ))),
803 },
804 }?;
805
806 let external_socket_addr = if let Some(address) = &external_addr {
807 match address.parse::<SocketAddr>().or_else(|_| format!("{address}:{}", network.default_port()).parse()) {
808 Ok(socket) => Ok(Some(socket)),
809 Err(_) => match address.parse::<IpAddr>() {
810 Ok(ip) => Ok(Some(SocketAddr::new(ip, network.default_port()))),
811 Err(err) => Err(de::Error::custom(format!(
812 "{err}; Hint: addresses can be a IPv4, IPv6 (with brackets), or a DNS name, the port is optional"
813 ))),
814 },
815 }?
816 } else {
817 None
818 };
819
820 let [max_connections_per_ip, peerset_initial_target_size] = [
821 ("max_connections_per_ip", max_connections_per_ip, DEFAULT_MAX_CONNS_PER_IP),
822 ("peerset_initial_target_size", Some(peerset_initial_target_size), DEFAULT_PEERSET_INITIAL_TARGET_SIZE)
825 ].map(|(field_name, non_zero_config_field, default_config_value)| {
826 if non_zero_config_field == Some(0) {
827 warn!(
828 ?field_name,
829 ?non_zero_config_field,
830 "{field_name} should be greater than 0, using default value of {default_config_value} instead"
831 );
832 }
833
834 non_zero_config_field.filter(|config_value| config_value > &0).unwrap_or(default_config_value)
835 });
836
837 Ok(Config {
838 listen_addr: canonical_socket_addr(listen_addr),
839 external_addr: external_socket_addr,
840 network,
841 initial_mainnet_peers,
842 initial_testnet_peers,
843 cache_dir,
844 peerset_initial_target_size,
845 crawl_new_peer_interval,
846 max_connections_per_ip,
847 })
848 }
849}
850
851fn contains_default_initial_peers(initial_peers: &IndexSet<String>) -> bool {
855 let Config {
856 initial_mainnet_peers: mut default_initial_peers,
857 initial_testnet_peers: default_initial_testnet_peers,
858 ..
859 } = Config::default();
860 default_initial_peers.extend(default_initial_testnet_peers);
861
862 initial_peers
863 .intersection(&default_initial_peers)
864 .next()
865 .is_some()
866}
867
868fn build_configured_testnet<'de, D>(
869 params: DTestnetParameters,
870 initial_testnet_peers: &IndexSet<String>,
871) -> Result<Network, D::Error>
872where
873 D: Deserializer<'de>,
874{
875 let DTestnetParameters {
876 network_name,
877 network_magic,
878 slow_start_interval,
879 target_difficulty_limit,
880 disable_pow,
881 genesis_hash,
882 activation_heights,
883 pre_nu6_funding_streams,
884 post_nu6_funding_streams,
885 funding_streams,
886 pre_blossom_halving_interval,
887 lockbox_disbursements,
888 checkpoints,
889 extend_funding_stream_addresses_as_required,
890 } = params;
891
892 let mut params_builder = testnet::Parameters::build();
893
894 if let Some(network_name) = network_name.clone() {
895 params_builder = params_builder
896 .with_network_name(network_name)
897 .map_err(de::Error::custom)?
898 }
899
900 if let Some(network_magic) = network_magic {
901 params_builder = params_builder
902 .with_network_magic(Magic(network_magic))
903 .map_err(de::Error::custom)?;
904 }
905
906 if let Some(genesis_hash) = genesis_hash {
907 params_builder = params_builder
908 .with_genesis_hash(genesis_hash)
909 .map_err(de::Error::custom)?;
910 }
911
912 if let Some(slow_start_interval) = slow_start_interval {
913 params_builder = params_builder
914 .with_slow_start_interval(slow_start_interval.try_into().map_err(de::Error::custom)?);
915 }
916
917 if let Some(target_difficulty_limit) = target_difficulty_limit.clone() {
918 params_builder = params_builder
919 .with_target_difficulty_limit(
920 target_difficulty_limit
921 .parse::<U256>()
922 .map_err(de::Error::custom)?,
923 )
924 .map_err(de::Error::custom)?;
925 }
926
927 if let Some(disable_pow) = disable_pow {
928 params_builder = params_builder.with_disable_pow(disable_pow);
929 }
930
931 if let Some(activation_heights) = activation_heights {
933 params_builder = params_builder
934 .with_activation_heights(activation_heights)
935 .map_err(de::Error::custom)?
936 }
937
938 if let Some(halving_interval) = pre_blossom_halving_interval {
939 params_builder = params_builder
940 .with_halving_interval(halving_interval.into())
941 .map_err(de::Error::custom)?
942 }
943
944 let mut funding_streams_vec = funding_streams.unwrap_or_default();
946
947 if let Some(funding_streams) = post_nu6_funding_streams {
948 funding_streams_vec.insert(0, funding_streams);
949 }
950
951 if let Some(funding_streams) = pre_nu6_funding_streams {
952 funding_streams_vec.insert(0, funding_streams);
953 }
954
955 if !funding_streams_vec.is_empty() {
956 params_builder = params_builder.with_funding_streams(funding_streams_vec);
957 }
958
959 if let Some(lockbox_disbursements) = lockbox_disbursements {
960 params_builder = params_builder.with_lockbox_disbursements(lockbox_disbursements);
961 }
962
963 params_builder = params_builder
964 .with_checkpoints(checkpoints)
965 .map_err(de::Error::custom)?;
966
967 if let Some(true) = extend_funding_stream_addresses_as_required {
968 params_builder = params_builder.extend_funding_streams();
969 }
970
971 if !params_builder.is_compatible_with_default_parameters()
974 && contains_default_initial_peers(initial_testnet_peers)
975 {
976 return Err(de::Error::custom(
977 "cannot use default initials peers with incompatible testnet",
978 ));
979 };
980
981 if network_name.is_none() && params_builder == testnet::Parameters::build() {
983 Ok(Network::new_default_testnet())
984 } else {
985 Ok(params_builder.to_network().map_err(de::Error::custom)?)
986 }
987}
988
989fn build_regtest_params(params: DTestnetParameters) -> RegtestParameters {
990 let DTestnetParameters {
991 activation_heights,
992 pre_nu6_funding_streams,
993 post_nu6_funding_streams,
994 funding_streams,
995 lockbox_disbursements,
996 checkpoints,
997 extend_funding_stream_addresses_as_required,
998 ..
999 } = params;
1000
1001 let mut funding_streams_vec = funding_streams.unwrap_or_default();
1002
1003 if let Some(funding_streams) = post_nu6_funding_streams {
1004 funding_streams_vec.insert(0, funding_streams);
1005 }
1006
1007 if let Some(funding_streams) = pre_nu6_funding_streams {
1008 funding_streams_vec.insert(0, funding_streams);
1009 }
1010
1011 RegtestParameters {
1012 activation_heights: activation_heights.unwrap_or_default(),
1013 funding_streams: Some(funding_streams_vec),
1014 lockbox_disbursements,
1015 checkpoints: Some(checkpoints),
1016 extend_funding_stream_addresses_as_required,
1017 }
1018}