1use std::cmp::{Ord, Ordering};
4use std::convert::TryFrom;
5use std::fmt;
6use std::ops::{Add, Bound, RangeBounds, Sub};
7use zcash_address;
8
9use crate::constants;
10
11#[repr(transparent)]
14#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
15pub struct BlockHeight(u32);
16
17pub const H0: BlockHeight = BlockHeight(0);
18
19impl BlockHeight {
20 pub const fn from_u32(v: u32) -> BlockHeight {
21 BlockHeight(v)
22 }
23}
24
25impl fmt::Display for BlockHeight {
26 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
27 self.0.fmt(formatter)
28 }
29}
30
31impl Ord for BlockHeight {
32 fn cmp(&self, other: &Self) -> Ordering {
33 self.0.cmp(&other.0)
34 }
35}
36
37impl PartialOrd for BlockHeight {
38 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
39 Some(self.cmp(other))
40 }
41}
42
43impl From<u32> for BlockHeight {
44 fn from(value: u32) -> Self {
45 BlockHeight(value)
46 }
47}
48
49impl From<BlockHeight> for u32 {
50 fn from(value: BlockHeight) -> u32 {
51 value.0
52 }
53}
54
55impl TryFrom<u64> for BlockHeight {
56 type Error = std::num::TryFromIntError;
57
58 fn try_from(value: u64) -> Result<Self, Self::Error> {
59 u32::try_from(value).map(BlockHeight)
60 }
61}
62
63impl From<BlockHeight> for u64 {
64 fn from(value: BlockHeight) -> u64 {
65 value.0 as u64
66 }
67}
68
69impl TryFrom<i32> for BlockHeight {
70 type Error = std::num::TryFromIntError;
71
72 fn try_from(value: i32) -> Result<Self, Self::Error> {
73 u32::try_from(value).map(BlockHeight)
74 }
75}
76
77impl TryFrom<i64> for BlockHeight {
78 type Error = std::num::TryFromIntError;
79
80 fn try_from(value: i64) -> Result<Self, Self::Error> {
81 u32::try_from(value).map(BlockHeight)
82 }
83}
84
85impl From<BlockHeight> for i64 {
86 fn from(value: BlockHeight) -> i64 {
87 value.0 as i64
88 }
89}
90
91impl Add<u32> for BlockHeight {
92 type Output = Self;
93
94 fn add(self, other: u32) -> Self {
95 BlockHeight(self.0 + other)
96 }
97}
98
99impl Add for BlockHeight {
100 type Output = Self;
101
102 fn add(self, other: Self) -> Self {
103 self + other.0
104 }
105}
106
107impl Sub<u32> for BlockHeight {
108 type Output = Self;
109
110 fn sub(self, other: u32) -> Self {
111 if other > self.0 {
112 panic!("Subtraction resulted in negative block height.");
113 }
114
115 BlockHeight(self.0 - other)
116 }
117}
118
119impl Sub for BlockHeight {
120 type Output = Self;
121
122 fn sub(self, other: Self) -> Self {
123 self - other.0
124 }
125}
126
127pub trait Parameters: Clone {
129 fn activation_height(&self, nu: NetworkUpgrade) -> Option<BlockHeight>;
132
133 fn is_nu_active(&self, nu: NetworkUpgrade, height: BlockHeight) -> bool {
136 self.activation_height(nu).map_or(false, |h| h <= height)
137 }
138
139 fn coin_type(&self) -> u32;
143
144 fn address_network(&self) -> Option<zcash_address::Network>;
147
148 fn hrp_sapling_extended_spending_key(&self) -> &str;
156
157 fn hrp_sapling_extended_full_viewing_key(&self) -> &str;
165
166 fn hrp_sapling_payment_address(&self) -> &str;
174
175 fn b58_pubkey_address_prefix(&self) -> [u8; 2];
181
182 fn b58_script_address_prefix(&self) -> [u8; 2];
187}
188
189#[derive(PartialEq, Copy, Clone, Debug)]
191pub struct MainNetwork;
192
193pub const MAIN_NETWORK: MainNetwork = MainNetwork;
194
195impl Parameters for MainNetwork {
196 fn activation_height(&self, nu: NetworkUpgrade) -> Option<BlockHeight> {
197 match nu {
198 NetworkUpgrade::Overwinter => Some(BlockHeight(347_500)),
199 NetworkUpgrade::Sapling => Some(BlockHeight(419_200)),
200 NetworkUpgrade::Blossom => Some(BlockHeight(653_600)),
201 NetworkUpgrade::Heartwood => Some(BlockHeight(903_000)),
202 NetworkUpgrade::Canopy => Some(BlockHeight(1_046_400)),
203 NetworkUpgrade::Nu5 => Some(BlockHeight(1_687_104)),
204 #[cfg(feature = "zfuture")]
205 NetworkUpgrade::ZFuture => None,
206 }
207 }
208
209 fn coin_type(&self) -> u32 {
210 constants::mainnet::COIN_TYPE
211 }
212
213 fn address_network(&self) -> Option<zcash_address::Network> {
214 Some(zcash_address::Network::Main)
215 }
216
217 fn hrp_sapling_extended_spending_key(&self) -> &str {
218 constants::mainnet::HRP_SAPLING_EXTENDED_SPENDING_KEY
219 }
220
221 fn hrp_sapling_extended_full_viewing_key(&self) -> &str {
222 constants::mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY
223 }
224
225 fn hrp_sapling_payment_address(&self) -> &str {
226 constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS
227 }
228
229 fn b58_pubkey_address_prefix(&self) -> [u8; 2] {
230 constants::mainnet::B58_PUBKEY_ADDRESS_PREFIX
231 }
232
233 fn b58_script_address_prefix(&self) -> [u8; 2] {
234 constants::mainnet::B58_SCRIPT_ADDRESS_PREFIX
235 }
236}
237
238#[derive(PartialEq, Copy, Clone, Debug)]
240pub struct TestNetwork;
241
242pub const TEST_NETWORK: TestNetwork = TestNetwork;
243
244impl Parameters for TestNetwork {
245 fn activation_height(&self, nu: NetworkUpgrade) -> Option<BlockHeight> {
246 match nu {
247 NetworkUpgrade::Overwinter => Some(BlockHeight(207_500)),
248 NetworkUpgrade::Sapling => Some(BlockHeight(280_000)),
249 NetworkUpgrade::Blossom => Some(BlockHeight(584_000)),
250 NetworkUpgrade::Heartwood => Some(BlockHeight(903_800)),
251 NetworkUpgrade::Canopy => Some(BlockHeight(1_028_500)),
252 NetworkUpgrade::Nu5 => Some(BlockHeight(1_842_420)),
253 #[cfg(feature = "zfuture")]
254 NetworkUpgrade::ZFuture => None,
255 }
256 }
257
258 fn coin_type(&self) -> u32 {
259 constants::testnet::COIN_TYPE
260 }
261
262 fn address_network(&self) -> Option<zcash_address::Network> {
263 Some(zcash_address::Network::Test)
264 }
265
266 fn hrp_sapling_extended_spending_key(&self) -> &str {
267 constants::testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY
268 }
269
270 fn hrp_sapling_extended_full_viewing_key(&self) -> &str {
271 constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY
272 }
273
274 fn hrp_sapling_payment_address(&self) -> &str {
275 constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS
276 }
277
278 fn b58_pubkey_address_prefix(&self) -> [u8; 2] {
279 constants::testnet::B58_PUBKEY_ADDRESS_PREFIX
280 }
281
282 fn b58_script_address_prefix(&self) -> [u8; 2] {
283 constants::testnet::B58_SCRIPT_ADDRESS_PREFIX
284 }
285}
286
287#[derive(PartialEq, Copy, Clone, Debug)]
288pub enum Network {
289 MainNetwork,
290 TestNetwork,
291}
292
293impl Parameters for Network {
294 fn activation_height(&self, nu: NetworkUpgrade) -> Option<BlockHeight> {
295 match self {
296 Network::MainNetwork => MAIN_NETWORK.activation_height(nu),
297 Network::TestNetwork => TEST_NETWORK.activation_height(nu),
298 }
299 }
300
301 fn coin_type(&self) -> u32 {
302 match self {
303 Network::MainNetwork => MAIN_NETWORK.coin_type(),
304 Network::TestNetwork => TEST_NETWORK.coin_type(),
305 }
306 }
307
308 fn address_network(&self) -> Option<zcash_address::Network> {
309 match self {
310 Network::MainNetwork => Some(zcash_address::Network::Main),
311 Network::TestNetwork => Some(zcash_address::Network::Test),
312 }
313 }
314
315 fn hrp_sapling_extended_spending_key(&self) -> &str {
316 match self {
317 Network::MainNetwork => MAIN_NETWORK.hrp_sapling_extended_spending_key(),
318 Network::TestNetwork => TEST_NETWORK.hrp_sapling_extended_spending_key(),
319 }
320 }
321
322 fn hrp_sapling_extended_full_viewing_key(&self) -> &str {
323 match self {
324 Network::MainNetwork => MAIN_NETWORK.hrp_sapling_extended_full_viewing_key(),
325 Network::TestNetwork => TEST_NETWORK.hrp_sapling_extended_full_viewing_key(),
326 }
327 }
328
329 fn hrp_sapling_payment_address(&self) -> &str {
330 match self {
331 Network::MainNetwork => MAIN_NETWORK.hrp_sapling_payment_address(),
332 Network::TestNetwork => TEST_NETWORK.hrp_sapling_payment_address(),
333 }
334 }
335
336 fn b58_pubkey_address_prefix(&self) -> [u8; 2] {
337 match self {
338 Network::MainNetwork => MAIN_NETWORK.b58_pubkey_address_prefix(),
339 Network::TestNetwork => TEST_NETWORK.b58_pubkey_address_prefix(),
340 }
341 }
342
343 fn b58_script_address_prefix(&self) -> [u8; 2] {
344 match self {
345 Network::MainNetwork => MAIN_NETWORK.b58_script_address_prefix(),
346 Network::TestNetwork => TEST_NETWORK.b58_script_address_prefix(),
347 }
348 }
349}
350
351#[derive(Clone, Copy, Debug)]
356pub enum NetworkUpgrade {
357 Overwinter,
361 Sapling,
365 Blossom,
369 Heartwood,
373 Canopy,
377 Nu5,
381 #[cfg(feature = "zfuture")]
387 ZFuture,
388}
389
390impl fmt::Display for NetworkUpgrade {
391 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
392 match self {
393 NetworkUpgrade::Overwinter => write!(f, "Overwinter"),
394 NetworkUpgrade::Sapling => write!(f, "Sapling"),
395 NetworkUpgrade::Blossom => write!(f, "Blossom"),
396 NetworkUpgrade::Heartwood => write!(f, "Heartwood"),
397 NetworkUpgrade::Canopy => write!(f, "Canopy"),
398 NetworkUpgrade::Nu5 => write!(f, "Nu5"),
399 #[cfg(feature = "zfuture")]
400 NetworkUpgrade::ZFuture => write!(f, "ZFUTURE"),
401 }
402 }
403}
404
405impl NetworkUpgrade {
406 fn branch_id(self) -> BranchId {
407 match self {
408 NetworkUpgrade::Overwinter => BranchId::Overwinter,
409 NetworkUpgrade::Sapling => BranchId::Sapling,
410 NetworkUpgrade::Blossom => BranchId::Blossom,
411 NetworkUpgrade::Heartwood => BranchId::Heartwood,
412 NetworkUpgrade::Canopy => BranchId::Canopy,
413 NetworkUpgrade::Nu5 => BranchId::Nu5,
414 #[cfg(feature = "zfuture")]
415 NetworkUpgrade::ZFuture => BranchId::ZFuture,
416 }
417 }
418}
419
420const UPGRADES_IN_ORDER: &[NetworkUpgrade] = &[
425 NetworkUpgrade::Overwinter,
426 NetworkUpgrade::Sapling,
427 NetworkUpgrade::Blossom,
428 NetworkUpgrade::Heartwood,
429 NetworkUpgrade::Canopy,
430 NetworkUpgrade::Nu5,
431];
432
433pub const ZIP212_GRACE_PERIOD: u32 = 32256;
434
435#[derive(Clone, Copy, Debug, PartialEq)]
449pub enum BranchId {
450 Sprout,
452 Overwinter,
454 Sapling,
456 Blossom,
458 Heartwood,
460 Canopy,
462 Nu5,
464 #[cfg(feature = "zfuture")]
467 ZFuture,
468}
469
470impl TryFrom<u32> for BranchId {
471 type Error = &'static str;
472
473 fn try_from(value: u32) -> Result<Self, Self::Error> {
474 match value {
475 0 => Ok(BranchId::Sprout),
476 0x5ba8_1b19 => Ok(BranchId::Overwinter),
477 0x76b8_09bb => Ok(BranchId::Sapling),
478 0x2bb4_0e60 => Ok(BranchId::Blossom),
479 0xf5b9_230b => Ok(BranchId::Heartwood),
480 0xe9ff_75a6 => Ok(BranchId::Canopy),
481 0xc2d6_d0b4 => Ok(BranchId::Nu5),
482 #[cfg(feature = "zfuture")]
483 0xffff_ffff => Ok(BranchId::ZFuture),
484 _ => Err("Unknown consensus branch ID"),
485 }
486 }
487}
488
489impl From<BranchId> for u32 {
490 fn from(consensus_branch_id: BranchId) -> u32 {
491 match consensus_branch_id {
492 BranchId::Sprout => 0,
493 BranchId::Overwinter => 0x5ba8_1b19,
494 BranchId::Sapling => 0x76b8_09bb,
495 BranchId::Blossom => 0x2bb4_0e60,
496 BranchId::Heartwood => 0xf5b9_230b,
497 BranchId::Canopy => 0xe9ff_75a6,
498 BranchId::Nu5 => 0xc2d6_d0b4,
499 #[cfg(feature = "zfuture")]
500 BranchId::ZFuture => 0xffff_ffff,
501 }
502 }
503}
504
505impl BranchId {
506 pub fn for_height<P: Parameters>(parameters: &P, height: BlockHeight) -> Self {
511 for nu in UPGRADES_IN_ORDER.iter().rev() {
512 if parameters.is_nu_active(*nu, height) {
513 return nu.branch_id();
514 }
515 }
516
517 BranchId::Sprout
519 }
520
521 pub fn height_range<P: Parameters>(&self, params: &P) -> Option<impl RangeBounds<BlockHeight>> {
525 self.height_bounds(params).map(|(lower, upper)| {
526 (
527 Bound::Included(lower),
528 upper.map_or(Bound::Unbounded, Bound::Excluded),
529 )
530 })
531 }
532
533 pub fn height_bounds<P: Parameters>(
542 &self,
543 params: &P,
544 ) -> Option<(BlockHeight, Option<BlockHeight>)> {
545 match self {
546 BranchId::Sprout => params
547 .activation_height(NetworkUpgrade::Overwinter)
548 .map(|upper| (BlockHeight(0), Some(upper))),
549 BranchId::Overwinter => params
550 .activation_height(NetworkUpgrade::Overwinter)
551 .map(|lower| (lower, params.activation_height(NetworkUpgrade::Sapling))),
552 BranchId::Sapling => params
553 .activation_height(NetworkUpgrade::Sapling)
554 .map(|lower| (lower, params.activation_height(NetworkUpgrade::Blossom))),
555 BranchId::Blossom => params
556 .activation_height(NetworkUpgrade::Blossom)
557 .map(|lower| (lower, params.activation_height(NetworkUpgrade::Heartwood))),
558 BranchId::Heartwood => params
559 .activation_height(NetworkUpgrade::Heartwood)
560 .map(|lower| (lower, params.activation_height(NetworkUpgrade::Canopy))),
561 BranchId::Canopy => params
562 .activation_height(NetworkUpgrade::Canopy)
563 .map(|lower| (lower, params.activation_height(NetworkUpgrade::Nu5))),
564 BranchId::Nu5 => params.activation_height(NetworkUpgrade::Nu5).map(|lower| {
565 #[cfg(feature = "zfuture")]
566 let upper = params.activation_height(NetworkUpgrade::ZFuture);
567 #[cfg(not(feature = "zfuture"))]
568 let upper = None;
569 (lower, upper)
570 }),
571 #[cfg(feature = "zfuture")]
572 BranchId::ZFuture => params
573 .activation_height(NetworkUpgrade::ZFuture)
574 .map(|lower| (lower, None)),
575 }
576 }
577
578 pub fn sprout_uses_groth_proofs(&self) -> bool {
579 !matches!(self, BranchId::Sprout | BranchId::Overwinter)
580 }
581}
582
583#[cfg(any(test, feature = "test-dependencies"))]
584pub mod testing {
585 use proptest::sample::select;
586 use proptest::strategy::{Just, Strategy};
587
588 use super::{BlockHeight, BranchId, Parameters};
589
590 pub fn arb_branch_id() -> impl Strategy<Value = BranchId> {
591 select(vec![
592 BranchId::Sprout,
593 BranchId::Overwinter,
594 BranchId::Sapling,
595 BranchId::Blossom,
596 BranchId::Heartwood,
597 BranchId::Canopy,
598 BranchId::Nu5,
599 #[cfg(feature = "zfuture")]
600 BranchId::ZFuture,
601 ])
602 }
603
604 pub fn arb_height<P: Parameters>(
605 branch_id: BranchId,
606 params: &P,
607 ) -> impl Strategy<Value = Option<BlockHeight>> {
608 branch_id
609 .height_bounds(params)
610 .map_or(Strategy::boxed(Just(None)), |(lower, upper)| {
611 Strategy::boxed(
612 (lower.0..upper.map_or(std::u32::MAX, |u| u.0))
613 .prop_map(|h| Some(BlockHeight(h))),
614 )
615 })
616 }
617}
618
619#[cfg(test)]
620mod tests {
621 use std::convert::TryFrom;
622
623 use super::{
624 BlockHeight, BranchId, NetworkUpgrade, Parameters, MAIN_NETWORK, UPGRADES_IN_ORDER,
625 };
626
627 #[test]
628 fn nu_ordering() {
629 for i in 1..UPGRADES_IN_ORDER.len() {
630 let nu_a = UPGRADES_IN_ORDER[i - 1];
631 let nu_b = UPGRADES_IN_ORDER[i];
632 match (
633 MAIN_NETWORK.activation_height(nu_a),
634 MAIN_NETWORK.activation_height(nu_b),
635 ) {
636 (Some(a), Some(b)) if a < b => (),
637 (Some(_), None) => (),
638 (None, None) => (),
639 _ => panic!(
640 "{} should not be before {} in UPGRADES_IN_ORDER",
641 nu_a, nu_b
642 ),
643 }
644 }
645 }
646
647 #[test]
648 fn nu_is_active() {
649 assert!(!MAIN_NETWORK.is_nu_active(NetworkUpgrade::Overwinter, BlockHeight(0)));
650 assert!(!MAIN_NETWORK.is_nu_active(NetworkUpgrade::Overwinter, BlockHeight(347_499)));
651 assert!(MAIN_NETWORK.is_nu_active(NetworkUpgrade::Overwinter, BlockHeight(347_500)));
652 }
653
654 #[test]
655 fn branch_id_from_u32() {
656 assert_eq!(BranchId::try_from(0), Ok(BranchId::Sprout));
657 assert!(BranchId::try_from(1).is_err());
658 }
659
660 #[test]
661 fn branch_id_for_height() {
662 assert_eq!(
663 BranchId::for_height(&MAIN_NETWORK, BlockHeight(0)),
664 BranchId::Sprout,
665 );
666 assert_eq!(
667 BranchId::for_height(&MAIN_NETWORK, BlockHeight(419_199)),
668 BranchId::Overwinter,
669 );
670 assert_eq!(
671 BranchId::for_height(&MAIN_NETWORK, BlockHeight(419_200)),
672 BranchId::Sapling,
673 );
674 assert_eq!(
675 BranchId::for_height(&MAIN_NETWORK, BlockHeight(903_000)),
676 BranchId::Heartwood,
677 );
678 assert_eq!(
679 BranchId::for_height(&MAIN_NETWORK, BlockHeight(1_046_400)),
680 BranchId::Canopy,
681 );
682 assert_eq!(
683 BranchId::for_height(&MAIN_NETWORK, BlockHeight(1_687_104)),
684 BranchId::Nu5,
685 );
686 assert_eq!(
687 BranchId::for_height(&MAIN_NETWORK, BlockHeight(5_000_000)),
688 BranchId::Nu5,
689 );
690 }
691}