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
26#[cfg(any(test, feature = "proptest-impl"))]
29pub const REGTEST_NU5_ACTIVATION_HEIGHT: u32 = 100;
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 into_recipient(self) -> (FundingStreamReceiver, FundingStreamRecipient) {
74 (
75 self.receiver,
76 FundingStreamRecipient::new(self.numerator, self.addresses.unwrap_or_default()),
77 )
78 }
79}
80
81#[derive(Serialize, Deserialize, Clone, Default, Debug)]
83#[serde(deny_unknown_fields)]
84pub struct ConfiguredFundingStreams {
85 pub height_range: Option<std::ops::Range<Height>>,
87 pub recipients: Option<Vec<ConfiguredFundingStreamRecipient>>,
89}
90
91impl From<&FundingStreams> for ConfiguredFundingStreams {
92 fn from(value: &FundingStreams) -> Self {
93 Self {
94 height_range: Some(value.height_range().clone()),
95 recipients: Some(
96 value
97 .recipients()
98 .iter()
99 .map(|(receiver, recipient)| ConfiguredFundingStreamRecipient {
100 receiver: *receiver,
101 numerator: recipient.numerator(),
102 addresses: Some(
103 recipient
104 .addresses()
105 .iter()
106 .map(ToString::to_string)
107 .collect(),
108 ),
109 })
110 .collect(),
111 ),
112 }
113 }
114}
115
116impl From<&BTreeMap<Height, NetworkUpgrade>> for ConfiguredActivationHeights {
117 fn from(activation_heights: &BTreeMap<Height, NetworkUpgrade>) -> Self {
118 let mut configured_activation_heights = ConfiguredActivationHeights::default();
119
120 for (height, network_upgrade) in activation_heights.iter() {
121 let field = match network_upgrade {
122 NetworkUpgrade::BeforeOverwinter => {
123 &mut configured_activation_heights.before_overwinter
124 }
125 NetworkUpgrade::Overwinter => &mut configured_activation_heights.overwinter,
126 NetworkUpgrade::Sapling => &mut configured_activation_heights.sapling,
127 NetworkUpgrade::Blossom => &mut configured_activation_heights.blossom,
128 NetworkUpgrade::Heartwood => &mut configured_activation_heights.heartwood,
129 NetworkUpgrade::Canopy => &mut configured_activation_heights.canopy,
130 NetworkUpgrade::Nu5 => &mut configured_activation_heights.nu5,
131 NetworkUpgrade::Nu6 => &mut configured_activation_heights.nu6,
132 NetworkUpgrade::Nu6_1 => &mut configured_activation_heights.nu6_1,
133 NetworkUpgrade::Nu7 => &mut configured_activation_heights.nu7,
134 NetworkUpgrade::Genesis => {
135 continue;
136 }
137 };
138
139 *field = Some(height.0)
140 }
141
142 configured_activation_heights
143 }
144}
145
146impl ConfiguredFundingStreams {
147 fn empty() -> Self {
149 Self {
150 height_range: None,
151 recipients: Some(Vec::new()),
152 }
153 }
154
155 fn convert_with_default(
158 self,
159 default_funding_streams: FundingStreams,
160 parameters_builder: &ParametersBuilder,
161 ) -> FundingStreams {
162 let network = parameters_builder.to_network_unchecked();
163 let height_range = self
164 .height_range
165 .unwrap_or(default_funding_streams.height_range().clone());
166
167 let recipients = self
168 .recipients
169 .map(|recipients| {
170 recipients
171 .into_iter()
172 .map(ConfiguredFundingStreamRecipient::into_recipient)
173 .collect()
174 })
175 .unwrap_or(default_funding_streams.recipients().clone());
176
177 assert!(
178 height_range.start < height_range.end,
179 "funding stream end height must be above start height"
180 );
181
182 let funding_streams = FundingStreams::new(height_range.clone(), recipients);
183
184 check_funding_stream_address_period(&funding_streams, &network);
185
186 let sum_numerators: u64 = funding_streams
189 .recipients()
190 .values()
191 .map(|r| r.numerator())
192 .sum();
193
194 assert!(
195 sum_numerators <= FUNDING_STREAM_RECEIVER_DENOMINATOR,
196 "sum of funding stream numerators must not be \
197 greater than denominator of {FUNDING_STREAM_RECEIVER_DENOMINATOR}"
198 );
199
200 funding_streams
201 }
202}
203
204fn check_funding_stream_address_period(funding_streams: &FundingStreams, network: &Network) {
207 let height_range = funding_streams.height_range();
208 let expected_min_num_addresses =
209 1u32.checked_add(funding_stream_address_period(
210 height_range
211 .end
212 .previous()
213 .expect("end height must be above start height and genesis height"),
214 network,
215 ))
216 .expect("no overflow should happen in this sum")
217 .checked_sub(funding_stream_address_period(height_range.start, network))
218 .expect("no overflow should happen in this sub") as usize;
219
220 for (&receiver, recipient) in funding_streams.recipients() {
221 if receiver == FundingStreamReceiver::Deferred {
222 continue;
224 }
225
226 assert!(
227 recipient.addresses().len() >= expected_min_num_addresses,
228 "recipients must have a sufficient number of addresses for height range, \
229 minimum num addresses required: {expected_min_num_addresses}"
230 );
231
232 for address in recipient.addresses() {
233 assert_eq!(
234 address.network_kind(),
235 NetworkKind::Testnet,
236 "configured funding stream addresses must be for Testnet"
237 );
238 }
239 }
240}
241
242#[derive(Serialize, Deserialize, Default, Clone)]
244#[serde(rename_all = "PascalCase", deny_unknown_fields)]
245pub struct ConfiguredActivationHeights {
246 pub before_overwinter: Option<u32>,
248 pub overwinter: Option<u32>,
250 pub sapling: Option<u32>,
252 pub blossom: Option<u32>,
254 pub heartwood: Option<u32>,
256 pub canopy: Option<u32>,
258 #[serde(rename = "NU5")]
260 pub nu5: Option<u32>,
261 #[serde(rename = "NU6")]
263 pub nu6: Option<u32>,
264 #[serde(rename = "NU6.1")]
266 pub nu6_1: Option<u32>,
267 #[serde(rename = "NU7")]
269 pub nu7: Option<u32>,
270}
271
272#[derive(Clone, Debug, Eq, PartialEq)]
274pub struct ParametersBuilder {
275 network_name: String,
277 network_magic: Magic,
279 genesis_hash: block::Hash,
281 activation_heights: BTreeMap<Height, NetworkUpgrade>,
283 slow_start_interval: Height,
285 pre_nu6_funding_streams: FundingStreams,
287 post_nu6_funding_streams: FundingStreams,
289 should_lock_funding_stream_address_period: bool,
292 target_difficulty_limit: ExpandedDifficulty,
294 disable_pow: bool,
296 should_allow_unshielded_coinbase_spends: bool,
299 pre_blossom_halving_interval: HeightDiff,
301 post_blossom_halving_interval: HeightDiff,
303}
304
305impl Default for ParametersBuilder {
306 fn default() -> Self {
308 Self {
309 network_name: "UnknownTestnet".to_string(),
310 network_magic: magics::TESTNET,
311 activation_heights: TESTNET_ACTIVATION_HEIGHTS.iter().cloned().collect(),
315 genesis_hash: TESTNET_GENESIS_HASH
316 .parse()
317 .expect("hard-coded hash parses"),
318 slow_start_interval: SLOW_START_INTERVAL,
319 target_difficulty_limit: ExpandedDifficulty::from((U256::one() << 251) - 1)
329 .to_compact()
330 .to_expanded()
331 .expect("difficulty limits are valid expanded values"),
332 disable_pow: false,
333 pre_nu6_funding_streams: PRE_NU6_FUNDING_STREAMS_TESTNET.clone(),
334 post_nu6_funding_streams: POST_NU6_FUNDING_STREAMS_TESTNET.clone(),
335 should_lock_funding_stream_address_period: false,
336 pre_blossom_halving_interval: PRE_BLOSSOM_HALVING_INTERVAL,
337 post_blossom_halving_interval: POST_BLOSSOM_HALVING_INTERVAL,
338 should_allow_unshielded_coinbase_spends: false,
339 }
340 }
341}
342
343impl ParametersBuilder {
344 pub fn with_network_name(mut self, network_name: impl fmt::Display) -> Self {
346 self.network_name = network_name.to_string();
347
348 assert!(
349 !RESERVED_NETWORK_NAMES.contains(&self.network_name.as_str()),
350 "cannot use reserved network name '{network_name}' as configured Testnet name, reserved names: {RESERVED_NETWORK_NAMES:?}"
351 );
352
353 assert!(
354 self.network_name.len() <= MAX_NETWORK_NAME_LENGTH,
355 "network name {network_name} is too long, must be {MAX_NETWORK_NAME_LENGTH} characters or less"
356 );
357
358 assert!(
359 self.network_name
360 .chars()
361 .all(|x| x.is_alphanumeric() || x == '_'),
362 "network name must include only alphanumeric characters or '_'"
363 );
364
365 self
366 }
367
368 pub fn with_network_magic(mut self, network_magic: Magic) -> Self {
370 assert!(
371 [magics::MAINNET, magics::REGTEST]
372 .into_iter()
373 .all(|reserved_magic| network_magic != reserved_magic),
374 "network magic should be distinct from reserved network magics"
375 );
376
377 self.network_magic = network_magic;
378
379 self
380 }
381
382 pub fn with_genesis_hash(mut self, genesis_hash: impl fmt::Display) -> Self {
384 self.genesis_hash = genesis_hash
385 .to_string()
386 .parse()
387 .expect("configured genesis hash must parse");
388 self
389 }
390
391 pub fn with_activation_heights(
394 mut self,
395 ConfiguredActivationHeights {
396 before_overwinter,
397 overwinter,
398 sapling,
399 blossom,
400 heartwood,
401 canopy,
402 nu5,
403 nu6,
404 nu6_1,
405 nu7,
406 }: ConfiguredActivationHeights,
407 ) -> Self {
408 use NetworkUpgrade::*;
409
410 if self.should_lock_funding_stream_address_period {
411 panic!("activation heights on ParametersBuilder must not be set after setting funding streams");
412 }
413
414 let activation_heights: BTreeMap<_, _> = before_overwinter
419 .into_iter()
420 .map(|h| (h, BeforeOverwinter))
421 .chain(overwinter.into_iter().map(|h| (h, Overwinter)))
422 .chain(sapling.into_iter().map(|h| (h, Sapling)))
423 .chain(blossom.into_iter().map(|h| (h, Blossom)))
424 .chain(heartwood.into_iter().map(|h| (h, Heartwood)))
425 .chain(canopy.into_iter().map(|h| (h, Canopy)))
426 .chain(nu5.into_iter().map(|h| (h, Nu5)))
427 .chain(nu6.into_iter().map(|h| (h, Nu6)))
428 .chain(nu6_1.into_iter().map(|h| (h, Nu6_1)))
429 .chain(nu7.into_iter().map(|h| (h, Nu7)))
430 .map(|(h, nu)| (h.try_into().expect("activation height must be valid"), nu))
431 .collect();
432
433 let network_upgrades: Vec<_> = activation_heights.iter().map(|(_h, &nu)| nu).collect();
434
435 let mut activation_heights_iter = activation_heights.iter();
437 for expected_network_upgrade in NetworkUpgrade::iter() {
438 if !network_upgrades.contains(&expected_network_upgrade) {
439 continue;
440 } else if let Some((&height, &network_upgrade)) = activation_heights_iter.next() {
441 assert_ne!(
442 height,
443 Height(0),
444 "Height(0) is reserved for the `Genesis` upgrade"
445 );
446
447 assert!(
448 network_upgrade == expected_network_upgrade,
449 "network upgrades must be activated in order specified by the protocol"
450 );
451 }
452 }
453
454 self.activation_heights.split_off(&Height(1));
458 self.activation_heights.extend(activation_heights);
459
460 self
461 }
462
463 pub fn with_slow_start_interval(mut self, slow_start_interval: Height) -> Self {
465 self.slow_start_interval = slow_start_interval;
466 self
467 }
468
469 pub fn with_pre_nu6_funding_streams(
471 mut self,
472 funding_streams: ConfiguredFundingStreams,
473 ) -> Self {
474 self.pre_nu6_funding_streams =
475 funding_streams.convert_with_default(PRE_NU6_FUNDING_STREAMS_TESTNET.clone(), &self);
476 self.should_lock_funding_stream_address_period = true;
477 self
478 }
479
480 pub fn with_post_nu6_funding_streams(
482 mut self,
483 funding_streams: ConfiguredFundingStreams,
484 ) -> Self {
485 self.post_nu6_funding_streams =
486 funding_streams.convert_with_default(POST_NU6_FUNDING_STREAMS_TESTNET.clone(), &self);
487 self.should_lock_funding_stream_address_period = true;
488 self
489 }
490
491 pub fn with_target_difficulty_limit(
494 mut self,
495 target_difficulty_limit: impl Into<ExpandedDifficulty>,
496 ) -> Self {
497 self.target_difficulty_limit = target_difficulty_limit
498 .into()
499 .to_compact()
500 .to_expanded()
501 .expect("difficulty limits are valid expanded values");
502 self
503 }
504
505 pub fn with_disable_pow(mut self, disable_pow: bool) -> Self {
507 self.disable_pow = disable_pow;
508 self
509 }
510
511 pub fn with_unshielded_coinbase_spends(
513 mut self,
514 should_allow_unshielded_coinbase_spends: bool,
515 ) -> Self {
516 self.should_allow_unshielded_coinbase_spends = should_allow_unshielded_coinbase_spends;
517 self
518 }
519
520 pub fn with_halving_interval(mut self, pre_blossom_halving_interval: HeightDiff) -> Self {
522 if self.should_lock_funding_stream_address_period {
523 panic!("halving interval on ParametersBuilder must not be set after setting funding streams");
524 }
525
526 self.pre_blossom_halving_interval = pre_blossom_halving_interval;
527 self.post_blossom_halving_interval =
528 self.pre_blossom_halving_interval * (BLOSSOM_POW_TARGET_SPACING_RATIO as HeightDiff);
529 self
530 }
531
532 fn finish(self) -> Parameters {
534 let Self {
535 network_name,
536 network_magic,
537 genesis_hash,
538 activation_heights,
539 slow_start_interval,
540 pre_nu6_funding_streams,
541 post_nu6_funding_streams,
542 should_lock_funding_stream_address_period: _,
543 target_difficulty_limit,
544 disable_pow,
545 should_allow_unshielded_coinbase_spends,
546 pre_blossom_halving_interval,
547 post_blossom_halving_interval,
548 } = self;
549 Parameters {
550 network_name,
551 network_magic,
552 genesis_hash,
553 activation_heights,
554 slow_start_interval,
555 slow_start_shift: Height(slow_start_interval.0 / 2),
556 pre_nu6_funding_streams,
557 post_nu6_funding_streams,
558 target_difficulty_limit,
559 disable_pow,
560 should_allow_unshielded_coinbase_spends,
561 pre_blossom_halving_interval,
562 post_blossom_halving_interval,
563 }
564 }
565
566 fn to_network_unchecked(&self) -> Network {
568 Network::new_configured_testnet(self.clone().finish())
569 }
570
571 pub fn to_network(self) -> Network {
573 let network = self.to_network_unchecked();
574
575 #[cfg(not(any(test, feature = "proptest-impl")))]
578 {
579 check_funding_stream_address_period(&self.pre_nu6_funding_streams, &network);
580 check_funding_stream_address_period(&self.post_nu6_funding_streams, &network);
581 }
582
583 network
584 }
585
586 pub fn is_compatible_with_default_parameters(&self) -> bool {
588 let Self {
589 network_name: _,
590 network_magic,
591 genesis_hash,
592 activation_heights,
593 slow_start_interval,
594 pre_nu6_funding_streams,
595 post_nu6_funding_streams,
596 should_lock_funding_stream_address_period: _,
597 target_difficulty_limit,
598 disable_pow,
599 should_allow_unshielded_coinbase_spends,
600 pre_blossom_halving_interval,
601 post_blossom_halving_interval,
602 } = Self::default();
603
604 self.activation_heights == activation_heights
605 && self.network_magic == network_magic
606 && self.genesis_hash == genesis_hash
607 && self.slow_start_interval == slow_start_interval
608 && self.pre_nu6_funding_streams == pre_nu6_funding_streams
609 && self.post_nu6_funding_streams == post_nu6_funding_streams
610 && self.target_difficulty_limit == target_difficulty_limit
611 && self.disable_pow == disable_pow
612 && self.should_allow_unshielded_coinbase_spends
613 == should_allow_unshielded_coinbase_spends
614 && self.pre_blossom_halving_interval == pre_blossom_halving_interval
615 && self.post_blossom_halving_interval == post_blossom_halving_interval
616 }
617}
618
619#[derive(Clone, Debug, Eq, PartialEq)]
621pub struct Parameters {
622 network_name: String,
624 network_magic: Magic,
626 genesis_hash: block::Hash,
628 activation_heights: BTreeMap<Height, NetworkUpgrade>,
634 slow_start_interval: Height,
636 slow_start_shift: Height,
638 pre_nu6_funding_streams: FundingStreams,
640 post_nu6_funding_streams: FundingStreams,
642 target_difficulty_limit: ExpandedDifficulty,
644 disable_pow: bool,
646 should_allow_unshielded_coinbase_spends: bool,
649 pre_blossom_halving_interval: HeightDiff,
651 post_blossom_halving_interval: HeightDiff,
653}
654
655impl Default for Parameters {
656 fn default() -> Self {
658 Self {
659 network_name: "Testnet".to_string(),
660 ..Self::build().finish()
661 }
662 }
663}
664
665impl Parameters {
666 pub fn build() -> ParametersBuilder {
668 ParametersBuilder::default()
669 }
670
671 pub fn new_regtest(
675 ConfiguredActivationHeights { nu5, nu6, nu7, .. }: ConfiguredActivationHeights,
676 ) -> Self {
677 #[cfg(any(test, feature = "proptest-impl"))]
678 let nu5 = nu5.or(Some(100));
679
680 let parameters = Self::build()
681 .with_genesis_hash(REGTEST_GENESIS_HASH)
682 .with_target_difficulty_limit(U256::from_big_endian(&[0x0f; 32]))
684 .with_disable_pow(true)
685 .with_unshielded_coinbase_spends(true)
686 .with_slow_start_interval(Height::MIN)
687 .with_activation_heights(ConfiguredActivationHeights {
690 canopy: Some(1),
691 nu5,
692 nu6,
693 nu7,
694 ..Default::default()
695 })
696 .with_halving_interval(PRE_BLOSSOM_REGTEST_HALVING_INTERVAL);
697
698 let parameters = parameters
701 .with_pre_nu6_funding_streams(ConfiguredFundingStreams::empty())
702 .with_post_nu6_funding_streams(ConfiguredFundingStreams::empty());
703
704 Self {
705 network_name: "Regtest".to_string(),
706 network_magic: magics::REGTEST,
707 ..parameters.finish()
708 }
709 }
710
711 pub fn is_default_testnet(&self) -> bool {
713 self == &Self::default()
714 }
715
716 pub fn is_regtest(&self) -> bool {
718 if self.network_magic != magics::REGTEST {
719 return false;
720 }
721
722 let Self {
723 network_name,
724 network_magic: _,
726 genesis_hash,
727 activation_heights: _,
729 slow_start_interval,
730 slow_start_shift,
731 pre_nu6_funding_streams,
732 post_nu6_funding_streams,
733 target_difficulty_limit,
734 disable_pow,
735 should_allow_unshielded_coinbase_spends,
736 pre_blossom_halving_interval,
737 post_blossom_halving_interval,
738 } = Self::new_regtest(Default::default());
739
740 self.network_name == network_name
741 && self.genesis_hash == genesis_hash
742 && self.slow_start_interval == slow_start_interval
743 && self.slow_start_shift == slow_start_shift
744 && self.pre_nu6_funding_streams == pre_nu6_funding_streams
745 && self.post_nu6_funding_streams == post_nu6_funding_streams
746 && self.target_difficulty_limit == target_difficulty_limit
747 && self.disable_pow == disable_pow
748 && self.should_allow_unshielded_coinbase_spends
749 == should_allow_unshielded_coinbase_spends
750 && self.pre_blossom_halving_interval == pre_blossom_halving_interval
751 && self.post_blossom_halving_interval == post_blossom_halving_interval
752 }
753
754 pub fn network_name(&self) -> &str {
756 &self.network_name
757 }
758
759 pub fn network_magic(&self) -> Magic {
761 self.network_magic
762 }
763
764 pub fn genesis_hash(&self) -> block::Hash {
766 self.genesis_hash
767 }
768
769 pub fn activation_heights(&self) -> &BTreeMap<Height, NetworkUpgrade> {
771 &self.activation_heights
772 }
773
774 pub fn slow_start_interval(&self) -> Height {
776 self.slow_start_interval
777 }
778
779 pub fn slow_start_shift(&self) -> Height {
781 self.slow_start_shift
782 }
783
784 pub fn pre_nu6_funding_streams(&self) -> &FundingStreams {
786 &self.pre_nu6_funding_streams
787 }
788
789 pub fn post_nu6_funding_streams(&self) -> &FundingStreams {
791 &self.post_nu6_funding_streams
792 }
793
794 pub fn target_difficulty_limit(&self) -> ExpandedDifficulty {
796 self.target_difficulty_limit
797 }
798
799 pub fn disable_pow(&self) -> bool {
801 self.disable_pow
802 }
803
804 pub fn should_allow_unshielded_coinbase_spends(&self) -> bool {
807 self.should_allow_unshielded_coinbase_spends
808 }
809
810 pub fn pre_blossom_halving_interval(&self) -> HeightDiff {
812 self.pre_blossom_halving_interval
813 }
814
815 pub fn post_blossom_halving_interval(&self) -> HeightDiff {
817 self.post_blossom_halving_interval
818 }
819}
820
821impl Network {
822 pub fn parameters(&self) -> Option<Arc<Parameters>> {
824 if let Self::Testnet(parameters) = self {
825 Some(parameters.clone())
826 } else {
827 None
828 }
829 }
830
831 pub fn disable_pow(&self) -> bool {
833 if let Self::Testnet(params) = self {
834 params.disable_pow()
835 } else {
836 false
837 }
838 }
839
840 pub fn slow_start_interval(&self) -> Height {
842 if let Self::Testnet(params) = self {
843 params.slow_start_interval()
844 } else {
845 SLOW_START_INTERVAL
846 }
847 }
848
849 pub fn slow_start_shift(&self) -> Height {
851 if let Self::Testnet(params) = self {
852 params.slow_start_shift()
853 } else {
854 SLOW_START_SHIFT
855 }
856 }
857
858 pub fn pre_nu6_funding_streams(&self) -> &FundingStreams {
866 if let Self::Testnet(params) = self {
867 params.pre_nu6_funding_streams()
868 } else {
869 &PRE_NU6_FUNDING_STREAMS_MAINNET
870 }
871 }
872
873 pub fn post_nu6_funding_streams(&self) -> &FundingStreams {
879 if let Self::Testnet(params) = self {
880 params.post_nu6_funding_streams()
881 } else {
882 &POST_NU6_FUNDING_STREAMS_MAINNET
883 }
884 }
885
886 pub fn funding_streams(&self, height: Height) -> &FundingStreams {
888 if NetworkUpgrade::current(self, height) < NetworkUpgrade::Nu6 {
889 self.pre_nu6_funding_streams()
890 } else {
891 self.post_nu6_funding_streams()
892 }
893 }
894
895 pub fn should_allow_unshielded_coinbase_spends(&self) -> bool {
898 if let Self::Testnet(params) = self {
899 params.should_allow_unshielded_coinbase_spends()
900 } else {
901 false
902 }
903 }
904}