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 ) -> FundingStreams {
181 let height_range = self.height_range.unwrap_or_else(|| {
182 default_funding_streams
183 .as_ref()
184 .expect("default required")
185 .height_range()
186 .clone()
187 });
188
189 let recipients = self
190 .recipients
191 .map(|recipients| {
192 recipients
193 .into_iter()
194 .map(ConfiguredFundingStreamRecipient::into_recipient)
195 .collect()
196 })
197 .unwrap_or_else(|| {
198 default_funding_streams
199 .as_ref()
200 .expect("default required")
201 .recipients()
202 .clone()
203 });
204
205 assert!(
206 height_range.start <= height_range.end,
207 "funding stream end height must be above start height"
208 );
209
210 let funding_streams = FundingStreams::new(height_range.clone(), recipients);
211
212 let sum_numerators: u64 = funding_streams
215 .recipients()
216 .values()
217 .map(|r| r.numerator())
218 .sum();
219
220 assert!(
221 sum_numerators <= FUNDING_STREAM_RECEIVER_DENOMINATOR,
222 "sum of funding stream numerators must not be \
223 greater than denominator of {FUNDING_STREAM_RECEIVER_DENOMINATOR}"
224 );
225
226 funding_streams
227 }
228
229 pub fn into_funding_streams_unchecked(self) -> FundingStreams {
235 let height_range = self.height_range.expect("must have height range");
236 let recipients = self
237 .recipients
238 .into_iter()
239 .flat_map(|recipients| {
240 recipients
241 .into_iter()
242 .map(ConfiguredFundingStreamRecipient::into_recipient)
243 })
244 .collect();
245
246 FundingStreams::new(height_range, recipients)
247 }
248}
249
250fn num_funding_stream_addresses_required_for_height_range(
252 height_range: &std::ops::Range<Height>,
253 network: &Network,
254) -> usize {
255 1u32.checked_add(funding_stream_address_period(
256 height_range
257 .end
258 .previous()
259 .expect("end height must be above start height and genesis height"),
260 network,
261 ))
262 .expect("no overflow should happen in this sum")
263 .checked_sub(funding_stream_address_period(height_range.start, network))
264 .expect("no overflow should happen in this sub") as usize
265}
266
267fn check_funding_stream_address_period(funding_streams: &FundingStreams, network: &Network) {
270 let expected_min_num_addresses = num_funding_stream_addresses_required_for_height_range(
271 funding_streams.height_range(),
272 network,
273 );
274
275 for (&receiver, recipient) in funding_streams.recipients() {
276 if receiver == FundingStreamReceiver::Deferred {
277 continue;
279 }
280
281 let num_addresses = recipient.addresses().len();
282 assert!(
283 num_addresses >= expected_min_num_addresses,
284 "recipients must have a sufficient number of addresses for height range, \
285 minimum num addresses required: {expected_min_num_addresses}, only {num_addresses} were provided.\
286 receiver: {receiver:?}, recipient: {recipient:?}"
287 );
288
289 for address in recipient.addresses() {
290 assert_eq!(
291 address.network_kind(),
292 NetworkKind::Testnet,
293 "configured funding stream addresses must be for Testnet"
294 );
295 }
296 }
297}
298
299#[derive(Serialize, Deserialize, Default, Clone, Copy, Debug, PartialEq)]
301#[serde(rename_all = "PascalCase", deny_unknown_fields)]
302pub struct ConfiguredActivationHeights {
303 pub before_overwinter: Option<u32>,
305 pub overwinter: Option<u32>,
307 pub sapling: Option<u32>,
309 pub blossom: Option<u32>,
311 pub heartwood: Option<u32>,
313 pub canopy: Option<u32>,
315 #[serde(rename = "NU5")]
317 pub nu5: Option<u32>,
318 #[serde(rename = "NU6")]
320 pub nu6: Option<u32>,
321 #[serde(rename = "NU6.1")]
323 pub nu6_1: Option<u32>,
324 #[serde(rename = "NU7")]
326 pub nu7: Option<u32>,
327 #[serde(rename = "ZFuture")]
329 #[cfg(zcash_unstable = "zfuture")]
330 pub zfuture: Option<u32>,
331}
332
333impl ConfiguredActivationHeights {
334 fn for_regtest(self) -> Self {
337 let Self {
338 before_overwinter,
339 overwinter,
340 sapling,
341 blossom,
342 heartwood,
343 canopy,
344 nu5,
345 nu6,
346 nu6_1,
347 nu7,
348 #[cfg(zcash_unstable = "zfuture")]
349 zfuture,
350 } = self;
351
352 let overwinter = overwinter.or(before_overwinter).or(Some(1));
353 let sapling = sapling.or(overwinter);
354 let blossom = blossom.or(sapling);
355 let heartwood = heartwood.or(blossom);
356 let canopy = canopy.or(heartwood);
357
358 Self {
359 before_overwinter,
360 overwinter,
361 sapling,
362 blossom,
363 heartwood,
364 canopy,
365 nu5,
366 nu6,
367 nu6_1,
368 nu7,
369 #[cfg(zcash_unstable = "zfuture")]
370 zfuture,
371 }
372 }
373}
374
375#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
378#[serde(untagged)]
379pub enum ConfiguredCheckpoints {
380 Default(bool),
382 Path(std::path::PathBuf),
384 HeightsAndHashes(Vec<(block::Height, block::Hash)>),
386}
387
388impl Default for ConfiguredCheckpoints {
389 fn default() -> Self {
390 Self::Default(false)
391 }
392}
393
394impl From<Arc<CheckpointList>> for ConfiguredCheckpoints {
395 fn from(value: Arc<CheckpointList>) -> Self {
396 Self::HeightsAndHashes(value.iter_cloned().collect())
397 }
398}
399
400impl From<bool> for ConfiguredCheckpoints {
401 fn from(value: bool) -> Self {
402 Self::Default(value)
403 }
404}
405
406#[derive(Clone, Debug, Eq, PartialEq)]
408pub struct ParametersBuilder {
409 network_name: String,
411 network_magic: Magic,
413 genesis_hash: block::Hash,
415 activation_heights: BTreeMap<Height, NetworkUpgrade>,
417 slow_start_interval: Height,
419 funding_streams: Vec<FundingStreams>,
421 should_lock_funding_stream_address_period: bool,
424 target_difficulty_limit: ExpandedDifficulty,
426 disable_pow: bool,
428 should_allow_unshielded_coinbase_spends: bool,
431 pre_blossom_halving_interval: HeightDiff,
433 post_blossom_halving_interval: HeightDiff,
435 lockbox_disbursements: Vec<(String, Amount<NonNegative>)>,
437 checkpoints: Arc<CheckpointList>,
439}
440
441impl Default for ParametersBuilder {
442 fn default() -> Self {
444 Self {
445 network_name: "UnknownTestnet".to_string(),
446 network_magic: magics::TESTNET,
447 activation_heights: TESTNET_ACTIVATION_HEIGHTS.iter().cloned().collect(),
451 genesis_hash: TESTNET_GENESIS_HASH
452 .parse()
453 .expect("hard-coded hash parses"),
454 slow_start_interval: SLOW_START_INTERVAL,
455 target_difficulty_limit: ExpandedDifficulty::from((U256::one() << 251) - 1)
461 .to_compact()
462 .to_expanded()
463 .expect("difficulty limits are valid expanded values"),
464 disable_pow: false,
465 funding_streams: FUNDING_STREAMS_TESTNET.clone(),
466 should_lock_funding_stream_address_period: false,
467 pre_blossom_halving_interval: PRE_BLOSSOM_HALVING_INTERVAL,
468 post_blossom_halving_interval: POST_BLOSSOM_HALVING_INTERVAL,
469 should_allow_unshielded_coinbase_spends: false,
470 lockbox_disbursements: NU6_1_LOCKBOX_DISBURSEMENTS_TESTNET
471 .iter()
472 .map(|(addr, amount)| (addr.to_string(), *amount))
473 .collect(),
474 checkpoints: TESTNET_CHECKPOINTS
475 .parse()
476 .map(Arc::new)
477 .expect("must be able to parse checkpoints"),
478 }
479 }
480}
481
482impl ParametersBuilder {
483 pub fn with_network_name(mut self, network_name: impl fmt::Display) -> Self {
485 self.network_name = network_name.to_string();
486
487 assert!(
488 !RESERVED_NETWORK_NAMES.contains(&self.network_name.as_str()),
489 "cannot use reserved network name '{network_name}' as configured Testnet name, reserved names: {RESERVED_NETWORK_NAMES:?}"
490 );
491
492 assert!(
493 self.network_name.len() <= MAX_NETWORK_NAME_LENGTH,
494 "network name {network_name} is too long, must be {MAX_NETWORK_NAME_LENGTH} characters or less"
495 );
496
497 assert!(
498 self.network_name
499 .chars()
500 .all(|x| x.is_alphanumeric() || x == '_'),
501 "network name must include only alphanumeric characters or '_'"
502 );
503
504 self
505 }
506
507 pub fn with_network_magic(mut self, network_magic: Magic) -> Self {
509 assert!(
510 [magics::MAINNET, magics::REGTEST]
511 .into_iter()
512 .all(|reserved_magic| network_magic != reserved_magic),
513 "network magic should be distinct from reserved network magics"
514 );
515
516 self.network_magic = network_magic;
517
518 self
519 }
520
521 pub fn with_genesis_hash(mut self, genesis_hash: impl fmt::Display) -> Self {
523 self.genesis_hash = genesis_hash
524 .to_string()
525 .parse()
526 .expect("configured genesis hash must parse");
527 self
528 }
529
530 pub fn with_activation_heights(
533 mut self,
534 ConfiguredActivationHeights {
535 before_overwinter,
536 overwinter,
537 sapling,
538 blossom,
539 heartwood,
540 canopy,
541 nu5,
542 nu6,
543 nu6_1,
544 nu7,
545 #[cfg(zcash_unstable = "zfuture")]
546 zfuture,
547 }: ConfiguredActivationHeights,
548 ) -> Self {
549 use NetworkUpgrade::*;
550
551 if self.should_lock_funding_stream_address_period {
552 panic!("activation heights on ParametersBuilder must not be set after setting funding streams");
553 }
554
555 let activation_heights: BTreeMap<_, _> = {
560 let activation_heights = before_overwinter
561 .into_iter()
562 .map(|h| (h, BeforeOverwinter))
563 .chain(overwinter.into_iter().map(|h| (h, Overwinter)))
564 .chain(sapling.into_iter().map(|h| (h, Sapling)))
565 .chain(blossom.into_iter().map(|h| (h, Blossom)))
566 .chain(heartwood.into_iter().map(|h| (h, Heartwood)))
567 .chain(canopy.into_iter().map(|h| (h, Canopy)))
568 .chain(nu5.into_iter().map(|h| (h, Nu5)))
569 .chain(nu6.into_iter().map(|h| (h, Nu6)))
570 .chain(nu6_1.into_iter().map(|h| (h, Nu6_1)))
571 .chain(nu7.into_iter().map(|h| (h, Nu7)));
572
573 #[cfg(zcash_unstable = "zfuture")]
574 let activation_heights =
575 activation_heights.chain(zfuture.into_iter().map(|h| (h, ZFuture)));
576
577 activation_heights
578 .map(|(h, nu)| (h.try_into().expect("activation height must be valid"), nu))
579 .collect()
580 };
581
582 let network_upgrades: Vec<_> = activation_heights.iter().map(|(_h, &nu)| nu).collect();
583
584 let mut activation_heights_iter = activation_heights.iter();
586 for expected_network_upgrade in NetworkUpgrade::iter() {
587 if !network_upgrades.contains(&expected_network_upgrade) {
588 continue;
589 } else if let Some((&height, &network_upgrade)) = activation_heights_iter.next() {
590 assert_ne!(
591 height,
592 Height(0),
593 "Height(0) is reserved for the `Genesis` upgrade"
594 );
595
596 assert!(
597 network_upgrade == expected_network_upgrade,
598 "network upgrades must be activated in order specified by the protocol"
599 );
600 }
601 }
602
603 self.activation_heights.split_off(&Height(1));
607 self.activation_heights.extend(activation_heights);
608
609 self
610 }
611
612 pub fn with_slow_start_interval(mut self, slow_start_interval: Height) -> Self {
614 self.slow_start_interval = slow_start_interval;
615 self
616 }
617
618 pub fn with_funding_streams(mut self, funding_streams: Vec<ConfiguredFundingStreams>) -> Self {
625 self.funding_streams = funding_streams
626 .into_iter()
627 .enumerate()
628 .map(|(idx, streams)| {
629 let default_streams = FUNDING_STREAMS_TESTNET.get(idx).cloned();
630 streams.convert_with_default(default_streams)
631 })
632 .collect();
633 self.should_lock_funding_stream_address_period = true;
634 self
635 }
636
637 pub fn clear_funding_streams(mut self) -> Self {
639 self.funding_streams = vec![];
640 self
641 }
642
643 pub fn extend_funding_streams(mut self) -> Self {
648 let network = self.to_network_unchecked();
651
652 for funding_streams in &mut self.funding_streams {
653 funding_streams.extend_recipient_addresses(
654 num_funding_stream_addresses_required_for_height_range(
655 funding_streams.height_range(),
656 &network,
657 ),
658 );
659 }
660
661 self
662 }
663
664 pub fn with_target_difficulty_limit(
667 mut self,
668 target_difficulty_limit: impl Into<ExpandedDifficulty>,
669 ) -> Self {
670 self.target_difficulty_limit = target_difficulty_limit
671 .into()
672 .to_compact()
673 .to_expanded()
674 .expect("difficulty limits are valid expanded values");
675 self
676 }
677
678 pub fn with_disable_pow(mut self, disable_pow: bool) -> Self {
680 self.disable_pow = disable_pow;
681 self
682 }
683
684 pub fn with_unshielded_coinbase_spends(
686 mut self,
687 should_allow_unshielded_coinbase_spends: bool,
688 ) -> Self {
689 self.should_allow_unshielded_coinbase_spends = should_allow_unshielded_coinbase_spends;
690 self
691 }
692
693 pub fn with_halving_interval(mut self, pre_blossom_halving_interval: HeightDiff) -> Self {
695 if self.should_lock_funding_stream_address_period {
696 panic!("halving interval on ParametersBuilder must not be set after setting funding streams");
697 }
698
699 self.pre_blossom_halving_interval = pre_blossom_halving_interval;
700 self.post_blossom_halving_interval =
701 self.pre_blossom_halving_interval * (BLOSSOM_POW_TARGET_SPACING_RATIO as HeightDiff);
702 self
703 }
704
705 pub fn with_lockbox_disbursements(
707 mut self,
708 lockbox_disbursements: Vec<ConfiguredLockboxDisbursement>,
709 ) -> Self {
710 self.lockbox_disbursements = lockbox_disbursements
711 .into_iter()
712 .map(|ConfiguredLockboxDisbursement { address, amount }| (address, amount))
713 .collect();
714 self
715 }
716
717 pub fn with_checkpoints(mut self, checkpoints: impl Into<ConfiguredCheckpoints>) -> Self {
719 self.checkpoints = Arc::new(match checkpoints.into() {
720 ConfiguredCheckpoints::Default(true) => TESTNET_CHECKPOINTS
721 .parse()
722 .expect("checkpoints file format must be valid"),
723 ConfiguredCheckpoints::Default(false) => {
724 CheckpointList::from_list([(block::Height(0), self.genesis_hash)])
725 .expect("must parse checkpoints")
726 }
727 ConfiguredCheckpoints::Path(path_buf) => {
728 let Ok(raw_checkpoints_str) = std::fs::read_to_string(&path_buf) else {
729 panic!("could not read file at configured checkpoints file path: {path_buf:?}");
730 };
731
732 raw_checkpoints_str.parse().unwrap_or_else(|err| {
733 panic!("could not parse checkpoints at the provided path: {path_buf:?}, err: {err}")
734 })
735 }
736 ConfiguredCheckpoints::HeightsAndHashes(items) => {
737 CheckpointList::from_list(items).expect("configured checkpoints must be valid")
738 }
739 });
740
741 self
742 }
743
744 pub fn clear_checkpoints(self) -> Self {
746 self.with_checkpoints(ConfiguredCheckpoints::Default(false))
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 pub extend_funding_stream_addresses_as_required: Option<bool>,
861}
862
863impl From<ConfiguredActivationHeights> for RegtestParameters {
864 fn from(value: ConfiguredActivationHeights) -> Self {
865 Self {
866 activation_heights: value,
867 ..Default::default()
868 }
869 }
870}
871
872#[derive(Clone, Debug, Eq, PartialEq)]
874pub struct Parameters {
875 network_name: String,
877 network_magic: Magic,
879 genesis_hash: block::Hash,
881 activation_heights: BTreeMap<Height, NetworkUpgrade>,
883 slow_start_interval: Height,
885 slow_start_shift: Height,
887 funding_streams: Vec<FundingStreams>,
889 target_difficulty_limit: ExpandedDifficulty,
891 disable_pow: bool,
893 should_allow_unshielded_coinbase_spends: bool,
896 pre_blossom_halving_interval: HeightDiff,
898 post_blossom_halving_interval: HeightDiff,
900 lockbox_disbursements: Vec<(String, Amount<NonNegative>)>,
902 checkpoints: Arc<CheckpointList>,
904}
905
906impl Default for Parameters {
907 fn default() -> Self {
909 Self {
910 network_name: "Testnet".to_string(),
911 ..Self::build().finish()
912 }
913 }
914}
915
916impl Parameters {
917 pub fn build() -> ParametersBuilder {
919 ParametersBuilder::default()
920 }
921
922 pub fn new_regtest(
926 RegtestParameters {
927 activation_heights,
928 funding_streams,
929 lockbox_disbursements,
930 checkpoints,
931 extend_funding_stream_addresses_as_required,
932 }: RegtestParameters,
933 ) -> Self {
934 let mut parameters = Self::build()
935 .with_genesis_hash(REGTEST_GENESIS_HASH)
936 .with_target_difficulty_limit(U256::from_big_endian(&[0x0f; 32]))
938 .with_disable_pow(true)
939 .with_unshielded_coinbase_spends(true)
940 .with_slow_start_interval(Height::MIN)
941 .with_activation_heights(activation_heights.for_regtest())
944 .with_halving_interval(PRE_BLOSSOM_REGTEST_HALVING_INTERVAL)
945 .with_funding_streams(funding_streams.unwrap_or_default())
946 .with_lockbox_disbursements(lockbox_disbursements.unwrap_or_default())
947 .with_checkpoints(checkpoints.unwrap_or_default());
948
949 if Some(true) == extend_funding_stream_addresses_as_required {
950 parameters = parameters.extend_funding_streams();
951 }
952
953 Self {
954 network_name: "Regtest".to_string(),
955 network_magic: magics::REGTEST,
956 ..parameters.finish()
957 }
958 }
959
960 pub fn is_default_testnet(&self) -> bool {
962 self == &Self::default()
963 }
964
965 pub fn is_regtest(&self) -> bool {
967 if self.network_magic != magics::REGTEST {
968 return false;
969 }
970
971 let Self {
972 network_name,
973 network_magic: _,
975 genesis_hash,
976 activation_heights: _,
978 slow_start_interval,
979 slow_start_shift,
980 funding_streams: _,
981 target_difficulty_limit,
982 disable_pow,
983 should_allow_unshielded_coinbase_spends,
984 pre_blossom_halving_interval,
985 post_blossom_halving_interval,
986 lockbox_disbursements: _,
987 checkpoints: _,
988 } = Self::new_regtest(Default::default());
989
990 self.network_name == network_name
991 && self.genesis_hash == genesis_hash
992 && self.slow_start_interval == slow_start_interval
993 && self.slow_start_shift == slow_start_shift
994 && self.target_difficulty_limit == target_difficulty_limit
995 && self.disable_pow == disable_pow
996 && self.should_allow_unshielded_coinbase_spends
997 == should_allow_unshielded_coinbase_spends
998 && self.pre_blossom_halving_interval == pre_blossom_halving_interval
999 && self.post_blossom_halving_interval == post_blossom_halving_interval
1000 }
1001
1002 pub fn network_name(&self) -> &str {
1004 &self.network_name
1005 }
1006
1007 pub fn network_magic(&self) -> Magic {
1009 self.network_magic
1010 }
1011
1012 pub fn genesis_hash(&self) -> block::Hash {
1014 self.genesis_hash
1015 }
1016
1017 pub fn activation_heights(&self) -> &BTreeMap<Height, NetworkUpgrade> {
1019 &self.activation_heights
1020 }
1021
1022 pub fn slow_start_interval(&self) -> Height {
1024 self.slow_start_interval
1025 }
1026
1027 pub fn slow_start_shift(&self) -> Height {
1029 self.slow_start_shift
1030 }
1031
1032 pub fn funding_streams(&self) -> &Vec<FundingStreams> {
1034 &self.funding_streams
1035 }
1036
1037 pub fn target_difficulty_limit(&self) -> ExpandedDifficulty {
1039 self.target_difficulty_limit
1040 }
1041
1042 pub fn disable_pow(&self) -> bool {
1044 self.disable_pow
1045 }
1046
1047 pub fn should_allow_unshielded_coinbase_spends(&self) -> bool {
1050 self.should_allow_unshielded_coinbase_spends
1051 }
1052
1053 pub fn pre_blossom_halving_interval(&self) -> HeightDiff {
1055 self.pre_blossom_halving_interval
1056 }
1057
1058 pub fn post_blossom_halving_interval(&self) -> HeightDiff {
1060 self.post_blossom_halving_interval
1061 }
1062
1063 pub fn lockbox_disbursement_total_amount(&self) -> Amount<NonNegative> {
1065 self.lockbox_disbursements()
1066 .into_iter()
1067 .map(|(_addr, amount)| amount)
1068 .reduce(|a, b| (a + b).expect("sum of configured amounts should be valid"))
1069 .unwrap_or_default()
1070 }
1071
1072 pub fn lockbox_disbursements(&self) -> Vec<(transparent::Address, Amount<NonNegative>)> {
1074 self.lockbox_disbursements
1075 .iter()
1076 .map(|(addr, amount)| {
1077 (
1078 addr.parse().expect("hard-coded address must deserialize"),
1079 *amount,
1080 )
1081 })
1082 .collect()
1083 }
1084
1085 pub fn checkpoints(&self) -> Arc<CheckpointList> {
1087 self.checkpoints.clone()
1088 }
1089}
1090
1091impl Network {
1092 pub fn parameters(&self) -> Option<Arc<Parameters>> {
1094 if let Self::Testnet(parameters) = self {
1095 Some(parameters.clone())
1096 } else {
1097 None
1098 }
1099 }
1100
1101 pub fn disable_pow(&self) -> bool {
1103 if let Self::Testnet(params) = self {
1104 params.disable_pow()
1105 } else {
1106 false
1107 }
1108 }
1109
1110 pub fn slow_start_interval(&self) -> Height {
1112 if let Self::Testnet(params) = self {
1113 params.slow_start_interval()
1114 } else {
1115 SLOW_START_INTERVAL
1116 }
1117 }
1118
1119 pub fn slow_start_shift(&self) -> Height {
1121 if let Self::Testnet(params) = self {
1122 params.slow_start_shift()
1123 } else {
1124 SLOW_START_SHIFT
1125 }
1126 }
1127
1128 pub fn funding_streams(&self, height: Height) -> Option<&FundingStreams> {
1130 self.all_funding_streams()
1131 .iter()
1132 .find(|&streams| streams.height_range().contains(&height))
1133 }
1134
1135 pub fn all_funding_streams(&self) -> &Vec<FundingStreams> {
1137 if let Self::Testnet(params) = self {
1138 params.funding_streams()
1139 } else {
1140 &FUNDING_STREAMS_MAINNET
1141 }
1142 }
1143
1144 pub fn should_allow_unshielded_coinbase_spends(&self) -> bool {
1147 if let Self::Testnet(params) = self {
1148 params.should_allow_unshielded_coinbase_spends()
1149 } else {
1150 false
1151 }
1152 }
1153}