Consensus parameters for network upgrades

This commit is contained in:
Jack Grigg 2019-11-26 23:44:57 +00:00
parent c24024b8e1
commit cd326f2b6a
No known key found for this signature in database
GPG Key ID: 9E8255172BBF9898
1 changed files with 178 additions and 13 deletions

View File

@ -1,5 +1,106 @@
//! Consensus parameters.
use std::fmt;
/// Zcash consensus parameters.
pub trait Parameters {
fn activation_height(nu: NetworkUpgrade) -> Option<u32>;
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<u32> {
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<u32> {
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<BranchId> 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<C: Parameters>(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::<MainNetwork>(0), BranchId::Sprout,);
assert_eq!(
BranchId::for_height::<MainNetwork>(419_199),
BranchId::Overwinter,
);
assert_eq!(
BranchId::for_height::<MainNetwork>(419_200),
BranchId::Sapling,
);
assert_eq!(
BranchId::for_height::<MainNetwork>(5_000_000),
BranchId::Blossom,
);
}
}