From cd326f2b6a1c5b37e246b594f7073b756cf26302 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 26 Nov 2019 23:44:57 +0000 Subject: [PATCH] Consensus parameters for network upgrades --- zcash_primitives/src/consensus.rs | 191 ++++++++++++++++++++++++++++-- 1 file changed, 178 insertions(+), 13 deletions(-) diff --git a/zcash_primitives/src/consensus.rs b/zcash_primitives/src/consensus.rs index aa2c67ffe..f7cf480d2 100644 --- a/zcash_primitives/src/consensus.rs +++ b/zcash_primitives/src/consensus.rs @@ -1,5 +1,106 @@ //! Consensus parameters. +use std::fmt; + +/// Zcash consensus parameters. +pub trait Parameters { + fn activation_height(nu: NetworkUpgrade) -> Option; + + fn is_nu_active(nu: NetworkUpgrade, height: u32) -> bool { + match Self::activation_height(nu) { + Some(h) if h <= height => true, + _ => false, + } + } +} + +/// Marker struct for the production network. +#[derive(Clone, Copy, Debug)] +pub struct MainNetwork; + +impl Parameters for MainNetwork { + fn activation_height(nu: NetworkUpgrade) -> Option { + match nu { + NetworkUpgrade::Overwinter => Some(347_500), + NetworkUpgrade::Sapling => Some(419_200), + NetworkUpgrade::Blossom => Some(653_600), + NetworkUpgrade::Heartwood => None, + } + } +} + +/// Marker struct for the test network. +#[derive(Clone, Copy, Debug)] +pub struct TestNetwork; + +impl Parameters for TestNetwork { + fn activation_height(nu: NetworkUpgrade) -> Option { + match nu { + NetworkUpgrade::Overwinter => Some(207_500), + NetworkUpgrade::Sapling => Some(280_000), + NetworkUpgrade::Blossom => Some(584_000), + NetworkUpgrade::Heartwood => None, + } + } +} + +/// An event that occurs at a specified height on the Zcash chain, at which point the +/// consensus rules enforced by the network are altered. +/// +/// See [ZIP 200](https://zips.z.cash/zip-0200) for more details. +#[derive(Clone, Copy, Debug)] +pub enum NetworkUpgrade { + /// The [Overwinter] network upgrade. + /// + /// [Overwinter]: https://z.cash/upgrade/overwinter/ + Overwinter, + /// The [Sapling] network upgrade. + /// + /// [Sapling]: https://z.cash/upgrade/sapling/ + Sapling, + /// The [Blossom] network upgrade. + /// + /// [Blossom]: https://z.cash/upgrade/blossom/ + Blossom, + /// The [Heartwood] network upgrade. + /// + /// [Heartwood]: https://z.cash/upgrade/heartwood/ + Heartwood, +} + +impl fmt::Display for NetworkUpgrade { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + NetworkUpgrade::Overwinter => write!(f, "Overwinter"), + NetworkUpgrade::Sapling => write!(f, "Sapling"), + NetworkUpgrade::Blossom => write!(f, "Blossom"), + NetworkUpgrade::Heartwood => write!(f, "Heartwood"), + } + } +} + +impl NetworkUpgrade { + fn branch_id(self) -> BranchId { + match self { + NetworkUpgrade::Overwinter => BranchId::Overwinter, + NetworkUpgrade::Sapling => BranchId::Sapling, + NetworkUpgrade::Blossom => BranchId::Blossom, + NetworkUpgrade::Heartwood => BranchId::Heartwood, + } + } +} + +/// The network upgrades on the Zcash chain in order of activation. +/// +/// This order corresponds to the activation heights, but because Rust enums are +/// full-fledged algebraic data types, we need to define it manually. +const UPGRADES_IN_ORDER: &[NetworkUpgrade] = &[ + NetworkUpgrade::Overwinter, + NetworkUpgrade::Sapling, + NetworkUpgrade::Blossom, + NetworkUpgrade::Heartwood, +]; + /// A globally-unique identifier for a set of consensus rules within the Zcash chain. /// /// Each branch ID in this enum corresponds to one of the epochs between a pair of Zcash @@ -13,25 +114,17 @@ /// See [ZIP 200](https://zips.z.cash/zip-0200) for more details. /// /// [`signature_hash`]: crate::transaction::signature_hash -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum BranchId { /// The consensus rules at the launch of Zcash. Sprout, - /// The consensus rules deployed in the [Overwinter] network upgrade. - /// - /// [Overwinter]: https://z.cash/upgrade/overwinter/ + /// The consensus rules deployed by [`NetworkUpgrade::Overwinter`]. Overwinter, - /// The consensus rules deployed in the [Sapling] network upgrade. - /// - /// [Sapling]: https://z.cash/upgrade/sapling/ + /// The consensus rules deployed by [`NetworkUpgrade::Sapling`]. Sapling, - /// The consensus rules deployed in the [Blossom] network upgrade. - /// - /// [Blossom]: https://z.cash/upgrade/blossom/ + /// The consensus rules deployed by [`NetworkUpgrade::Blossom`]. Blossom, - /// The consensus rules deployed in the [Heartwood] network upgrade. - /// - /// [Heartwood]: https://z.cash/upgrade/heartwood/ + /// The consensus rules deployed by [`NetworkUpgrade::Heartwood`]. Heartwood, } @@ -46,3 +139,75 @@ impl From for u32 { } } } + +impl BranchId { + /// Returns the branch ID corresponding to the consensus rule set that is active at + /// the given height. + /// + /// This is the branch ID that should be used when creating transactions. + pub fn for_height(height: u32) -> Self { + for nu in UPGRADES_IN_ORDER.iter().rev() { + if C::is_nu_active(*nu, height) { + return nu.branch_id(); + } + } + + // Sprout rules apply before any network upgrade + BranchId::Sprout + } +} + +#[cfg(test)] +mod tests { + use super::{BranchId, Parameters, MainNetwork, NetworkUpgrade, UPGRADES_IN_ORDER}; + + #[test] + fn nu_ordering() { + for i in 1..UPGRADES_IN_ORDER.len() { + let nu_a = UPGRADES_IN_ORDER[i - 1]; + let nu_b = UPGRADES_IN_ORDER[i]; + match ( + MainNetwork::activation_height(nu_a), + MainNetwork::activation_height(nu_b), + ) { + (Some(a), Some(b)) if a < b => (), + (Some(_), None) => (), + (None, None) => (), + _ => panic!( + "{} should not be before {} in UPGRADES_IN_ORDER", + nu_a, nu_b + ), + } + } + } + + #[test] + fn nu_is_active() { + assert!(!MainNetwork::is_nu_active(NetworkUpgrade::Overwinter, 0)); + assert!(!MainNetwork::is_nu_active( + NetworkUpgrade::Overwinter, + 347_499 + )); + assert!(MainNetwork::is_nu_active( + NetworkUpgrade::Overwinter, + 347_500 + )); + } + + #[test] + fn branch_id_for_height() { + assert_eq!(BranchId::for_height::(0), BranchId::Sprout,); + assert_eq!( + BranchId::for_height::(419_199), + BranchId::Overwinter, + ); + assert_eq!( + BranchId::for_height::(419_200), + BranchId::Sapling, + ); + assert_eq!( + BranchId::for_height::(5_000_000), + BranchId::Blossom, + ); + } +}