1use std::{collections::BTreeMap, fmt, sync::Arc};
3
4use crate::{
5 amount::{Amount, NonNegative},
6 block::{self, Height, HeightDiff},
7 parameters::{
8 checkpoint::list::{CheckpointList, TESTNET_CHECKPOINTS},
9 constants::{magics, SLOW_START_INTERVAL, SLOW_START_SHIFT},
10 network_upgrade::TESTNET_ACTIVATION_HEIGHTS,
11 subsidy::{
12 funding_stream_address_period, FUNDING_STREAMS_MAINNET, FUNDING_STREAMS_TESTNET,
13 FUNDING_STREAM_RECEIVER_DENOMINATOR, NU6_1_LOCKBOX_DISBURSEMENTS_TESTNET,
14 },
15 Network, NetworkKind, NetworkUpgrade,
16 },
17 transparent,
18 work::difficulty::{ExpandedDifficulty, U256},
19};
20
21use super::{
22 magic::Magic,
23 subsidy::{
24 FundingStreamReceiver, FundingStreamRecipient, FundingStreams,
25 BLOSSOM_POW_TARGET_SPACING_RATIO, POST_BLOSSOM_HALVING_INTERVAL,
26 PRE_BLOSSOM_HALVING_INTERVAL,
27 },
28};
29
30pub const RESERVED_NETWORK_NAMES: [&str; 6] = [
32 "Mainnet",
33 "Testnet",
34 "Regtest",
35 "MainnetKind",
36 "TestnetKind",
37 "RegtestKind",
38];
39
40pub const MAX_NETWORK_NAME_LENGTH: usize = 30;
42
43pub const MAX_HRP_LENGTH: usize = 30;
45
46const REGTEST_GENESIS_HASH: &str =
48 "029f11d80ef9765602235e1bc9727e3eb6ba20839319f761fee920d63401e327";
49
50const TESTNET_GENESIS_HASH: &str =
52 "05a60a92d99d85997cce3b87616c089f6124d7342af37106edc76126334a2c38";
53
54const PRE_BLOSSOM_REGTEST_HALVING_INTERVAL: HeightDiff = 144;
57
58#[derive(Serialize, Deserialize, Clone, Debug)]
60#[serde(deny_unknown_fields)]
61pub struct ConfiguredFundingStreamRecipient {
62 pub receiver: FundingStreamReceiver,
64 pub numerator: u64,
66 pub addresses: Option<Vec<String>>,
68}
69
70impl ConfiguredFundingStreamRecipient {
71 pub fn into_recipient(self) -> (FundingStreamReceiver, FundingStreamRecipient) {
73 (
74 self.receiver,
75 FundingStreamRecipient::new(self.numerator, self.addresses.unwrap_or_default()),
76 )
77 }
78}
79
80#[derive(Serialize, Deserialize, Clone, Debug)]
82#[serde(deny_unknown_fields)]
83pub struct ConfiguredLockboxDisbursement {
84 pub address: String,
86 pub amount: Amount<NonNegative>,
88}
89
90#[derive(Serialize, Deserialize, Clone, Default, Debug)]
92#[serde(deny_unknown_fields)]
93pub struct ConfiguredFundingStreams {
94 pub height_range: Option<std::ops::Range<Height>>,
96 pub recipients: Option<Vec<ConfiguredFundingStreamRecipient>>,
98}
99
100impl From<&FundingStreams> for ConfiguredFundingStreams {
101 fn from(value: &FundingStreams) -> Self {
102 Self {
103 height_range: Some(value.height_range().clone()),
104 recipients: Some(
105 value
106 .recipients()
107 .iter()
108 .map(|(receiver, recipient)| ConfiguredFundingStreamRecipient {
109 receiver: *receiver,
110 numerator: recipient.numerator(),
111 addresses: Some(
112 recipient
113 .addresses()
114 .iter()
115 .map(ToString::to_string)
116 .collect(),
117 ),
118 })
119 .collect(),
120 ),
121 }
122 }
123}
124
125impl From<(transparent::Address, Amount<NonNegative>)> for ConfiguredLockboxDisbursement {
126 fn from((address, amount): (transparent::Address, Amount<NonNegative>)) -> Self {
127 Self {
128 address: address.to_string(),
129 amount,
130 }
131 }
132}
133
134impl From<&BTreeMap<Height, NetworkUpgrade>> for ConfiguredActivationHeights {
135 fn from(activation_heights: &BTreeMap<Height, NetworkUpgrade>) -> Self {
136 let mut configured_activation_heights = ConfiguredActivationHeights::default();
137
138 for (height, network_upgrade) in activation_heights {
139 let field = match network_upgrade {
140 NetworkUpgrade::BeforeOverwinter => {
141 &mut configured_activation_heights.before_overwinter
142 }
143 NetworkUpgrade::Overwinter => &mut configured_activation_heights.overwinter,
144 NetworkUpgrade::Sapling => &mut configured_activation_heights.sapling,
145 NetworkUpgrade::Blossom => &mut configured_activation_heights.blossom,
146 NetworkUpgrade::Heartwood => &mut configured_activation_heights.heartwood,
147 NetworkUpgrade::Canopy => &mut configured_activation_heights.canopy,
148 NetworkUpgrade::Nu5 => &mut configured_activation_heights.nu5,
149 NetworkUpgrade::Nu6 => &mut configured_activation_heights.nu6,
150 NetworkUpgrade::Nu6_1 => &mut configured_activation_heights.nu6_1,
151 NetworkUpgrade::Nu7 => &mut configured_activation_heights.nu7,
152 #[cfg(zcash_unstable = "zfuture")]
153 NetworkUpgrade::ZFuture => &mut configured_activation_heights.zfuture,
154 NetworkUpgrade::Genesis => continue,
155 };
156
157 *field = Some(height.0)
158 }
159
160 configured_activation_heights
161 }
162}
163
164impl From<BTreeMap<Height, NetworkUpgrade>> for ConfiguredActivationHeights {
165 fn from(value: BTreeMap<Height, NetworkUpgrade>) -> Self {
166 Self::from(&value)
167 }
168}
169
170impl ConfiguredFundingStreams {
171 fn convert_with_default(
178 self,
179 default_funding_streams: Option<FundingStreams>,
180 parameters_builder: &ParametersBuilder,
181 ) -> FundingStreams {
182 let network = parameters_builder.to_network_unchecked();
183 let height_range = self.height_range.unwrap_or_else(|| {
184 default_funding_streams
185 .as_ref()
186 .expect("default required")
187 .height_range()
188 .clone()
189 });
190
191 let recipients = self
192 .recipients
193 .map(|recipients| {
194 recipients
195 .into_iter()
196 .map(ConfiguredFundingStreamRecipient::into_recipient)
197 .collect()
198 })
199 .unwrap_or_else(|| {
200 default_funding_streams
201 .as_ref()
202 .expect("default required")
203 .recipients()
204 .clone()
205 });
206
207 assert!(
208 height_range.start <= height_range.end,
209 "funding stream end height must be above start height"
210 );
211
212 let funding_streams = FundingStreams::new(height_range.clone(), recipients);
213
214 check_funding_stream_address_period(&funding_streams, &network);
215
216 let sum_numerators: u64 = funding_streams
219 .recipients()
220 .values()
221 .map(|r| r.numerator())
222 .sum();
223
224 assert!(
225 sum_numerators <= FUNDING_STREAM_RECEIVER_DENOMINATOR,
226 "sum of funding stream numerators must not be \
227 greater than denominator of {FUNDING_STREAM_RECEIVER_DENOMINATOR}"
228 );
229
230 funding_streams
231 }
232
233 pub fn into_funding_streams_unchecked(self) -> FundingStreams {
239 let height_range = self.height_range.expect("must have height range");
240 let recipients = self
241 .recipients
242 .into_iter()
243 .flat_map(|recipients| {
244 recipients
245 .into_iter()
246 .map(ConfiguredFundingStreamRecipient::into_recipient)
247 })
248 .collect();
249
250 FundingStreams::new(height_range, recipients)
251 }
252}
253
254fn num_funding_stream_addresses_required_for_height_range(
256 height_range: &std::ops::Range<Height>,
257 network: &Network,
258) -> usize {
259 1u32.checked_add(funding_stream_address_period(
260 height_range
261 .end
262 .previous()
263 .expect("end height must be above start height and genesis height"),
264 network,
265 ))
266 .expect("no overflow should happen in this sum")
267 .checked_sub(funding_stream_address_period(height_range.start, network))
268 .expect("no overflow should happen in this sub") as usize
269}
270
271fn check_funding_stream_address_period(funding_streams: &FundingStreams, network: &Network) {
274 let expected_min_num_addresses = num_funding_stream_addresses_required_for_height_range(
275 funding_streams.height_range(),
276 network,
277 );
278
279 for (&receiver, recipient) in funding_streams.recipients() {
280 if receiver == FundingStreamReceiver::Deferred {
281 continue;
283 }
284
285 let num_addresses = recipient.addresses().len();
286 assert!(
287 num_addresses >= expected_min_num_addresses,
288 "recipients must have a sufficient number of addresses for height range, \
289 minimum num addresses required: {expected_min_num_addresses}, only {num_addresses} were provided.\
290 receiver: {receiver:?}, recipient: {recipient:?}"
291 );
292
293 for address in recipient.addresses() {
294 assert_eq!(
295 address.network_kind(),
296 NetworkKind::Testnet,
297 "configured funding stream addresses must be for Testnet"
298 );
299 }
300 }
301}
302
303#[derive(Serialize, Deserialize, Default, Debug, Clone)]
305#[serde(rename_all = "PascalCase", deny_unknown_fields)]
306pub struct ConfiguredActivationHeights {
307 pub before_overwinter: Option<u32>,
309 pub overwinter: Option<u32>,
311 pub sapling: Option<u32>,
313 pub blossom: Option<u32>,
315 pub heartwood: Option<u32>,
317 pub canopy: Option<u32>,
319 #[serde(rename = "NU5")]
321 pub nu5: Option<u32>,
322 #[serde(rename = "NU6")]
324 pub nu6: Option<u32>,
325 #[serde(rename = "NU6.1")]
327 pub nu6_1: Option<u32>,
328 #[serde(rename = "NU7")]
330 pub nu7: Option<u32>,
331 #[serde(rename = "ZFuture")]
333 #[cfg(zcash_unstable = "zfuture")]
334 pub zfuture: Option<u32>,
335}
336
337impl ConfiguredActivationHeights {
338 fn for_regtest(self) -> Self {
341 let Self {
342 before_overwinter,
343 overwinter,
344 sapling,
345 blossom,
346 heartwood,
347 canopy,
348 nu5,
349 nu6,
350 nu6_1,
351 nu7,
352 #[cfg(zcash_unstable = "zfuture")]
353 zfuture,
354 } = self;
355
356 let overwinter = overwinter.or(before_overwinter).or(Some(1));
357 let sapling = sapling.or(overwinter);
358 let blossom = blossom.or(sapling);
359 let heartwood = heartwood.or(blossom);
360 let canopy = canopy.or(heartwood);
361
362 Self {
363 before_overwinter,
364 overwinter,
365 sapling,
366 blossom,
367 heartwood,
368 canopy,
369 nu5,
370 nu6,
371 nu6_1,
372 nu7,
373 #[cfg(zcash_unstable = "zfuture")]
374 zfuture,
375 }
376 }
377}
378
379#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
382#[serde(untagged)]
383pub enum ConfiguredCheckpoints {
384 Default(bool),
386 Path(std::path::PathBuf),
388 HeightsAndHashes(Vec<(block::Height, block::Hash)>),
390}
391
392impl Default for ConfiguredCheckpoints {
393 fn default() -> Self {
394 Self::Default(false)
395 }
396}
397
398impl From<Arc<CheckpointList>> for ConfiguredCheckpoints {
399 fn from(value: Arc<CheckpointList>) -> Self {
400 Self::HeightsAndHashes(value.iter_cloned().collect())
401 }
402}
403
404impl From<bool> for ConfiguredCheckpoints {
405 fn from(value: bool) -> Self {
406 Self::Default(value)
407 }
408}
409
410#[derive(Clone, Debug, Eq, PartialEq)]
412pub struct ParametersBuilder {
413 network_name: String,
415 network_magic: Magic,
417 genesis_hash: block::Hash,
419 activation_heights: BTreeMap<Height, NetworkUpgrade>,
421 slow_start_interval: Height,
423 funding_streams: Vec<FundingStreams>,
425 should_lock_funding_stream_address_period: bool,
428 target_difficulty_limit: ExpandedDifficulty,
430 disable_pow: bool,
432 should_allow_unshielded_coinbase_spends: bool,
435 pre_blossom_halving_interval: HeightDiff,
437 post_blossom_halving_interval: HeightDiff,
439 lockbox_disbursements: Vec<(String, Amount<NonNegative>)>,
441 checkpoints: Arc<CheckpointList>,
443}
444
445impl Default for ParametersBuilder {
446 fn default() -> Self {
448 Self {
449 network_name: "UnknownTestnet".to_string(),
450 network_magic: magics::TESTNET,
451 activation_heights: TESTNET_ACTIVATION_HEIGHTS.iter().cloned().collect(),
455 genesis_hash: TESTNET_GENESIS_HASH
456 .parse()
457 .expect("hard-coded hash parses"),
458 slow_start_interval: SLOW_START_INTERVAL,
459 target_difficulty_limit: ExpandedDifficulty::from((U256::one() << 251) - 1)
465 .to_compact()
466 .to_expanded()
467 .expect("difficulty limits are valid expanded values"),
468 disable_pow: false,
469 funding_streams: FUNDING_STREAMS_TESTNET.clone(),
470 should_lock_funding_stream_address_period: false,
471 pre_blossom_halving_interval: PRE_BLOSSOM_HALVING_INTERVAL,
472 post_blossom_halving_interval: POST_BLOSSOM_HALVING_INTERVAL,
473 should_allow_unshielded_coinbase_spends: false,
474 lockbox_disbursements: NU6_1_LOCKBOX_DISBURSEMENTS_TESTNET
475 .iter()
476 .map(|(addr, amount)| (addr.to_string(), *amount))
477 .collect(),
478 checkpoints: TESTNET_CHECKPOINTS
479 .parse()
480 .map(Arc::new)
481 .expect("must be able to parse checkpoints"),
482 }
483 }
484}
485
486impl ParametersBuilder {
487 pub fn with_network_name(mut self, network_name: impl fmt::Display) -> Self {
489 self.network_name = network_name.to_string();
490
491 assert!(
492 !RESERVED_NETWORK_NAMES.contains(&self.network_name.as_str()),
493 "cannot use reserved network name '{network_name}' as configured Testnet name, reserved names: {RESERVED_NETWORK_NAMES:?}"
494 );
495
496 assert!(
497 self.network_name.len() <= MAX_NETWORK_NAME_LENGTH,
498 "network name {network_name} is too long, must be {MAX_NETWORK_NAME_LENGTH} characters or less"
499 );
500
501 assert!(
502 self.network_name
503 .chars()
504 .all(|x| x.is_alphanumeric() || x == '_'),
505 "network name must include only alphanumeric characters or '_'"
506 );
507
508 self
509 }
510
511 pub fn with_network_magic(mut self, network_magic: Magic) -> Self {
513 assert!(
514 [magics::MAINNET, magics::REGTEST]
515 .into_iter()
516 .all(|reserved_magic| network_magic != reserved_magic),
517 "network magic should be distinct from reserved network magics"
518 );
519
520 self.network_magic = network_magic;
521
522 self
523 }
524
525 pub fn with_genesis_hash(mut self, genesis_hash: impl fmt::Display) -> Self {
527 self.genesis_hash = genesis_hash
528 .to_string()
529 .parse()
530 .expect("configured genesis hash must parse");
531 self
532 }
533
534 pub fn with_activation_heights(
537 mut self,
538 ConfiguredActivationHeights {
539 before_overwinter,
540 overwinter,
541 sapling,
542 blossom,
543 heartwood,
544 canopy,
545 nu5,
546 nu6,
547 nu6_1,
548 nu7,
549 #[cfg(zcash_unstable = "zfuture")]
550 zfuture,
551 }: ConfiguredActivationHeights,
552 ) -> Self {
553 use NetworkUpgrade::*;
554
555 if self.should_lock_funding_stream_address_period {
556 panic!("activation heights on ParametersBuilder must not be set after setting funding streams");
557 }
558
559 let activation_heights: BTreeMap<_, _> = {
564 let activation_heights = before_overwinter
565 .into_iter()
566 .map(|h| (h, BeforeOverwinter))
567 .chain(overwinter.into_iter().map(|h| (h, Overwinter)))
568 .chain(sapling.into_iter().map(|h| (h, Sapling)))
569 .chain(blossom.into_iter().map(|h| (h, Blossom)))
570 .chain(heartwood.into_iter().map(|h| (h, Heartwood)))
571 .chain(canopy.into_iter().map(|h| (h, Canopy)))
572 .chain(nu5.into_iter().map(|h| (h, Nu5)))
573 .chain(nu6.into_iter().map(|h| (h, Nu6)))
574 .chain(nu6_1.into_iter().map(|h| (h, Nu6_1)))
575 .chain(nu7.into_iter().map(|h| (h, Nu7)));
576
577 #[cfg(zcash_unstable = "zfuture")]
578 let activation_heights =
579 activation_heights.chain(zfuture.into_iter().map(|h| (h, ZFuture)));
580
581 activation_heights
582 .map(|(h, nu)| (h.try_into().expect("activation height must be valid"), nu))
583 .collect()
584 };
585
586 let network_upgrades: Vec<_> = activation_heights.iter().map(|(_h, &nu)| nu).collect();
587
588 let mut activation_heights_iter = activation_heights.iter();
590 for expected_network_upgrade in NetworkUpgrade::iter() {
591 if !network_upgrades.contains(&expected_network_upgrade) {
592 continue;
593 } else if let Some((&height, &network_upgrade)) = activation_heights_iter.next() {
594 assert_ne!(
595 height,
596 Height(0),
597 "Height(0) is reserved for the `Genesis` upgrade"
598 );
599
600 assert!(
601 network_upgrade == expected_network_upgrade,
602 "network upgrades must be activated in order specified by the protocol"
603 );
604 }
605 }
606
607 self.activation_heights.split_off(&Height(1));
611 self.activation_heights.extend(activation_heights);
612
613 self
614 }
615
616 pub fn with_slow_start_interval(mut self, slow_start_interval: Height) -> Self {
618 self.slow_start_interval = slow_start_interval;
619 self
620 }
621
622 pub fn with_funding_streams(mut self, funding_streams: Vec<ConfiguredFundingStreams>) -> Self {
629 self.funding_streams = funding_streams
630 .into_iter()
631 .enumerate()
632 .map(|(idx, streams)| {
633 let default_streams = FUNDING_STREAMS_TESTNET.get(idx).cloned();
634 streams.convert_with_default(default_streams, &self)
635 })
636 .collect();
637 self.should_lock_funding_stream_address_period = true;
638 self
639 }
640
641 pub fn clear_funding_streams(mut self) -> Self {
643 self.funding_streams = vec![];
644 self
645 }
646
647 #[cfg(any(test, feature = "proptest-impl"))]
652 pub fn extend_funding_streams(mut self) -> Self {
653 let network = self.to_network_unchecked();
656
657 for funding_streams in &mut self.funding_streams {
658 funding_streams.extend_recipient_addresses(
659 num_funding_stream_addresses_required_for_height_range(
660 funding_streams.height_range(),
661 &network,
662 ),
663 );
664 }
665
666 self
667 }
668
669 pub fn with_target_difficulty_limit(
672 mut self,
673 target_difficulty_limit: impl Into<ExpandedDifficulty>,
674 ) -> Self {
675 self.target_difficulty_limit = target_difficulty_limit
676 .into()
677 .to_compact()
678 .to_expanded()
679 .expect("difficulty limits are valid expanded values");
680 self
681 }
682
683 pub fn with_disable_pow(mut self, disable_pow: bool) -> Self {
685 self.disable_pow = disable_pow;
686 self
687 }
688
689 pub fn with_unshielded_coinbase_spends(
691 mut self,
692 should_allow_unshielded_coinbase_spends: bool,
693 ) -> Self {
694 self.should_allow_unshielded_coinbase_spends = should_allow_unshielded_coinbase_spends;
695 self
696 }
697
698 pub fn with_halving_interval(mut self, pre_blossom_halving_interval: HeightDiff) -> Self {
700 if self.should_lock_funding_stream_address_period {
701 panic!("halving interval on ParametersBuilder must not be set after setting funding streams");
702 }
703
704 self.pre_blossom_halving_interval = pre_blossom_halving_interval;
705 self.post_blossom_halving_interval =
706 self.pre_blossom_halving_interval * (BLOSSOM_POW_TARGET_SPACING_RATIO as HeightDiff);
707 self
708 }
709
710 pub fn with_lockbox_disbursements(
712 mut self,
713 lockbox_disbursements: Vec<ConfiguredLockboxDisbursement>,
714 ) -> Self {
715 self.lockbox_disbursements = lockbox_disbursements
716 .into_iter()
717 .map(|ConfiguredLockboxDisbursement { address, amount }| (address, amount))
718 .collect();
719 self
720 }
721
722 pub fn with_checkpoints(mut self, checkpoints: impl Into<ConfiguredCheckpoints>) -> Self {
724 self.checkpoints = Arc::new(match checkpoints.into() {
725 ConfiguredCheckpoints::Default(true) => TESTNET_CHECKPOINTS
726 .parse()
727 .expect("checkpoints file format must be valid"),
728 ConfiguredCheckpoints::Default(false) => {
729 CheckpointList::from_list([(block::Height(0), self.genesis_hash)])
730 .expect("must parse checkpoints")
731 }
732 ConfiguredCheckpoints::Path(path_buf) => {
733 let Ok(raw_checkpoints_str) = std::fs::read_to_string(&path_buf) else {
734 panic!("could not read file at configured checkpoints file path: {path_buf:?}");
735 };
736
737 raw_checkpoints_str.parse().unwrap_or_else(|err| {
738 panic!("could not parse checkpoints at the provided path: {path_buf:?}, err: {err}")
739 })
740 }
741 ConfiguredCheckpoints::HeightsAndHashes(items) => {
742 CheckpointList::from_list(items).expect("configured checkpoints must be valid")
743 }
744 });
745
746 self
747 }
748
749 fn finish(self) -> Parameters {
751 let Self {
752 network_name,
753 network_magic,
754 genesis_hash,
755 activation_heights,
756 slow_start_interval,
757 funding_streams,
758 should_lock_funding_stream_address_period: _,
759 target_difficulty_limit,
760 disable_pow,
761 should_allow_unshielded_coinbase_spends,
762 pre_blossom_halving_interval,
763 post_blossom_halving_interval,
764 lockbox_disbursements,
765 checkpoints,
766 } = self;
767 Parameters {
768 network_name,
769 network_magic,
770 genesis_hash,
771 activation_heights,
772 slow_start_interval,
773 slow_start_shift: Height(slow_start_interval.0 / 2),
774 funding_streams,
775 target_difficulty_limit,
776 disable_pow,
777 should_allow_unshielded_coinbase_spends,
778 pre_blossom_halving_interval,
779 post_blossom_halving_interval,
780 lockbox_disbursements,
781 checkpoints,
782 }
783 }
784
785 fn to_network_unchecked(&self) -> Network {
787 Network::new_configured_testnet(self.clone().finish())
788 }
789
790 pub fn to_network(self) -> Network {
792 let network = self.to_network_unchecked();
793
794 for fs in &self.funding_streams {
796 check_funding_stream_address_period(fs, &network);
798 }
799
800 assert_eq!(
802 network.checkpoint_list().hash(Height(0)),
803 Some(network.genesis_hash()),
804 "first checkpoint hash must match genesis hash"
805 );
806 assert!(
807 network.checkpoint_list().max_height() >= network.mandatory_checkpoint_height(),
808 "checkpoints must be provided for block heights below the mandatory checkpoint height"
809 );
810
811 network
812 }
813
814 pub fn is_compatible_with_default_parameters(&self) -> bool {
816 let Self {
817 network_name: _,
818 network_magic,
819 genesis_hash,
820 activation_heights,
821 slow_start_interval,
822 funding_streams,
823 should_lock_funding_stream_address_period: _,
824 target_difficulty_limit,
825 disable_pow,
826 should_allow_unshielded_coinbase_spends,
827 pre_blossom_halving_interval,
828 post_blossom_halving_interval,
829 lockbox_disbursements,
830 checkpoints: _,
831 } = Self::default();
832
833 self.activation_heights == activation_heights
834 && self.network_magic == network_magic
835 && self.genesis_hash == genesis_hash
836 && self.slow_start_interval == slow_start_interval
837 && self.funding_streams == funding_streams
838 && self.target_difficulty_limit == target_difficulty_limit
839 && self.disable_pow == disable_pow
840 && self.should_allow_unshielded_coinbase_spends
841 == should_allow_unshielded_coinbase_spends
842 && self.pre_blossom_halving_interval == pre_blossom_halving_interval
843 && self.post_blossom_halving_interval == post_blossom_halving_interval
844 && self.lockbox_disbursements == lockbox_disbursements
845 }
846}
847
848#[derive(Debug, Default, Clone)]
850pub struct RegtestParameters {
851 pub activation_heights: ConfiguredActivationHeights,
853 pub funding_streams: Option<Vec<ConfiguredFundingStreams>>,
855 pub lockbox_disbursements: Option<Vec<ConfiguredLockboxDisbursement>>,
857 pub checkpoints: Option<ConfiguredCheckpoints>,
859}
860
861impl From<ConfiguredActivationHeights> for RegtestParameters {
862 fn from(value: ConfiguredActivationHeights) -> Self {
863 Self {
864 activation_heights: value,
865 ..Default::default()
866 }
867 }
868}
869
870#[derive(Clone, Debug, Eq, PartialEq)]
872pub struct Parameters {
873 network_name: String,
875 network_magic: Magic,
877 genesis_hash: block::Hash,
879 activation_heights: BTreeMap<Height, NetworkUpgrade>,
881 slow_start_interval: Height,
883 slow_start_shift: Height,
885 funding_streams: Vec<FundingStreams>,
887 target_difficulty_limit: ExpandedDifficulty,
889 disable_pow: bool,
891 should_allow_unshielded_coinbase_spends: bool,
894 pre_blossom_halving_interval: HeightDiff,
896 post_blossom_halving_interval: HeightDiff,
898 lockbox_disbursements: Vec<(String, Amount<NonNegative>)>,
900 checkpoints: Arc<CheckpointList>,
902}
903
904impl Default for Parameters {
905 fn default() -> Self {
907 Self {
908 network_name: "Testnet".to_string(),
909 ..Self::build().finish()
910 }
911 }
912}
913
914impl Parameters {
915 pub fn build() -> ParametersBuilder {
917 ParametersBuilder::default()
918 }
919
920 pub fn new_regtest(
924 RegtestParameters {
925 activation_heights,
926 funding_streams,
927 lockbox_disbursements,
928 checkpoints,
929 }: RegtestParameters,
930 ) -> Self {
931 let parameters = Self::build()
932 .with_genesis_hash(REGTEST_GENESIS_HASH)
933 .with_target_difficulty_limit(U256::from_big_endian(&[0x0f; 32]))
935 .with_disable_pow(true)
936 .with_unshielded_coinbase_spends(true)
937 .with_slow_start_interval(Height::MIN)
938 .with_activation_heights(activation_heights.for_regtest())
941 .with_halving_interval(PRE_BLOSSOM_REGTEST_HALVING_INTERVAL)
942 .with_funding_streams(funding_streams.unwrap_or_default())
943 .with_lockbox_disbursements(lockbox_disbursements.unwrap_or_default())
944 .with_checkpoints(checkpoints.unwrap_or_default());
945
946 Self {
947 network_name: "Regtest".to_string(),
948 network_magic: magics::REGTEST,
949 ..parameters.finish()
950 }
951 }
952
953 pub fn is_default_testnet(&self) -> bool {
955 self == &Self::default()
956 }
957
958 pub fn is_regtest(&self) -> bool {
960 if self.network_magic != magics::REGTEST {
961 return false;
962 }
963
964 let Self {
965 network_name,
966 network_magic: _,
968 genesis_hash,
969 activation_heights: _,
971 slow_start_interval,
972 slow_start_shift,
973 funding_streams: _,
974 target_difficulty_limit,
975 disable_pow,
976 should_allow_unshielded_coinbase_spends,
977 pre_blossom_halving_interval,
978 post_blossom_halving_interval,
979 lockbox_disbursements: _,
980 checkpoints: _,
981 } = Self::new_regtest(Default::default());
982
983 self.network_name == network_name
984 && self.genesis_hash == genesis_hash
985 && self.slow_start_interval == slow_start_interval
986 && self.slow_start_shift == slow_start_shift
987 && self.target_difficulty_limit == target_difficulty_limit
988 && self.disable_pow == disable_pow
989 && self.should_allow_unshielded_coinbase_spends
990 == should_allow_unshielded_coinbase_spends
991 && self.pre_blossom_halving_interval == pre_blossom_halving_interval
992 && self.post_blossom_halving_interval == post_blossom_halving_interval
993 }
994
995 pub fn network_name(&self) -> &str {
997 &self.network_name
998 }
999
1000 pub fn network_magic(&self) -> Magic {
1002 self.network_magic
1003 }
1004
1005 pub fn genesis_hash(&self) -> block::Hash {
1007 self.genesis_hash
1008 }
1009
1010 pub fn activation_heights(&self) -> &BTreeMap<Height, NetworkUpgrade> {
1012 &self.activation_heights
1013 }
1014
1015 pub fn slow_start_interval(&self) -> Height {
1017 self.slow_start_interval
1018 }
1019
1020 pub fn slow_start_shift(&self) -> Height {
1022 self.slow_start_shift
1023 }
1024
1025 pub fn funding_streams(&self) -> &Vec<FundingStreams> {
1027 &self.funding_streams
1028 }
1029
1030 pub fn target_difficulty_limit(&self) -> ExpandedDifficulty {
1032 self.target_difficulty_limit
1033 }
1034
1035 pub fn disable_pow(&self) -> bool {
1037 self.disable_pow
1038 }
1039
1040 pub fn should_allow_unshielded_coinbase_spends(&self) -> bool {
1043 self.should_allow_unshielded_coinbase_spends
1044 }
1045
1046 pub fn pre_blossom_halving_interval(&self) -> HeightDiff {
1048 self.pre_blossom_halving_interval
1049 }
1050
1051 pub fn post_blossom_halving_interval(&self) -> HeightDiff {
1053 self.post_blossom_halving_interval
1054 }
1055
1056 pub fn lockbox_disbursement_total_amount(&self) -> Amount<NonNegative> {
1058 self.lockbox_disbursements()
1059 .into_iter()
1060 .map(|(_addr, amount)| amount)
1061 .reduce(|a, b| (a + b).expect("sum of configured amounts should be valid"))
1062 .unwrap_or_default()
1063 }
1064
1065 pub fn lockbox_disbursements(&self) -> Vec<(transparent::Address, Amount<NonNegative>)> {
1067 self.lockbox_disbursements
1068 .iter()
1069 .map(|(addr, amount)| {
1070 (
1071 addr.parse().expect("hard-coded address must deserialize"),
1072 *amount,
1073 )
1074 })
1075 .collect()
1076 }
1077
1078 pub fn checkpoints(&self) -> Arc<CheckpointList> {
1080 self.checkpoints.clone()
1081 }
1082}
1083
1084impl Network {
1085 pub fn parameters(&self) -> Option<Arc<Parameters>> {
1087 if let Self::Testnet(parameters) = self {
1088 Some(parameters.clone())
1089 } else {
1090 None
1091 }
1092 }
1093
1094 pub fn disable_pow(&self) -> bool {
1096 if let Self::Testnet(params) = self {
1097 params.disable_pow()
1098 } else {
1099 false
1100 }
1101 }
1102
1103 pub fn slow_start_interval(&self) -> Height {
1105 if let Self::Testnet(params) = self {
1106 params.slow_start_interval()
1107 } else {
1108 SLOW_START_INTERVAL
1109 }
1110 }
1111
1112 pub fn slow_start_shift(&self) -> Height {
1114 if let Self::Testnet(params) = self {
1115 params.slow_start_shift()
1116 } else {
1117 SLOW_START_SHIFT
1118 }
1119 }
1120
1121 pub fn funding_streams(&self, height: Height) -> Option<&FundingStreams> {
1123 self.all_funding_streams()
1124 .iter()
1125 .find(|&streams| streams.height_range().contains(&height))
1126 }
1127
1128 pub fn all_funding_streams(&self) -> &Vec<FundingStreams> {
1130 if let Self::Testnet(params) = self {
1131 params.funding_streams()
1132 } else {
1133 &FUNDING_STREAMS_MAINNET
1134 }
1135 }
1136
1137 pub fn should_allow_unshielded_coinbase_spends(&self) -> bool {
1140 if let Self::Testnet(params) = self {
1141 params.should_allow_unshielded_coinbase_spends()
1142 } else {
1143 false
1144 }
1145 }
1146}