feature: Add Consensus Branch Ids

This commit is contained in:
teor 2020-07-22 22:34:48 +10:00
parent 85f113bc18
commit 4b683ea2b1
2 changed files with 127 additions and 1 deletions

View File

@ -2,7 +2,7 @@
use NetworkUpgrade::*;
use std::collections::BTreeMap;
use std::collections::{BTreeMap, HashMap};
use std::ops::Bound::*;
use zebra_chain::types::BlockHeight;
@ -58,6 +58,32 @@ pub(crate) const TESTNET_ACTIVATION_HEIGHTS: &[(BlockHeight, NetworkUpgrade)] =
// See ZIP 251 for updates.
];
/// The Consensus Branch Id, used to bind transactions and blocks to a
/// particular network upgrade.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub struct ConsensusBranchId(u32);
/// Network Upgrade Consensus Branch Ids.
///
/// Branch ids are the same for mainnet and testnet. If there is a testnet
/// rollback after a bug, the branch id changes.
///
/// Branch ids were introduced in the Overwinter upgrade, so there is no
/// BeforeOverwinter branch id.
///
/// This is actually a bijective map, but it is const, so we use a vector, and
/// do the uniqueness check in the unit tests.
pub(crate) const CONSENSUS_BRANCH_IDS: &[(NetworkUpgrade, ConsensusBranchId)] = &[
// TODO(teor): byte order?
(Overwinter, ConsensusBranchId(0x5ba81b19)),
(Sapling, ConsensusBranchId(0x76b809bb)),
(Blossom, ConsensusBranchId(0x2bb40e60)),
(Heartwood, ConsensusBranchId(0xf5b9230b)),
// As of 21 July 2020. Could change before mainnet activation.
// See ZIP 251 for updates.
(Canopy, ConsensusBranchId(0xe9ff75a6)),
];
impl NetworkUpgrade {
/// Returns a BTreeMap of activation heights and network upgrades for
/// `network`.
@ -106,4 +132,32 @@ impl NetworkUpgrade {
.map(|(height, _)| *height)
.next()
}
/// Returns a BTreeMap of NetworkUpgrades and their ConsensusBranchIds.
///
/// Branch ids are the same for mainnet and testnet.
///
/// If network upgrade does not have a branch id, that network upgrade does
/// not appear in the list.
///
/// This is actually a bijective map.
pub(crate) fn branch_id_list() -> HashMap<NetworkUpgrade, ConsensusBranchId> {
CONSENSUS_BRANCH_IDS.iter().cloned().collect()
}
/// Returns the consensus branch id for this network upgrade.
///
/// Returns None if this network upgrade has no consensus branch id.
pub fn branch_id(&self) -> Option<ConsensusBranchId> {
NetworkUpgrade::branch_id_list().get(&self).cloned()
}
}
impl ConsensusBranchId {
/// Returns the current consensus branch id for `network` and `height`.
///
/// Returns None if the network has no branch id at this height.
pub fn current(network: Network, height: BlockHeight) -> Option<ConsensusBranchId> {
NetworkUpgrade::current(network, height).branch_id()
}
}

View File

@ -103,3 +103,75 @@ fn activation_consistent(network: Network) {
);
}
}
/// Check that the network upgrades and branch ids are unique.
#[test]
fn branch_id_bijective() {
let branch_id_list = NetworkUpgrade::branch_id_list();
let nus: HashSet<&NetworkUpgrade> = branch_id_list.keys().collect();
assert_eq!(CONSENSUS_BRANCH_IDS.len(), nus.len());
let branch_ids: HashSet<&ConsensusBranchId> = branch_id_list.values().collect();
assert_eq!(CONSENSUS_BRANCH_IDS.len(), branch_ids.len());
}
#[test]
fn branch_id_extremes_mainnet() {
branch_id_extremes(Mainnet)
}
#[test]
fn branch_id_extremes_testnet() {
branch_id_extremes(Testnet)
}
/// Test the branch_id_list, branch_id, and current functions for `network` with
/// extreme values.
fn branch_id_extremes(network: Network) {
// Branch ids were introduced in Overwinter
assert_eq!(
NetworkUpgrade::branch_id_list().get(&BeforeOverwinter),
None
);
assert_eq!(ConsensusBranchId::current(network, BlockHeight(0)), None);
assert_eq!(
NetworkUpgrade::branch_id_list().get(&Overwinter).cloned(),
Overwinter.branch_id()
);
// We assume that the last upgrade we know about continues forever
// (even if we suspect that won't be true)
assert_ne!(
NetworkUpgrade::branch_id_list().get(&NetworkUpgrade::current(network, BlockHeight::MAX)),
None
);
assert_ne!(ConsensusBranchId::current(network, BlockHeight::MAX), None);
}
#[test]
fn branch_id_consistent_mainnet() {
branch_id_consistent(Mainnet)
}
#[test]
fn branch_id_consistent_testnet() {
branch_id_consistent(Testnet)
}
/// Check that the branch_id and current functions are consistent for `network`.
fn branch_id_consistent(network: Network) {
let branch_id_list = NetworkUpgrade::branch_id_list();
let network_upgrades: HashSet<&NetworkUpgrade> = branch_id_list.keys().collect();
for &network_upgrade in network_upgrades {
let height = network_upgrade.activation_height(network);
// Skip network upgrades that don't have activation heights yet
if let Some(height) = height {
assert_eq!(
ConsensusBranchId::current(network, height),
network_upgrade.branch_id()
);
}
}
}