use core::cmp::{Ord, Ordering};
use core::convert::TryFrom;
use core::fmt;
use core::num::TryFromIntError;
use core::ops::{Add, Bound, RangeBounds, Sub};
#[cfg(feature = "std")]
use memuse::DynamicUsage;
use crate::constants::{mainnet, regtest, testnet};
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct BlockHeight(u32);
#[cfg(feature = "std")]
memuse::impl_no_dynamic_usage!(BlockHeight);
pub const H0: BlockHeight = BlockHeight(0);
impl BlockHeight {
pub const fn from_u32(v: u32) -> BlockHeight {
BlockHeight(v)
}
pub fn saturating_sub(self, v: u32) -> BlockHeight {
BlockHeight(self.0.saturating_sub(v))
}
}
impl fmt::Display for BlockHeight {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(formatter)
}
}
impl Ord for BlockHeight {
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}
impl PartialOrd for BlockHeight {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl From<u32> for BlockHeight {
fn from(value: u32) -> Self {
BlockHeight(value)
}
}
impl From<BlockHeight> for u32 {
fn from(value: BlockHeight) -> u32 {
value.0
}
}
impl TryFrom<u64> for BlockHeight {
type Error = core::num::TryFromIntError;
fn try_from(value: u64) -> Result<Self, Self::Error> {
u32::try_from(value).map(BlockHeight)
}
}
impl From<BlockHeight> for u64 {
fn from(value: BlockHeight) -> u64 {
value.0 as u64
}
}
impl TryFrom<i32> for BlockHeight {
type Error = core::num::TryFromIntError;
fn try_from(value: i32) -> Result<Self, Self::Error> {
u32::try_from(value).map(BlockHeight)
}
}
impl TryFrom<i64> for BlockHeight {
type Error = core::num::TryFromIntError;
fn try_from(value: i64) -> Result<Self, Self::Error> {
u32::try_from(value).map(BlockHeight)
}
}
impl From<BlockHeight> for i64 {
fn from(value: BlockHeight) -> i64 {
value.0 as i64
}
}
impl Add<u32> for BlockHeight {
type Output = Self;
fn add(self, other: u32) -> Self {
BlockHeight(self.0.saturating_add(other))
}
}
impl Sub<u32> for BlockHeight {
type Output = Self;
fn sub(self, other: u32) -> Self {
BlockHeight(self.0.saturating_sub(other))
}
}
impl Sub<BlockHeight> for BlockHeight {
type Output = u32;
fn sub(self, other: BlockHeight) -> u32 {
self.0.saturating_sub(other.0)
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct TxIndex(u16);
impl From<TxIndex> for u16 {
fn from(value: TxIndex) -> Self {
value.0
}
}
impl From<TxIndex> for u32 {
fn from(value: TxIndex) -> Self {
value.0.into()
}
}
impl From<TxIndex> for u64 {
fn from(value: TxIndex) -> Self {
value.0.into()
}
}
impl From<u16> for TxIndex {
fn from(value: u16) -> Self {
TxIndex(value)
}
}
impl TryFrom<u32> for TxIndex {
type Error = TryFromIntError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
Ok(TxIndex(u16::try_from(value)?))
}
}
impl TryFrom<u64> for TxIndex {
type Error = TryFromIntError;
fn try_from(value: u64) -> Result<Self, Self::Error> {
Ok(TxIndex(u16::try_from(value)?))
}
}
impl TryFrom<usize> for TxIndex {
type Error = TryFromIntError;
fn try_from(value: usize) -> Result<Self, Self::Error> {
Ok(TxIndex(u16::try_from(value)?))
}
}
#[cfg(feature = "std")]
memuse::impl_no_dynamic_usage!(TxIndex);
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum NetworkType {
Main,
Test,
Regtest,
}
#[cfg(feature = "std")]
memuse::impl_no_dynamic_usage!(NetworkType);
pub(crate) mod private {
pub trait Sealed {}
}
pub trait NetworkConstants: private::Sealed + Clone {
fn coin_type(&self) -> u32;
fn hrp_sapling_extended_spending_key(&self) -> &'static str;
fn hrp_sapling_extended_full_viewing_key(&self) -> &'static str;
fn hrp_sapling_payment_address(&self) -> &'static str;
fn b58_sprout_address_prefix(&self) -> [u8; 2];
fn b58_pubkey_address_prefix(&self) -> [u8; 2];
fn b58_secret_key_prefix(&self) -> [u8; 1];
fn b58_script_address_prefix(&self) -> [u8; 2];
fn hrp_tex_address(&self) -> &'static str;
fn hrp_unified_address(&self) -> &'static str;
fn hrp_unified_fvk(&self) -> &'static str;
fn hrp_unified_ivk(&self) -> &'static str;
}
impl private::Sealed for NetworkType {}
impl NetworkConstants for NetworkType {
fn coin_type(&self) -> u32 {
match self {
NetworkType::Main => mainnet::COIN_TYPE,
NetworkType::Test => testnet::COIN_TYPE,
NetworkType::Regtest => regtest::COIN_TYPE,
}
}
fn hrp_sapling_extended_spending_key(&self) -> &'static str {
match self {
NetworkType::Main => mainnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
NetworkType::Test => testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
NetworkType::Regtest => regtest::HRP_SAPLING_EXTENDED_SPENDING_KEY,
}
}
fn hrp_sapling_extended_full_viewing_key(&self) -> &'static str {
match self {
NetworkType::Main => mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
NetworkType::Test => testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
NetworkType::Regtest => regtest::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
}
}
fn hrp_sapling_payment_address(&self) -> &'static str {
match self {
NetworkType::Main => mainnet::HRP_SAPLING_PAYMENT_ADDRESS,
NetworkType::Test => testnet::HRP_SAPLING_PAYMENT_ADDRESS,
NetworkType::Regtest => regtest::HRP_SAPLING_PAYMENT_ADDRESS,
}
}
fn b58_sprout_address_prefix(&self) -> [u8; 2] {
match self {
NetworkType::Main => mainnet::B58_SPROUT_ADDRESS_PREFIX,
NetworkType::Test => testnet::B58_SPROUT_ADDRESS_PREFIX,
NetworkType::Regtest => regtest::B58_SPROUT_ADDRESS_PREFIX,
}
}
fn b58_pubkey_address_prefix(&self) -> [u8; 2] {
match self {
NetworkType::Main => mainnet::B58_PUBKEY_ADDRESS_PREFIX,
NetworkType::Test => testnet::B58_PUBKEY_ADDRESS_PREFIX,
NetworkType::Regtest => regtest::B58_PUBKEY_ADDRESS_PREFIX,
}
}
fn b58_secret_key_prefix(&self) -> [u8; 1] {
match self {
NetworkType::Main => mainnet::B58_SECRET_KEY_PREFIX,
NetworkType::Test => testnet::B58_SECRET_KEY_PREFIX,
NetworkType::Regtest => regtest::B58_SECRET_KEY_PREFIX,
}
}
fn b58_script_address_prefix(&self) -> [u8; 2] {
match self {
NetworkType::Main => mainnet::B58_SCRIPT_ADDRESS_PREFIX,
NetworkType::Test => testnet::B58_SCRIPT_ADDRESS_PREFIX,
NetworkType::Regtest => regtest::B58_SCRIPT_ADDRESS_PREFIX,
}
}
fn hrp_tex_address(&self) -> &'static str {
match self {
NetworkType::Main => mainnet::HRP_TEX_ADDRESS,
NetworkType::Test => testnet::HRP_TEX_ADDRESS,
NetworkType::Regtest => regtest::HRP_TEX_ADDRESS,
}
}
fn hrp_unified_address(&self) -> &'static str {
match self {
NetworkType::Main => mainnet::HRP_UNIFIED_ADDRESS,
NetworkType::Test => testnet::HRP_UNIFIED_ADDRESS,
NetworkType::Regtest => regtest::HRP_UNIFIED_ADDRESS,
}
}
fn hrp_unified_fvk(&self) -> &'static str {
match self {
NetworkType::Main => mainnet::HRP_UNIFIED_FVK,
NetworkType::Test => testnet::HRP_UNIFIED_FVK,
NetworkType::Regtest => regtest::HRP_UNIFIED_FVK,
}
}
fn hrp_unified_ivk(&self) -> &'static str {
match self {
NetworkType::Main => mainnet::HRP_UNIFIED_IVK,
NetworkType::Test => testnet::HRP_UNIFIED_IVK,
NetworkType::Regtest => regtest::HRP_UNIFIED_IVK,
}
}
}
pub trait Parameters: Clone {
fn network_type(&self) -> NetworkType;
fn activation_height(&self, nu: NetworkUpgrade) -> Option<BlockHeight>;
fn is_nu_active(&self, nu: NetworkUpgrade, height: BlockHeight) -> bool {
self.activation_height(nu).is_some_and(|h| h <= height)
}
}
impl<P: Parameters> Parameters for &P {
fn network_type(&self) -> NetworkType {
(*self).network_type()
}
fn activation_height(&self, nu: NetworkUpgrade) -> Option<BlockHeight> {
(*self).activation_height(nu)
}
}
impl<P: Parameters> private::Sealed for P {}
impl<P: Parameters> NetworkConstants for P {
fn coin_type(&self) -> u32 {
self.network_type().coin_type()
}
fn hrp_sapling_extended_spending_key(&self) -> &'static str {
self.network_type().hrp_sapling_extended_spending_key()
}
fn hrp_sapling_extended_full_viewing_key(&self) -> &'static str {
self.network_type().hrp_sapling_extended_full_viewing_key()
}
fn hrp_sapling_payment_address(&self) -> &'static str {
self.network_type().hrp_sapling_payment_address()
}
fn b58_sprout_address_prefix(&self) -> [u8; 2] {
self.network_type().b58_sprout_address_prefix()
}
fn b58_pubkey_address_prefix(&self) -> [u8; 2] {
self.network_type().b58_pubkey_address_prefix()
}
fn b58_secret_key_prefix(&self) -> [u8; 1] {
self.network_type().b58_secret_key_prefix()
}
fn b58_script_address_prefix(&self) -> [u8; 2] {
self.network_type().b58_script_address_prefix()
}
fn hrp_tex_address(&self) -> &'static str {
self.network_type().hrp_tex_address()
}
fn hrp_unified_address(&self) -> &'static str {
self.network_type().hrp_unified_address()
}
fn hrp_unified_fvk(&self) -> &'static str {
self.network_type().hrp_unified_fvk()
}
fn hrp_unified_ivk(&self) -> &'static str {
self.network_type().hrp_unified_ivk()
}
}
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct MainNetwork;
#[cfg(feature = "std")]
memuse::impl_no_dynamic_usage!(MainNetwork);
pub const MAIN_NETWORK: MainNetwork = MainNetwork;
impl Parameters for MainNetwork {
fn network_type(&self) -> NetworkType {
NetworkType::Main
}
fn activation_height(&self, nu: NetworkUpgrade) -> Option<BlockHeight> {
match nu {
NetworkUpgrade::Overwinter => Some(BlockHeight(347_500)),
NetworkUpgrade::Sapling => Some(BlockHeight(419_200)),
NetworkUpgrade::Blossom => Some(BlockHeight(653_600)),
NetworkUpgrade::Heartwood => Some(BlockHeight(903_000)),
NetworkUpgrade::Canopy => Some(BlockHeight(1_046_400)),
NetworkUpgrade::Nu5 => Some(BlockHeight(1_687_104)),
NetworkUpgrade::Nu6 => Some(BlockHeight(2_726_400)),
NetworkUpgrade::Nu6_1 => Some(BlockHeight(3_146_400)),
NetworkUpgrade::Nu6_2 => Some(BlockHeight(3_364_600)),
#[cfg(zcash_unstable = "nu7")]
NetworkUpgrade::Nu7 => None,
#[cfg(zcash_unstable = "zfuture")]
NetworkUpgrade::ZFuture => None,
}
}
}
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct TestNetwork;
#[cfg(feature = "std")]
memuse::impl_no_dynamic_usage!(TestNetwork);
pub const TEST_NETWORK: TestNetwork = TestNetwork;
impl Parameters for TestNetwork {
fn network_type(&self) -> NetworkType {
NetworkType::Test
}
fn activation_height(&self, nu: NetworkUpgrade) -> Option<BlockHeight> {
match nu {
NetworkUpgrade::Overwinter => Some(BlockHeight(207_500)),
NetworkUpgrade::Sapling => Some(BlockHeight(280_000)),
NetworkUpgrade::Blossom => Some(BlockHeight(584_000)),
NetworkUpgrade::Heartwood => Some(BlockHeight(903_800)),
NetworkUpgrade::Canopy => Some(BlockHeight(1_028_500)),
NetworkUpgrade::Nu5 => Some(BlockHeight(1_842_420)),
NetworkUpgrade::Nu6 => Some(BlockHeight(2_976_000)),
NetworkUpgrade::Nu6_1 => Some(BlockHeight(3_536_500)),
NetworkUpgrade::Nu6_2 => Some(BlockHeight(4_052_000)),
#[cfg(zcash_unstable = "nu7")]
NetworkUpgrade::Nu7 => None,
#[cfg(zcash_unstable = "zfuture")]
NetworkUpgrade::ZFuture => None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Network {
MainNetwork,
TestNetwork,
}
#[cfg(feature = "std")]
memuse::impl_no_dynamic_usage!(Network);
impl Parameters for Network {
fn network_type(&self) -> NetworkType {
match self {
Network::MainNetwork => NetworkType::Main,
Network::TestNetwork => NetworkType::Test,
}
}
fn activation_height(&self, nu: NetworkUpgrade) -> Option<BlockHeight> {
match self {
Network::MainNetwork => MAIN_NETWORK.activation_height(nu),
Network::TestNetwork => TEST_NETWORK.activation_height(nu),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NetworkUpgrade {
Overwinter,
Sapling,
Blossom,
Heartwood,
Canopy,
Nu5,
Nu6,
Nu6_1,
Nu6_2,
#[cfg(zcash_unstable = "nu7")]
Nu7,
#[cfg(zcash_unstable = "zfuture")]
ZFuture,
}
#[cfg(feature = "std")]
memuse::impl_no_dynamic_usage!(NetworkUpgrade);
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"),
NetworkUpgrade::Canopy => write!(f, "Canopy"),
NetworkUpgrade::Nu5 => write!(f, "Nu5"),
NetworkUpgrade::Nu6 => write!(f, "Nu6"),
NetworkUpgrade::Nu6_1 => write!(f, "Nu6.1"),
NetworkUpgrade::Nu6_2 => write!(f, "Nu6.2"),
#[cfg(zcash_unstable = "nu7")]
NetworkUpgrade::Nu7 => write!(f, "Nu7"),
#[cfg(zcash_unstable = "zfuture")]
NetworkUpgrade::ZFuture => write!(f, "ZFUTURE"),
}
}
}
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,
NetworkUpgrade::Canopy => BranchId::Canopy,
NetworkUpgrade::Nu5 => BranchId::Nu5,
NetworkUpgrade::Nu6 => BranchId::Nu6,
NetworkUpgrade::Nu6_1 => BranchId::Nu6_1,
NetworkUpgrade::Nu6_2 => BranchId::Nu6_2,
#[cfg(zcash_unstable = "nu7")]
NetworkUpgrade::Nu7 => BranchId::Nu7,
#[cfg(zcash_unstable = "zfuture")]
NetworkUpgrade::ZFuture => BranchId::ZFuture,
}
}
}
const UPGRADES_IN_ORDER: &[NetworkUpgrade] = &[
NetworkUpgrade::Overwinter,
NetworkUpgrade::Sapling,
NetworkUpgrade::Blossom,
NetworkUpgrade::Heartwood,
NetworkUpgrade::Canopy,
NetworkUpgrade::Nu5,
NetworkUpgrade::Nu6,
NetworkUpgrade::Nu6_1,
NetworkUpgrade::Nu6_2,
#[cfg(zcash_unstable = "nu7")]
NetworkUpgrade::Nu7,
];
pub const ZIP212_GRACE_PERIOD: u32 = 32256;
pub const COINBASE_MATURITY_BLOCKS: u32 = 100;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BranchId {
Sprout,
Overwinter,
Sapling,
Blossom,
Heartwood,
Canopy,
Nu5,
Nu6,
Nu6_1,
Nu6_2,
#[cfg(zcash_unstable = "nu7")]
Nu7,
#[cfg(zcash_unstable = "zfuture")]
ZFuture,
}
#[cfg(feature = "std")]
memuse::impl_no_dynamic_usage!(BranchId);
impl TryFrom<u32> for BranchId {
type Error = &'static str;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(BranchId::Sprout),
0x5ba8_1b19 => Ok(BranchId::Overwinter),
0x76b8_09bb => Ok(BranchId::Sapling),
0x2bb4_0e60 => Ok(BranchId::Blossom),
0xf5b9_230b => Ok(BranchId::Heartwood),
0xe9ff_75a6 => Ok(BranchId::Canopy),
0xc2d6_d0b4 => Ok(BranchId::Nu5),
0xc8e7_1055 => Ok(BranchId::Nu6),
0x4dec_4df0 => Ok(BranchId::Nu6_1),
0x5437_f330 => Ok(BranchId::Nu6_2),
#[cfg(zcash_unstable = "nu7")]
0xffff_ffff => Ok(BranchId::Nu7),
#[cfg(zcash_unstable = "zfuture")]
0xffff_ffff => Ok(BranchId::ZFuture),
_ => Err("Unknown consensus branch ID"),
}
}
}
impl From<BranchId> for u32 {
fn from(consensus_branch_id: BranchId) -> u32 {
match consensus_branch_id {
BranchId::Sprout => 0,
BranchId::Overwinter => 0x5ba8_1b19,
BranchId::Sapling => 0x76b8_09bb,
BranchId::Blossom => 0x2bb4_0e60,
BranchId::Heartwood => 0xf5b9_230b,
BranchId::Canopy => 0xe9ff_75a6,
BranchId::Nu5 => 0xc2d6_d0b4,
BranchId::Nu6 => 0xc8e7_1055,
BranchId::Nu6_1 => 0x4dec_4df0,
BranchId::Nu6_2 => 0x5437_f330,
#[cfg(zcash_unstable = "nu7")]
BranchId::Nu7 => 0xffff_ffff,
#[cfg(zcash_unstable = "zfuture")]
BranchId::ZFuture => 0xffff_ffff,
}
}
}
impl BranchId {
pub fn for_height<P: Parameters>(parameters: &P, height: BlockHeight) -> Self {
for nu in UPGRADES_IN_ORDER.iter().rev() {
if parameters.is_nu_active(*nu, height) {
return nu.branch_id();
}
}
BranchId::Sprout
}
pub fn height_range<P: Parameters>(&self, params: &P) -> Option<impl RangeBounds<BlockHeight>> {
self.height_bounds(params).map(|(lower, upper)| {
(
Bound::Included(lower),
upper.map_or(Bound::Unbounded, Bound::Excluded),
)
})
}
pub fn height_bounds<P: Parameters>(
&self,
params: &P,
) -> Option<(BlockHeight, Option<BlockHeight>)> {
match self {
BranchId::Sprout => params
.activation_height(NetworkUpgrade::Overwinter)
.map(|upper| (BlockHeight(0), Some(upper))),
BranchId::Overwinter => params
.activation_height(NetworkUpgrade::Overwinter)
.map(|lower| (lower, params.activation_height(NetworkUpgrade::Sapling))),
BranchId::Sapling => params
.activation_height(NetworkUpgrade::Sapling)
.map(|lower| (lower, params.activation_height(NetworkUpgrade::Blossom))),
BranchId::Blossom => params
.activation_height(NetworkUpgrade::Blossom)
.map(|lower| (lower, params.activation_height(NetworkUpgrade::Heartwood))),
BranchId::Heartwood => params
.activation_height(NetworkUpgrade::Heartwood)
.map(|lower| (lower, params.activation_height(NetworkUpgrade::Canopy))),
BranchId::Canopy => params
.activation_height(NetworkUpgrade::Canopy)
.map(|lower| (lower, params.activation_height(NetworkUpgrade::Nu5))),
BranchId::Nu5 => params
.activation_height(NetworkUpgrade::Nu5)
.map(|lower| (lower, params.activation_height(NetworkUpgrade::Nu6))),
BranchId::Nu6 => params
.activation_height(NetworkUpgrade::Nu6)
.map(|lower| (lower, params.activation_height(NetworkUpgrade::Nu6_1))),
BranchId::Nu6_1 => params
.activation_height(NetworkUpgrade::Nu6_1)
.map(|lower| (lower, params.activation_height(NetworkUpgrade::Nu6_2))),
BranchId::Nu6_2 => params
.activation_height(NetworkUpgrade::Nu6_2)
.map(|lower| {
#[cfg(zcash_unstable = "nu7")]
let upper = params.activation_height(NetworkUpgrade::Nu7);
#[cfg(zcash_unstable = "zfuture")]
let upper = params.activation_height(NetworkUpgrade::ZFuture);
#[cfg(not(any(zcash_unstable = "nu7", zcash_unstable = "zfuture")))]
let upper = None;
(lower, upper)
}),
#[cfg(zcash_unstable = "nu7")]
BranchId::Nu7 => params
.activation_height(NetworkUpgrade::Nu7)
.map(|lower| (lower, None)),
#[cfg(zcash_unstable = "zfuture")]
BranchId::ZFuture => params
.activation_height(NetworkUpgrade::ZFuture)
.map(|lower| (lower, None)),
}
}
pub fn sprout_uses_groth_proofs(&self) -> bool {
!matches!(self, BranchId::Sprout | BranchId::Overwinter)
}
pub fn has_sprout(&self) -> bool {
use BranchId::*;
match self {
Sprout | Overwinter | Sapling | Blossom | Heartwood | Canopy | Nu5 | Nu6 | Nu6_1
| Nu6_2 => true,
#[cfg(zcash_unstable = "nu7")]
BranchId::Nu7 => false,
#[cfg(zcash_unstable = "zfuture")]
BranchId::ZFuture => false,
}
}
pub fn has_sapling(&self) -> bool {
use BranchId::*;
match self {
Sprout | Overwinter => false,
Sapling | Blossom | Heartwood | Canopy | Nu5 | Nu6 | Nu6_1 | Nu6_2 => true,
#[cfg(zcash_unstable = "nu7")]
BranchId::Nu7 => true,
#[cfg(zcash_unstable = "zfuture")]
BranchId::ZFuture => true,
}
}
pub fn has_orchard(&self) -> bool {
use BranchId::*;
match self {
Sprout | Overwinter | Sapling | Blossom | Heartwood | Canopy => false,
Nu5 | Nu6 | Nu6_1 | Nu6_2 => true,
#[cfg(zcash_unstable = "nu7")]
BranchId::Nu7 => true,
#[cfg(zcash_unstable = "zfuture")]
BranchId::ZFuture => true,
}
}
}
#[cfg(any(test, feature = "test-dependencies"))]
pub mod testing {
use proptest::sample::select;
use proptest::strategy::{Just, Strategy};
use super::{BlockHeight, BranchId, Parameters};
pub fn arb_branch_id() -> impl Strategy<Value = BranchId> {
select(vec![
BranchId::Sprout,
BranchId::Overwinter,
BranchId::Sapling,
BranchId::Blossom,
BranchId::Heartwood,
BranchId::Canopy,
BranchId::Nu5,
BranchId::Nu6,
BranchId::Nu6_1,
BranchId::Nu6_2,
#[cfg(zcash_unstable = "nu7")]
BranchId::Nu7,
#[cfg(zcash_unstable = "zfuture")]
BranchId::ZFuture,
])
}
pub fn arb_height<P: Parameters>(
branch_id: BranchId,
params: &P,
) -> impl Strategy<Value = Option<BlockHeight>> {
branch_id
.height_bounds(params)
.map_or(Strategy::boxed(Just(None)), |(lower, upper)| {
Strategy::boxed(
(lower.0..upper.map_or(u32::MAX, |u| u.0)).prop_map(|h| Some(BlockHeight(h))),
)
})
}
#[cfg(feature = "test-dependencies")]
impl incrementalmerkletree_testing::TestCheckpoint for BlockHeight {
fn from_u64(value: u64) -> Self {
BlockHeight(u32::try_from(value).expect("Test checkpoint ids do not exceed 32 bits"))
}
}
}
#[cfg(test)]
mod tests {
use super::{
BlockHeight, BranchId, MAIN_NETWORK, NetworkUpgrade, Parameters, 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 (
MAIN_NETWORK.activation_height(nu_a),
MAIN_NETWORK.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!(!MAIN_NETWORK.is_nu_active(NetworkUpgrade::Overwinter, BlockHeight(0)));
assert!(!MAIN_NETWORK.is_nu_active(NetworkUpgrade::Overwinter, BlockHeight(347_499)));
assert!(MAIN_NETWORK.is_nu_active(NetworkUpgrade::Overwinter, BlockHeight(347_500)));
}
#[test]
fn branch_id_from_u32() {
assert_eq!(BranchId::try_from(0), Ok(BranchId::Sprout));
assert!(BranchId::try_from(1).is_err());
}
#[test]
fn branch_id_for_height() {
assert_eq!(
BranchId::for_height(&MAIN_NETWORK, BlockHeight(0)),
BranchId::Sprout,
);
assert_eq!(
BranchId::for_height(&MAIN_NETWORK, BlockHeight(419_199)),
BranchId::Overwinter,
);
assert_eq!(
BranchId::for_height(&MAIN_NETWORK, BlockHeight(419_200)),
BranchId::Sapling,
);
assert_eq!(
BranchId::for_height(&MAIN_NETWORK, BlockHeight(903_000)),
BranchId::Heartwood,
);
assert_eq!(
BranchId::for_height(&MAIN_NETWORK, BlockHeight(1_046_400)),
BranchId::Canopy,
);
assert_eq!(
BranchId::for_height(&MAIN_NETWORK, BlockHeight(1_687_104)),
BranchId::Nu5,
);
assert_eq!(
BranchId::for_height(&MAIN_NETWORK, BlockHeight(2_726_399)),
BranchId::Nu5,
);
assert_eq!(
BranchId::for_height(&MAIN_NETWORK, BlockHeight(2_726_400)),
BranchId::Nu6,
);
assert_eq!(
BranchId::for_height(&MAIN_NETWORK, BlockHeight(3_146_400)),
BranchId::Nu6_1,
);
assert_eq!(
BranchId::for_height(&MAIN_NETWORK, BlockHeight(5_000_000)),
BranchId::Nu6_1,
);
}
}