1use core::cmp::{Ord, Ordering};
4use core::convert::TryFrom;
5use core::fmt;
6use core::ops::{Add, Bound, RangeBounds, Sub};
7
8#[cfg(feature = "std")]
9use memuse::DynamicUsage;
10
11use crate::constants::{mainnet, regtest, testnet};
12
13#[repr(transparent)]
18#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
19pub struct BlockHeight(u32);
20
21#[cfg(feature = "std")]
22memuse::impl_no_dynamic_usage!(BlockHeight);
23
24pub const H0: BlockHeight = BlockHeight(0);
26
27impl BlockHeight {
28 pub const fn from_u32(v: u32) -> BlockHeight {
29 BlockHeight(v)
30 }
31
32 pub fn saturating_sub(self, v: u32) -> BlockHeight {
35 BlockHeight(self.0.saturating_sub(v))
36 }
37}
38
39impl fmt::Display for BlockHeight {
40 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
41 self.0.fmt(formatter)
42 }
43}
44
45impl Ord for BlockHeight {
46 fn cmp(&self, other: &Self) -> Ordering {
47 self.0.cmp(&other.0)
48 }
49}
50
51impl PartialOrd for BlockHeight {
52 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
53 Some(self.cmp(other))
54 }
55}
56
57impl From<u32> for BlockHeight {
58 fn from(value: u32) -> Self {
59 BlockHeight(value)
60 }
61}
62
63impl From<BlockHeight> for u32 {
64 fn from(value: BlockHeight) -> u32 {
65 value.0
66 }
67}
68
69impl TryFrom<u64> for BlockHeight {
70 type Error = core::num::TryFromIntError;
71
72 fn try_from(value: u64) -> Result<Self, Self::Error> {
73 u32::try_from(value).map(BlockHeight)
74 }
75}
76
77impl From<BlockHeight> for u64 {
78 fn from(value: BlockHeight) -> u64 {
79 value.0 as u64
80 }
81}
82
83impl TryFrom<i32> for BlockHeight {
84 type Error = core::num::TryFromIntError;
85
86 fn try_from(value: i32) -> Result<Self, Self::Error> {
87 u32::try_from(value).map(BlockHeight)
88 }
89}
90
91impl TryFrom<i64> for BlockHeight {
92 type Error = core::num::TryFromIntError;
93
94 fn try_from(value: i64) -> Result<Self, Self::Error> {
95 u32::try_from(value).map(BlockHeight)
96 }
97}
98
99impl From<BlockHeight> for i64 {
100 fn from(value: BlockHeight) -> i64 {
101 value.0 as i64
102 }
103}
104
105impl Add<u32> for BlockHeight {
106 type Output = Self;
107
108 fn add(self, other: u32) -> Self {
109 BlockHeight(self.0.saturating_add(other))
110 }
111}
112
113impl Sub<u32> for BlockHeight {
114 type Output = Self;
115
116 fn sub(self, other: u32) -> Self {
117 BlockHeight(self.0.saturating_sub(other))
118 }
119}
120
121impl Sub<BlockHeight> for BlockHeight {
122 type Output = u32;
123
124 fn sub(self, other: BlockHeight) -> u32 {
125 self.0.saturating_sub(other.0)
126 }
127}
128
129pub trait NetworkConstants: Clone {
131 fn coin_type(&self) -> u32;
135
136 fn hrp_sapling_extended_spending_key(&self) -> &'static str;
143
144 fn hrp_sapling_extended_full_viewing_key(&self) -> &'static str;
151
152 fn hrp_sapling_payment_address(&self) -> &'static str;
159
160 fn b58_sprout_address_prefix(&self) -> [u8; 2];
168
169 fn b58_pubkey_address_prefix(&self) -> [u8; 2];
173
174 fn b58_script_address_prefix(&self) -> [u8; 2];
177
178 fn hrp_tex_address(&self) -> &'static str;
185
186 fn hrp_unified_address(&self) -> &'static str;
192
193 fn hrp_unified_fvk(&self) -> &'static str;
199
200 fn hrp_unified_ivk(&self) -> &'static str;
206}
207
208#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
210pub enum NetworkType {
211 Main,
213 Test,
215 Regtest,
220}
221
222#[cfg(feature = "std")]
223memuse::impl_no_dynamic_usage!(NetworkType);
224
225impl NetworkConstants for NetworkType {
226 fn coin_type(&self) -> u32 {
227 match self {
228 NetworkType::Main => mainnet::COIN_TYPE,
229 NetworkType::Test => testnet::COIN_TYPE,
230 NetworkType::Regtest => regtest::COIN_TYPE,
231 }
232 }
233
234 fn hrp_sapling_extended_spending_key(&self) -> &'static str {
235 match self {
236 NetworkType::Main => mainnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
237 NetworkType::Test => testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
238 NetworkType::Regtest => regtest::HRP_SAPLING_EXTENDED_SPENDING_KEY,
239 }
240 }
241
242 fn hrp_sapling_extended_full_viewing_key(&self) -> &'static str {
243 match self {
244 NetworkType::Main => mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
245 NetworkType::Test => testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
246 NetworkType::Regtest => regtest::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
247 }
248 }
249
250 fn hrp_sapling_payment_address(&self) -> &'static str {
251 match self {
252 NetworkType::Main => mainnet::HRP_SAPLING_PAYMENT_ADDRESS,
253 NetworkType::Test => testnet::HRP_SAPLING_PAYMENT_ADDRESS,
254 NetworkType::Regtest => regtest::HRP_SAPLING_PAYMENT_ADDRESS,
255 }
256 }
257
258 fn b58_sprout_address_prefix(&self) -> [u8; 2] {
259 match self {
260 NetworkType::Main => mainnet::B58_SPROUT_ADDRESS_PREFIX,
261 NetworkType::Test => testnet::B58_SPROUT_ADDRESS_PREFIX,
262 NetworkType::Regtest => regtest::B58_SPROUT_ADDRESS_PREFIX,
263 }
264 }
265
266 fn b58_pubkey_address_prefix(&self) -> [u8; 2] {
267 match self {
268 NetworkType::Main => mainnet::B58_PUBKEY_ADDRESS_PREFIX,
269 NetworkType::Test => testnet::B58_PUBKEY_ADDRESS_PREFIX,
270 NetworkType::Regtest => regtest::B58_PUBKEY_ADDRESS_PREFIX,
271 }
272 }
273
274 fn b58_script_address_prefix(&self) -> [u8; 2] {
275 match self {
276 NetworkType::Main => mainnet::B58_SCRIPT_ADDRESS_PREFIX,
277 NetworkType::Test => testnet::B58_SCRIPT_ADDRESS_PREFIX,
278 NetworkType::Regtest => regtest::B58_SCRIPT_ADDRESS_PREFIX,
279 }
280 }
281
282 fn hrp_tex_address(&self) -> &'static str {
283 match self {
284 NetworkType::Main => mainnet::HRP_TEX_ADDRESS,
285 NetworkType::Test => testnet::HRP_TEX_ADDRESS,
286 NetworkType::Regtest => regtest::HRP_TEX_ADDRESS,
287 }
288 }
289
290 fn hrp_unified_address(&self) -> &'static str {
291 match self {
292 NetworkType::Main => mainnet::HRP_UNIFIED_ADDRESS,
293 NetworkType::Test => testnet::HRP_UNIFIED_ADDRESS,
294 NetworkType::Regtest => regtest::HRP_UNIFIED_ADDRESS,
295 }
296 }
297
298 fn hrp_unified_fvk(&self) -> &'static str {
299 match self {
300 NetworkType::Main => mainnet::HRP_UNIFIED_FVK,
301 NetworkType::Test => testnet::HRP_UNIFIED_FVK,
302 NetworkType::Regtest => regtest::HRP_UNIFIED_FVK,
303 }
304 }
305
306 fn hrp_unified_ivk(&self) -> &'static str {
307 match self {
308 NetworkType::Main => mainnet::HRP_UNIFIED_IVK,
309 NetworkType::Test => testnet::HRP_UNIFIED_IVK,
310 NetworkType::Regtest => regtest::HRP_UNIFIED_IVK,
311 }
312 }
313}
314
315pub trait Parameters: Clone {
317 fn network_type(&self) -> NetworkType;
319
320 fn activation_height(&self, nu: NetworkUpgrade) -> Option<BlockHeight>;
323
324 fn is_nu_active(&self, nu: NetworkUpgrade, height: BlockHeight) -> bool {
327 self.activation_height(nu).is_some_and(|h| h <= height)
328 }
329}
330
331impl<P: Parameters> Parameters for &P {
332 fn network_type(&self) -> NetworkType {
333 (*self).network_type()
334 }
335
336 fn activation_height(&self, nu: NetworkUpgrade) -> Option<BlockHeight> {
337 (*self).activation_height(nu)
338 }
339}
340
341impl<P: Parameters> NetworkConstants for P {
342 fn coin_type(&self) -> u32 {
343 self.network_type().coin_type()
344 }
345
346 fn hrp_sapling_extended_spending_key(&self) -> &'static str {
347 self.network_type().hrp_sapling_extended_spending_key()
348 }
349
350 fn hrp_sapling_extended_full_viewing_key(&self) -> &'static str {
351 self.network_type().hrp_sapling_extended_full_viewing_key()
352 }
353
354 fn hrp_sapling_payment_address(&self) -> &'static str {
355 self.network_type().hrp_sapling_payment_address()
356 }
357
358 fn b58_sprout_address_prefix(&self) -> [u8; 2] {
359 self.network_type().b58_sprout_address_prefix()
360 }
361
362 fn b58_pubkey_address_prefix(&self) -> [u8; 2] {
363 self.network_type().b58_pubkey_address_prefix()
364 }
365
366 fn b58_script_address_prefix(&self) -> [u8; 2] {
367 self.network_type().b58_script_address_prefix()
368 }
369
370 fn hrp_tex_address(&self) -> &'static str {
371 self.network_type().hrp_tex_address()
372 }
373
374 fn hrp_unified_address(&self) -> &'static str {
375 self.network_type().hrp_unified_address()
376 }
377
378 fn hrp_unified_fvk(&self) -> &'static str {
379 self.network_type().hrp_unified_fvk()
380 }
381
382 fn hrp_unified_ivk(&self) -> &'static str {
383 self.network_type().hrp_unified_ivk()
384 }
385}
386
387#[derive(PartialEq, Eq, Copy, Clone, Debug)]
389pub struct MainNetwork;
390
391#[cfg(feature = "std")]
392memuse::impl_no_dynamic_usage!(MainNetwork);
393
394pub const MAIN_NETWORK: MainNetwork = MainNetwork;
396
397impl Parameters for MainNetwork {
398 fn network_type(&self) -> NetworkType {
399 NetworkType::Main
400 }
401
402 fn activation_height(&self, nu: NetworkUpgrade) -> Option<BlockHeight> {
403 match nu {
404 NetworkUpgrade::Overwinter => Some(BlockHeight(347_500)),
405 NetworkUpgrade::Sapling => Some(BlockHeight(419_200)),
406 NetworkUpgrade::Blossom => Some(BlockHeight(653_600)),
407 NetworkUpgrade::Heartwood => Some(BlockHeight(903_000)),
408 NetworkUpgrade::Canopy => Some(BlockHeight(1_046_400)),
409 NetworkUpgrade::Nu5 => Some(BlockHeight(1_687_104)),
410 NetworkUpgrade::Nu6 => Some(BlockHeight(2_726_400)),
411 #[cfg(zcash_unstable = "nu7")]
412 NetworkUpgrade::Nu7 => None,
413 #[cfg(zcash_unstable = "zfuture")]
414 NetworkUpgrade::ZFuture => None,
415 }
416 }
417}
418
419#[derive(PartialEq, Eq, Copy, Clone, Debug)]
421pub struct TestNetwork;
422
423#[cfg(feature = "std")]
424memuse::impl_no_dynamic_usage!(TestNetwork);
425
426pub const TEST_NETWORK: TestNetwork = TestNetwork;
428
429impl Parameters for TestNetwork {
430 fn network_type(&self) -> NetworkType {
431 NetworkType::Test
432 }
433
434 fn activation_height(&self, nu: NetworkUpgrade) -> Option<BlockHeight> {
435 match nu {
436 NetworkUpgrade::Overwinter => Some(BlockHeight(207_500)),
437 NetworkUpgrade::Sapling => Some(BlockHeight(280_000)),
438 NetworkUpgrade::Blossom => Some(BlockHeight(584_000)),
439 NetworkUpgrade::Heartwood => Some(BlockHeight(903_800)),
440 NetworkUpgrade::Canopy => Some(BlockHeight(1_028_500)),
441 NetworkUpgrade::Nu5 => Some(BlockHeight(1_842_420)),
442 NetworkUpgrade::Nu6 => Some(BlockHeight(2_976_000)),
443 #[cfg(zcash_unstable = "nu7")]
444 NetworkUpgrade::Nu7 => None,
445 #[cfg(zcash_unstable = "zfuture")]
446 NetworkUpgrade::ZFuture => None,
447 }
448 }
449}
450
451#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
453pub enum Network {
454 MainNetwork,
456 TestNetwork,
458}
459
460#[cfg(feature = "std")]
461memuse::impl_no_dynamic_usage!(Network);
462
463impl Parameters for Network {
464 fn network_type(&self) -> NetworkType {
465 match self {
466 Network::MainNetwork => NetworkType::Main,
467 Network::TestNetwork => NetworkType::Test,
468 }
469 }
470
471 fn activation_height(&self, nu: NetworkUpgrade) -> Option<BlockHeight> {
472 match self {
473 Network::MainNetwork => MAIN_NETWORK.activation_height(nu),
474 Network::TestNetwork => TEST_NETWORK.activation_height(nu),
475 }
476 }
477}
478
479#[derive(Clone, Copy, Debug, PartialEq, Eq)]
484pub enum NetworkUpgrade {
485 Overwinter,
489 Sapling,
493 Blossom,
497 Heartwood,
501 Canopy,
505 Nu5,
509 Nu6,
513 #[cfg(zcash_unstable = "nu7")]
517 Nu7,
518 #[cfg(zcash_unstable = "zfuture")]
524 ZFuture,
525}
526
527#[cfg(feature = "std")]
528memuse::impl_no_dynamic_usage!(NetworkUpgrade);
529
530impl fmt::Display for NetworkUpgrade {
531 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
532 match self {
533 NetworkUpgrade::Overwinter => write!(f, "Overwinter"),
534 NetworkUpgrade::Sapling => write!(f, "Sapling"),
535 NetworkUpgrade::Blossom => write!(f, "Blossom"),
536 NetworkUpgrade::Heartwood => write!(f, "Heartwood"),
537 NetworkUpgrade::Canopy => write!(f, "Canopy"),
538 NetworkUpgrade::Nu5 => write!(f, "Nu5"),
539 NetworkUpgrade::Nu6 => write!(f, "Nu6"),
540 #[cfg(zcash_unstable = "nu7")]
541 NetworkUpgrade::Nu7 => write!(f, "Nu7"),
542 #[cfg(zcash_unstable = "zfuture")]
543 NetworkUpgrade::ZFuture => write!(f, "ZFUTURE"),
544 }
545 }
546}
547
548impl NetworkUpgrade {
549 fn branch_id(self) -> BranchId {
550 match self {
551 NetworkUpgrade::Overwinter => BranchId::Overwinter,
552 NetworkUpgrade::Sapling => BranchId::Sapling,
553 NetworkUpgrade::Blossom => BranchId::Blossom,
554 NetworkUpgrade::Heartwood => BranchId::Heartwood,
555 NetworkUpgrade::Canopy => BranchId::Canopy,
556 NetworkUpgrade::Nu5 => BranchId::Nu5,
557 NetworkUpgrade::Nu6 => BranchId::Nu6,
558 #[cfg(zcash_unstable = "nu7")]
559 NetworkUpgrade::Nu7 => BranchId::Nu7,
560 #[cfg(zcash_unstable = "zfuture")]
561 NetworkUpgrade::ZFuture => BranchId::ZFuture,
562 }
563 }
564}
565
566const UPGRADES_IN_ORDER: &[NetworkUpgrade] = &[
571 NetworkUpgrade::Overwinter,
572 NetworkUpgrade::Sapling,
573 NetworkUpgrade::Blossom,
574 NetworkUpgrade::Heartwood,
575 NetworkUpgrade::Canopy,
576 NetworkUpgrade::Nu5,
577 NetworkUpgrade::Nu6,
578 #[cfg(zcash_unstable = "nu7")]
579 NetworkUpgrade::Nu7,
580];
581
582pub const ZIP212_GRACE_PERIOD: u32 = 32256;
586
587#[derive(Clone, Copy, Debug, PartialEq, Eq)]
601pub enum BranchId {
602 Sprout,
604 Overwinter,
606 Sapling,
608 Blossom,
610 Heartwood,
612 Canopy,
614 Nu5,
616 Nu6,
618 #[cfg(zcash_unstable = "nu7")]
620 Nu7,
621 #[cfg(zcash_unstable = "zfuture")]
624 ZFuture,
625}
626
627#[cfg(feature = "std")]
628memuse::impl_no_dynamic_usage!(BranchId);
629
630impl TryFrom<u32> for BranchId {
631 type Error = &'static str;
632
633 fn try_from(value: u32) -> Result<Self, Self::Error> {
634 match value {
635 0 => Ok(BranchId::Sprout),
636 0x5ba8_1b19 => Ok(BranchId::Overwinter),
637 0x76b8_09bb => Ok(BranchId::Sapling),
638 0x2bb4_0e60 => Ok(BranchId::Blossom),
639 0xf5b9_230b => Ok(BranchId::Heartwood),
640 0xe9ff_75a6 => Ok(BranchId::Canopy),
641 0xc2d6_d0b4 => Ok(BranchId::Nu5),
642 0xc8e7_1055 => Ok(BranchId::Nu6),
643 #[cfg(zcash_unstable = "nu7")]
644 0xffff_ffff => Ok(BranchId::Nu7),
645 #[cfg(zcash_unstable = "zfuture")]
646 0xffff_ffff => Ok(BranchId::ZFuture),
647 _ => Err("Unknown consensus branch ID"),
648 }
649 }
650}
651
652impl From<BranchId> for u32 {
653 fn from(consensus_branch_id: BranchId) -> u32 {
654 match consensus_branch_id {
655 BranchId::Sprout => 0,
656 BranchId::Overwinter => 0x5ba8_1b19,
657 BranchId::Sapling => 0x76b8_09bb,
658 BranchId::Blossom => 0x2bb4_0e60,
659 BranchId::Heartwood => 0xf5b9_230b,
660 BranchId::Canopy => 0xe9ff_75a6,
661 BranchId::Nu5 => 0xc2d6_d0b4,
662 BranchId::Nu6 => 0xc8e7_1055,
663 #[cfg(zcash_unstable = "nu7")]
664 BranchId::Nu7 => 0xffff_ffff,
665 #[cfg(zcash_unstable = "zfuture")]
666 BranchId::ZFuture => 0xffff_ffff,
667 }
668 }
669}
670
671impl BranchId {
672 pub fn for_height<P: Parameters>(parameters: &P, height: BlockHeight) -> Self {
677 for nu in UPGRADES_IN_ORDER.iter().rev() {
678 if parameters.is_nu_active(*nu, height) {
679 return nu.branch_id();
680 }
681 }
682
683 BranchId::Sprout
685 }
686
687 pub fn height_range<P: Parameters>(&self, params: &P) -> Option<impl RangeBounds<BlockHeight>> {
691 self.height_bounds(params).map(|(lower, upper)| {
692 (
693 Bound::Included(lower),
694 upper.map_or(Bound::Unbounded, Bound::Excluded),
695 )
696 })
697 }
698
699 pub fn height_bounds<P: Parameters>(
708 &self,
709 params: &P,
710 ) -> Option<(BlockHeight, Option<BlockHeight>)> {
711 match self {
712 BranchId::Sprout => params
713 .activation_height(NetworkUpgrade::Overwinter)
714 .map(|upper| (BlockHeight(0), Some(upper))),
715 BranchId::Overwinter => params
716 .activation_height(NetworkUpgrade::Overwinter)
717 .map(|lower| (lower, params.activation_height(NetworkUpgrade::Sapling))),
718 BranchId::Sapling => params
719 .activation_height(NetworkUpgrade::Sapling)
720 .map(|lower| (lower, params.activation_height(NetworkUpgrade::Blossom))),
721 BranchId::Blossom => params
722 .activation_height(NetworkUpgrade::Blossom)
723 .map(|lower| (lower, params.activation_height(NetworkUpgrade::Heartwood))),
724 BranchId::Heartwood => params
725 .activation_height(NetworkUpgrade::Heartwood)
726 .map(|lower| (lower, params.activation_height(NetworkUpgrade::Canopy))),
727 BranchId::Canopy => params
728 .activation_height(NetworkUpgrade::Canopy)
729 .map(|lower| (lower, params.activation_height(NetworkUpgrade::Nu5))),
730 BranchId::Nu5 => params
731 .activation_height(NetworkUpgrade::Nu5)
732 .map(|lower| (lower, params.activation_height(NetworkUpgrade::Nu6))),
733 BranchId::Nu6 => params.activation_height(NetworkUpgrade::Nu6).map(|lower| {
734 #[cfg(zcash_unstable = "nu7")]
735 let upper = params.activation_height(NetworkUpgrade::Nu7);
736 #[cfg(zcash_unstable = "zfuture")]
737 let upper = params.activation_height(NetworkUpgrade::ZFuture);
738 #[cfg(not(any(zcash_unstable = "nu7", zcash_unstable = "zfuture")))]
739 let upper = None;
740 (lower, upper)
741 }),
742 #[cfg(zcash_unstable = "nu7")]
743 BranchId::Nu7 => params
744 .activation_height(NetworkUpgrade::Nu7)
745 .map(|lower| (lower, None)),
746 #[cfg(zcash_unstable = "zfuture")]
747 BranchId::ZFuture => params
748 .activation_height(NetworkUpgrade::ZFuture)
749 .map(|lower| (lower, None)),
750 }
751 }
752
753 pub fn sprout_uses_groth_proofs(&self) -> bool {
754 !matches!(self, BranchId::Sprout | BranchId::Overwinter)
755 }
756}
757
758#[cfg(any(test, feature = "test-dependencies"))]
759pub mod testing {
760 use proptest::sample::select;
761 use proptest::strategy::{Just, Strategy};
762
763 use super::{BlockHeight, BranchId, Parameters};
764
765 pub fn arb_branch_id() -> impl Strategy<Value = BranchId> {
766 select(vec![
767 BranchId::Sprout,
768 BranchId::Overwinter,
769 BranchId::Sapling,
770 BranchId::Blossom,
771 BranchId::Heartwood,
772 BranchId::Canopy,
773 BranchId::Nu5,
774 BranchId::Nu6,
775 #[cfg(zcash_unstable = "nu7")]
776 BranchId::Nu7,
777 #[cfg(zcash_unstable = "zfuture")]
778 BranchId::ZFuture,
779 ])
780 }
781
782 pub fn arb_height<P: Parameters>(
783 branch_id: BranchId,
784 params: &P,
785 ) -> impl Strategy<Value = Option<BlockHeight>> {
786 branch_id
787 .height_bounds(params)
788 .map_or(Strategy::boxed(Just(None)), |(lower, upper)| {
789 Strategy::boxed(
790 (lower.0..upper.map_or(u32::MAX, |u| u.0)).prop_map(|h| Some(BlockHeight(h))),
791 )
792 })
793 }
794
795 #[cfg(feature = "test-dependencies")]
796 impl incrementalmerkletree_testing::TestCheckpoint for BlockHeight {
797 fn from_u64(value: u64) -> Self {
798 BlockHeight(u32::try_from(value).expect("Test checkpoint ids do not exceed 32 bits"))
799 }
800 }
801}
802
803#[cfg(test)]
804mod tests {
805 use super::{
806 BlockHeight, BranchId, NetworkUpgrade, Parameters, MAIN_NETWORK, UPGRADES_IN_ORDER,
807 };
808
809 #[test]
810 fn nu_ordering() {
811 for i in 1..UPGRADES_IN_ORDER.len() {
812 let nu_a = UPGRADES_IN_ORDER[i - 1];
813 let nu_b = UPGRADES_IN_ORDER[i];
814 match (
815 MAIN_NETWORK.activation_height(nu_a),
816 MAIN_NETWORK.activation_height(nu_b),
817 ) {
818 (Some(a), Some(b)) if a < b => (),
819 (Some(_), None) => (),
820 (None, None) => (),
821 _ => panic!(
822 "{} should not be before {} in UPGRADES_IN_ORDER",
823 nu_a, nu_b
824 ),
825 }
826 }
827 }
828
829 #[test]
830 fn nu_is_active() {
831 assert!(!MAIN_NETWORK.is_nu_active(NetworkUpgrade::Overwinter, BlockHeight(0)));
832 assert!(!MAIN_NETWORK.is_nu_active(NetworkUpgrade::Overwinter, BlockHeight(347_499)));
833 assert!(MAIN_NETWORK.is_nu_active(NetworkUpgrade::Overwinter, BlockHeight(347_500)));
834 }
835
836 #[test]
837 fn branch_id_from_u32() {
838 assert_eq!(BranchId::try_from(0), Ok(BranchId::Sprout));
839 assert!(BranchId::try_from(1).is_err());
840 }
841
842 #[test]
843 fn branch_id_for_height() {
844 assert_eq!(
845 BranchId::for_height(&MAIN_NETWORK, BlockHeight(0)),
846 BranchId::Sprout,
847 );
848 assert_eq!(
849 BranchId::for_height(&MAIN_NETWORK, BlockHeight(419_199)),
850 BranchId::Overwinter,
851 );
852 assert_eq!(
853 BranchId::for_height(&MAIN_NETWORK, BlockHeight(419_200)),
854 BranchId::Sapling,
855 );
856 assert_eq!(
857 BranchId::for_height(&MAIN_NETWORK, BlockHeight(903_000)),
858 BranchId::Heartwood,
859 );
860 assert_eq!(
861 BranchId::for_height(&MAIN_NETWORK, BlockHeight(1_046_400)),
862 BranchId::Canopy,
863 );
864 assert_eq!(
865 BranchId::for_height(&MAIN_NETWORK, BlockHeight(1_687_104)),
866 BranchId::Nu5,
867 );
868 assert_eq!(
869 BranchId::for_height(&MAIN_NETWORK, BlockHeight(2_726_399)),
870 BranchId::Nu5,
871 );
872 assert_eq!(
873 BranchId::for_height(&MAIN_NETWORK, BlockHeight(2_726_400)),
874 BranchId::Nu6,
875 );
876 assert_eq!(
877 BranchId::for_height(&MAIN_NETWORK, BlockHeight(5_000_000)),
878 BranchId::Nu6,
879 );
880 }
881}