Adds an empty `NetworkParameters` struct to `Network::Testnet` variant, updates production code

This commit is contained in:
Arya 2024-03-18 21:07:15 -04:00
parent c22404f639
commit fd57e40d07
16 changed files with 127 additions and 55 deletions

View File

@ -51,6 +51,11 @@ mod tests;
/// after the grace period.
const ZIP_212_GRACE_PERIOD_DURATION: HeightDiff = 32_256;
/// Network consensus parameters for test networks such as Regtest and the default Testnet.
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
pub struct NetworkParameters {}
/// An enum describing the possible network choices.
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
@ -60,7 +65,7 @@ pub enum Network {
Mainnet,
/// The oldest public test network.
Testnet,
Testnet(NetworkParameters),
}
use zcash_primitives::consensus::{Network as ZcashPrimitivesNetwork, Parameters as _};
@ -68,13 +73,17 @@ impl Network {
/// Returns the human-readable prefix for Base58Check-encoded transparent
/// pay-to-public-key-hash payment addresses for the network.
pub fn b58_pubkey_address_prefix(&self) -> [u8; 2] {
<ZcashPrimitivesNetwork>::from(*self).b58_pubkey_address_prefix()
<ZcashPrimitivesNetwork>::try_from(*self)
.expect("must match zcash_primitives network")
.b58_pubkey_address_prefix()
}
/// Returns the human-readable prefix for Base58Check-encoded transparent pay-to-script-hash
/// payment addresses for the network.
pub fn b58_script_address_prefix(&self) -> [u8; 2] {
<ZcashPrimitivesNetwork>::from(*self).b58_script_address_prefix()
<ZcashPrimitivesNetwork>::try_from(*self)
.expect("must match zcash_primitives network")
.b58_script_address_prefix()
}
/// Returns true if the maximum block time rule is active for `network` and `height`.
///
@ -87,7 +96,7 @@ impl Network {
pub fn is_max_block_time_enforced(self, height: block::Height) -> bool {
match self {
Network::Mainnet => true,
Network::Testnet => height >= super::TESTNET_MAX_TIME_START_HEIGHT,
Network::Testnet(_params) => height >= super::TESTNET_MAX_TIME_START_HEIGHT,
}
}
}
@ -96,7 +105,7 @@ impl From<Network> for &'static str {
fn from(network: Network) -> &'static str {
match network {
Network::Mainnet => "Mainnet",
Network::Testnet => "Testnet",
Network::Testnet(_params) => "Testnet",
}
}
}
@ -114,17 +123,27 @@ impl fmt::Display for Network {
}
impl Network {
/// Creates a new [`Network::Testnet`] with the default Testnet network parameters.
pub fn new_default_testnet() -> Self {
Self::Testnet(NetworkParameters::default())
}
/// Returns true if the network is the default Testnet, or false otherwise.
pub fn is_default_testnet(&self) -> bool {
self == &Self::new_default_testnet()
}
/// Returns an iterator over [`Network`] variants.
pub fn iter() -> impl Iterator<Item = Self> {
// TODO: Use default values of `Testnet` variant when adding fields for #7845.
[Self::Mainnet, Self::Testnet].into_iter()
[Self::Mainnet, Self::new_default_testnet()].into_iter()
}
/// Get the default port associated to this network.
pub fn default_port(&self) -> u16 {
match self {
Network::Mainnet => 8233,
Network::Testnet => 18233,
Network::Testnet(_params) => 18233,
}
}
@ -151,7 +170,7 @@ impl Network {
pub fn bip70_network_name(&self) -> String {
match self {
Network::Mainnet => "main".to_string(),
Network::Testnet => "test".to_string(),
Network::Testnet(_params) => "test".to_string(),
}
}
@ -179,7 +198,7 @@ impl FromStr for Network {
fn from_str(string: &str) -> Result<Self, Self::Err> {
match string.to_lowercase().as_str() {
"mainnet" => Ok(Network::Mainnet),
"testnet" => Ok(Network::Testnet),
"testnet" => Ok(Network::new_default_testnet()),
_ => Err(InvalidNetworkError(string.to_owned())),
}
}

View File

@ -271,7 +271,8 @@ impl Network {
};
match self {
Mainnet => mainnet_heights,
Testnet => testnet_heights,
Testnet(_params) if self.is_default_testnet() => testnet_heights,
Testnet(_) => unimplemented!("custom testnet activation heights"),
}
.iter()
.cloned()
@ -392,12 +393,18 @@ impl NetworkUpgrade {
height: block::Height,
) -> Option<Duration> {
match (network, height) {
(Network::Testnet, height) if height < TESTNET_MINIMUM_DIFFICULTY_START_HEIGHT => None,
(Network::Testnet(_params), height)
if network.is_default_testnet()
&& height < TESTNET_MINIMUM_DIFFICULTY_START_HEIGHT =>
{
None
}
(Network::Mainnet, _) => None,
(Network::Testnet, _) => {
(Network::Testnet(_params), _) if network.is_default_testnet() => {
let network_upgrade = NetworkUpgrade::current(network, height);
Some(network_upgrade.target_spacing() * TESTNET_MINIMUM_DIFFICULTY_GAP_MULTIPLIER)
}
_ => unimplemented!("custom testnet minimum difficulty spacing"),
}
}

View File

@ -47,17 +47,25 @@ impl TryFrom<zcash_address::Network> for Network {
fn try_from(network: zcash_address::Network) -> Result<Self, Self::Error> {
match network {
zcash_address::Network::Main => Ok(Network::Mainnet),
zcash_address::Network::Test => Ok(Network::Testnet),
zcash_address::Network::Test => Ok(Network::new_default_testnet()),
zcash_address::Network::Regtest => Err("unsupported Zcash network parameters".into()),
}
}
}
impl From<Network> for zcash_address::Network {
fn from(network: Network) -> Self {
/// An error indicating that network cannot be converted to a [`zcash_address::Network`].
#[derive(Debug)]
pub struct UnsupportedNetwork;
impl TryFrom<Network> for zcash_address::Network {
type Error = UnsupportedNetwork;
fn try_from(network: Network) -> Result<Self, Self::Error> {
match network {
Network::Mainnet => zcash_address::Network::Main,
Network::Testnet => zcash_address::Network::Test,
Network::Mainnet => Ok(zcash_address::Network::Main),
Network::Testnet(_params) if network.is_default_testnet() => {
Ok(zcash_address::Network::Test)
}
Network::Testnet(_params) => Err(UnsupportedNetwork),
}
}
}
@ -185,7 +193,7 @@ impl Address {
Self::Transparent(address) => Some(address.to_string()),
Self::Sapling { address, network } => {
let data = address.to_bytes();
let address = ZcashAddress::from_sapling((*network).into(), data);
let address = ZcashAddress::from_sapling((*network).try_into().ok()?, data);
Some(address.encode())
}
Self::Unified { .. } => None,

View File

@ -26,7 +26,8 @@ pub fn decrypts_successfully(transaction: &Transaction, network: Network, height
if let Some(bundle) = alt_tx.sapling_bundle() {
for output in bundle.shielded_outputs().iter() {
let recovery = zcash_primitives::sapling::note_encryption::try_sapling_output_recovery(
&<zcash_primitives::consensus::Network>::from(network),
&<zcash_primitives::consensus::Network>::try_from(network)
.expect("must match zcash_primitives network"),
alt_height,
&null_sapling_ovk,
output,

View File

@ -13,6 +13,8 @@ use crate::{
transparent::{self, Script},
};
use super::address::UnsupportedNetwork;
// TODO: move copied and modified code to a separate module.
//
// Used by boilerplate code below.
@ -337,11 +339,16 @@ pub(crate) fn transparent_output_address(
}
}
impl From<Network> for zcash_primitives::consensus::Network {
fn from(network: Network) -> Self {
impl TryFrom<Network> for zcash_primitives::consensus::Network {
type Error = UnsupportedNetwork;
fn try_from(network: Network) -> Result<Self, Self::Error> {
match network {
Network::Mainnet => zcash_primitives::consensus::Network::MainNetwork,
Network::Testnet => zcash_primitives::consensus::Network::TestNetwork,
Network::Mainnet => Ok(zcash_primitives::consensus::Network::MainNetwork),
Network::Testnet(_params) if network.is_default_testnet() => {
Ok(zcash_primitives::consensus::Network::TestNetwork)
}
Network::Testnet(_params) => Err(UnsupportedNetwork),
}
}
}
@ -350,7 +357,7 @@ impl From<zcash_primitives::consensus::Network> for Network {
fn from(network: zcash_primitives::consensus::Network) -> Self {
match network {
zcash_primitives::consensus::Network::MainNetwork => Network::Mainnet,
zcash_primitives::consensus::Network::TestNetwork => Network::Testnet,
zcash_primitives::consensus::Network::TestNetwork => Network::new_default_testnet(),
}
}
}

View File

@ -137,7 +137,7 @@ impl ZcashDeserialize for Address {
}
zcash_primitives::constants::testnet::B58_SCRIPT_ADDRESS_PREFIX => {
Ok(Address::PayToScriptHash {
network: Network::Testnet,
network: Network::new_default_testnet(),
script_hash: hash_bytes,
})
}
@ -149,7 +149,7 @@ impl ZcashDeserialize for Address {
}
zcash_primitives::constants::testnet::B58_PUBKEY_ADDRESS_PREFIX => {
Ok(Address::PayToPublicKeyHash {
network: Network::Testnet,
network: Network::new_default_testnet(),
pub_key_hash: hash_bytes,
})
}
@ -298,7 +298,7 @@ mod tests {
])
.expect("A PublicKey from slice");
let t_addr = pub_key.to_address(Network::Testnet);
let t_addr = pub_key.to_address(Network::new_default_testnet());
assert_eq!(format!("{t_addr}"), "tmTc6trRhbv96kGfA99i7vrFwb5p7BVFwc3");
}
@ -320,7 +320,7 @@ mod tests {
let script = Script::new(&[0; 20]);
let t_addr = script.to_address(Network::Testnet);
let t_addr = script.to_address(Network::new_default_testnet());
assert_eq!(format!("{t_addr}"), "t2L51LcmpA43UMvKTw2Lwtt9LMjwyqU2V1P");
}

View File

@ -699,7 +699,11 @@ impl ParameterDifficulty for Network {
/* 2^243 - 1 */
Network::Mainnet => (U256::one() << 243) - 1,
/* 2^251 - 1 */
Network::Testnet => (U256::one() << 251) - 1,
Network::Testnet(_) if self.is_default_testnet() => (U256::one() << 251) - 1,
// Custom value
Network::Testnet(_params) => {
unimplemented!("custom testnet target difficulty limit")
}
};
// `zcashd` converts the PoWLimit into a compact representation before

View File

@ -57,7 +57,11 @@ impl ParameterCheckpoint for Network {
// zcash-cli getblockhash 0
Network::Mainnet => "00040fe8ec8471911baa1db1266ea15dd06b4a8a5c453883c000b031973dce08",
// zcash-cli -testnet getblockhash 0
Network::Testnet => "05a60a92d99d85997cce3b87616c089f6124d7342af37106edc76126334a2c38",
Network::Testnet(_params) if self.is_default_testnet() => {
"05a60a92d99d85997cce3b87616c089f6124d7342af37106edc76126334a2c38"
}
Network::Testnet(_params) => unimplemented!("custom test network genesis hash"),
}
.parse()
.expect("hard-coded hash parses")
@ -69,9 +73,10 @@ impl ParameterCheckpoint for Network {
Network::Mainnet => MAINNET_CHECKPOINTS
.parse()
.expect("Hard-coded Mainnet checkpoint list parses and validates"),
Network::Testnet => TESTNET_CHECKPOINTS
Network::Testnet(_params) if self.is_default_testnet() => TESTNET_CHECKPOINTS
.parse()
.expect("Hard-coded Testnet checkpoint list parses and validates"),
Network::Testnet(_params) => unimplemented!("custom test network checkpoints"),
};
match checkpoint_list.hash(block::Height(0)) {
@ -144,7 +149,8 @@ impl CheckpointList {
match checkpoints.iter().next() {
Some((block::Height(0), hash))
if (hash == &Network::Mainnet.genesis_hash()
|| hash == &Network::Testnet.genesis_hash()) => {}
// TODO: Accept `network` argument and check that the network's first item is the genesis hash.
|| hash == &Network::new_default_testnet().genesis_hash()) => {}
Some((block::Height(0), _)) => {
Err("the genesis checkpoint does not match the Mainnet or Testnet genesis hash")?
}

View File

@ -107,7 +107,7 @@ lazy_static! {
pub static ref FUNDING_STREAM_HEIGHT_RANGES: HashMap<Network, std::ops::Range<Height>> = {
let mut hash_map = HashMap::new();
hash_map.insert(Network::Mainnet, Height(1_046_400)..Height(2_726_400));
hash_map.insert(Network::Testnet, Height(1_028_500)..Height(2_796_000));
hash_map.insert(Network::new_default_testnet(), Height(1_028_500)..Height(2_796_000));
hash_map
};
@ -127,7 +127,7 @@ lazy_static! {
testnet_addresses.insert(FundingStreamReceiver::Ecc, FUNDING_STREAM_ECC_ADDRESSES_TESTNET.iter().map(|a| a.to_string()).collect());
testnet_addresses.insert(FundingStreamReceiver::ZcashFoundation, FUNDING_STREAM_ZF_ADDRESSES_TESTNET.iter().map(|a| a.to_string()).collect());
testnet_addresses.insert(FundingStreamReceiver::MajorGrants, FUNDING_STREAM_MG_ADDRESSES_TESTNET.iter().map(|a| a.to_string()).collect());
addresses_by_network.insert(Network::Testnet, testnet_addresses);
addresses_by_network.insert(Network::new_default_testnet(), testnet_addresses);
addresses_by_network
};
@ -215,7 +215,10 @@ impl ParameterSubsidy for Network {
fn num_funding_streams(&self) -> usize {
match self {
Network::Mainnet => FUNDING_STREAMS_NUM_ADDRESSES_MAINNET,
Network::Testnet => FUNDING_STREAMS_NUM_ADDRESSES_TESTNET,
Network::Testnet(_params) if self.is_default_testnet() => {
FUNDING_STREAMS_NUM_ADDRESSES_TESTNET
}
Network::Testnet(_params) => unimplemented!("custom test network funding streams"),
}
}
fn height_for_first_halving(&self) -> Height {
@ -226,7 +229,10 @@ impl ParameterSubsidy for Network {
Network::Mainnet => NetworkUpgrade::Canopy
.activation_height(*self)
.expect("canopy activation height should be available"),
Network::Testnet => FIRST_HALVING_TESTNET,
Network::Testnet(_params) if self.is_default_testnet() => FIRST_HALVING_TESTNET,
Network::Testnet(_params) => {
unimplemented!("custom test network height for first halving")
}
}
}
}

View File

@ -225,7 +225,12 @@ impl Config {
pub fn initial_peer_hostnames(&self) -> &IndexSet<String> {
match self.network {
Network::Mainnet => &self.initial_mainnet_peers,
Network::Testnet => &self.initial_testnet_peers,
Network::Testnet(_) if self.network.is_default_testnet() => &self.initial_testnet_peers,
// TODO: Add an `initial_custom_testnet` config field?
// Regtest should always be isolated but it may be nice to have a separate field for custom public testnets.
Network::Testnet(_params) => {
unimplemented!("custom test network initial peer hostnames")
}
}
}

View File

@ -396,7 +396,7 @@ lazy_static! {
let mut hash_map = HashMap::new();
hash_map.insert(Mainnet, Version::min_specified_for_upgrade(Mainnet, Nu5));
hash_map.insert(Testnet, Version::min_specified_for_upgrade(Testnet, Nu5));
hash_map.insert(Network::new_default_testnet(), Version::min_specified_for_upgrade(Network::new_default_testnet(), Nu5));
hash_map
};

View File

@ -31,7 +31,10 @@ impl ParameterMagic for Network {
fn magic_value(&self) -> Magic {
match self {
Network::Mainnet => magics::MAINNET,
Network::Testnet => magics::TESTNET,
Network::Testnet(_) if self.is_default_testnet() => magics::TESTNET,
Network::Testnet(_params) => {
unimplemented!("custom testnet network magic")
}
}
}
}
@ -78,6 +81,7 @@ impl Version {
/// - during the initial block download,
/// - after Zebra restarts, and
/// - after Zebra's local network is slow or shut down.
// TODO: Move constant to a network method
fn initial_min_for_network(network: Network) -> Version {
*constants::INITIAL_MIN_NETWORK_PROTOCOL_VERSION
.get(&network)
@ -103,17 +107,18 @@ impl Version {
// sync? zcashd accepts 170_002 or later during its initial sync.
Version(match (network, network_upgrade) {
(_, Genesis) | (_, BeforeOverwinter) => 170_002,
(Testnet, Overwinter) => 170_003,
(Testnet(_), Overwinter) if network.is_default_testnet() => 170_003,
(Mainnet, Overwinter) => 170_005,
(_, Sapling) => 170_007,
(Testnet, Blossom) => 170_008,
(Testnet(_), Blossom) if network.is_default_testnet() => 170_008,
(Mainnet, Blossom) => 170_009,
(Testnet, Heartwood) => 170_010,
(Testnet(_), Heartwood) if network.is_default_testnet() => 170_010,
(Mainnet, Heartwood) => 170_011,
(Testnet, Canopy) => 170_012,
(Testnet(_), Canopy) if network.is_default_testnet() => 170_012,
(Mainnet, Canopy) => 170_013,
(Testnet, Nu5) => 170_050,
(Testnet(_), Nu5) if network.is_default_testnet() => 170_050,
(Mainnet, Nu5) => 170_100,
_ => unimplemented!("custom network activation heights"),
})
}
}
@ -201,7 +206,7 @@ mod test {
#[test]
fn version_extremes_testnet() {
version_extremes(Testnet)
version_extremes(Network::new_default_testnet())
}
/// Test the min_specified_for_upgrade and min_specified_for_height functions for `network` with
@ -229,7 +234,7 @@ mod test {
#[test]
fn version_consistent_testnet() {
version_consistent(Testnet)
version_consistent(Network::new_default_testnet())
}
/// Check that the min_specified_for_upgrade and min_specified_for_height functions

View File

@ -383,7 +383,9 @@ pub fn scan_block<K: ScanningKey>(
// TODO: Implement a check that returns early when the block height is below the Sapling
// activation height.
let network: zcash_primitives::consensus::Network = network.into();
let network: zcash_primitives::consensus::Network = network
.try_into()
.expect("must match zcash_primitives network");
let chain_metadata = ChainMetadata {
sapling_commitment_tree_size: sapling_tree_size,

View File

@ -185,7 +185,7 @@ impl AdjustedDifficulty {
) {
assert_eq!(
self.network,
Network::Testnet,
Network::new_default_testnet(),
"invalid network: the minimum difficulty rule only applies on testnet"
);
self.network.target_difficulty_limit().to_compact()

View File

@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize};
use zebra_chain::{
amount::{self, Amount, NonNegative},
block::Height,
parameters::Network::*,
parameters::Network::{self, *},
serialization::{ZcashDeserializeInto, ZcashSerialize},
transparent::{self, Address::*},
};
@ -501,11 +501,13 @@ fn address_variant(address: &transparent::Address) -> u8 {
// Return smaller values for more common variants.
//
// (This probably doesn't matter, but it might help slightly with data compression.)
match (address.network(), address) {
let network = address.network();
match (network, address) {
(Mainnet, PayToPublicKeyHash { .. }) => 0,
(Mainnet, PayToScriptHash { .. }) => 1,
(Testnet, PayToPublicKeyHash { .. }) => 2,
(Testnet, PayToScriptHash { .. }) => 3,
(Testnet(_), PayToPublicKeyHash { .. }) if network.is_default_testnet() => 2,
(Testnet(_), PayToScriptHash { .. }) if network.is_default_testnet() => 3,
_ => unimplemented!("custom testnet address variant"),
}
}
@ -531,7 +533,7 @@ impl FromDisk for transparent::Address {
let network = if address_variant < 2 {
Mainnet
} else {
Testnet
Network::new_default_testnet()
};
if address_variant % 2 == 0 {

View File

@ -15,7 +15,7 @@ use tempfile::TempDir;
use zebra_chain::{
block::{Height, HeightDiff, TryIntoHeight},
parameters::Network::{self, *},
parameters::Network,
transparent::MIN_TRANSPARENT_COINBASE_MATURITY,
};
use zebra_consensus::MAX_CHECKPOINT_HEIGHT_GAP;
@ -82,7 +82,7 @@ pub async fn run(network: Network) -> Result<()> {
// Wait for the upgrade if needed.
// Currently we only write an image for testnet, which is quick.
// (Mainnet would need to wait at the end of this function, if the upgrade is long.)
if network == Testnet {
if network.is_default_testnet() {
let state_version_message = wait_for_state_version_message(&mut zebrad)?;
// Before we write a cached state image, wait for a database upgrade.