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 temporary_orchard_disabling_soft_fork_height: Option<u32>,
615}
616
617#[derive(Serialize, Deserialize)]
619#[serde(untagged)]
620enum DNetwork {
621 DefaultForKind(NetworkKind),
622 ConfiguredRegtest {
623 params: Box<DTestnetParameters>,
624
625 #[serde(default, skip_serializing)]
626 regtest: Option<bool>,
627 },
628 ConfiguredTestnet(Box<DTestnetParameters>),
629}
630
631impl Default for DNetwork {
632 fn default() -> Self {
633 DNetwork::DefaultForKind(NetworkKind::Mainnet)
634 }
635}
636
637#[derive(Serialize, Deserialize)]
638#[serde(deny_unknown_fields, default)]
639struct DConfig {
640 listen_addr: String,
641 external_addr: Option<String>,
642 network: DNetwork,
643
644 #[serde(default, skip_serializing_if = "Option::is_none")]
646 testnet_parameters: Option<DTestnetParameters>,
647
648 initial_mainnet_peers: IndexSet<String>,
649 initial_testnet_peers: IndexSet<String>,
650 cache_dir: CacheDir,
651 peerset_initial_target_size: usize,
652 #[serde(alias = "new_peer_interval", with = "humantime_serde")]
653 crawl_new_peer_interval: Duration,
654 max_connections_per_ip: Option<usize>,
655}
656
657impl Default for DConfig {
658 fn default() -> Self {
659 let config = Config::default();
660 Self {
661 listen_addr: "[::]".to_string(),
662 external_addr: None,
663 network: Default::default(),
664 testnet_parameters: None,
665 initial_mainnet_peers: config.initial_mainnet_peers,
666 initial_testnet_peers: config.initial_testnet_peers,
667 cache_dir: config.cache_dir,
668 peerset_initial_target_size: config.peerset_initial_target_size,
669 crawl_new_peer_interval: config.crawl_new_peer_interval,
670 max_connections_per_ip: Some(config.max_connections_per_ip),
671 }
672 }
673}
674
675impl From<Arc<testnet::Parameters>> for DTestnetParameters {
676 fn from(params: Arc<testnet::Parameters>) -> Self {
677 Self {
678 network_name: Some(params.network_name().to_string()),
679 network_magic: Some(params.network_magic().0),
680 slow_start_interval: Some(params.slow_start_interval().0),
681 target_difficulty_limit: Some(params.target_difficulty_limit().to_string()),
682 disable_pow: Some(params.disable_pow()),
683 genesis_hash: Some(params.genesis_hash().to_string()),
684 activation_heights: Some(params.activation_heights().into()),
685 pre_nu6_funding_streams: None,
686 post_nu6_funding_streams: None,
687 funding_streams: Some(params.funding_streams().iter().map(Into::into).collect()),
688 pre_blossom_halving_interval: Some(
689 params
690 .pre_blossom_halving_interval()
691 .try_into()
692 .expect("should convert"),
693 ),
694 lockbox_disbursements: Some(
695 params
696 .lockbox_disbursements()
697 .into_iter()
698 .map(Into::into)
699 .collect(),
700 ),
701 checkpoints: if params.checkpoints() == testnet::Parameters::default().checkpoints() {
702 ConfiguredCheckpoints::Default(true)
703 } else {
704 params.checkpoints().into()
705 },
706 extend_funding_stream_addresses_as_required: None,
707 temporary_orchard_disabling_soft_fork_height: params
708 .temporary_orchard_disabling_soft_fork_height()
709 .map(|height| height.0),
710 }
711 }
712}
713
714impl From<Config> for DConfig {
715 fn from(
716 Config {
717 listen_addr,
718 external_addr,
719 network,
720 initial_mainnet_peers,
721 initial_testnet_peers,
722 cache_dir,
723 peerset_initial_target_size,
724 crawl_new_peer_interval,
725 max_connections_per_ip,
726 }: Config,
727 ) -> Self {
728 let dnetwork = match network.kind() {
729 NetworkKind::Testnet => match network
730 .parameters()
731 .filter(|params| !params.is_default_testnet())
732 .map(Into::into)
733 {
734 Some(params) => DNetwork::ConfiguredTestnet(Box::new(params)),
735 None => DNetwork::DefaultForKind(NetworkKind::Testnet),
736 },
737
738 NetworkKind::Regtest => match network.parameters().map(Into::into) {
739 Some(params) => DNetwork::ConfiguredRegtest {
740 params: Box::new(params),
741 regtest: Some(true),
742 },
743 None => DNetwork::DefaultForKind(NetworkKind::Regtest),
744 },
745
746 other_kind => DNetwork::DefaultForKind(other_kind),
747 };
748
749 DConfig {
750 listen_addr: listen_addr.to_string(),
751 external_addr: external_addr.map(|addr| addr.to_string()),
752 network: dnetwork,
753 testnet_parameters: None,
754 initial_mainnet_peers,
755 initial_testnet_peers,
756 cache_dir,
757 peerset_initial_target_size,
758 crawl_new_peer_interval,
759 max_connections_per_ip: Some(max_connections_per_ip),
760 }
761 }
762}
763
764impl<'de> Deserialize<'de> for Config {
765 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
766 where
767 D: Deserializer<'de>,
768 {
769 let DConfig {
770 listen_addr,
771 external_addr,
772 network: dnetwork,
773 testnet_parameters,
774 initial_mainnet_peers,
775 initial_testnet_peers,
776 cache_dir,
777 peerset_initial_target_size,
778 crawl_new_peer_interval,
779 max_connections_per_ip,
780 } = DConfig::deserialize(deserializer)?;
781
782 let network = match (dnetwork, testnet_parameters) {
783 (DNetwork::ConfiguredTestnet(params), _) => {
784 build_configured_testnet::<D>(*params, &initial_testnet_peers)?
785 }
786 (DNetwork::ConfiguredRegtest { params, .. }, _) => {
787 Network::new_regtest(build_regtest_params(*params))
788 }
789 (DNetwork::DefaultForKind(NetworkKind::Mainnet), _) => Network::Mainnet,
790 (DNetwork::DefaultForKind(NetworkKind::Testnet), Some(params)) => {
791 build_configured_testnet::<D>(params, &initial_testnet_peers)?
792 }
793 (DNetwork::DefaultForKind(NetworkKind::Testnet), None) => {
794 Network::new_default_testnet()
795 }
796 (DNetwork::DefaultForKind(NetworkKind::Regtest), Some(params)) => {
797 Network::new_regtest(build_regtest_params(params))
798 }
799 (DNetwork::DefaultForKind(NetworkKind::Regtest), None) => {
800 Network::new_regtest(Default::default())
801 }
802 };
803
804 let listen_addr = match listen_addr.parse::<SocketAddr>().or_else(|_| format!("{listen_addr}:{}", network.default_port()).parse()) {
805 Ok(socket) => Ok(socket),
806 Err(_) => match listen_addr.parse::<IpAddr>() {
807 Ok(ip) => Ok(SocketAddr::new(ip, network.default_port())),
808 Err(err) => Err(de::Error::custom(format!(
809 "{err}; Hint: addresses can be a IPv4, IPv6 (with brackets), or a DNS name, the port is optional"
810 ))),
811 },
812 }?;
813
814 let external_socket_addr = if let Some(address) = &external_addr {
815 match address.parse::<SocketAddr>().or_else(|_| format!("{address}:{}", network.default_port()).parse()) {
816 Ok(socket) => Ok(Some(socket)),
817 Err(_) => match address.parse::<IpAddr>() {
818 Ok(ip) => Ok(Some(SocketAddr::new(ip, network.default_port()))),
819 Err(err) => Err(de::Error::custom(format!(
820 "{err}; Hint: addresses can be a IPv4, IPv6 (with brackets), or a DNS name, the port is optional"
821 ))),
822 },
823 }?
824 } else {
825 None
826 };
827
828 let [max_connections_per_ip, peerset_initial_target_size] = [
829 ("max_connections_per_ip", max_connections_per_ip, DEFAULT_MAX_CONNS_PER_IP),
830 ("peerset_initial_target_size", Some(peerset_initial_target_size), DEFAULT_PEERSET_INITIAL_TARGET_SIZE)
833 ].map(|(field_name, non_zero_config_field, default_config_value)| {
834 if non_zero_config_field == Some(0) {
835 warn!(
836 ?field_name,
837 ?non_zero_config_field,
838 "{field_name} should be greater than 0, using default value of {default_config_value} instead"
839 );
840 }
841
842 non_zero_config_field.filter(|config_value| config_value > &0).unwrap_or(default_config_value)
843 });
844
845 Ok(Config {
846 listen_addr: canonical_socket_addr(listen_addr),
847 external_addr: external_socket_addr,
848 network,
849 initial_mainnet_peers,
850 initial_testnet_peers,
851 cache_dir,
852 peerset_initial_target_size,
853 crawl_new_peer_interval,
854 max_connections_per_ip,
855 })
856 }
857}
858
859fn contains_default_initial_peers(initial_peers: &IndexSet<String>) -> bool {
863 let Config {
864 initial_mainnet_peers: mut default_initial_peers,
865 initial_testnet_peers: default_initial_testnet_peers,
866 ..
867 } = Config::default();
868 default_initial_peers.extend(default_initial_testnet_peers);
869
870 initial_peers
871 .intersection(&default_initial_peers)
872 .next()
873 .is_some()
874}
875
876fn build_configured_testnet<'de, D>(
877 params: DTestnetParameters,
878 initial_testnet_peers: &IndexSet<String>,
879) -> Result<Network, D::Error>
880where
881 D: Deserializer<'de>,
882{
883 let DTestnetParameters {
884 network_name,
885 network_magic,
886 slow_start_interval,
887 target_difficulty_limit,
888 disable_pow,
889 genesis_hash,
890 activation_heights,
891 pre_nu6_funding_streams,
892 post_nu6_funding_streams,
893 funding_streams,
894 pre_blossom_halving_interval,
895 lockbox_disbursements,
896 checkpoints,
897 extend_funding_stream_addresses_as_required,
898 temporary_orchard_disabling_soft_fork_height,
899 } = params;
900
901 let mut params_builder = testnet::Parameters::build();
902
903 if let Some(network_name) = network_name.clone() {
904 params_builder = params_builder
905 .with_network_name(network_name)
906 .map_err(de::Error::custom)?
907 }
908
909 if let Some(network_magic) = network_magic {
910 params_builder = params_builder
911 .with_network_magic(Magic(network_magic))
912 .map_err(de::Error::custom)?;
913 }
914
915 if let Some(genesis_hash) = genesis_hash {
916 params_builder = params_builder
917 .with_genesis_hash(genesis_hash)
918 .map_err(de::Error::custom)?;
919 }
920
921 if let Some(slow_start_interval) = slow_start_interval {
922 params_builder = params_builder
923 .with_slow_start_interval(slow_start_interval.try_into().map_err(de::Error::custom)?);
924 }
925
926 if let Some(target_difficulty_limit) = target_difficulty_limit.clone() {
927 params_builder = params_builder
928 .with_target_difficulty_limit(
929 target_difficulty_limit
930 .parse::<U256>()
931 .map_err(de::Error::custom)?,
932 )
933 .map_err(de::Error::custom)?;
934 }
935
936 if let Some(disable_pow) = disable_pow {
937 params_builder = params_builder.with_disable_pow(disable_pow);
938 }
939
940 if let Some(activation_heights) = activation_heights {
942 params_builder = params_builder
943 .with_activation_heights(activation_heights)
944 .map_err(de::Error::custom)?
945 }
946
947 if let Some(halving_interval) = pre_blossom_halving_interval {
948 params_builder = params_builder
949 .with_halving_interval(halving_interval.into())
950 .map_err(de::Error::custom)?
951 }
952
953 let mut funding_streams_vec = funding_streams.unwrap_or_default();
955
956 if let Some(funding_streams) = post_nu6_funding_streams {
957 funding_streams_vec.insert(0, funding_streams);
958 }
959
960 if let Some(funding_streams) = pre_nu6_funding_streams {
961 funding_streams_vec.insert(0, funding_streams);
962 }
963
964 if !funding_streams_vec.is_empty() {
965 params_builder = params_builder.with_funding_streams(funding_streams_vec);
966 }
967
968 if let Some(lockbox_disbursements) = lockbox_disbursements {
969 params_builder = params_builder.with_lockbox_disbursements(lockbox_disbursements);
970 }
971
972 params_builder = params_builder
973 .with_checkpoints(checkpoints)
974 .map_err(de::Error::custom)?;
975
976 if let Some(true) = extend_funding_stream_addresses_as_required {
977 params_builder = params_builder.extend_funding_streams();
978 }
979
980 if let Some(height) = temporary_orchard_disabling_soft_fork_height {
982 params_builder = params_builder.with_temporary_orchard_disabling_soft_fork_height(
983 height.try_into().map_err(de::Error::custom)?,
984 );
985 }
986
987 if !params_builder.is_compatible_with_default_parameters()
990 && contains_default_initial_peers(initial_testnet_peers)
991 {
992 return Err(de::Error::custom(
993 "cannot use default initials peers with incompatible testnet",
994 ));
995 };
996
997 if network_name.is_none() && params_builder == testnet::Parameters::build() {
999 Ok(Network::new_default_testnet())
1000 } else {
1001 Ok(params_builder.to_network().map_err(de::Error::custom)?)
1002 }
1003}
1004
1005fn build_regtest_params(params: DTestnetParameters) -> RegtestParameters {
1006 let DTestnetParameters {
1007 activation_heights,
1008 pre_nu6_funding_streams,
1009 post_nu6_funding_streams,
1010 funding_streams,
1011 lockbox_disbursements,
1012 checkpoints,
1013 extend_funding_stream_addresses_as_required,
1014 ..
1015 } = params;
1016
1017 let mut funding_streams_vec = funding_streams.unwrap_or_default();
1018
1019 if let Some(funding_streams) = post_nu6_funding_streams {
1020 funding_streams_vec.insert(0, funding_streams);
1021 }
1022
1023 if let Some(funding_streams) = pre_nu6_funding_streams {
1024 funding_streams_vec.insert(0, funding_streams);
1025 }
1026
1027 RegtestParameters {
1028 activation_heights: activation_heights.unwrap_or_default(),
1029 funding_streams: Some(funding_streams_vec),
1030 lockbox_disbursements,
1031 checkpoints: Some(checkpoints),
1032 extend_funding_stream_addresses_as_required,
1033 }
1034}