feature: Add Consensus Branch Ids
This commit is contained in:
parent
85f113bc18
commit
4b683ea2b1
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue