zcash_protocol/
consensus.rs

1//! Consensus logic and parameters.
2
3use 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/// A wrapper type representing blockchain heights.
14///
15/// Safe conversion from various integer types, as well as addition and subtraction, are
16/// provided.
17#[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
24/// The height of the genesis block on a network.
25pub const H0: BlockHeight = BlockHeight(0);
26
27impl BlockHeight {
28    pub const fn from_u32(v: u32) -> BlockHeight {
29        BlockHeight(v)
30    }
31
32    /// Subtracts the provided value from this height, returning `H0` if this would result in
33    /// underflow of the wrapped `u32`.
34    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
129/// Constants associated with a given Zcash network.
130pub trait NetworkConstants: Clone {
131    /// The coin type for ZEC, as defined by [SLIP 44].
132    ///
133    /// [SLIP 44]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md
134    fn coin_type(&self) -> u32;
135
136    /// Returns the human-readable prefix for Bech32-encoded Sapling extended spending keys
137    /// for the network to which this NetworkConstants value applies.
138    ///
139    /// Defined in [ZIP 32].
140    ///
141    /// [ZIP 32]: https://github.com/zcash/zips/blob/main/zips/zip-0032.rst
142    fn hrp_sapling_extended_spending_key(&self) -> &'static str;
143
144    /// Returns the human-readable prefix for Bech32-encoded Sapling extended full
145    /// viewing keys for the network to which this NetworkConstants value applies.
146    ///
147    /// Defined in [ZIP 32].
148    ///
149    /// [ZIP 32]: https://github.com/zcash/zips/blob/master/zip-0032.rst
150    fn hrp_sapling_extended_full_viewing_key(&self) -> &'static str;
151
152    /// Returns the Bech32-encoded human-readable prefix for Sapling payment addresses
153    /// for the network to which this NetworkConstants value applies.
154    ///
155    /// Defined in section 5.6.4 of the [Zcash Protocol Specification].
156    ///
157    /// [Zcash Protocol Specification]: https://github.com/zcash/zips/blob/main/rendered/protocol/protocol.pdf
158    fn hrp_sapling_payment_address(&self) -> &'static str;
159
160    /// Returns the human-readable prefix for Base58Check-encoded Sprout
161    /// payment addresses for the network to which this NetworkConstants value
162    /// applies.
163    ///
164    /// Defined in the [Zcash Protocol Specification section 5.6.3][sproutpaymentaddrencoding].
165    ///
166    /// [sproutpaymentaddrencoding]: https://zips.z.cash/protocol/protocol.pdf#sproutpaymentaddrencoding
167    fn b58_sprout_address_prefix(&self) -> [u8; 2];
168
169    /// Returns the human-readable prefix for Base58Check-encoded transparent
170    /// pay-to-public-key-hash payment addresses for the network to which this NetworkConstants value
171    /// applies.
172    fn b58_pubkey_address_prefix(&self) -> [u8; 2];
173
174    /// Returns the human-readable prefix for Base58Check-encoded transparent pay-to-script-hash
175    /// payment addresses for the network to which this NetworkConstants value applies.
176    fn b58_script_address_prefix(&self) -> [u8; 2];
177
178    /// Returns the Bech32-encoded human-readable prefix for TEX addresses, for the
179    /// network to which this `NetworkConstants` value applies.
180    ///
181    /// Defined in [ZIP 320].
182    ///
183    /// [ZIP 320]: https://zips.z.cash/zip-0320
184    fn hrp_tex_address(&self) -> &'static str;
185
186    /// The HRP for a Bech32m-encoded mainnet Unified Address.
187    ///
188    /// Defined in [ZIP 316][zip-0316].
189    ///
190    /// [zip-0316]: https://zips.z.cash/zip-0316
191    fn hrp_unified_address(&self) -> &'static str;
192
193    /// The HRP for a Bech32m-encoded mainnet Unified FVK.
194    ///
195    /// Defined in [ZIP 316][zip-0316].
196    ///
197    /// [zip-0316]: https://zips.z.cash/zip-0316
198    fn hrp_unified_fvk(&self) -> &'static str;
199
200    /// The HRP for a Bech32m-encoded mainnet Unified IVK.
201    ///
202    /// Defined in [ZIP 316][zip-0316].
203    ///
204    /// [zip-0316]: https://zips.z.cash/zip-0316
205    fn hrp_unified_ivk(&self) -> &'static str;
206}
207
208/// The enumeration of known Zcash network types.
209#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
210pub enum NetworkType {
211    /// Zcash Mainnet.
212    Main,
213    /// Zcash Testnet.
214    Test,
215    /// Private integration / regression testing, used in `zcashd`.
216    ///
217    /// For some address types there is no distinction between test and regtest encodings;
218    /// those will always be parsed as `Network::Test`.
219    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
315/// Zcash consensus parameters.
316pub trait Parameters: Clone {
317    /// Returns the type of network configured by this set of consensus parameters.
318    fn network_type(&self) -> NetworkType;
319
320    /// Returns the activation height for a particular network upgrade,
321    /// if an activation height has been set.
322    fn activation_height(&self, nu: NetworkUpgrade) -> Option<BlockHeight>;
323
324    /// Determines whether the specified network upgrade is active as of the
325    /// provided block height on the network to which this Parameters value applies.
326    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/// Marker struct for the production network.
388#[derive(PartialEq, Eq, Copy, Clone, Debug)]
389pub struct MainNetwork;
390
391#[cfg(feature = "std")]
392memuse::impl_no_dynamic_usage!(MainNetwork);
393
394/// The production network.
395pub 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/// Marker struct for the test network.
420#[derive(PartialEq, Eq, Copy, Clone, Debug)]
421pub struct TestNetwork;
422
423#[cfg(feature = "std")]
424memuse::impl_no_dynamic_usage!(TestNetwork);
425
426/// The test network.
427pub 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/// The enumeration of known Zcash networks.
452#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
453pub enum Network {
454    /// Zcash Mainnet.
455    MainNetwork,
456    /// Zcash Testnet.
457    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/// An event that occurs at a specified height on the Zcash chain, at which point the
480/// consensus rules enforced by the network are altered.
481///
482/// See [ZIP 200](https://zips.z.cash/zip-0200) for more details.
483#[derive(Clone, Copy, Debug, PartialEq, Eq)]
484pub enum NetworkUpgrade {
485    /// The [Overwinter] network upgrade.
486    ///
487    /// [Overwinter]: https://z.cash/upgrade/overwinter/
488    Overwinter,
489    /// The [Sapling] network upgrade.
490    ///
491    /// [Sapling]: https://z.cash/upgrade/sapling/
492    Sapling,
493    /// The [Blossom] network upgrade.
494    ///
495    /// [Blossom]: https://z.cash/upgrade/blossom/
496    Blossom,
497    /// The [Heartwood] network upgrade.
498    ///
499    /// [Heartwood]: https://z.cash/upgrade/heartwood/
500    Heartwood,
501    /// The [Canopy] network upgrade.
502    ///
503    /// [Canopy]: https://z.cash/upgrade/canopy/
504    Canopy,
505    /// The [Nu5] network upgrade.
506    ///
507    /// [Nu5]: https://z.cash/upgrade/nu5/
508    Nu5,
509    /// The [Nu6] network upgrade.
510    ///
511    /// [Nu6]: https://z.cash/upgrade/nu6/
512    Nu6,
513    /// The [Nu7 (proposed)] network upgrade.
514    ///
515    /// [Nu7 (proposed)]: https://z.cash/upgrade/nu7/
516    #[cfg(zcash_unstable = "nu7")]
517    Nu7,
518    /// The ZFUTURE network upgrade.
519    ///
520    /// This upgrade is expected never to activate on mainnet;
521    /// it is intended for use in integration testing of functionality
522    /// that is a candidate for integration in a future network upgrade.
523    #[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
566/// The network upgrades on the Zcash chain in order of activation.
567///
568/// This order corresponds to the activation heights, but because Rust enums are
569/// full-fledged algebraic data types, we need to define it manually.
570const 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
582/// The "grace period" defined in [ZIP 212].
583///
584/// [ZIP 212]: https://zips.z.cash/zip-0212#changes-to-the-process-of-receiving-sapling-or-orchard-notes
585pub const ZIP212_GRACE_PERIOD: u32 = 32256;
586
587/// A globally-unique identifier for a set of consensus rules within the Zcash chain.
588///
589/// Each branch ID in this enum corresponds to one of the epochs between a pair of Zcash
590/// network upgrades. For example, `BranchId::Overwinter` corresponds to the blocks
591/// starting at Overwinter activation, and ending the block before Sapling activation.
592///
593/// The main use of the branch ID is in signature generation: transactions commit to a
594/// specific branch ID by including it as part of [`signature_hash`]. This ensures
595/// two-way replay protection for transactions across network upgrades.
596///
597/// See [ZIP 200](https://zips.z.cash/zip-0200) for more details.
598///
599/// [`signature_hash`]: https://docs.rs/zcash_primitives/latest/zcash_primitives/transaction/sighash/fn.signature_hash.html
600#[derive(Clone, Copy, Debug, PartialEq, Eq)]
601pub enum BranchId {
602    /// The consensus rules at the launch of Zcash.
603    Sprout,
604    /// The consensus rules deployed by [`NetworkUpgrade::Overwinter`].
605    Overwinter,
606    /// The consensus rules deployed by [`NetworkUpgrade::Sapling`].
607    Sapling,
608    /// The consensus rules deployed by [`NetworkUpgrade::Blossom`].
609    Blossom,
610    /// The consensus rules deployed by [`NetworkUpgrade::Heartwood`].
611    Heartwood,
612    /// The consensus rules deployed by [`NetworkUpgrade::Canopy`].
613    Canopy,
614    /// The consensus rules deployed by [`NetworkUpgrade::Nu5`].
615    Nu5,
616    /// The consensus rules deployed by [`NetworkUpgrade::Nu6`].
617    Nu6,
618    /// The consensus rules to be deployed by [`NetworkUpgrade::Nu7`].
619    #[cfg(zcash_unstable = "nu7")]
620    Nu7,
621    /// Candidates for future consensus rules; this branch will never
622    /// activate on mainnet.
623    #[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    /// Returns the branch ID corresponding to the consensus rule set that is active at
673    /// the given height.
674    ///
675    /// This is the branch ID that should be used when creating transactions.
676    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        // Sprout rules apply before any network upgrade
684        BranchId::Sprout
685    }
686
687    /// Returns the range of heights for the consensus epoch associated with this branch id.
688    ///
689    /// The resulting tuple implements the [`RangeBounds<BlockHeight>`] trait.
690    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    /// Returns the range of heights for the consensus epoch associated with this branch id.
700    ///
701    /// The return type of this value is slightly more precise than [`Self::height_range`]:
702    /// - `Some((x, Some(y)))` means that the consensus rules corresponding to this branch id
703    ///   are in effect for the range `x..y`
704    /// - `Some((x, None))` means that the consensus rules corresponding to this branch id are
705    ///   in effect for the range `x..`
706    /// - `None` means that the consensus rules corresponding to this branch id are never in effect.
707    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}