1use std::{collections::BTreeMap, fmt, sync::Arc};
4
5use crate::{
6 amount::{Amount, NonNegative},
7 block::{self, Height, HeightDiff},
8 parameters::{
9 checkpoint::list::{CheckpointList, TESTNET_CHECKPOINT_LIST},
10 constants::{magics, SLOW_START_INTERVAL, SLOW_START_SHIFT},
11 network::error::ParametersBuilderError,
12 network_upgrade::TESTNET_ACTIVATION_HEIGHTS,
13 subsidy::{
14 constants::mainnet,
15 constants::testnet,
16 constants::{
17 BLOSSOM_POW_TARGET_SPACING_RATIO, FUNDING_STREAM_RECEIVER_DENOMINATOR,
18 POST_BLOSSOM_HALVING_INTERVAL, PRE_BLOSSOM_HALVING_INTERVAL,
19 },
20 funding_stream_address_period, FundingStreamReceiver, FundingStreamRecipient,
21 FundingStreams,
22 },
23 Network, NetworkKind, NetworkUpgrade,
24 },
25 transparent,
26 work::difficulty::{ExpandedDifficulty, U256},
27};
28
29use super::magic::Magic;
30
31pub const RESERVED_NETWORK_NAMES: [&str; 6] = [
33 "Mainnet",
34 "Testnet",
35 "Regtest",
36 "MainnetKind",
37 "TestnetKind",
38 "RegtestKind",
39];
40
41pub const MAX_NETWORK_NAME_LENGTH: usize = 30;
43
44pub const MAX_HRP_LENGTH: usize = 30;
46
47const REGTEST_GENESIS_HASH: &str =
49 "029f11d80ef9765602235e1bc9727e3eb6ba20839319f761fee920d63401e327";
50
51const TESTNET_GENESIS_HASH: &str =
53 "05a60a92d99d85997cce3b87616c089f6124d7342af37106edc76126334a2c38";
54
55const PRE_BLOSSOM_REGTEST_HALVING_INTERVAL: HeightDiff = 144;
58
59#[derive(Serialize, Deserialize, Clone, Debug)]
61#[serde(deny_unknown_fields)]
62pub struct ConfiguredFundingStreamRecipient {
63 pub receiver: FundingStreamReceiver,
65 pub numerator: u64,
67 pub addresses: Option<Vec<String>>,
69}
70
71impl ConfiguredFundingStreamRecipient {
72 pub fn new_for(receiver: FundingStreamReceiver) -> Self {
75 use FundingStreamReceiver::*;
76 match receiver {
77 Ecc => Self {
78 receiver: Ecc,
79 numerator: 7,
80 addresses: Some(
81 testnet::FUNDING_STREAM_ECC_ADDRESSES
82 .map(ToString::to_string)
83 .to_vec(),
84 ),
85 },
86 ZcashFoundation => Self {
87 receiver: ZcashFoundation,
88 numerator: 5,
89 addresses: Some(
90 testnet::FUNDING_STREAM_ZF_ADDRESSES
91 .map(ToString::to_string)
92 .to_vec(),
93 ),
94 },
95 MajorGrants => Self {
96 receiver: MajorGrants,
97 numerator: 8,
98 addresses: Some(
99 testnet::FUNDING_STREAM_MG_ADDRESSES
100 .map(ToString::to_string)
101 .to_vec(),
102 ),
103 },
104 Deferred => Self {
105 receiver,
106 numerator: 0,
107 addresses: None,
108 },
109 }
110 }
111
112 pub fn into_recipient(self) -> (FundingStreamReceiver, FundingStreamRecipient) {
114 (
115 self.receiver,
116 FundingStreamRecipient::new(self.numerator, self.addresses.unwrap_or_default()),
117 )
118 }
119}
120
121#[derive(Serialize, Deserialize, Clone, Debug)]
123#[serde(deny_unknown_fields)]
124pub struct ConfiguredLockboxDisbursement {
125 pub address: String,
127 pub amount: Amount<NonNegative>,
129}
130
131#[derive(Serialize, Deserialize, Clone, Default, Debug)]
133#[serde(deny_unknown_fields)]
134pub struct ConfiguredFundingStreams {
135 pub height_range: Option<std::ops::Range<Height>>,
137 pub recipients: Option<Vec<ConfiguredFundingStreamRecipient>>,
139}
140
141impl From<&FundingStreams> for ConfiguredFundingStreams {
142 fn from(value: &FundingStreams) -> Self {
143 Self {
144 height_range: Some(value.height_range().clone()),
145 recipients: Some(
146 value
147 .recipients()
148 .iter()
149 .map(|(receiver, recipient)| ConfiguredFundingStreamRecipient {
150 receiver: *receiver,
151 numerator: recipient.numerator(),
152 addresses: Some(
153 recipient
154 .addresses()
155 .iter()
156 .map(ToString::to_string)
157 .collect(),
158 ),
159 })
160 .collect(),
161 ),
162 }
163 }
164}
165
166impl From<(transparent::Address, Amount<NonNegative>)> for ConfiguredLockboxDisbursement {
167 fn from((address, amount): (transparent::Address, Amount<NonNegative>)) -> Self {
168 Self {
169 address: address.to_string(),
170 amount,
171 }
172 }
173}
174
175impl From<&BTreeMap<Height, NetworkUpgrade>> for ConfiguredActivationHeights {
176 fn from(activation_heights: &BTreeMap<Height, NetworkUpgrade>) -> Self {
177 let mut configured_activation_heights = ConfiguredActivationHeights::default();
178
179 for (height, network_upgrade) in activation_heights {
180 let field = match network_upgrade {
181 NetworkUpgrade::BeforeOverwinter => {
182 &mut configured_activation_heights.before_overwinter
183 }
184 NetworkUpgrade::Overwinter => &mut configured_activation_heights.overwinter,
185 NetworkUpgrade::Sapling => &mut configured_activation_heights.sapling,
186 NetworkUpgrade::Blossom => &mut configured_activation_heights.blossom,
187 NetworkUpgrade::Heartwood => &mut configured_activation_heights.heartwood,
188 NetworkUpgrade::Canopy => &mut configured_activation_heights.canopy,
189 NetworkUpgrade::Nu5 => &mut configured_activation_heights.nu5,
190 NetworkUpgrade::Nu6 => &mut configured_activation_heights.nu6,
191 NetworkUpgrade::Nu6_1 => &mut configured_activation_heights.nu6_1,
192 NetworkUpgrade::Nu6_2 => &mut configured_activation_heights.nu6_2,
193 NetworkUpgrade::Nu7 => &mut configured_activation_heights.nu7,
194 #[cfg(zcash_unstable = "zfuture")]
195 NetworkUpgrade::ZFuture => &mut configured_activation_heights.zfuture,
196 NetworkUpgrade::Genesis => continue,
197 };
198
199 *field = Some(height.0)
200 }
201
202 configured_activation_heights
203 }
204}
205
206impl From<BTreeMap<Height, NetworkUpgrade>> for ConfiguredActivationHeights {
207 fn from(value: BTreeMap<Height, NetworkUpgrade>) -> Self {
208 Self::from(&value)
209 }
210}
211
212impl ConfiguredFundingStreams {
213 fn convert_with_default(
220 self,
221 default_funding_streams: Option<FundingStreams>,
222 ) -> FundingStreams {
223 let height_range = self.height_range.unwrap_or_else(|| {
224 default_funding_streams
225 .as_ref()
226 .expect("default required")
227 .height_range()
228 .clone()
229 });
230
231 let recipients = self
232 .recipients
233 .map(|recipients| {
234 recipients
235 .into_iter()
236 .map(ConfiguredFundingStreamRecipient::into_recipient)
237 .collect()
238 })
239 .unwrap_or_else(|| {
240 default_funding_streams
241 .as_ref()
242 .expect("default required")
243 .recipients()
244 .clone()
245 });
246
247 assert!(
248 height_range.start <= height_range.end,
249 "funding stream end height must be above start height"
250 );
251
252 let funding_streams = FundingStreams::new(height_range.clone(), recipients);
253
254 let sum_numerators: u64 = funding_streams
257 .recipients()
258 .values()
259 .map(|r| r.numerator())
260 .sum();
261
262 assert!(
263 sum_numerators <= FUNDING_STREAM_RECEIVER_DENOMINATOR,
264 "sum of funding stream numerators must not be \
265 greater than denominator of {FUNDING_STREAM_RECEIVER_DENOMINATOR}"
266 );
267
268 funding_streams
269 }
270
271 pub fn into_funding_streams_unchecked(self) -> FundingStreams {
277 let height_range = self.height_range.expect("must have height range");
278 let recipients = self
279 .recipients
280 .into_iter()
281 .flat_map(|recipients| {
282 recipients
283 .into_iter()
284 .map(ConfiguredFundingStreamRecipient::into_recipient)
285 })
286 .collect();
287
288 FundingStreams::new(height_range, recipients)
289 }
290}
291
292fn num_funding_stream_addresses_required_for_height_range(
294 height_range: &std::ops::Range<Height>,
295 network: &Network,
296) -> usize {
297 1u32.checked_add(funding_stream_address_period(
298 height_range
299 .end
300 .previous()
301 .expect("end height must be above start height and genesis height"),
302 network,
303 ))
304 .expect("no overflow should happen in this sum")
305 .checked_sub(funding_stream_address_period(height_range.start, network))
306 .expect("no overflow should happen in this sub") as usize
307}
308
309fn check_funding_stream_address_period(funding_streams: &FundingStreams, network: &Network) {
312 let expected_min_num_addresses = num_funding_stream_addresses_required_for_height_range(
313 funding_streams.height_range(),
314 network,
315 );
316
317 for (&receiver, recipient) in funding_streams.recipients() {
318 if receiver == FundingStreamReceiver::Deferred {
319 continue;
321 }
322
323 let num_addresses = recipient.addresses().len();
324 assert!(
325 num_addresses >= expected_min_num_addresses,
326 "recipients must have a sufficient number of addresses for height range, \
327 minimum num addresses required: {expected_min_num_addresses}, only {num_addresses} were provided.\
328 receiver: {receiver:?}, recipient: {recipient:?}"
329 );
330
331 for address in recipient.addresses() {
332 assert_eq!(
333 address.network_kind(),
334 NetworkKind::Testnet,
335 "configured funding stream addresses must be for Testnet"
336 );
337 }
338 }
339}
340
341#[derive(Serialize, Deserialize, Default, Clone, Copy, Debug, PartialEq)]
343#[serde(rename_all = "PascalCase", deny_unknown_fields)]
344pub struct ConfiguredActivationHeights {
345 pub before_overwinter: Option<u32>,
347 pub overwinter: Option<u32>,
349 pub sapling: Option<u32>,
351 pub blossom: Option<u32>,
353 pub heartwood: Option<u32>,
355 pub canopy: Option<u32>,
357 #[serde(rename = "NU5")]
359 pub nu5: Option<u32>,
360 #[serde(rename = "NU6")]
362 pub nu6: Option<u32>,
363 #[serde(rename = "NU6.1")]
365 pub nu6_1: Option<u32>,
366 #[serde(rename = "NU6.2")]
368 pub nu6_2: Option<u32>,
369 #[serde(rename = "NU7")]
371 pub nu7: Option<u32>,
372 #[serde(rename = "ZFuture")]
374 #[cfg(zcash_unstable = "zfuture")]
375 pub zfuture: Option<u32>,
376}
377
378impl ConfiguredActivationHeights {
379 fn for_regtest(self) -> Self {
382 let Self {
383 before_overwinter,
384 overwinter,
385 sapling,
386 blossom,
387 heartwood,
388 canopy,
389 nu5,
390 nu6,
391 nu6_1,
392 nu6_2,
393 nu7,
394 #[cfg(zcash_unstable = "zfuture")]
395 zfuture,
396 } = self;
397
398 let overwinter = overwinter.or(before_overwinter).or(Some(1));
399 let sapling = sapling.or(overwinter);
400 let blossom = blossom.or(sapling);
401 let heartwood = heartwood.or(blossom);
402 let canopy = canopy.or(heartwood);
403
404 Self {
405 before_overwinter,
406 overwinter,
407 sapling,
408 blossom,
409 heartwood,
410 canopy,
411 nu5,
412 nu6,
413 nu6_1,
414 nu6_2,
415 nu7,
416 #[cfg(zcash_unstable = "zfuture")]
417 zfuture,
418 }
419 }
420}
421
422#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
425#[serde(untagged)]
426pub enum ConfiguredCheckpoints {
427 Default(bool),
429 Path(std::path::PathBuf),
431 HeightsAndHashes(Vec<(block::Height, block::Hash)>),
433}
434
435impl Default for ConfiguredCheckpoints {
436 fn default() -> Self {
437 Self::Default(false)
438 }
439}
440
441impl From<Arc<CheckpointList>> for ConfiguredCheckpoints {
442 fn from(value: Arc<CheckpointList>) -> Self {
443 Self::HeightsAndHashes(value.iter_cloned().collect())
444 }
445}
446
447impl From<bool> for ConfiguredCheckpoints {
448 fn from(value: bool) -> Self {
449 Self::Default(value)
450 }
451}
452
453#[derive(Clone, Debug, Eq, PartialEq)]
455pub struct ParametersBuilder {
456 network_name: String,
458 network_magic: Magic,
460 genesis_hash: block::Hash,
462 activation_heights: BTreeMap<Height, NetworkUpgrade>,
464 slow_start_interval: Height,
466 funding_streams: Vec<FundingStreams>,
468 should_lock_funding_stream_address_period: bool,
471 target_difficulty_limit: ExpandedDifficulty,
473 disable_pow: bool,
475 should_allow_unshielded_coinbase_spends: bool,
478 pre_blossom_halving_interval: HeightDiff,
480 post_blossom_halving_interval: HeightDiff,
482 lockbox_disbursements: Vec<(String, Amount<NonNegative>)>,
484 checkpoints: Arc<CheckpointList>,
486 temporary_orchard_disabling_soft_fork_height: Option<Height>,
488}
489
490impl Default for ParametersBuilder {
491 fn default() -> Self {
493 Self {
494 network_name: "UnknownTestnet".to_string(),
495 network_magic: magics::TESTNET,
496 activation_heights: TESTNET_ACTIVATION_HEIGHTS.iter().cloned().collect(),
500 genesis_hash: TESTNET_GENESIS_HASH
501 .parse()
502 .expect("hard-coded hash parses"),
503 slow_start_interval: SLOW_START_INTERVAL,
504 target_difficulty_limit: ExpandedDifficulty::from((U256::one() << 251) - 1)
510 .to_compact()
511 .to_expanded()
512 .expect("difficulty limits are valid expanded values"),
513 disable_pow: false,
514 funding_streams: testnet::FUNDING_STREAMS.clone(),
515 should_lock_funding_stream_address_period: false,
516 pre_blossom_halving_interval: PRE_BLOSSOM_HALVING_INTERVAL,
517 post_blossom_halving_interval: POST_BLOSSOM_HALVING_INTERVAL,
518 should_allow_unshielded_coinbase_spends: false,
519 lockbox_disbursements: testnet::NU6_1_LOCKBOX_DISBURSEMENTS
520 .iter()
521 .map(|(addr, amount)| (addr.to_string(), *amount))
522 .collect(),
523 checkpoints: TESTNET_CHECKPOINT_LIST.clone(),
524 temporary_orchard_disabling_soft_fork_height: Some(
525 super::TESTNET_TEMPORARY_ORCHARD_DISABLING_SOFT_FORK_HEIGHT,
526 ),
527 }
528 }
529}
530
531impl ParametersBuilder {
532 pub fn with_network_name(
534 mut self,
535 network_name: impl fmt::Display,
536 ) -> Result<Self, ParametersBuilderError> {
537 let network_name = network_name.to_string();
538
539 if RESERVED_NETWORK_NAMES.contains(&network_name.as_str()) {
540 return Err(ParametersBuilderError::ReservedNetworkName {
541 network_name,
542 reserved_names: RESERVED_NETWORK_NAMES.to_vec(),
543 });
544 }
545
546 if network_name.len() > MAX_NETWORK_NAME_LENGTH {
547 return Err(ParametersBuilderError::NetworkNameTooLong {
548 network_name,
549 max_length: MAX_NETWORK_NAME_LENGTH,
550 });
551 }
552
553 if !network_name
554 .chars()
555 .all(|x| x.is_alphanumeric() || x == '_')
556 {
557 return Err(ParametersBuilderError::InvalidCharacter);
558 }
559
560 self.network_name = network_name;
561 Ok(self)
562 }
563
564 pub fn with_network_magic(
566 mut self,
567 network_magic: Magic,
568 ) -> Result<Self, ParametersBuilderError> {
569 if [magics::MAINNET, magics::REGTEST]
570 .into_iter()
571 .any(|reserved_magic| network_magic == reserved_magic)
572 {
573 return Err(ParametersBuilderError::ReservedNetworkMagic);
574 }
575
576 self.network_magic = network_magic;
577
578 Ok(self)
579 }
580
581 pub fn with_genesis_hash(
583 mut self,
584 genesis_hash: impl fmt::Display,
585 ) -> Result<Self, ParametersBuilderError> {
586 self.genesis_hash = genesis_hash
587 .to_string()
588 .parse()
589 .map_err(|_| ParametersBuilderError::InvalidGenesisHash)?;
590 Ok(self)
591 }
592
593 pub fn with_activation_heights(
596 mut self,
597 ConfiguredActivationHeights {
598 before_overwinter,
599 overwinter,
600 sapling,
601 blossom,
602 heartwood,
603 canopy,
604 nu5,
605 nu6,
606 nu6_1,
607 nu6_2,
608 nu7,
609 #[cfg(zcash_unstable = "zfuture")]
610 zfuture,
611 }: ConfiguredActivationHeights,
612 ) -> Result<Self, ParametersBuilderError> {
613 use NetworkUpgrade::*;
614
615 if self.should_lock_funding_stream_address_period {
616 return Err(ParametersBuilderError::LockFundingStreams);
617 }
618
619 let activation_heights: BTreeMap<_, _> = {
624 let activation_heights = before_overwinter
625 .into_iter()
626 .map(|h| (h, BeforeOverwinter))
627 .chain(overwinter.into_iter().map(|h| (h, Overwinter)))
628 .chain(sapling.into_iter().map(|h| (h, Sapling)))
629 .chain(blossom.into_iter().map(|h| (h, Blossom)))
630 .chain(heartwood.into_iter().map(|h| (h, Heartwood)))
631 .chain(canopy.into_iter().map(|h| (h, Canopy)))
632 .chain(nu5.into_iter().map(|h| (h, Nu5)))
633 .chain(nu6.into_iter().map(|h| (h, Nu6)))
634 .chain(nu6_1.into_iter().map(|h| (h, Nu6_1)))
635 .chain(nu6_2.into_iter().map(|h| (h, Nu6_2)))
636 .chain(nu7.into_iter().map(|h| (h, Nu7)));
637
638 #[cfg(zcash_unstable = "zfuture")]
639 let activation_heights =
640 activation_heights.chain(zfuture.into_iter().map(|h| (h, ZFuture)));
641
642 activation_heights
643 .map(|(h, nu)| {
644 let height = h
645 .try_into()
646 .map_err(|_| ParametersBuilderError::InvalidActivationHeight)?;
647 Ok((height, nu))
648 })
649 .collect::<Result<BTreeMap<_, _>, _>>()?
650 };
651
652 let network_upgrades: Vec<_> = activation_heights.iter().map(|(_h, &nu)| nu).collect();
653
654 let mut activation_heights_iter = activation_heights.iter();
656 for expected_network_upgrade in NetworkUpgrade::iter() {
657 if !network_upgrades.contains(&expected_network_upgrade) {
658 continue;
659 } else if let Some((&height, &network_upgrade)) = activation_heights_iter.next() {
660 if height == Height(0) {
661 return Err(ParametersBuilderError::InvalidHeightZero);
662 }
663
664 if network_upgrade != expected_network_upgrade {
665 return Err(ParametersBuilderError::OutOfOrderUpgrades);
666 }
667 }
668 }
669
670 self.activation_heights.split_off(&Height(1));
674 self.activation_heights.extend(activation_heights);
675
676 Ok(self)
677 }
678
679 pub fn with_slow_start_interval(mut self, slow_start_interval: Height) -> Self {
681 self.slow_start_interval = slow_start_interval;
682 self
683 }
684
685 pub fn with_funding_streams(mut self, funding_streams: Vec<ConfiguredFundingStreams>) -> Self {
692 self.funding_streams = funding_streams
693 .into_iter()
694 .enumerate()
695 .map(|(idx, streams)| {
696 let default_streams = testnet::FUNDING_STREAMS.get(idx).cloned();
697 streams.convert_with_default(default_streams)
698 })
699 .collect();
700 self.should_lock_funding_stream_address_period = true;
701 self
702 }
703
704 pub fn clear_funding_streams(mut self) -> Self {
706 self.funding_streams = vec![];
707 self
708 }
709
710 pub fn extend_funding_streams(mut self) -> Self {
715 let network = self.to_network_unchecked();
716
717 for funding_streams in &mut self.funding_streams {
718 funding_streams.extend_recipient_addresses(
719 num_funding_stream_addresses_required_for_height_range(
720 funding_streams.height_range(),
721 &network,
722 ),
723 );
724 }
725
726 self
727 }
728
729 pub fn with_target_difficulty_limit(
732 mut self,
733 target_difficulty_limit: impl Into<ExpandedDifficulty>,
734 ) -> Result<Self, ParametersBuilderError> {
735 self.target_difficulty_limit = target_difficulty_limit
736 .into()
737 .to_compact()
738 .to_expanded()
739 .ok_or(ParametersBuilderError::InvaildDifficultyLimits)?;
740 Ok(self)
741 }
742
743 pub fn with_disable_pow(mut self, disable_pow: bool) -> Self {
745 self.disable_pow = disable_pow;
746 self
747 }
748
749 pub fn with_unshielded_coinbase_spends(
751 mut self,
752 should_allow_unshielded_coinbase_spends: bool,
753 ) -> Self {
754 self.should_allow_unshielded_coinbase_spends = should_allow_unshielded_coinbase_spends;
755 self
756 }
757
758 pub fn with_halving_interval(
760 mut self,
761 pre_blossom_halving_interval: HeightDiff,
762 ) -> Result<Self, ParametersBuilderError> {
763 if self.should_lock_funding_stream_address_period {
764 return Err(ParametersBuilderError::HalvingIntervalAfterFundingStreams);
765 }
766
767 self.pre_blossom_halving_interval = pre_blossom_halving_interval;
768 self.post_blossom_halving_interval =
769 self.pre_blossom_halving_interval * (BLOSSOM_POW_TARGET_SPACING_RATIO as HeightDiff);
770 Ok(self)
771 }
772
773 pub fn with_lockbox_disbursements(
775 mut self,
776 lockbox_disbursements: Vec<ConfiguredLockboxDisbursement>,
777 ) -> Self {
778 self.lockbox_disbursements = lockbox_disbursements
779 .into_iter()
780 .map(|ConfiguredLockboxDisbursement { address, amount }| (address, amount))
781 .collect();
782 self
783 }
784
785 pub fn with_checkpoints(
787 mut self,
788 checkpoints: impl Into<ConfiguredCheckpoints>,
789 ) -> Result<Self, ParametersBuilderError> {
790 self.checkpoints = match checkpoints.into() {
791 ConfiguredCheckpoints::Default(true) => TESTNET_CHECKPOINT_LIST.clone(),
792 ConfiguredCheckpoints::Default(false) => Arc::new(
793 CheckpointList::from_list([(block::Height(0), self.genesis_hash)])
794 .map_err(|_| ParametersBuilderError::FailedToParseDefaultCheckpoint)?,
795 ),
796 ConfiguredCheckpoints::Path(path_buf) => {
797 let Ok(raw_checkpoints_str) = std::fs::read_to_string(&path_buf) else {
798 return Err(ParametersBuilderError::FailedToReadCheckpointFile {
799 path_buf: path_buf.clone(),
800 });
801 };
802
803 Arc::new(
804 raw_checkpoints_str
805 .parse::<CheckpointList>()
806 .map_err(|err| ParametersBuilderError::FailedToParseCheckpointFile {
807 path_buf: path_buf.clone(),
808 err: err.to_string(),
809 })?,
810 )
811 }
812 ConfiguredCheckpoints::HeightsAndHashes(items) => Arc::new(
813 CheckpointList::from_list(items)
814 .map_err(|_| ParametersBuilderError::InvalidCustomCheckpoints)?,
815 ),
816 };
817
818 Ok(self)
819 }
820
821 pub fn clear_checkpoints(self) -> Result<Self, ParametersBuilderError> {
823 self.with_checkpoints(ConfiguredCheckpoints::Default(false))
824 }
825
826 pub fn with_temporary_orchard_disabling_soft_fork_height(mut self, height: Height) -> Self {
829 self.temporary_orchard_disabling_soft_fork_height = Some(height);
830 self
831 }
832
833 pub fn disable_temporary_orchard_disabling_soft_fork(mut self) -> Self {
835 self.temporary_orchard_disabling_soft_fork_height = None;
836 self
837 }
838
839 fn finish(self) -> Parameters {
841 let Self {
842 network_name,
843 network_magic,
844 genesis_hash,
845 activation_heights,
846 slow_start_interval,
847 funding_streams,
848 should_lock_funding_stream_address_period: _,
849 target_difficulty_limit,
850 disable_pow,
851 should_allow_unshielded_coinbase_spends,
852 pre_blossom_halving_interval,
853 post_blossom_halving_interval,
854 lockbox_disbursements,
855 checkpoints,
856 temporary_orchard_disabling_soft_fork_height,
857 } = self;
858 Parameters {
859 network_name,
860 network_magic,
861 genesis_hash,
862 activation_heights,
863 slow_start_interval,
864 slow_start_shift: Height(slow_start_interval.0 / 2),
865 funding_streams,
866 target_difficulty_limit,
867 disable_pow,
868 should_allow_unshielded_coinbase_spends,
869 pre_blossom_halving_interval,
870 post_blossom_halving_interval,
871 lockbox_disbursements,
872 checkpoints,
873 temporary_orchard_disabling_soft_fork_height,
874 }
875 }
876
877 fn to_network_unchecked(&self) -> Network {
879 Network::new_configured_testnet(self.clone().finish())
880 }
881
882 pub fn to_network(self) -> Result<Network, ParametersBuilderError> {
884 let network = self.to_network_unchecked();
885
886 for fs in &self.funding_streams {
888 check_funding_stream_address_period(fs, &network);
890 }
891
892 if network.checkpoint_list().hash(Height(0)) != Some(network.genesis_hash()) {
894 return Err(ParametersBuilderError::CheckpointGenesisMismatch);
895 }
896 if network.checkpoint_list().max_height() < network.mandatory_checkpoint_height() {
897 return Err(ParametersBuilderError::InsufficientCheckpointCoverage);
898 }
899
900 Ok(network)
901 }
902
903 pub fn is_compatible_with_default_parameters(&self) -> bool {
905 let Self {
906 network_name: _,
907 network_magic,
908 genesis_hash,
909 activation_heights,
910 slow_start_interval,
911 funding_streams,
912 should_lock_funding_stream_address_period: _,
913 target_difficulty_limit,
914 disable_pow,
915 should_allow_unshielded_coinbase_spends,
916 pre_blossom_halving_interval,
917 post_blossom_halving_interval,
918 lockbox_disbursements,
919 checkpoints: _,
920 temporary_orchard_disabling_soft_fork_height: _,
921 } = Self::default();
922
923 self.activation_heights == activation_heights
924 && self.network_magic == network_magic
925 && self.genesis_hash == genesis_hash
926 && self.slow_start_interval == slow_start_interval
927 && self.funding_streams == funding_streams
928 && self.target_difficulty_limit == target_difficulty_limit
929 && self.disable_pow == disable_pow
930 && self.should_allow_unshielded_coinbase_spends
931 == should_allow_unshielded_coinbase_spends
932 && self.pre_blossom_halving_interval == pre_blossom_halving_interval
933 && self.post_blossom_halving_interval == post_blossom_halving_interval
934 && self.lockbox_disbursements == lockbox_disbursements
935 }
936}
937
938#[derive(Debug, Default, Clone)]
940pub struct RegtestParameters {
941 pub activation_heights: ConfiguredActivationHeights,
943 pub funding_streams: Option<Vec<ConfiguredFundingStreams>>,
945 pub lockbox_disbursements: Option<Vec<ConfiguredLockboxDisbursement>>,
947 pub checkpoints: Option<ConfiguredCheckpoints>,
949 pub extend_funding_stream_addresses_as_required: Option<bool>,
951}
952
953impl From<ConfiguredActivationHeights> for RegtestParameters {
954 fn from(value: ConfiguredActivationHeights) -> Self {
955 Self {
956 activation_heights: value,
957 ..Default::default()
958 }
959 }
960}
961
962#[derive(Clone, Debug, Eq, PartialEq)]
964pub struct Parameters {
965 network_name: String,
967 network_magic: Magic,
969 genesis_hash: block::Hash,
971 activation_heights: BTreeMap<Height, NetworkUpgrade>,
973 slow_start_interval: Height,
975 slow_start_shift: Height,
977 funding_streams: Vec<FundingStreams>,
979 target_difficulty_limit: ExpandedDifficulty,
981 disable_pow: bool,
983 should_allow_unshielded_coinbase_spends: bool,
986 pre_blossom_halving_interval: HeightDiff,
988 post_blossom_halving_interval: HeightDiff,
990 lockbox_disbursements: Vec<(String, Amount<NonNegative>)>,
992 checkpoints: Arc<CheckpointList>,
994 temporary_orchard_disabling_soft_fork_height: Option<Height>,
996}
997
998impl Default for Parameters {
999 fn default() -> Self {
1001 Self {
1002 network_name: "Testnet".to_string(),
1003 ..Self::build().finish()
1004 }
1005 }
1006}
1007
1008impl Parameters {
1009 pub fn build() -> ParametersBuilder {
1011 ParametersBuilder::default()
1012 }
1013
1014 pub fn new_regtest(
1018 RegtestParameters {
1019 activation_heights,
1020 funding_streams,
1021 lockbox_disbursements,
1022 checkpoints,
1023 extend_funding_stream_addresses_as_required,
1024 }: RegtestParameters,
1025 ) -> Result<Self, ParametersBuilderError> {
1026 let mut parameters = Self::build()
1027 .with_genesis_hash(REGTEST_GENESIS_HASH)?
1028 .with_target_difficulty_limit(U256::from_big_endian(&[0x0f; 32]))?
1030 .with_disable_pow(true)
1031 .with_unshielded_coinbase_spends(true)
1032 .with_slow_start_interval(Height::MIN)
1033 .disable_temporary_orchard_disabling_soft_fork()
1036 .with_activation_heights(activation_heights.for_regtest())?
1039 .with_halving_interval(PRE_BLOSSOM_REGTEST_HALVING_INTERVAL)?
1040 .with_funding_streams(funding_streams.unwrap_or_default())
1041 .with_lockbox_disbursements(lockbox_disbursements.unwrap_or_default())
1042 .with_checkpoints(checkpoints.unwrap_or_default())?;
1043
1044 if Some(true) == extend_funding_stream_addresses_as_required {
1045 parameters = parameters.extend_funding_streams();
1046 }
1047
1048 Ok(Self {
1049 network_name: "Regtest".to_string(),
1050 network_magic: magics::REGTEST,
1051 ..parameters.finish()
1052 })
1053 }
1054
1055 pub fn is_default_testnet(&self) -> bool {
1057 self == &Self::default()
1058 }
1059
1060 pub fn is_regtest(&self) -> bool {
1062 if self.network_magic != magics::REGTEST {
1063 return false;
1064 }
1065
1066 let Self {
1067 network_name,
1068 network_magic: _,
1070 genesis_hash,
1071 activation_heights: _,
1073 slow_start_interval,
1074 slow_start_shift,
1075 funding_streams: _,
1076 target_difficulty_limit,
1077 disable_pow,
1078 should_allow_unshielded_coinbase_spends,
1079 pre_blossom_halving_interval,
1080 post_blossom_halving_interval,
1081 lockbox_disbursements: _,
1082 checkpoints: _,
1083 temporary_orchard_disabling_soft_fork_height: _,
1084 } = Self::new_regtest(Default::default()).expect("default regtest parameters are valid");
1085
1086 self.network_name == network_name
1087 && self.genesis_hash == genesis_hash
1088 && self.slow_start_interval == slow_start_interval
1089 && self.slow_start_shift == slow_start_shift
1090 && self.target_difficulty_limit == target_difficulty_limit
1091 && self.disable_pow == disable_pow
1092 && self.should_allow_unshielded_coinbase_spends
1093 == should_allow_unshielded_coinbase_spends
1094 && self.pre_blossom_halving_interval == pre_blossom_halving_interval
1095 && self.post_blossom_halving_interval == post_blossom_halving_interval
1096 }
1097
1098 pub fn network_name(&self) -> &str {
1100 &self.network_name
1101 }
1102
1103 pub fn network_magic(&self) -> Magic {
1105 self.network_magic
1106 }
1107
1108 pub fn genesis_hash(&self) -> block::Hash {
1110 self.genesis_hash
1111 }
1112
1113 pub fn activation_heights(&self) -> &BTreeMap<Height, NetworkUpgrade> {
1115 &self.activation_heights
1116 }
1117
1118 pub fn slow_start_interval(&self) -> Height {
1120 self.slow_start_interval
1121 }
1122
1123 pub fn slow_start_shift(&self) -> Height {
1125 self.slow_start_shift
1126 }
1127
1128 pub fn funding_streams(&self) -> &Vec<FundingStreams> {
1130 &self.funding_streams
1131 }
1132
1133 pub fn target_difficulty_limit(&self) -> ExpandedDifficulty {
1135 self.target_difficulty_limit
1136 }
1137
1138 pub fn disable_pow(&self) -> bool {
1140 self.disable_pow
1141 }
1142
1143 pub fn should_allow_unshielded_coinbase_spends(&self) -> bool {
1146 self.should_allow_unshielded_coinbase_spends
1147 }
1148
1149 pub fn pre_blossom_halving_interval(&self) -> HeightDiff {
1151 self.pre_blossom_halving_interval
1152 }
1153
1154 pub fn post_blossom_halving_interval(&self) -> HeightDiff {
1156 self.post_blossom_halving_interval
1157 }
1158
1159 pub fn lockbox_disbursement_total_amount(&self) -> Amount<NonNegative> {
1161 self.lockbox_disbursements()
1162 .into_iter()
1163 .map(|(_addr, amount)| amount)
1164 .reduce(|a, b| (a + b).expect("sum of configured amounts should be valid"))
1165 .unwrap_or_default()
1166 }
1167
1168 pub fn lockbox_disbursements(&self) -> Vec<(transparent::Address, Amount<NonNegative>)> {
1170 self.lockbox_disbursements
1171 .iter()
1172 .map(|(addr, amount)| {
1173 (
1174 addr.parse().expect("hard-coded address must deserialize"),
1175 *amount,
1176 )
1177 })
1178 .collect()
1179 }
1180
1181 pub fn checkpoints(&self) -> Arc<CheckpointList> {
1183 self.checkpoints.clone()
1184 }
1185
1186 pub fn temporary_orchard_disabling_soft_fork_height(&self) -> Option<Height> {
1189 self.temporary_orchard_disabling_soft_fork_height
1190 }
1191}
1192
1193impl Network {
1194 pub fn parameters(&self) -> Option<Arc<Parameters>> {
1196 if let Self::Testnet(parameters) = self {
1197 Some(parameters.clone())
1198 } else {
1199 None
1200 }
1201 }
1202
1203 pub fn disable_pow(&self) -> bool {
1205 if let Self::Testnet(params) = self {
1206 params.disable_pow()
1207 } else {
1208 false
1209 }
1210 }
1211
1212 pub fn slow_start_interval(&self) -> Height {
1214 if let Self::Testnet(params) = self {
1215 params.slow_start_interval()
1216 } else {
1217 SLOW_START_INTERVAL
1218 }
1219 }
1220
1221 pub fn slow_start_shift(&self) -> Height {
1223 if let Self::Testnet(params) = self {
1224 params.slow_start_shift()
1225 } else {
1226 SLOW_START_SHIFT
1227 }
1228 }
1229
1230 pub fn funding_streams(&self, height: Height) -> Option<&FundingStreams> {
1232 self.all_funding_streams()
1233 .iter()
1234 .find(|&streams| streams.height_range().contains(&height))
1235 }
1236
1237 pub fn all_funding_streams(&self) -> &Vec<FundingStreams> {
1239 if let Self::Testnet(params) = self {
1240 params.funding_streams()
1241 } else {
1242 &mainnet::FUNDING_STREAMS
1243 }
1244 }
1245
1246 pub fn should_allow_unshielded_coinbase_spends(&self) -> bool {
1249 if let Self::Testnet(params) = self {
1250 params.should_allow_unshielded_coinbase_spends()
1251 } else {
1252 false
1253 }
1254 }
1255
1256 pub fn founder_address_list(&self) -> &[&str] {
1258 match self {
1259 Network::Mainnet => &mainnet::FOUNDER_ADDRESS_LIST,
1260 Network::Testnet(_) => &testnet::FOUNDER_ADDRESS_LIST,
1261 }
1262 }
1263}