1use std::{collections::BTreeMap, fmt, sync::Arc};
3
4use crate::{
5 block::{self, Height, HeightDiff},
6 parameters::{
7 constants::{magics, SLOW_START_INTERVAL, SLOW_START_SHIFT},
8 network_upgrade::TESTNET_ACTIVATION_HEIGHTS,
9 subsidy::{funding_stream_address_period, FUNDING_STREAM_RECEIVER_DENOMINATOR},
10 Network, NetworkKind, NetworkUpgrade,
11 },
12 work::difficulty::{ExpandedDifficulty, U256},
13};
14
15use super::{
16 magic::Magic,
17 subsidy::{
18 FundingStreamReceiver, FundingStreamRecipient, FundingStreams,
19 BLOSSOM_POW_TARGET_SPACING_RATIO, POST_BLOSSOM_HALVING_INTERVAL,
20 POST_NU6_FUNDING_STREAMS_MAINNET, POST_NU6_FUNDING_STREAMS_TESTNET,
21 PRE_BLOSSOM_HALVING_INTERVAL, PRE_NU6_FUNDING_STREAMS_MAINNET,
22 PRE_NU6_FUNDING_STREAMS_TESTNET,
23 },
24};
25
26pub const RESERVED_NETWORK_NAMES: [&str; 6] = [
28 "Mainnet",
29 "Testnet",
30 "Regtest",
31 "MainnetKind",
32 "TestnetKind",
33 "RegtestKind",
34];
35
36pub const MAX_NETWORK_NAME_LENGTH: usize = 30;
38
39pub const MAX_HRP_LENGTH: usize = 30;
41
42const REGTEST_GENESIS_HASH: &str =
44 "029f11d80ef9765602235e1bc9727e3eb6ba20839319f761fee920d63401e327";
45
46const TESTNET_GENESIS_HASH: &str =
48 "05a60a92d99d85997cce3b87616c089f6124d7342af37106edc76126334a2c38";
49
50const PRE_BLOSSOM_REGTEST_HALVING_INTERVAL: HeightDiff = 144;
53
54#[derive(Serialize, Deserialize, Clone, Debug)]
56#[serde(deny_unknown_fields)]
57pub struct ConfiguredFundingStreamRecipient {
58 pub receiver: FundingStreamReceiver,
60 pub numerator: u64,
62 pub addresses: Option<Vec<String>>,
64}
65
66impl ConfiguredFundingStreamRecipient {
67 pub fn into_recipient(self) -> (FundingStreamReceiver, FundingStreamRecipient) {
69 (
70 self.receiver,
71 FundingStreamRecipient::new(self.numerator, self.addresses.unwrap_or_default()),
72 )
73 }
74}
75
76#[derive(Serialize, Deserialize, Clone, Default, Debug)]
78#[serde(deny_unknown_fields)]
79pub struct ConfiguredFundingStreams {
80 pub height_range: Option<std::ops::Range<Height>>,
82 pub recipients: Option<Vec<ConfiguredFundingStreamRecipient>>,
84}
85
86impl From<&FundingStreams> for ConfiguredFundingStreams {
87 fn from(value: &FundingStreams) -> Self {
88 Self {
89 height_range: Some(value.height_range().clone()),
90 recipients: Some(
91 value
92 .recipients()
93 .iter()
94 .map(|(receiver, recipient)| ConfiguredFundingStreamRecipient {
95 receiver: *receiver,
96 numerator: recipient.numerator(),
97 addresses: Some(
98 recipient
99 .addresses()
100 .iter()
101 .map(ToString::to_string)
102 .collect(),
103 ),
104 })
105 .collect(),
106 ),
107 }
108 }
109}
110
111impl From<&BTreeMap<Height, NetworkUpgrade>> for ConfiguredActivationHeights {
112 fn from(activation_heights: &BTreeMap<Height, NetworkUpgrade>) -> Self {
113 let mut configured_activation_heights = ConfiguredActivationHeights::default();
114
115 for (height, network_upgrade) in activation_heights.iter() {
116 let field = match network_upgrade {
117 NetworkUpgrade::BeforeOverwinter => {
118 &mut configured_activation_heights.before_overwinter
119 }
120 NetworkUpgrade::Overwinter => &mut configured_activation_heights.overwinter,
121 NetworkUpgrade::Sapling => &mut configured_activation_heights.sapling,
122 NetworkUpgrade::Blossom => &mut configured_activation_heights.blossom,
123 NetworkUpgrade::Heartwood => &mut configured_activation_heights.heartwood,
124 NetworkUpgrade::Canopy => &mut configured_activation_heights.canopy,
125 NetworkUpgrade::Nu5 => &mut configured_activation_heights.nu5,
126 NetworkUpgrade::Nu6 => &mut configured_activation_heights.nu6,
127 NetworkUpgrade::Nu6_1 => &mut configured_activation_heights.nu6_1,
128 NetworkUpgrade::Nu7 => &mut configured_activation_heights.nu7,
129 NetworkUpgrade::Genesis => {
130 continue;
131 }
132 };
133
134 *field = Some(height.0)
135 }
136
137 configured_activation_heights
138 }
139}
140
141impl ConfiguredFundingStreams {
142 fn empty() -> Self {
144 Self {
145 height_range: None,
146 recipients: Some(Vec::new()),
147 }
148 }
149
150 fn convert_with_default(
153 self,
154 default_funding_streams: FundingStreams,
155 parameters_builder: &ParametersBuilder,
156 ) -> FundingStreams {
157 let network = parameters_builder.to_network_unchecked();
158 let height_range = self
159 .height_range
160 .unwrap_or(default_funding_streams.height_range().clone());
161
162 let recipients = self
163 .recipients
164 .map(|recipients| {
165 recipients
166 .into_iter()
167 .map(ConfiguredFundingStreamRecipient::into_recipient)
168 .collect()
169 })
170 .unwrap_or(default_funding_streams.recipients().clone());
171
172 assert!(
173 height_range.start < height_range.end,
174 "funding stream end height must be above start height"
175 );
176
177 let funding_streams = FundingStreams::new(height_range.clone(), recipients);
178
179 check_funding_stream_address_period(&funding_streams, &network);
180
181 let sum_numerators: u64 = funding_streams
184 .recipients()
185 .values()
186 .map(|r| r.numerator())
187 .sum();
188
189 assert!(
190 sum_numerators <= FUNDING_STREAM_RECEIVER_DENOMINATOR,
191 "sum of funding stream numerators must not be \
192 greater than denominator of {FUNDING_STREAM_RECEIVER_DENOMINATOR}"
193 );
194
195 funding_streams
196 }
197}
198
199fn check_funding_stream_address_period(funding_streams: &FundingStreams, network: &Network) {
202 let height_range = funding_streams.height_range();
203 let expected_min_num_addresses =
204 1u32.checked_add(funding_stream_address_period(
205 height_range
206 .end
207 .previous()
208 .expect("end height must be above start height and genesis height"),
209 network,
210 ))
211 .expect("no overflow should happen in this sum")
212 .checked_sub(funding_stream_address_period(height_range.start, network))
213 .expect("no overflow should happen in this sub") as usize;
214
215 for (&receiver, recipient) in funding_streams.recipients() {
216 if receiver == FundingStreamReceiver::Deferred {
217 continue;
219 }
220
221 assert!(
222 recipient.addresses().len() >= expected_min_num_addresses,
223 "recipients must have a sufficient number of addresses for height range, \
224 minimum num addresses required: {expected_min_num_addresses}"
225 );
226
227 for address in recipient.addresses() {
228 assert_eq!(
229 address.network_kind(),
230 NetworkKind::Testnet,
231 "configured funding stream addresses must be for Testnet"
232 );
233 }
234 }
235}
236
237#[derive(Serialize, Deserialize, Default, Clone)]
239#[serde(rename_all = "PascalCase", deny_unknown_fields)]
240pub struct ConfiguredActivationHeights {
241 pub before_overwinter: Option<u32>,
243 pub overwinter: Option<u32>,
245 pub sapling: Option<u32>,
247 pub blossom: Option<u32>,
249 pub heartwood: Option<u32>,
251 pub canopy: Option<u32>,
253 #[serde(rename = "NU5")]
255 pub nu5: Option<u32>,
256 #[serde(rename = "NU6")]
258 pub nu6: Option<u32>,
259 #[serde(rename = "NU6.1")]
261 pub nu6_1: Option<u32>,
262 #[serde(rename = "NU7")]
264 pub nu7: Option<u32>,
265}
266
267#[derive(Clone, Debug, Eq, PartialEq)]
269pub struct ParametersBuilder {
270 network_name: String,
272 network_magic: Magic,
274 genesis_hash: block::Hash,
276 activation_heights: BTreeMap<Height, NetworkUpgrade>,
278 slow_start_interval: Height,
280 pre_nu6_funding_streams: FundingStreams,
282 post_nu6_funding_streams: FundingStreams,
284 should_lock_funding_stream_address_period: bool,
287 target_difficulty_limit: ExpandedDifficulty,
289 disable_pow: bool,
291 should_allow_unshielded_coinbase_spends: bool,
294 pre_blossom_halving_interval: HeightDiff,
296 post_blossom_halving_interval: HeightDiff,
298}
299
300impl Default for ParametersBuilder {
301 fn default() -> Self {
303 Self {
304 network_name: "UnknownTestnet".to_string(),
305 network_magic: magics::TESTNET,
306 activation_heights: TESTNET_ACTIVATION_HEIGHTS.iter().cloned().collect(),
310 genesis_hash: TESTNET_GENESIS_HASH
311 .parse()
312 .expect("hard-coded hash parses"),
313 slow_start_interval: SLOW_START_INTERVAL,
314 target_difficulty_limit: ExpandedDifficulty::from((U256::one() << 251) - 1)
324 .to_compact()
325 .to_expanded()
326 .expect("difficulty limits are valid expanded values"),
327 disable_pow: false,
328 pre_nu6_funding_streams: PRE_NU6_FUNDING_STREAMS_TESTNET.clone(),
329 post_nu6_funding_streams: POST_NU6_FUNDING_STREAMS_TESTNET.clone(),
330 should_lock_funding_stream_address_period: false,
331 pre_blossom_halving_interval: PRE_BLOSSOM_HALVING_INTERVAL,
332 post_blossom_halving_interval: POST_BLOSSOM_HALVING_INTERVAL,
333 should_allow_unshielded_coinbase_spends: false,
334 }
335 }
336}
337
338impl ParametersBuilder {
339 pub fn with_network_name(mut self, network_name: impl fmt::Display) -> Self {
341 self.network_name = network_name.to_string();
342
343 assert!(
344 !RESERVED_NETWORK_NAMES.contains(&self.network_name.as_str()),
345 "cannot use reserved network name '{network_name}' as configured Testnet name, reserved names: {RESERVED_NETWORK_NAMES:?}"
346 );
347
348 assert!(
349 self.network_name.len() <= MAX_NETWORK_NAME_LENGTH,
350 "network name {network_name} is too long, must be {MAX_NETWORK_NAME_LENGTH} characters or less"
351 );
352
353 assert!(
354 self.network_name
355 .chars()
356 .all(|x| x.is_alphanumeric() || x == '_'),
357 "network name must include only alphanumeric characters or '_'"
358 );
359
360 self
361 }
362
363 pub fn with_network_magic(mut self, network_magic: Magic) -> Self {
365 assert!(
366 [magics::MAINNET, magics::REGTEST]
367 .into_iter()
368 .all(|reserved_magic| network_magic != reserved_magic),
369 "network magic should be distinct from reserved network magics"
370 );
371
372 self.network_magic = network_magic;
373
374 self
375 }
376
377 pub fn with_genesis_hash(mut self, genesis_hash: impl fmt::Display) -> Self {
379 self.genesis_hash = genesis_hash
380 .to_string()
381 .parse()
382 .expect("configured genesis hash must parse");
383 self
384 }
385
386 pub fn with_activation_heights(
389 mut self,
390 ConfiguredActivationHeights {
391 before_overwinter,
392 overwinter,
393 sapling,
394 blossom,
395 heartwood,
396 canopy,
397 nu5,
398 nu6,
399 nu6_1,
400 nu7,
401 }: ConfiguredActivationHeights,
402 ) -> Self {
403 use NetworkUpgrade::*;
404
405 if self.should_lock_funding_stream_address_period {
406 panic!("activation heights on ParametersBuilder must not be set after setting funding streams");
407 }
408
409 let activation_heights: BTreeMap<_, _> = before_overwinter
414 .into_iter()
415 .map(|h| (h, BeforeOverwinter))
416 .chain(overwinter.into_iter().map(|h| (h, Overwinter)))
417 .chain(sapling.into_iter().map(|h| (h, Sapling)))
418 .chain(blossom.into_iter().map(|h| (h, Blossom)))
419 .chain(heartwood.into_iter().map(|h| (h, Heartwood)))
420 .chain(canopy.into_iter().map(|h| (h, Canopy)))
421 .chain(nu5.into_iter().map(|h| (h, Nu5)))
422 .chain(nu6.into_iter().map(|h| (h, Nu6)))
423 .chain(nu6_1.into_iter().map(|h| (h, Nu6_1)))
424 .chain(nu7.into_iter().map(|h| (h, Nu7)))
425 .map(|(h, nu)| (h.try_into().expect("activation height must be valid"), nu))
426 .collect();
427
428 let network_upgrades: Vec<_> = activation_heights.iter().map(|(_h, &nu)| nu).collect();
429
430 let mut activation_heights_iter = activation_heights.iter();
432 for expected_network_upgrade in NetworkUpgrade::iter() {
433 if !network_upgrades.contains(&expected_network_upgrade) {
434 continue;
435 } else if let Some((&height, &network_upgrade)) = activation_heights_iter.next() {
436 assert_ne!(
437 height,
438 Height(0),
439 "Height(0) is reserved for the `Genesis` upgrade"
440 );
441
442 assert!(
443 network_upgrade == expected_network_upgrade,
444 "network upgrades must be activated in order specified by the protocol"
445 );
446 }
447 }
448
449 self.activation_heights.split_off(&Height(1));
453 self.activation_heights.extend(activation_heights);
454
455 self
456 }
457
458 pub fn with_slow_start_interval(mut self, slow_start_interval: Height) -> Self {
460 self.slow_start_interval = slow_start_interval;
461 self
462 }
463
464 pub fn with_pre_nu6_funding_streams(
466 mut self,
467 funding_streams: ConfiguredFundingStreams,
468 ) -> Self {
469 self.pre_nu6_funding_streams =
470 funding_streams.convert_with_default(PRE_NU6_FUNDING_STREAMS_TESTNET.clone(), &self);
471 self.should_lock_funding_stream_address_period = true;
472 self
473 }
474
475 pub fn with_post_nu6_funding_streams(
477 mut self,
478 funding_streams: ConfiguredFundingStreams,
479 ) -> Self {
480 self.post_nu6_funding_streams =
481 funding_streams.convert_with_default(POST_NU6_FUNDING_STREAMS_TESTNET.clone(), &self);
482 self.should_lock_funding_stream_address_period = true;
483 self
484 }
485
486 pub fn with_target_difficulty_limit(
489 mut self,
490 target_difficulty_limit: impl Into<ExpandedDifficulty>,
491 ) -> Self {
492 self.target_difficulty_limit = target_difficulty_limit
493 .into()
494 .to_compact()
495 .to_expanded()
496 .expect("difficulty limits are valid expanded values");
497 self
498 }
499
500 pub fn with_disable_pow(mut self, disable_pow: bool) -> Self {
502 self.disable_pow = disable_pow;
503 self
504 }
505
506 pub fn with_unshielded_coinbase_spends(
508 mut self,
509 should_allow_unshielded_coinbase_spends: bool,
510 ) -> Self {
511 self.should_allow_unshielded_coinbase_spends = should_allow_unshielded_coinbase_spends;
512 self
513 }
514
515 pub fn with_halving_interval(mut self, pre_blossom_halving_interval: HeightDiff) -> Self {
517 if self.should_lock_funding_stream_address_period {
518 panic!("halving interval on ParametersBuilder must not be set after setting funding streams");
519 }
520
521 self.pre_blossom_halving_interval = pre_blossom_halving_interval;
522 self.post_blossom_halving_interval =
523 self.pre_blossom_halving_interval * (BLOSSOM_POW_TARGET_SPACING_RATIO as HeightDiff);
524 self
525 }
526
527 fn finish(self) -> Parameters {
529 let Self {
530 network_name,
531 network_magic,
532 genesis_hash,
533 activation_heights,
534 slow_start_interval,
535 pre_nu6_funding_streams,
536 post_nu6_funding_streams,
537 should_lock_funding_stream_address_period: _,
538 target_difficulty_limit,
539 disable_pow,
540 should_allow_unshielded_coinbase_spends,
541 pre_blossom_halving_interval,
542 post_blossom_halving_interval,
543 } = self;
544 Parameters {
545 network_name,
546 network_magic,
547 genesis_hash,
548 activation_heights,
549 slow_start_interval,
550 slow_start_shift: Height(slow_start_interval.0 / 2),
551 pre_nu6_funding_streams,
552 post_nu6_funding_streams,
553 target_difficulty_limit,
554 disable_pow,
555 should_allow_unshielded_coinbase_spends,
556 pre_blossom_halving_interval,
557 post_blossom_halving_interval,
558 }
559 }
560
561 fn to_network_unchecked(&self) -> Network {
563 Network::new_configured_testnet(self.clone().finish())
564 }
565
566 pub fn to_network(self) -> Network {
568 let network = self.to_network_unchecked();
569
570 #[cfg(not(any(test, feature = "proptest-impl")))]
573 {
574 check_funding_stream_address_period(&self.pre_nu6_funding_streams, &network);
575 check_funding_stream_address_period(&self.post_nu6_funding_streams, &network);
576 }
577
578 network
579 }
580
581 pub fn is_compatible_with_default_parameters(&self) -> bool {
583 let Self {
584 network_name: _,
585 network_magic,
586 genesis_hash,
587 activation_heights,
588 slow_start_interval,
589 pre_nu6_funding_streams,
590 post_nu6_funding_streams,
591 should_lock_funding_stream_address_period: _,
592 target_difficulty_limit,
593 disable_pow,
594 should_allow_unshielded_coinbase_spends,
595 pre_blossom_halving_interval,
596 post_blossom_halving_interval,
597 } = Self::default();
598
599 self.activation_heights == activation_heights
600 && self.network_magic == network_magic
601 && self.genesis_hash == genesis_hash
602 && self.slow_start_interval == slow_start_interval
603 && self.pre_nu6_funding_streams == pre_nu6_funding_streams
604 && self.post_nu6_funding_streams == post_nu6_funding_streams
605 && self.target_difficulty_limit == target_difficulty_limit
606 && self.disable_pow == disable_pow
607 && self.should_allow_unshielded_coinbase_spends
608 == should_allow_unshielded_coinbase_spends
609 && self.pre_blossom_halving_interval == pre_blossom_halving_interval
610 && self.post_blossom_halving_interval == post_blossom_halving_interval
611 }
612}
613
614#[derive(Clone, Debug, Eq, PartialEq)]
616pub struct Parameters {
617 network_name: String,
619 network_magic: Magic,
621 genesis_hash: block::Hash,
623 activation_heights: BTreeMap<Height, NetworkUpgrade>,
629 slow_start_interval: Height,
631 slow_start_shift: Height,
633 pre_nu6_funding_streams: FundingStreams,
635 post_nu6_funding_streams: FundingStreams,
637 target_difficulty_limit: ExpandedDifficulty,
639 disable_pow: bool,
641 should_allow_unshielded_coinbase_spends: bool,
644 pre_blossom_halving_interval: HeightDiff,
646 post_blossom_halving_interval: HeightDiff,
648}
649
650impl Default for Parameters {
651 fn default() -> Self {
653 Self {
654 network_name: "Testnet".to_string(),
655 ..Self::build().finish()
656 }
657 }
658}
659
660impl Parameters {
661 pub fn build() -> ParametersBuilder {
663 ParametersBuilder::default()
664 }
665
666 pub fn new_regtest(
670 ConfiguredActivationHeights { nu5, nu6, nu7, .. }: ConfiguredActivationHeights,
671 ) -> Self {
672 #[cfg(any(test, feature = "proptest-impl"))]
673 let nu5 = nu5.or(Some(100));
674
675 let parameters = Self::build()
676 .with_genesis_hash(REGTEST_GENESIS_HASH)
677 .with_target_difficulty_limit(U256::from_big_endian(&[0x0f; 32]))
679 .with_disable_pow(true)
680 .with_unshielded_coinbase_spends(true)
681 .with_slow_start_interval(Height::MIN)
682 .with_activation_heights(ConfiguredActivationHeights {
685 canopy: Some(1),
686 nu5,
687 nu6,
688 nu7,
689 ..Default::default()
690 })
691 .with_halving_interval(PRE_BLOSSOM_REGTEST_HALVING_INTERVAL);
692
693 let parameters = parameters
696 .with_pre_nu6_funding_streams(ConfiguredFundingStreams::empty())
697 .with_post_nu6_funding_streams(ConfiguredFundingStreams::empty());
698
699 Self {
700 network_name: "Regtest".to_string(),
701 network_magic: magics::REGTEST,
702 ..parameters.finish()
703 }
704 }
705
706 pub fn is_default_testnet(&self) -> bool {
708 self == &Self::default()
709 }
710
711 pub fn is_regtest(&self) -> bool {
713 if self.network_magic != magics::REGTEST {
714 return false;
715 }
716
717 let Self {
718 network_name,
719 network_magic: _,
721 genesis_hash,
722 activation_heights: _,
724 slow_start_interval,
725 slow_start_shift,
726 pre_nu6_funding_streams,
727 post_nu6_funding_streams,
728 target_difficulty_limit,
729 disable_pow,
730 should_allow_unshielded_coinbase_spends,
731 pre_blossom_halving_interval,
732 post_blossom_halving_interval,
733 } = Self::new_regtest(Default::default());
734
735 self.network_name == network_name
736 && self.genesis_hash == genesis_hash
737 && self.slow_start_interval == slow_start_interval
738 && self.slow_start_shift == slow_start_shift
739 && self.pre_nu6_funding_streams == pre_nu6_funding_streams
740 && self.post_nu6_funding_streams == post_nu6_funding_streams
741 && self.target_difficulty_limit == target_difficulty_limit
742 && self.disable_pow == disable_pow
743 && self.should_allow_unshielded_coinbase_spends
744 == should_allow_unshielded_coinbase_spends
745 && self.pre_blossom_halving_interval == pre_blossom_halving_interval
746 && self.post_blossom_halving_interval == post_blossom_halving_interval
747 }
748
749 pub fn network_name(&self) -> &str {
751 &self.network_name
752 }
753
754 pub fn network_magic(&self) -> Magic {
756 self.network_magic
757 }
758
759 pub fn genesis_hash(&self) -> block::Hash {
761 self.genesis_hash
762 }
763
764 pub fn activation_heights(&self) -> &BTreeMap<Height, NetworkUpgrade> {
766 &self.activation_heights
767 }
768
769 pub fn slow_start_interval(&self) -> Height {
771 self.slow_start_interval
772 }
773
774 pub fn slow_start_shift(&self) -> Height {
776 self.slow_start_shift
777 }
778
779 pub fn pre_nu6_funding_streams(&self) -> &FundingStreams {
781 &self.pre_nu6_funding_streams
782 }
783
784 pub fn post_nu6_funding_streams(&self) -> &FundingStreams {
786 &self.post_nu6_funding_streams
787 }
788
789 pub fn target_difficulty_limit(&self) -> ExpandedDifficulty {
791 self.target_difficulty_limit
792 }
793
794 pub fn disable_pow(&self) -> bool {
796 self.disable_pow
797 }
798
799 pub fn should_allow_unshielded_coinbase_spends(&self) -> bool {
802 self.should_allow_unshielded_coinbase_spends
803 }
804
805 pub fn pre_blossom_halving_interval(&self) -> HeightDiff {
807 self.pre_blossom_halving_interval
808 }
809
810 pub fn post_blossom_halving_interval(&self) -> HeightDiff {
812 self.post_blossom_halving_interval
813 }
814}
815
816impl Network {
817 pub fn parameters(&self) -> Option<Arc<Parameters>> {
819 if let Self::Testnet(parameters) = self {
820 Some(parameters.clone())
821 } else {
822 None
823 }
824 }
825
826 pub fn disable_pow(&self) -> bool {
828 if let Self::Testnet(params) = self {
829 params.disable_pow()
830 } else {
831 false
832 }
833 }
834
835 pub fn slow_start_interval(&self) -> Height {
837 if let Self::Testnet(params) = self {
838 params.slow_start_interval()
839 } else {
840 SLOW_START_INTERVAL
841 }
842 }
843
844 pub fn slow_start_shift(&self) -> Height {
846 if let Self::Testnet(params) = self {
847 params.slow_start_shift()
848 } else {
849 SLOW_START_SHIFT
850 }
851 }
852
853 pub fn pre_nu6_funding_streams(&self) -> &FundingStreams {
861 if let Self::Testnet(params) = self {
862 params.pre_nu6_funding_streams()
863 } else {
864 &PRE_NU6_FUNDING_STREAMS_MAINNET
865 }
866 }
867
868 pub fn post_nu6_funding_streams(&self) -> &FundingStreams {
874 if let Self::Testnet(params) = self {
875 params.post_nu6_funding_streams()
876 } else {
877 &POST_NU6_FUNDING_STREAMS_MAINNET
878 }
879 }
880
881 pub fn funding_streams(&self, height: Height) -> &FundingStreams {
883 if NetworkUpgrade::current(self, height) < NetworkUpgrade::Nu6 {
884 self.pre_nu6_funding_streams()
885 } else {
886 self.post_nu6_funding_streams()
887 }
888 }
889
890 pub fn should_allow_unshielded_coinbase_spends(&self) -> bool {
893 if let Self::Testnet(params) = self {
894 params.should_allow_unshielded_coinbase_spends()
895 } else {
896 false
897 }
898 }
899}