From 9db4762751cbf40420e88ccb744d5b1e6844710c Mon Sep 17 00:00:00 2001 From: Arya Date: Tue, 19 Mar 2024 20:16:49 -0400 Subject: [PATCH 01/51] minor cleanup and rename --- zebra-consensus/src/router.rs | 12 ++++-------- zebra-state/src/service/finalized_state/zebra_db.rs | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/zebra-consensus/src/router.rs b/zebra-consensus/src/router.rs index d85873096..d9545ff09 100644 --- a/zebra-consensus/src/router.rs +++ b/zebra-consensus/src/router.rs @@ -240,13 +240,9 @@ where // Make sure the state contains the known best chain checkpoints, in a separate thread. - let (checkpoint_state_service, checkpoint_sync, network_clone) = { - let checkpoint_state_service = state_service.clone(); - let checkpoint_sync = config.checkpoint_sync; - let network_clone = network.clone(); - - (checkpoint_state_service, checkpoint_sync, network_clone) - }; + let checkpoint_state_service = state_service.clone(); + let checkpoint_sync = config.checkpoint_sync; + let checkpoint_network = network.clone(); let state_checkpoint_verify_handle = tokio::task::spawn( // TODO: move this into an async function? @@ -269,7 +265,7 @@ where // > activation block hashes given in § 3.12 ‘Mainnet and Testnet’ on p. 20. // // - let full_checkpoints = network_clone.checkpoint_list(); + let full_checkpoints = checkpoint_network.checkpoint_list(); let mut already_warned = false; for (height, checkpoint_hash) in full_checkpoints.iter() { diff --git a/zebra-state/src/service/finalized_state/zebra_db.rs b/zebra-state/src/service/finalized_state/zebra_db.rs index 71c619e4f..17891e38f 100644 --- a/zebra-state/src/service/finalized_state/zebra_db.rs +++ b/zebra-state/src/service/finalized_state/zebra_db.rs @@ -215,7 +215,7 @@ impl ZebraDb { /// Returns the configured network for this database. pub fn network(&self) -> Network { - self.db.network().clone() + self.db.network() } /// Returns the `Path` where the files used by this database are located. From 4b86bd6952ecb7281fcc76bda6e21c5f78af3505 Mon Sep 17 00:00:00 2001 From: Arya Date: Wed, 20 Mar 2024 03:26:07 -0400 Subject: [PATCH 02/51] Adds an empty NetworkParameters struct to Network::Testnet variant, updates production code. --- zebra-chain/src/parameters.rs | 2 + zebra-chain/src/parameters/error.rs | 15 +++ zebra-chain/src/parameters/network.rs | 95 ++++++++++++++++--- zebra-chain/src/parameters/network_upgrade.rs | 12 ++- zebra-chain/src/primitives/address.rs | 32 +++++-- .../src/primitives/zcash_note_encryption.rs | 4 +- .../src/primitives/zcash_primitives.rs | 22 +++-- zebra-chain/src/transparent/address.rs | 6 +- zebra-chain/src/work/difficulty.rs | 3 +- .../src/block/subsidy/funding_streams.rs | 11 ++- .../block/subsidy/funding_streams/tests.rs | 4 +- zebra-consensus/src/checkpoint/list.rs | 15 ++- zebra-consensus/src/parameters/subsidy.rs | 22 +++-- zebra-network/src/config.rs | 14 ++- zebra-network/src/constants.rs | 7 +- zebra-network/src/protocol/external/types.rs | 22 +++-- zebra-scan/src/service/scan_task/scan.rs | 5 +- zebra-state/src/service/check/difficulty.rs | 7 +- .../disk_format/transparent.rs | 12 ++- 19 files changed, 241 insertions(+), 69 deletions(-) create mode 100644 zebra-chain/src/parameters/error.rs diff --git a/zebra-chain/src/parameters.rs b/zebra-chain/src/parameters.rs index fe62f7b60..545d132ca 100644 --- a/zebra-chain/src/parameters.rs +++ b/zebra-chain/src/parameters.rs @@ -12,6 +12,7 @@ //! Typically, consensus parameters are accessed via a function that takes a //! `Network` and `block::Height`. +mod error; mod genesis; mod network; mod network_upgrade; @@ -20,6 +21,7 @@ mod transaction; #[cfg(any(test, feature = "proptest-impl"))] pub mod arbitrary; +pub use error::*; pub use genesis::*; pub use network::Network; pub use network_upgrade::*; diff --git a/zebra-chain/src/parameters/error.rs b/zebra-chain/src/parameters/error.rs new file mode 100644 index 000000000..0c9ce6306 --- /dev/null +++ b/zebra-chain/src/parameters/error.rs @@ -0,0 +1,15 @@ +//! Error types for zebra-chain parameters + +use std::fmt; + +/// An error indicating that Zebra network is not supported in type conversions. +#[derive(Debug)] +pub struct UnsupportedNetwork; + +impl fmt::Display for UnsupportedNetwork { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "unsupported Zcash network parameters") + } +} + +impl std::error::Error for UnsupportedNetwork {} diff --git a/zebra-chain/src/parameters/network.rs b/zebra-chain/src/parameters/network.rs index 3ba5a0902..a35719484 100644 --- a/zebra-chain/src/parameters/network.rs +++ b/zebra-chain/src/parameters/network.rs @@ -1,9 +1,12 @@ //! Consensus parameters for each Zcash network. -use std::{fmt, str::FromStr}; +use std::{fmt, str::FromStr, sync::Arc}; +use serde::{Deserialize, Deserializer}; use thiserror::Error; +use zcash_primitives::consensus::{Network as ZcashPrimitivesNetwork, Parameters as _}; + use crate::{ block::{self, Height, HeightDiff}, parameters::NetworkUpgrade::Canopy, @@ -51,30 +54,77 @@ mod tests; /// after the grace period. const ZIP_212_GRACE_PERIOD_DURATION: HeightDiff = 32_256; -/// An enum describing the possible network choices. +/// Network consensus parameters for test networks such as Regtest and the default Testnet. #[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Serialize, Deserialize)] #[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))] +pub struct NetworkParameters {} + +impl NetworkParameters { + /// Returns true if the instance of [`NetworkParameters`] represents the default public Testnet. + pub fn is_default_testnet(&self) -> bool { + self == &Self::default() + } +} + +/// An enum describing the possible network choices. +#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Serialize)] +#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))] pub enum Network { /// The production mainnet. #[default] Mainnet, /// The oldest public test network. - Testnet, + Testnet(Arc), +} + +impl<'de> Deserialize<'de> for Network { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Serialize, Deserialize)] + #[serde(deny_unknown_fields)] + enum DNetwork { + #[default] + Mainnet, + #[serde(alias = "Testnet")] + DefaultTestnet, + Regtest, + #[serde(untagged)] + ConfiguredTestnet(NetworkParameters), + } + + let network = match DNetwork::deserialize(deserializer)? { + DNetwork::Mainnet => Network::Mainnet, + DNetwork::DefaultTestnet => Network::new_default_testnet(), + DNetwork::Regtest => unimplemented!("Regtest is not yet implemented"), + DNetwork::ConfiguredTestnet(params) => Network::Testnet(Arc::new(params)), + }; + + Ok(network) + } } -use zcash_primitives::consensus::{Network as ZcashPrimitivesNetwork, Parameters as _}; 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] { - ::from(self).b58_pubkey_address_prefix() + ::try_from(self) + // This prefix is the same for Testnet and Regtest in zcashd. + // TODO: Use the constants directly when implementing `Parameters` for `Network` (#8365) + .unwrap_or(ZcashPrimitivesNetwork::TestNetwork) + .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] { - ::from(self).b58_script_address_prefix() + ::try_from(self) + // This prefix is the same for Testnet and Regtest in zcashd. + // TODO: Use the constants directly when implementing `Parameters` for `Network` (#8365) + .unwrap_or(ZcashPrimitivesNetwork::TestNetwork) + .b58_script_address_prefix() } /// Returns true if the maximum block time rule is active for `network` and `height`. /// @@ -87,7 +137,8 @@ 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, + // TODO: Move `TESTNET_MAX_TIME_START_HEIGHT` to a field on NetworkParameters (#8364) + Network::Testnet(_params) => height >= super::TESTNET_MAX_TIME_START_HEIGHT, } } } @@ -96,7 +147,12 @@ impl From<&Network> for &'static str { fn from(network: &Network) -> &'static str { match network { Network::Mainnet => "Mainnet", - Network::Testnet => "Testnet", + // TODO: + // - Add a `name` field to use here instead of checking `is_default_testnet()` + // - Find out what zcashd calls the regtest cache dir for the `Network::new_regtest()` method, or + // if it always uses an ephemeral db, and do the same for Regtest in Zebra (#8327). + Network::Testnet(params) if params.is_default_testnet() => "Testnet", + Network::Testnet(_params) => "UnknownTestnet", } } } @@ -108,17 +164,32 @@ 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(Arc::new(NetworkParameters::default())) + } + + /// Returns true if the network is the default Testnet, or false otherwise. + pub fn is_default_testnet(&self) -> bool { + if let Self::Testnet(params) = self { + params.is_default_testnet() + } else { + false + } + } + /// Returns an iterator over [`Network`] variants. pub fn iter() -> impl Iterator { // 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, + // TODO: Add a `default_port` field to `NetworkParameters` to return here. + Network::Testnet(_params) => 18233, } } @@ -145,7 +216,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(), } } @@ -173,7 +244,7 @@ impl FromStr for Network { fn from_str(string: &str) -> Result { 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())), } } diff --git a/zebra-chain/src/parameters/network_upgrade.rs b/zebra-chain/src/parameters/network_upgrade.rs index 4d88e96bd..a5613d1ea 100644 --- a/zebra-chain/src/parameters/network_upgrade.rs +++ b/zebra-chain/src/parameters/network_upgrade.rs @@ -271,7 +271,8 @@ impl Network { }; match self { Mainnet => mainnet_heights, - Testnet => testnet_heights, + // TODO: Add an `activation_heights` field to `NetworkParameters` to return here. (#7970) + Testnet(_params) => testnet_heights, } .iter() .cloned() @@ -394,9 +395,14 @@ impl NetworkUpgrade { height: block::Height, ) -> Option { match (network, height) { - (Network::Testnet, height) if height < TESTNET_MINIMUM_DIFFICULTY_START_HEIGHT => None, + // TODO: Move `TESTNET_MINIMUM_DIFFICULTY_START_HEIGHT` to a field on NetworkParameters (#8364) + (Network::Testnet(_params), height) + if height < TESTNET_MINIMUM_DIFFICULTY_START_HEIGHT => + { + None + } (Network::Mainnet, _) => None, - (Network::Testnet, _) => { + (Network::Testnet(_params), _) => { let network_upgrade = NetworkUpgrade::current(network, height); Some(network_upgrade.target_spacing() * TESTNET_MINIMUM_DIFFICULTY_GAP_MULTIPLIER) } diff --git a/zebra-chain/src/primitives/address.rs b/zebra-chain/src/primitives/address.rs index 553e523b7..2e7890c29 100644 --- a/zebra-chain/src/primitives/address.rs +++ b/zebra-chain/src/primitives/address.rs @@ -5,7 +5,10 @@ use zcash_address::unified::{self, Container}; use zcash_primitives::sapling; -use crate::{parameters::Network, transparent, BoxError}; +use crate::{ + parameters::{Network, UnsupportedNetwork}, + transparent, BoxError, +}; /// Zcash address variants pub enum Address { @@ -47,17 +50,25 @@ impl TryFrom for Network { fn try_from(network: zcash_address::Network) -> Result { 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 { +impl TryFrom<&Network> for zcash_address::Network { + type Error = UnsupportedNetwork; + + fn try_from(network: &Network) -> Result { 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) + } + // TODO: If the network parameters match `Regtest` parameters, convert to + // `zcash_address::Network::Regtest instead of returning `UnsupportedAddress` error. + // (#7119, #7839) + Network::Testnet(_params) => Err(UnsupportedNetwork), } } } @@ -185,7 +196,14 @@ 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 network = network + .try_into() + .map_err(|err| { + warn!(?err, "could not convert address network to zcash network") + }) + .ok()?; + + let address = ZcashAddress::from_sapling(network, data); Some(address.encode()) } Self::Unified { .. } => None, diff --git a/zebra-chain/src/primitives/zcash_note_encryption.rs b/zebra-chain/src/primitives/zcash_note_encryption.rs index 0870dac56..e5a46955a 100644 --- a/zebra-chain/src/primitives/zcash_note_encryption.rs +++ b/zebra-chain/src/primitives/zcash_note_encryption.rs @@ -26,7 +26,9 @@ pub fn decrypts_successfully(transaction: &Transaction, network: &Network, heigh 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( - &::from(network), + // TODO: Implement `Parameters` trait for `Network` and pass network here without conversion (#8365) + &::try_from(network) + .expect("network must match zcash_primitives network"), alt_height, &null_sapling_ovk, output, diff --git a/zebra-chain/src/primitives/zcash_primitives.rs b/zebra-chain/src/primitives/zcash_primitives.rs index c4e439184..af54a05a4 100644 --- a/zebra-chain/src/primitives/zcash_primitives.rs +++ b/zebra-chain/src/primitives/zcash_primitives.rs @@ -7,7 +7,7 @@ use zcash_primitives::transaction as zp_tx; use crate::{ amount::{Amount, NonNegative}, - parameters::{Network, NetworkUpgrade}, + parameters::{Network, NetworkUpgrade, UnsupportedNetwork}, serialization::ZcashSerialize, transaction::{AuthDigest, HashType, SigHash, Transaction}, transparent::{self, Script}, @@ -337,11 +337,21 @@ 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 { match network { - Network::Mainnet => zcash_primitives::consensus::Network::MainNetwork, - Network::Testnet => zcash_primitives::consensus::Network::TestNetwork, + Network::Mainnet => Ok(zcash_primitives::consensus::Network::MainNetwork), + // Note: There are differences between the `TestNetwork` parameters and those returned by + // `CRegTestParams()` in zcashd, so this function can't return `TestNetwork` unless + // Zebra is using the default public Testnet. + // TODO: Try to remove this conversion, if possible, by implementing `zcash_primitives::consensus::Parameters` + // on `Network` (#8365). + Network::Testnet(_params) if network.is_default_testnet() => { + Ok(zcash_primitives::consensus::Network::TestNetwork) + } + Network::Testnet(_params) => Err(UnsupportedNetwork), } } } @@ -350,7 +360,7 @@ impl From 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(), } } } diff --git a/zebra-chain/src/transparent/address.rs b/zebra-chain/src/transparent/address.rs index 0c7ab62de..18fbe8a68 100644 --- a/zebra-chain/src/transparent/address.rs +++ b/zebra-chain/src/transparent/address.rs @@ -137,7 +137,8 @@ impl ZcashDeserialize for Address { } zcash_primitives::constants::testnet::B58_SCRIPT_ADDRESS_PREFIX => { Ok(Address::PayToScriptHash { - network: Network::Testnet, + // TODO: Replace `network` field on `Address` with the prefix and a method for checking if it's the mainnet prefix. + network: Network::new_default_testnet(), script_hash: hash_bytes, }) } @@ -149,7 +150,8 @@ impl ZcashDeserialize for Address { } zcash_primitives::constants::testnet::B58_PUBKEY_ADDRESS_PREFIX => { Ok(Address::PayToPublicKeyHash { - network: Network::Testnet, + // TODO: Replace `network` field on `Address` with the prefix and a method for checking if it's the mainnet prefix. + network: Network::new_default_testnet(), pub_key_hash: hash_bytes, }) } diff --git a/zebra-chain/src/work/difficulty.rs b/zebra-chain/src/work/difficulty.rs index 9c5252c2b..2f5e46821 100644 --- a/zebra-chain/src/work/difficulty.rs +++ b/zebra-chain/src/work/difficulty.rs @@ -699,7 +699,8 @@ impl ParameterDifficulty for Network { /* 2^243 - 1 */ Network::Mainnet => (U256::one() << 243) - 1, /* 2^251 - 1 */ - Network::Testnet => (U256::one() << 251) - 1, + // TODO: Add a `target_difficulty_limit` field to `NetworkParameters` to return here. + Network::Testnet(_params) => (U256::one() << 251) - 1, }; // `zcashd` converts the PoWLimit into a compact representation before diff --git a/zebra-consensus/src/block/subsidy/funding_streams.rs b/zebra-consensus/src/block/subsidy/funding_streams.rs index 39382e7a2..ad43b6156 100644 --- a/zebra-consensus/src/block/subsidy/funding_streams.rs +++ b/zebra-consensus/src/block/subsidy/funding_streams.rs @@ -29,7 +29,9 @@ pub fn funding_stream_values( let mut results = HashMap::new(); if height >= canopy_height { - let range = FUNDING_STREAM_HEIGHT_RANGES.get(network).unwrap(); + let range = FUNDING_STREAM_HEIGHT_RANGES + .get(&network.bip70_network_name()) + .unwrap(); if range.contains(&height) { let block_subsidy = block_subsidy(height, network)?; for (&receiver, &numerator) in FUNDING_STREAM_RECEIVER_NUMERATORS.iter() { @@ -83,7 +85,10 @@ fn funding_stream_address_index(height: Height, network: &Network) -> usize { .checked_add(funding_stream_address_period(height, network)) .expect("no overflow should happen in this sum") .checked_sub(funding_stream_address_period( - FUNDING_STREAM_HEIGHT_RANGES.get(network).unwrap().start, + FUNDING_STREAM_HEIGHT_RANGES + .get(&network.bip70_network_name()) + .unwrap() + .start, network, )) .expect("no overflow should happen in this sub") as usize; @@ -105,7 +110,7 @@ pub fn funding_stream_address( ) -> transparent::Address { let index = funding_stream_address_index(height, network); let address = &FUNDING_STREAM_ADDRESSES - .get(network) + .get(&network.bip70_network_name()) .expect("there is always another hash map as value for a given valid network") .get(&receiver) .expect("in the inner hash map there is always a vector of strings with addresses")[index]; diff --git a/zebra-consensus/src/block/subsidy/funding_streams/tests.rs b/zebra-consensus/src/block/subsidy/funding_streams/tests.rs index 763fa1727..2f1f1845c 100644 --- a/zebra-consensus/src/block/subsidy/funding_streams/tests.rs +++ b/zebra-consensus/src/block/subsidy/funding_streams/tests.rs @@ -44,7 +44,9 @@ fn test_funding_stream_values() -> Result<(), Report> { ); // funding stream period is ending - let range = FUNDING_STREAM_HEIGHT_RANGES.get(network).unwrap(); + let range = FUNDING_STREAM_HEIGHT_RANGES + .get(&network.bip70_network_name()) + .unwrap(); let end = range.end; let last = end - 1; diff --git a/zebra-consensus/src/checkpoint/list.rs b/zebra-consensus/src/checkpoint/list.rs index 2b45e4365..7b2a5ca0c 100644 --- a/zebra-consensus/src/checkpoint/list.rs +++ b/zebra-consensus/src/checkpoint/list.rs @@ -57,7 +57,10 @@ impl ParameterCheckpoint for Network { // zcash-cli getblockhash 0 Network::Mainnet => "00040fe8ec8471911baa1db1266ea15dd06b4a8a5c453883c000b031973dce08", // zcash-cli -testnet getblockhash 0 - Network::Testnet => "05a60a92d99d85997cce3b87616c089f6124d7342af37106edc76126334a2c38", + // TODO: Add a `genesis_hash` field to `NetworkParameters` and return it here (#8366) + Network::Testnet(_params) => { + "05a60a92d99d85997cce3b87616c089f6124d7342af37106edc76126334a2c38" + } } .parse() .expect("hard-coded hash parses") @@ -65,11 +68,15 @@ impl ParameterCheckpoint for Network { fn checkpoint_list(&self) -> CheckpointList { // parse calls CheckpointList::from_list + // TODO: + // - Add a `genesis_hash` field to `NetworkParameters` and return it here (#8366) + // - Consider adding a `CUSTOM_TESTNET_CHECKPOINTS` constant to enable building with another checkpoints list + // when using a configured testnet? let checkpoint_list: CheckpointList = match self { Network::Mainnet => MAINNET_CHECKPOINTS .parse() .expect("Hard-coded Mainnet checkpoint list parses and validates"), - Network::Testnet => TESTNET_CHECKPOINTS + Network::Testnet(_params) => TESTNET_CHECKPOINTS .parse() .expect("Hard-coded Testnet checkpoint list parses and validates"), }; @@ -142,9 +149,11 @@ impl CheckpointList { // Check that the list starts with the correct genesis block match checkpoints.iter().next() { + // TODO: Move this check to `::checkpoint_list(&network)` method above (#8366), + // See Some((block::Height(0), hash)) if (hash == &Network::Mainnet.genesis_hash() - || hash == &Network::Testnet.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")? } diff --git a/zebra-consensus/src/parameters/subsidy.rs b/zebra-consensus/src/parameters/subsidy.rs index 8dfd42bd9..d8ac576f6 100644 --- a/zebra-consensus/src/parameters/subsidy.rs +++ b/zebra-consensus/src/parameters/subsidy.rs @@ -104,15 +104,17 @@ lazy_static! { /// as described in [protocol specification §7.10.1][7.10.1]. /// /// [7.10.1]: https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams - pub static ref FUNDING_STREAM_HEIGHT_RANGES: HashMap> = { + // TODO: Move the value here to a field on `NetworkParameters` (#8367) + pub static ref FUNDING_STREAM_HEIGHT_RANGES: HashMap> = { 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::Mainnet.bip70_network_name(), Height(1_046_400)..Height(2_726_400)); + hash_map.insert(Network::new_default_testnet().bip70_network_name(), Height(1_028_500)..Height(2_796_000)); hash_map }; /// Convenient storage for all addresses, for all receivers and networks - pub static ref FUNDING_STREAM_ADDRESSES: HashMap>> = { + // TODO: Move the value here to a field on `NetworkParameters` (#8367) + pub static ref FUNDING_STREAM_ADDRESSES: HashMap>> = { let mut addresses_by_network = HashMap::with_capacity(2); // Mainnet addresses @@ -120,14 +122,14 @@ lazy_static! { mainnet_addresses.insert(FundingStreamReceiver::Ecc, FUNDING_STREAM_ECC_ADDRESSES_MAINNET.iter().map(|a| a.to_string()).collect()); mainnet_addresses.insert(FundingStreamReceiver::ZcashFoundation, FUNDING_STREAM_ZF_ADDRESSES_MAINNET.iter().map(|a| a.to_string()).collect()); mainnet_addresses.insert(FundingStreamReceiver::MajorGrants, FUNDING_STREAM_MG_ADDRESSES_MAINNET.iter().map(|a| a.to_string()).collect()); - addresses_by_network.insert(Network::Mainnet, mainnet_addresses); + addresses_by_network.insert(Network::Mainnet.bip70_network_name(), mainnet_addresses); // Testnet addresses let mut testnet_addresses = HashMap::with_capacity(3); 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().bip70_network_name(), testnet_addresses); addresses_by_network }; @@ -215,9 +217,11 @@ 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, + // TODO: Check what zcashd does here, consider adding a field to `NetworkParamters` to make this configurable. + Network::Testnet(_params) => FUNDING_STREAMS_NUM_ADDRESSES_TESTNET, } } + fn height_for_first_halving(&self) -> Height { // First halving on Mainnet is at Canopy // while in Testnet is at block constant height of `1_116_000` @@ -226,7 +230,9 @@ impl ParameterSubsidy for Network { Network::Mainnet => NetworkUpgrade::Canopy .activation_height(self) .expect("canopy activation height should be available"), - Network::Testnet => FIRST_HALVING_TESTNET, + + // TODO: Check what zcashd does here, consider adding a field to `NetworkParamters` to make this configurable. + Network::Testnet(_params) => FIRST_HALVING_TESTNET, } } } diff --git a/zebra-network/src/config.rs b/zebra-network/src/config.rs index da989f990..33d12eb02 100644 --- a/zebra-network/src/config.rs +++ b/zebra-network/src/config.rs @@ -15,6 +15,8 @@ use tempfile::NamedTempFile; use tokio::{fs, io::AsyncWriteExt}; use tracing::Span; +use lazy_static::lazy_static; + use zebra_chain::parameters::Network; use crate::{ @@ -176,6 +178,10 @@ pub struct Config { pub max_connections_per_ip: usize, } +lazy_static! { + static ref EMPTY_INITIAL_REGTEST_PEERS: IndexSet = IndexSet::new(); +} + impl Config { /// The maximum number of outbound connections that Zebra will open at the same time. /// When this limit is reached, Zebra stops opening outbound connections. @@ -223,9 +229,13 @@ impl Config { /// Returns the initial seed peer hostnames for the configured network. pub fn initial_peer_hostnames(&self) -> &IndexSet { - match self.network { + match &self.network { Network::Mainnet => &self.initial_mainnet_peers, - Network::Testnet => &self.initial_testnet_peers, + Network::Testnet(params) if params.is_default_testnet() => &self.initial_testnet_peers, + // TODO: Check if the network is an incompatible custom testnet (_not_ Regtest), then panic if `initial_testnet_peers` + // contains any of the default testnet peers, or return `initial_testnet_peers` otherwise. See: + // + Network::Testnet(_params) => &EMPTY_INITIAL_REGTEST_PEERS, } } diff --git a/zebra-network/src/constants.rs b/zebra-network/src/constants.rs index 7d7543d84..c8ecc02fe 100644 --- a/zebra-network/src/constants.rs +++ b/zebra-network/src/constants.rs @@ -392,11 +392,12 @@ lazy_static! { /// /// The minimum network protocol version typically changes after Mainnet and /// Testnet network upgrades. - pub static ref INITIAL_MIN_NETWORK_PROTOCOL_VERSION: HashMap = { + // TODO: Move the value here to a field on `NetworkParameters` (#8367) + pub static ref INITIAL_MIN_NETWORK_PROTOCOL_VERSION: HashMap = { 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(Mainnet.bip70_network_name(), Version::min_specified_for_upgrade(&Mainnet, Nu5)); + hash_map.insert(Network::new_default_testnet().bip70_network_name(), Version::min_specified_for_upgrade(&Network::new_default_testnet(), Nu5)); hash_map }; diff --git a/zebra-network/src/protocol/external/types.rs b/zebra-network/src/protocol/external/types.rs index 08e2aa380..5c132710d 100644 --- a/zebra-network/src/protocol/external/types.rs +++ b/zebra-network/src/protocol/external/types.rs @@ -8,7 +8,7 @@ use zebra_chain::{ }, }; -use crate::constants::{self, magics}; +use crate::constants::{self, magics, CURRENT_NETWORK_PROTOCOL_VERSION}; #[cfg(any(test, feature = "proptest-impl"))] use proptest_derive::Arbitrary; @@ -31,7 +31,8 @@ impl ParameterMagic for Network { fn magic_value(&self) -> Magic { match self { Network::Mainnet => magics::MAINNET, - Network::Testnet => magics::TESTNET, + // TODO: Move `Magic` struct definition to `zebra-chain`, add it as a field in `NetworkParameters`, and return it here. + Network::Testnet(_params) => magics::TESTNET, } } } @@ -80,7 +81,7 @@ impl Version { /// - after Zebra's local network is slow or shut down. fn initial_min_for_network(network: &Network) -> Version { *constants::INITIAL_MIN_NETWORK_PROTOCOL_VERSION - .get(network) + .get(&network.bip70_network_name()) .expect("We always have a value for testnet or mainnet") } @@ -103,17 +104,22 @@ 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(params), Overwinter) if params.is_default_testnet() => 170_003, (Mainnet, Overwinter) => 170_005, + // TODO: Use 170_006 for (Testnet(params), Sapling) if params.is_regtest() (`Regtest` in zcashd uses + // version 170_006 for Sapling, and the same values as Testnet for other network upgrades.) (_, Sapling) => 170_007, - (Testnet, Blossom) => 170_008, + (Testnet(params), Blossom) if params.is_default_testnet() => 170_008, (Mainnet, Blossom) => 170_009, - (Testnet, Heartwood) => 170_010, + (Testnet(params), Heartwood) if params.is_default_testnet() => 170_010, (Mainnet, Heartwood) => 170_011, - (Testnet, Canopy) => 170_012, + (Testnet(params), Canopy) if params.is_default_testnet() => 170_012, (Mainnet, Canopy) => 170_013, - (Testnet, Nu5) => 170_050, + (Testnet(params), Nu5) if params.is_default_testnet() => 170_050, (Mainnet, Nu5) => 170_100, + + // It should be fine to reject peers with earlier network protocol versions on custom testnets for now. + (Testnet(_params), _) => CURRENT_NETWORK_PROTOCOL_VERSION.0, }) } } diff --git a/zebra-scan/src/service/scan_task/scan.rs b/zebra-scan/src/service/scan_task/scan.rs index b1207ff09..c24f744ce 100644 --- a/zebra-scan/src/service/scan_task/scan.rs +++ b/zebra-scan/src/service/scan_task/scan.rs @@ -384,7 +384,10 @@ pub fn scan_block( // 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(); + // TODO: Implement `zcash_primitives::consensus::Parameters` for `Network` and remove this conversion (#8365) + 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, diff --git a/zebra-state/src/service/check/difficulty.rs b/zebra-state/src/service/check/difficulty.rs index 3f6cd9e96..ab5837356 100644 --- a/zebra-state/src/service/check/difficulty.rs +++ b/zebra-state/src/service/check/difficulty.rs @@ -183,10 +183,9 @@ impl AdjustedDifficulty { self.candidate_time, self.relevant_times[0], ) { - assert_eq!( - self.network, - Network::Testnet, - "invalid network: the minimum difficulty rule only applies on testnet" + assert!( + self.network.is_a_test_network(), + "invalid network: the minimum difficulty rule only applies on test networks" ); self.network.target_difficulty_limit().to_compact() } else { diff --git a/zebra-state/src/service/finalized_state/disk_format/transparent.rs b/zebra-state/src/service/finalized_state/disk_format/transparent.rs index 89620cdae..c6f80a6d1 100644 --- a/zebra-state/src/service/finalized_state/disk_format/transparent.rs +++ b/zebra-state/src/service/finalized_state/disk_format/transparent.rs @@ -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::*}, }; @@ -504,8 +504,11 @@ fn address_variant(address: &transparent::Address) -> u8 { match (address.network(), address) { (Mainnet, PayToPublicKeyHash { .. }) => 0, (Mainnet, PayToScriptHash { .. }) => 1, - (Testnet, PayToPublicKeyHash { .. }) => 2, - (Testnet, PayToScriptHash { .. }) => 3, + (Testnet(params), PayToPublicKeyHash { .. }) if params.is_default_testnet() => 2, + (Testnet(params), PayToScriptHash { .. }) if params.is_default_testnet() => 3, + // TODO: Use 4 and 5 for `Regtest` (#7839) + (Testnet(_params), PayToPublicKeyHash { .. }) => 6, + (Testnet(_params), PayToScriptHash { .. }) => 7, } } @@ -531,7 +534,8 @@ impl FromDisk for transparent::Address { let network = if address_variant < 2 { Mainnet } else { - Testnet + // TODO: Replace `network` field on `Address` with the prefix and a method for checking if it's the mainnet prefix. + Network::new_default_testnet() }; if address_variant % 2 == 0 { From 7533e2fbc002409de509a6aeb22d7bda14885ee4 Mon Sep 17 00:00:00 2001 From: Arya Date: Wed, 20 Mar 2024 03:26:14 -0400 Subject: [PATCH 03/51] Updates tests --- zebra-chain/src/block/tests/vectors.rs | 4 +- zebra-chain/src/history_tree/tests/vectors.rs | 13 ++-- .../src/parameters/network/tests/prop.rs | 2 +- zebra-chain/src/parameters/tests.rs | 10 ++-- .../primitives/zcash_history/tests/vectors.rs | 8 +-- zebra-chain/src/sapling/tests/tree.rs | 5 +- zebra-chain/src/sprout/tests/tree.rs | 6 +- zebra-chain/src/transaction/tests/vectors.rs | 18 +++--- zebra-chain/src/transparent/address.rs | 4 +- zebra-chain/src/transparent/tests/vectors.rs | 16 ++--- .../src/work/difficulty/tests/vectors.rs | 19 +++--- .../block/subsidy/funding_streams/tests.rs | 2 +- zebra-consensus/src/block/subsidy/general.rs | 11 ++-- zebra-consensus/src/block/tests.rs | 39 ++++++------ zebra-consensus/src/checkpoint/list/tests.rs | 6 +- zebra-consensus/src/checkpoint/tests.rs | 26 +++++--- zebra-consensus/src/transaction/tests.rs | 50 ++++++++-------- zebra-grpc/src/tests/snapshot.rs | 2 +- zebra-network/src/isolated/tests/vectors.rs | 13 ++-- .../src/peer_set/initialize/tests/vectors.rs | 59 ++++++++++++++----- zebra-network/src/protocol/external/types.rs | 4 +- zebra-rpc/src/methods/tests/snapshot.rs | 12 ++-- zebra-rpc/src/methods/tests/vectors.rs | 2 +- zebra-scan/src/storage/db/tests/snapshot.rs | 10 ++-- zebra-state/src/service/arbitrary.rs | 15 ++--- .../disk_format/tests/snapshot.rs | 12 ++-- .../zebra_db/block/tests/snapshot.rs | 8 +-- .../zebra_db/block/tests/vectors.rs | 2 +- .../non_finalized_state/tests/vectors.rs | 43 ++++++++------ zebra-state/tests/basic.rs | 2 +- .../mempool/storage/tests/vectors.rs | 12 ++-- zebrad/tests/acceptance.rs | 19 +++--- zebrad/tests/common/checkpoints.rs | 4 +- 33 files changed, 253 insertions(+), 205 deletions(-) diff --git a/zebra-chain/src/block/tests/vectors.rs b/zebra-chain/src/block/tests/vectors.rs index 8aa81dbd3..5ff19ca10 100644 --- a/zebra-chain/src/block/tests/vectors.rs +++ b/zebra-chain/src/block/tests/vectors.rs @@ -202,7 +202,7 @@ fn block_test_vectors_height_mainnet() { fn block_test_vectors_height_testnet() { let _init_guard = zebra_test::init(); - block_test_vectors_height(Testnet); + block_test_vectors_height(Network::new_default_testnet()); } /// Test that the block test vector indexes match the heights in the block data, @@ -245,7 +245,7 @@ fn block_commitment_mainnet() { fn block_commitment_testnet() { let _init_guard = zebra_test::init(); - block_commitment(Testnet); + block_commitment(Network::new_default_testnet()); } /// Check that the block commitment field parses without errors. diff --git a/zebra-chain/src/history_tree/tests/vectors.rs b/zebra-chain/src/history_tree/tests/vectors.rs index 9ddb32bd0..e65da99b6 100644 --- a/zebra-chain/src/history_tree/tests/vectors.rs +++ b/zebra-chain/src/history_tree/tests/vectors.rs @@ -22,10 +22,10 @@ use eyre::Result; /// higher level API. #[test] fn push_and_prune() -> Result<()> { - push_and_prune_for_network_upgrade(Network::Mainnet, NetworkUpgrade::Heartwood)?; - push_and_prune_for_network_upgrade(Network::Testnet, NetworkUpgrade::Heartwood)?; - push_and_prune_for_network_upgrade(Network::Mainnet, NetworkUpgrade::Canopy)?; - push_and_prune_for_network_upgrade(Network::Testnet, NetworkUpgrade::Canopy)?; + for network in Network::iter() { + push_and_prune_for_network_upgrade(network.clone(), NetworkUpgrade::Heartwood)?; + push_and_prune_for_network_upgrade(network, NetworkUpgrade::Canopy)?; + } Ok(()) } @@ -109,8 +109,9 @@ fn push_and_prune_for_network_upgrade( fn upgrade() -> Result<()> { // The history tree only exists Hearwood-onward, and the only upgrade for which // we have vectors since then is Canopy. Therefore, only test the Heartwood->Canopy upgrade. - upgrade_for_network_upgrade(Network::Mainnet, NetworkUpgrade::Canopy)?; - upgrade_for_network_upgrade(Network::Testnet, NetworkUpgrade::Canopy)?; + for network in Network::iter() { + upgrade_for_network_upgrade(network, NetworkUpgrade::Canopy)?; + } Ok(()) } diff --git a/zebra-chain/src/parameters/network/tests/prop.rs b/zebra-chain/src/parameters/network/tests/prop.rs index cab04496d..c2b1e31e0 100644 --- a/zebra-chain/src/parameters/network/tests/prop.rs +++ b/zebra-chain/src/parameters/network/tests/prop.rs @@ -34,6 +34,6 @@ proptest! { let _init_guard = zebra_test::init(); assert!(Network::Mainnet.is_max_block_time_enforced(height)); - assert_eq!(Network::Testnet.is_max_block_time_enforced(height), TESTNET_MAX_TIME_START_HEIGHT <= height); + assert_eq!(Network::new_default_testnet().is_max_block_time_enforced(height), TESTNET_MAX_TIME_START_HEIGHT <= height); } } diff --git a/zebra-chain/src/parameters/tests.rs b/zebra-chain/src/parameters/tests.rs index 715ceb682..2b0f8b4a2 100644 --- a/zebra-chain/src/parameters/tests.rs +++ b/zebra-chain/src/parameters/tests.rs @@ -21,7 +21,7 @@ fn activation_bijective() { let mainnet_nus: HashSet<&NetworkUpgrade> = mainnet_activations.values().collect(); assert_eq!(MAINNET_ACTIVATION_HEIGHTS.len(), mainnet_nus.len()); - let testnet_activations = Testnet.activation_list(); + let testnet_activations = Network::new_default_testnet().activation_list(); let testnet_heights: HashSet<&block::Height> = testnet_activations.keys().collect(); assert_eq!(TESTNET_ACTIVATION_HEIGHTS.len(), testnet_heights.len()); @@ -38,7 +38,7 @@ fn activation_extremes_mainnet() { #[test] fn activation_extremes_testnet() { let _init_guard = zebra_test::init(); - activation_extremes(Testnet) + activation_extremes(Network::new_default_testnet()) } /// Test the activation_list, activation_height, current, and next functions @@ -115,7 +115,7 @@ fn activation_consistent_mainnet() { #[test] fn activation_consistent_testnet() { let _init_guard = zebra_test::init(); - activation_consistent(Testnet) + activation_consistent(Network::new_default_testnet()) } /// Check that the `activation_height`, `is_activation_height`, @@ -178,7 +178,7 @@ fn branch_id_extremes_mainnet() { #[test] fn branch_id_extremes_testnet() { let _init_guard = zebra_test::init(); - branch_id_extremes(Testnet) + branch_id_extremes(Network::new_default_testnet()) } /// Test the branch_id_list, branch_id, and current functions for `network` with @@ -217,7 +217,7 @@ fn branch_id_consistent_mainnet() { #[test] fn branch_id_consistent_testnet() { let _init_guard = zebra_test::init(); - branch_id_consistent(Testnet) + branch_id_consistent(Network::new_default_testnet()) } /// Check that the branch_id and current functions are consistent for `network`. diff --git a/zebra-chain/src/primitives/zcash_history/tests/vectors.rs b/zebra-chain/src/primitives/zcash_history/tests/vectors.rs index b5ead1098..d23cb078d 100644 --- a/zebra-chain/src/primitives/zcash_history/tests/vectors.rs +++ b/zebra-chain/src/primitives/zcash_history/tests/vectors.rs @@ -11,10 +11,10 @@ use eyre::Result; /// and its next block. #[test] fn tree() -> Result<()> { - tree_for_network_upgrade(&Network::Mainnet, NetworkUpgrade::Heartwood)?; - tree_for_network_upgrade(&Network::Testnet, NetworkUpgrade::Heartwood)?; - tree_for_network_upgrade(&Network::Mainnet, NetworkUpgrade::Canopy)?; - tree_for_network_upgrade(&Network::Testnet, NetworkUpgrade::Canopy)?; + for network in Network::iter() { + tree_for_network_upgrade(&network, NetworkUpgrade::Heartwood)?; + tree_for_network_upgrade(&network, NetworkUpgrade::Canopy)?; + } Ok(()) } diff --git a/zebra-chain/src/sapling/tests/tree.rs b/zebra-chain/src/sapling/tests/tree.rs index 17ee640f0..37773e01e 100644 --- a/zebra-chain/src/sapling/tests/tree.rs +++ b/zebra-chain/src/sapling/tests/tree.rs @@ -51,8 +51,9 @@ fn incremental_roots() { #[test] fn incremental_roots_with_blocks() -> Result<()> { - incremental_roots_with_blocks_for_network(Network::Mainnet)?; - incremental_roots_with_blocks_for_network(Network::Testnet)?; + for network in Network::iter() { + incremental_roots_with_blocks_for_network(network)?; + } Ok(()) } diff --git a/zebra-chain/src/sprout/tests/tree.rs b/zebra-chain/src/sprout/tests/tree.rs index f71c8c6a3..f04ee9af3 100644 --- a/zebra-chain/src/sprout/tests/tree.rs +++ b/zebra-chain/src/sprout/tests/tree.rs @@ -79,9 +79,9 @@ fn incremental_roots() { #[test] fn incremental_roots_with_blocks() -> Result<()> { - incremental_roots_with_blocks_for_network(Network::Mainnet)?; - incremental_roots_with_blocks_for_network(Network::Testnet)?; - + for network in Network::iter() { + incremental_roots_with_blocks_for_network(network)?; + } Ok(()) } diff --git a/zebra-chain/src/transaction/tests/vectors.rs b/zebra-chain/src/transaction/tests/vectors.rs index 129565a6c..aa95a9c55 100644 --- a/zebra-chain/src/transaction/tests/vectors.rs +++ b/zebra-chain/src/transaction/tests/vectors.rs @@ -343,9 +343,9 @@ fn empty_v5_librustzcash_round_trip() { #[test] fn fake_v5_round_trip() { let _init_guard = zebra_test::init(); - - fake_v5_round_trip_for_network(Network::Mainnet); - fake_v5_round_trip_for_network(Network::Testnet); + for network in Network::iter() { + fake_v5_round_trip_for_network(network); + } } fn fake_v5_round_trip_for_network(network: Network) { @@ -491,9 +491,9 @@ fn invalid_orchard_nullifier() { #[test] fn fake_v5_librustzcash_round_trip() { let _init_guard = zebra_test::init(); - - fake_v5_librustzcash_round_trip_for_network(Network::Mainnet); - fake_v5_librustzcash_round_trip_for_network(Network::Testnet); + for network in Network::iter() { + fake_v5_librustzcash_round_trip_for_network(network); + } } fn fake_v5_librustzcash_round_trip_for_network(network: Network) { @@ -931,9 +931,9 @@ fn zip244_sighash() -> Result<()> { #[test] fn binding_signatures() { let _init_guard = zebra_test::init(); - - binding_signatures_for_network(Network::Mainnet); - binding_signatures_for_network(Network::Testnet); + for network in Network::iter() { + binding_signatures_for_network(network); + } } fn binding_signatures_for_network(network: Network) { diff --git a/zebra-chain/src/transparent/address.rs b/zebra-chain/src/transparent/address.rs index 18fbe8a68..754ea6de8 100644 --- a/zebra-chain/src/transparent/address.rs +++ b/zebra-chain/src/transparent/address.rs @@ -300,7 +300,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"); } @@ -322,7 +322,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"); } diff --git a/zebra-chain/src/transparent/tests/vectors.rs b/zebra-chain/src/transparent/tests/vectors.rs index d3ceae413..f83dbf1c6 100644 --- a/zebra-chain/src/transparent/tests/vectors.rs +++ b/zebra-chain/src/transparent/tests/vectors.rs @@ -67,15 +67,17 @@ fn get_transparent_output_address() -> Result<()> { let addr = transparent_output_address(&transaction.outputs()[0], &Network::Mainnet) .expect("should return address"); assert_eq!(addr.to_string(), "t3M5FDmPfWNRG3HRLddbicsuSCvKuk9hxzZ"); - let addr = transparent_output_address(&transaction.outputs()[0], &Network::Testnet) - .expect("should return address"); + let addr = + transparent_output_address(&transaction.outputs()[0], &Network::new_default_testnet()) + .expect("should return address"); assert_eq!(addr.to_string(), "t294SGSVoNq2daz15ZNbmAW65KQZ5e3nN5G"); // Public key hash e4ff5512ffafe9287992a1cd177ca6e408e03003 let addr = transparent_output_address(&transaction.outputs()[1], &Network::Mainnet) .expect("should return address"); assert_eq!(addr.to_string(), "t1ekRwsd4LaSsd6NXgsx66q2HxQWTLCF44y"); - let addr = transparent_output_address(&transaction.outputs()[1], &Network::Testnet) - .expect("should return address"); + let addr = + transparent_output_address(&transaction.outputs()[1], &Network::new_default_testnet()) + .expect("should return address"); assert_eq!(addr.to_string(), "tmWbBGi7TjExNmLZyMcFpxVh3ZPbGrpbX3H"); Ok(()) @@ -84,9 +86,9 @@ fn get_transparent_output_address() -> Result<()> { #[test] fn get_transparent_output_address_with_blocks() { let _init_guard = zebra_test::init(); - - get_transparent_output_address_with_blocks_for_network(Network::Mainnet); - get_transparent_output_address_with_blocks_for_network(Network::Testnet); + for network in Network::iter() { + get_transparent_output_address_with_blocks_for_network(network); + } } /// Test that the block test vector indexes match the heights in the block data, diff --git a/zebra-chain/src/work/difficulty/tests/vectors.rs b/zebra-chain/src/work/difficulty/tests/vectors.rs index 9f9c03544..09250e6b2 100644 --- a/zebra-chain/src/work/difficulty/tests/vectors.rs +++ b/zebra-chain/src/work/difficulty/tests/vectors.rs @@ -263,8 +263,9 @@ fn compact_bitcoin_test_vectors() { /// Test blocks using CompactDifficulty. #[test] fn block_difficulty() -> Result<(), Report> { - block_difficulty_for_network(Network::Mainnet)?; - block_difficulty_for_network(Network::Testnet)?; + for network in Network::iter() { + block_difficulty_for_network(network)?; + } Ok(()) } @@ -349,8 +350,9 @@ fn block_difficulty_for_network(network: Network) -> Result<(), Report> { /// Test that the genesis block threshold is PowLimit #[test] fn genesis_block_difficulty() -> Result<(), Report> { - genesis_block_difficulty_for_network(Network::Mainnet)?; - genesis_block_difficulty_for_network(Network::Testnet)?; + for network in Network::iter() { + genesis_block_difficulty_for_network(network)?; + } Ok(()) } @@ -454,7 +456,10 @@ fn check_testnet_minimum_difficulty_block(height: block::Height) -> Result<(), R // threshold, as documented in ZIP-205 and ZIP-208: // https://zips.z.cash/zip-0205#change-to-difficulty-adjustment-on-testnet // https://zips.z.cash/zip-0208#minimum-difficulty-blocks-on-testnet - match NetworkUpgrade::minimum_difficulty_spacing_for_height(&Network::Testnet, height) { + match NetworkUpgrade::minimum_difficulty_spacing_for_height( + &Network::new_default_testnet(), + height, + ) { None => Err(eyre!("the minimum difficulty rule is not active"))?, Some(spacing) if (time_gap <= spacing) => Err(eyre!( "minimum difficulty block times must be more than 6 target spacing intervals apart" @@ -477,12 +482,12 @@ fn check_testnet_minimum_difficulty_block(height: block::Height) -> Result<(), R /// SPANDOC: Check that the testnet minimum difficulty is the PoWLimit {?height, ?threshold, ?hash} { - assert_eq!(threshold, Network::Testnet.target_difficulty_limit(), + assert_eq!(threshold, Network::new_default_testnet().target_difficulty_limit(), "testnet minimum difficulty thresholds should be equal to the PoWLimit. Hint: Blocks with large gaps are allowed to have the minimum difficulty, but it's not required."); // all blocks pass the minimum difficulty threshold, even if they aren't minimum // difficulty blocks, because it's the lowest permitted difficulty assert!( - hash <= Network::Testnet.target_difficulty_limit(), + hash <= Network::new_default_testnet().target_difficulty_limit(), "testnet minimum difficulty hashes must be less than the PoWLimit" ); } diff --git a/zebra-consensus/src/block/subsidy/funding_streams/tests.rs b/zebra-consensus/src/block/subsidy/funding_streams/tests.rs index 2f1f1845c..0527c6b48 100644 --- a/zebra-consensus/src/block/subsidy/funding_streams/tests.rs +++ b/zebra-consensus/src/block/subsidy/funding_streams/tests.rs @@ -70,7 +70,7 @@ fn test_funding_stream_addresses() -> Result<(), Report> { let address = transparent::Address::from_str(address).expect("address should deserialize"); assert_eq!( - &address.network(), + &address.network().bip70_network_name(), network, "incorrect network for {receiver:?} funding stream address constant: {address}", ); diff --git a/zebra-consensus/src/block/subsidy/general.rs b/zebra-consensus/src/block/subsidy/general.rs index 37ba1cfe9..d9fbe0a89 100644 --- a/zebra-consensus/src/block/subsidy/general.rs +++ b/zebra-consensus/src/block/subsidy/general.rs @@ -122,9 +122,9 @@ mod test { #[test] fn halving_test() -> Result<(), Report> { let _init_guard = zebra_test::init(); - - halving_for_network(&Network::Mainnet)?; - halving_for_network(&Network::Testnet)?; + for network in Network::iter() { + halving_for_network(&network)?; + } Ok(()) } @@ -249,8 +249,9 @@ mod test { fn block_subsidy_test() -> Result<(), Report> { let _init_guard = zebra_test::init(); - block_subsidy_for_network(&Network::Mainnet)?; - block_subsidy_for_network(&Network::Testnet)?; + for network in Network::iter() { + block_subsidy_for_network(&network)?; + } Ok(()) } diff --git a/zebra-consensus/src/block/tests.rs b/zebra-consensus/src/block/tests.rs index 44e577b1c..6dec96df5 100644 --- a/zebra-consensus/src/block/tests.rs +++ b/zebra-consensus/src/block/tests.rs @@ -181,9 +181,9 @@ fn coinbase_is_first_for_historical_blocks() -> Result<(), Report> { #[test] fn difficulty_is_valid_for_historical_blocks() -> Result<(), Report> { let _init_guard = zebra_test::init(); - - difficulty_is_valid_for_network(Network::Mainnet)?; - difficulty_is_valid_for_network(Network::Testnet)?; + for network in Network::iter() { + difficulty_is_valid_for_network(network)?; + } Ok(()) } @@ -285,9 +285,9 @@ fn equihash_is_valid_for_historical_blocks() -> Result<(), Report> { #[test] fn subsidy_is_valid_for_historical_blocks() -> Result<(), Report> { let _init_guard = zebra_test::init(); - - subsidy_is_valid_for_network(Network::Mainnet)?; - subsidy_is_valid_for_network(Network::Testnet)?; + for network in Network::iter() { + subsidy_is_valid_for_network(network)?; + } Ok(()) } @@ -388,9 +388,9 @@ fn coinbase_validation_failure() -> Result<(), Report> { #[test] fn funding_stream_validation() -> Result<(), Report> { let _init_guard = zebra_test::init(); - - funding_stream_validation_for_network(Network::Mainnet)?; - funding_stream_validation_for_network(Network::Testnet)?; + for network in Network::iter() { + funding_stream_validation_for_network(network)?; + } Ok(()) } @@ -463,9 +463,9 @@ fn funding_stream_validation_failure() -> Result<(), Report> { #[test] fn miner_fees_validation_success() -> Result<(), Report> { let _init_guard = zebra_test::init(); - - miner_fees_validation_for_network(Network::Mainnet)?; - miner_fees_validation_for_network(Network::Testnet)?; + for network in Network::iter() { + miner_fees_validation_for_network(network)?; + } Ok(()) } @@ -546,12 +546,14 @@ fn merkle_root_is_valid() -> Result<(), Report> { let _init_guard = zebra_test::init(); // test all original blocks available, all blocks validate - merkle_root_is_valid_for_network(Network::Mainnet)?; - merkle_root_is_valid_for_network(Network::Testnet)?; + for network in Network::iter() { + merkle_root_is_valid_for_network(network)?; + } // create and test fake blocks with v5 transactions, all blocks fail validation - merkle_root_fake_v5_for_network(Network::Mainnet)?; - merkle_root_fake_v5_for_network(Network::Testnet)?; + for network in Network::iter() { + merkle_root_fake_v5_for_network(network)?; + } Ok(()) } @@ -683,8 +685,9 @@ fn legacy_sigops_count_for_historic_blocks() { fn transaction_expiration_height_validation() -> Result<(), Report> { let _init_guard = zebra_test::init(); - transaction_expiration_height_for_network(&Network::Mainnet)?; - transaction_expiration_height_for_network(&Network::Testnet)?; + for network in Network::iter() { + transaction_expiration_height_for_network(&network)?; + } Ok(()) } diff --git a/zebra-consensus/src/checkpoint/list/tests.rs b/zebra-consensus/src/checkpoint/list/tests.rs index 0588a365c..037dd027e 100644 --- a/zebra-consensus/src/checkpoint/list/tests.rs +++ b/zebra-consensus/src/checkpoint/list/tests.rs @@ -236,7 +236,7 @@ fn checkpoint_list_load_hard_coded() -> Result<(), BoxError> { .expect("hard-coded Testnet checkpoint list should parse"); let _ = Mainnet.checkpoint_list(); - let _ = Testnet.checkpoint_list(); + let _ = Network::new_default_testnet().checkpoint_list(); Ok(()) } @@ -248,7 +248,7 @@ fn checkpoint_list_hard_coded_mandatory_mainnet() -> Result<(), BoxError> { #[test] fn checkpoint_list_hard_coded_mandatory_testnet() -> Result<(), BoxError> { - checkpoint_list_hard_coded_mandatory(Testnet) + checkpoint_list_hard_coded_mandatory(Network::new_default_testnet()) } /// Check that the hard-coded lists cover the mandatory checkpoint @@ -274,7 +274,7 @@ fn checkpoint_list_hard_coded_max_gap_mainnet() -> Result<(), BoxError> { #[test] fn checkpoint_list_hard_coded_max_gap_testnet() -> Result<(), BoxError> { - checkpoint_list_hard_coded_max_gap(Testnet) + checkpoint_list_hard_coded_max_gap(Network::new_default_testnet()) } /// Check that the hard-coded checkpoints are within [`MAX_CHECKPOINT_HEIGHT_GAP`], diff --git a/zebra-consensus/src/checkpoint/tests.rs b/zebra-consensus/src/checkpoint/tests.rs index b2493540f..b8f41cc6f 100644 --- a/zebra-consensus/src/checkpoint/tests.rs +++ b/zebra-consensus/src/checkpoint/tests.rs @@ -205,18 +205,30 @@ async fn multi_item_checkpoint_list() -> Result<(), Report> { #[tokio::test(flavor = "multi_thread")] async fn continuous_blockchain_no_restart() -> Result<(), Report> { - continuous_blockchain(None, Mainnet).await?; - continuous_blockchain(None, Testnet).await?; + for network in Network::iter() { + continuous_blockchain(None, network).await?; + } Ok(()) } #[tokio::test(flavor = "multi_thread")] async fn continuous_blockchain_restart() -> Result<(), Report> { - for height in 0..zebra_test::vectors::CONTINUOUS_MAINNET_BLOCKS.len() { - continuous_blockchain(Some(block::Height(height.try_into().unwrap())), Mainnet).await?; - } - for height in 0..zebra_test::vectors::CONTINUOUS_TESTNET_BLOCKS.len() { - continuous_blockchain(Some(block::Height(height.try_into().unwrap())), Testnet).await?; + for network in Network::iter() { + for height in 0..zebra_test::vectors::CONTINUOUS_MAINNET_BLOCKS.len() { + continuous_blockchain( + Some(block::Height(height.try_into().unwrap())), + network.clone(), + ) + .await?; + } + + for height in 0..zebra_test::vectors::CONTINUOUS_TESTNET_BLOCKS.len() { + continuous_blockchain( + Some(block::Height(height.try_into().unwrap())), + network.clone(), + ) + .await?; + } } Ok(()) } diff --git a/zebra-consensus/src/transaction/tests.rs b/zebra-consensus/src/transaction/tests.rs index 758c13fa9..6f5c3639e 100644 --- a/zebra-consensus/src/transaction/tests.rs +++ b/zebra-consensus/src/transaction/tests.rs @@ -42,13 +42,8 @@ mod prop; fn v5_fake_transactions() -> Result<(), Report> { let _init_guard = zebra_test::init(); - let networks = vec![ - (Network::Mainnet, zebra_test::vectors::MAINNET_BLOCKS.iter()), - (Network::Testnet, zebra_test::vectors::TESTNET_BLOCKS.iter()), - ]; - - for (network, blocks) in networks { - for transaction in fake_v5_transactions_for_network(&network, blocks) { + for network in Network::iter() { + for transaction in fake_v5_transactions_for_network(&network, network.block_iter()) { match check::has_inputs_and_outputs(&transaction) { Ok(()) => (), Err(TransactionError::NoInputs) | Err(TransactionError::NoOutputs) => (), @@ -858,16 +853,12 @@ async fn v5_transaction_is_rejected_before_nu5_activation() { const V5_TRANSACTION_VERSION: u32 = 5; let canopy = NetworkUpgrade::Canopy; - let networks = vec![ - (Network::Mainnet, zebra_test::vectors::MAINNET_BLOCKS.iter()), - (Network::Testnet, zebra_test::vectors::TESTNET_BLOCKS.iter()), - ]; - for (network, blocks) in networks { + for network in Network::iter() { let state_service = service_fn(|_| async { unreachable!("Service should not be called") }); let verifier = Verifier::new(&network, state_service); - let transaction = fake_v5_transactions_for_network(&network, blocks) + let transaction = fake_v5_transactions_for_network(&network, network.block_iter()) .next_back() .expect("At least one fake V5 transaction in the test vectors"); @@ -899,7 +890,7 @@ fn v5_transaction_is_accepted_after_nu5_activation_mainnet() { #[test] fn v5_transaction_is_accepted_after_nu5_activation_testnet() { - v5_transaction_is_accepted_after_nu5_activation_for_network(Network::Testnet) + v5_transaction_is_accepted_after_nu5_activation_for_network(Network::new_default_testnet()) } fn v5_transaction_is_accepted_after_nu5_activation_for_network(network: Network) { @@ -1544,7 +1535,7 @@ fn v4_transaction_with_conflicting_sprout_nullifier_across_joinsplits_is_rejecte /// Test if V5 transaction with transparent funds is accepted. #[tokio::test] async fn v5_transaction_with_transparent_transfer_is_accepted() { - let network = Network::Testnet; + let network = Network::new_default_testnet(); let network_upgrade = NetworkUpgrade::Nu5; let nu5_activation_height = network_upgrade @@ -1601,12 +1592,13 @@ async fn v5_transaction_with_transparent_transfer_is_accepted() { /// accepted. #[tokio::test] async fn v5_transaction_with_last_valid_expiry_height() { + let network = Network::new_default_testnet(); let state_service = service_fn(|_| async { unreachable!("State service should not be called") }); - let verifier = Verifier::new(&Network::Testnet, state_service); + let verifier = Verifier::new(&network, state_service); let block_height = NetworkUpgrade::Nu5 - .activation_height(&Network::Testnet) + .activation_height(&network) .expect("Nu5 activation height for testnet is specified"); let fund_height = (block_height - 1).expect("fake source fund block height is too small"); let (input, output, known_utxos) = mock_transparent_transfer( @@ -1646,12 +1638,13 @@ async fn v5_transaction_with_last_valid_expiry_height() { /// is equal to the height of the block the transaction belongs to. #[tokio::test] async fn v5_coinbase_transaction_expiry_height() { + let network = Network::new_default_testnet(); let state_service = service_fn(|_| async { unreachable!("State service should not be called") }); - let verifier = Verifier::new(&Network::Testnet, state_service); + let verifier = Verifier::new(&network, state_service); let block_height = NetworkUpgrade::Nu5 - .activation_height(&Network::Testnet) + .activation_height(&network) .expect("Nu5 activation height for testnet is specified"); let (input, output) = mock_coinbase_transparent_output(block_height); @@ -1761,12 +1754,14 @@ async fn v5_coinbase_transaction_expiry_height() { /// Tests if an expired non-coinbase V5 transaction is rejected. #[tokio::test] async fn v5_transaction_with_too_low_expiry_height() { + let network = Network::new_default_testnet(); + let state_service = service_fn(|_| async { unreachable!("State service should not be called") }); - let verifier = Verifier::new(&Network::Testnet, state_service); + let verifier = Verifier::new(&network, state_service); let block_height = NetworkUpgrade::Nu5 - .activation_height(&Network::Testnet) + .activation_height(&network) .expect("Nu5 activation height for testnet is specified"); let fund_height = (block_height - 1).expect("fake source fund block height is too small"); let (input, output, known_utxos) = mock_transparent_transfer( @@ -1864,7 +1859,7 @@ async fn v5_transaction_with_exceeding_expiry_height() { /// Test if V5 coinbase transaction is accepted. #[tokio::test] async fn v5_coinbase_transaction_is_accepted() { - let network = Network::Testnet; + let network = Network::new_default_testnet(); let network_upgrade = NetworkUpgrade::Nu5; let nu5_activation_height = network_upgrade @@ -1916,7 +1911,7 @@ async fn v5_coinbase_transaction_is_accepted() { /// script prevents spending the source UTXO. #[tokio::test] async fn v5_transaction_with_transparent_transfer_is_rejected_by_the_script() { - let network = Network::Testnet; + let network = Network::new_default_testnet(); let network_upgrade = NetworkUpgrade::Nu5; let nu5_activation_height = network_upgrade @@ -2759,8 +2754,9 @@ fn add_to_sprout_pool_after_nu() { fn coinbase_outputs_are_decryptable_for_historical_blocks() -> Result<(), Report> { let _init_guard = zebra_test::init(); - coinbase_outputs_are_decryptable_for_historical_blocks_for_network(Network::Mainnet)?; - coinbase_outputs_are_decryptable_for_historical_blocks_for_network(Network::Testnet)?; + for network in Network::iter() { + coinbase_outputs_are_decryptable_for_historical_blocks_for_network(network)?; + } Ok(()) } @@ -2844,7 +2840,7 @@ fn fill_action_with_note_encryption_test_vector( /// viewing key. #[test] fn coinbase_outputs_are_decryptable_for_fake_v5_blocks() { - let network = Network::Testnet; + let network = Network::new_default_testnet(); for v in zebra_test::vectors::ORCHARD_NOTE_ENCRYPTION_ZERO_VECTOR.iter() { // Find a transaction with no inputs or outputs to use as base @@ -2886,7 +2882,7 @@ fn coinbase_outputs_are_decryptable_for_fake_v5_blocks() { /// viewing key. #[test] fn shielded_outputs_are_not_decryptable_for_fake_v5_blocks() { - let network = Network::Testnet; + let network = Network::new_default_testnet(); for v in zebra_test::vectors::ORCHARD_NOTE_ENCRYPTION_VECTOR.iter() { // Find a transaction with no inputs or outputs to use as base diff --git a/zebra-grpc/src/tests/snapshot.rs b/zebra-grpc/src/tests/snapshot.rs index 37acb59fe..6b81a3c70 100644 --- a/zebra-grpc/src/tests/snapshot.rs +++ b/zebra-grpc/src/tests/snapshot.rs @@ -38,7 +38,7 @@ async fn test_grpc_response_data() { zebra_test::net::random_known_port() ), test_mocked_rpc_response_data_for_network( - Network::Testnet, + Network::new_default_testnet(), zebra_test::net::random_known_port() ), ); diff --git a/zebra-network/src/isolated/tests/vectors.rs b/zebra-network/src/isolated/tests/vectors.rs index 94f75fe57..a037fcb5e 100644 --- a/zebra-network/src/isolated/tests/vectors.rs +++ b/zebra-network/src/isolated/tests/vectors.rs @@ -14,8 +14,6 @@ use crate::{ use super::super::*; -use Network::*; - /// Test that `connect_isolated` sends a version message with minimal distinguishing features, /// when sent over TCP. #[tokio::test] @@ -26,8 +24,9 @@ async fn connect_isolated_sends_anonymised_version_message_tcp() { return; } - connect_isolated_sends_anonymised_version_message_tcp_net(Mainnet).await; - connect_isolated_sends_anonymised_version_message_tcp_net(Testnet).await; + for network in Network::iter() { + connect_isolated_sends_anonymised_version_message_tcp_net(network).await; + } } async fn connect_isolated_sends_anonymised_version_message_tcp_net(network: Network) { @@ -82,9 +81,9 @@ async fn connect_isolated_sends_anonymised_version_message_tcp_net(network: Netw #[tokio::test] async fn connect_isolated_sends_anonymised_version_message_mem() { let _init_guard = zebra_test::init(); - - connect_isolated_sends_anonymised_version_message_mem_net(Mainnet).await; - connect_isolated_sends_anonymised_version_message_mem_net(Testnet).await; + for network in Network::iter() { + connect_isolated_sends_anonymised_version_message_mem_net(network).await; + } } async fn connect_isolated_sends_anonymised_version_message_mem_net(network: Network) { diff --git a/zebra-network/src/peer_set/initialize/tests/vectors.rs b/zebra-network/src/peer_set/initialize/tests/vectors.rs index 54fd25a26..c9e0c26fb 100644 --- a/zebra-network/src/peer_set/initialize/tests/vectors.rs +++ b/zebra-network/src/peer_set/initialize/tests/vectors.rs @@ -80,8 +80,9 @@ async fn local_listener_unspecified_port_unspecified_addr_v4() { // these tests might fail on machines with no configured IPv4 addresses // (localhost should be enough) - local_listener_port_with("0.0.0.0:0".parse().unwrap(), Mainnet).await; - local_listener_port_with("0.0.0.0:0".parse().unwrap(), Testnet).await; + for network in Network::iter() { + local_listener_port_with("0.0.0.0:0".parse().unwrap(), network).await; + } } /// Test that zebra-network discovers dynamic bind-to-all-interfaces listener ports, @@ -101,8 +102,9 @@ async fn local_listener_unspecified_port_unspecified_addr_v6() { } // these tests might fail on machines with no configured IPv6 addresses - local_listener_port_with("[::]:0".parse().unwrap(), Mainnet).await; - local_listener_port_with("[::]:0".parse().unwrap(), Testnet).await; + for network in Network::iter() { + local_listener_port_with("[::]:0".parse().unwrap(), network).await; + } } /// Test that zebra-network discovers dynamic localhost listener ports, @@ -116,8 +118,9 @@ async fn local_listener_unspecified_port_localhost_addr_v4() { } // these tests might fail on machines with unusual IPv4 localhost configs - local_listener_port_with("127.0.0.1:0".parse().unwrap(), Mainnet).await; - local_listener_port_with("127.0.0.1:0".parse().unwrap(), Testnet).await; + for network in Network::iter() { + local_listener_port_with("127.0.0.1:0".parse().unwrap(), network).await; + } } /// Test that zebra-network discovers dynamic localhost listener ports, @@ -135,8 +138,9 @@ async fn local_listener_unspecified_port_localhost_addr_v6() { } // these tests might fail on machines with no configured IPv6 addresses - local_listener_port_with("[::1]:0".parse().unwrap(), Mainnet).await; - local_listener_port_with("[::1]:0".parse().unwrap(), Testnet).await; + for network in Network::iter() { + local_listener_port_with("[::1]:0".parse().unwrap(), network).await; + } } /// Test that zebra-network propagates fixed localhost listener ports to the `AddressBook`. @@ -150,8 +154,9 @@ async fn local_listener_fixed_port_localhost_addr_v4() { return; } - local_listener_port_with(SocketAddr::new(localhost_v4, random_known_port()), Mainnet).await; - local_listener_port_with(SocketAddr::new(localhost_v4, random_known_port()), Testnet).await; + for network in Network::iter() { + local_listener_port_with(SocketAddr::new(localhost_v4, random_known_port()), network).await; + } } /// Test that zebra-network propagates fixed localhost listener ports to the `AddressBook`. @@ -169,8 +174,9 @@ async fn local_listener_fixed_port_localhost_addr_v6() { return; } - local_listener_port_with(SocketAddr::new(localhost_v6, random_known_port()), Mainnet).await; - local_listener_port_with(SocketAddr::new(localhost_v6, random_known_port()), Testnet).await; + for network in Network::iter() { + local_listener_port_with(SocketAddr::new(localhost_v6, random_known_port()), network).await; + } } /// Test zebra-network with a peer limit of zero peers on mainnet. @@ -207,8 +213,15 @@ async fn peer_limit_zero_testnet() { let unreachable_inbound_service = service_fn(|_| async { unreachable!("inbound service should never be called") }); - let address_book = - init_with_peer_limit(0, unreachable_inbound_service, Testnet, None, None).await; + let address_book = init_with_peer_limit( + 0, + unreachable_inbound_service, + Network::new_default_testnet(), + None, + None, + ) + .await; + assert_eq!( address_book.lock().unwrap().peers().count(), 0, @@ -247,7 +260,14 @@ async fn peer_limit_one_testnet() { let nil_inbound_service = service_fn(|_| async { Ok(Response::Nil) }); - let _ = init_with_peer_limit(1, nil_inbound_service, Testnet, None, None).await; + let _ = init_with_peer_limit( + 1, + nil_inbound_service, + Network::new_default_testnet(), + None, + None, + ) + .await; // Let the crawler run for a while. tokio::time::sleep(CRAWLER_TEST_DURATION).await; @@ -285,7 +305,14 @@ async fn peer_limit_two_testnet() { let nil_inbound_service = service_fn(|_| async { Ok(Response::Nil) }); - let _ = init_with_peer_limit(2, nil_inbound_service, Testnet, None, None).await; + let _ = init_with_peer_limit( + 2, + nil_inbound_service, + Network::new_default_testnet(), + None, + None, + ) + .await; // Let the crawler run for a while. tokio::time::sleep(CRAWLER_TEST_DURATION).await; diff --git a/zebra-network/src/protocol/external/types.rs b/zebra-network/src/protocol/external/types.rs index 5c132710d..c1b98523b 100644 --- a/zebra-network/src/protocol/external/types.rs +++ b/zebra-network/src/protocol/external/types.rs @@ -207,7 +207,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 @@ -235,7 +235,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 diff --git a/zebra-rpc/src/methods/tests/snapshot.rs b/zebra-rpc/src/methods/tests/snapshot.rs index 94e12cc80..b4f1f9fbf 100644 --- a/zebra-rpc/src/methods/tests/snapshot.rs +++ b/zebra-rpc/src/methods/tests/snapshot.rs @@ -11,11 +11,8 @@ use insta::dynamic_redaction; use tower::buffer::Buffer; use zebra_chain::{ - block::Block, - chain_tip::mock::MockChainTip, - parameters::Network::{Mainnet, Testnet}, - serialization::ZcashDeserializeInto, - subtree::NoteCommitmentSubtreeData, + block::Block, chain_tip::mock::MockChainTip, parameters::Network::Mainnet, + serialization::ZcashDeserializeInto, subtree::NoteCommitmentSubtreeData, }; use zebra_state::{ReadRequest, ReadResponse, MAX_ON_DISK_HEIGHT}; use zebra_test::mock_service::MockService; @@ -33,12 +30,13 @@ pub const EXCESSIVE_BLOCK_HEIGHT: u32 = MAX_ON_DISK_HEIGHT.0 + 1; #[tokio::test(flavor = "multi_thread")] async fn test_rpc_response_data() { let _init_guard = zebra_test::init(); + let default_testnet = Network::new_default_testnet(); tokio::join!( test_rpc_response_data_for_network(&Mainnet), - test_rpc_response_data_for_network(&Testnet), + test_rpc_response_data_for_network(&default_testnet), test_mocked_rpc_response_data_for_network(&Mainnet), - test_mocked_rpc_response_data_for_network(&Testnet), + test_mocked_rpc_response_data_for_network(&default_testnet), ); } diff --git a/zebra-rpc/src/methods/tests/vectors.rs b/zebra-rpc/src/methods/tests/vectors.rs index 28ca7b198..d2828b085 100644 --- a/zebra-rpc/src/methods/tests/vectors.rs +++ b/zebra-rpc/src/methods/tests/vectors.rs @@ -654,7 +654,7 @@ async fn rpc_getaddresstxids_invalid_arguments() { async fn rpc_getaddresstxids_response() { let _init_guard = zebra_test::init(); - for network in [Mainnet, Testnet] { + for network in Network::iter() { let blocks: Vec> = network .blockchain_map() .iter() diff --git a/zebra-scan/src/storage/db/tests/snapshot.rs b/zebra-scan/src/storage/db/tests/snapshot.rs index 81dc1a711..93f376785 100644 --- a/zebra-scan/src/storage/db/tests/snapshot.rs +++ b/zebra-scan/src/storage/db/tests/snapshot.rs @@ -28,10 +28,7 @@ use std::collections::BTreeMap; use itertools::Itertools; -use zebra_chain::{ - block::Height, - parameters::Network::{self, *}, -}; +use zebra_chain::{block::Height, parameters::Network}; use zebra_state::{RawBytes, ReadDisk, SaplingScannedDatabaseIndex, TransactionLocation, KV}; use crate::storage::{db::ScannerDb, Storage}; @@ -45,8 +42,9 @@ use crate::storage::{db::ScannerDb, Storage}; fn test_database_format() { let _init_guard = zebra_test::init(); - test_database_format_with_network(Mainnet); - test_database_format_with_network(Testnet); + for network in Network::iter() { + test_database_format_with_network(network); + } } /// Snapshot raw and typed database formats for `network`. diff --git a/zebra-state/src/service/arbitrary.rs b/zebra-state/src/service/arbitrary.rs index 08eb42a83..308953ff0 100644 --- a/zebra-state/src/service/arbitrary.rs +++ b/zebra-state/src/service/arbitrary.rs @@ -89,13 +89,14 @@ impl PreparedChain { // The history tree only works with Heartwood onward. // Since the network will be chosen later, we pick the larger // between the mainnet and testnet Heartwood activation heights. - let main_height = NetworkUpgrade::Heartwood - .activation_height(&Network::Mainnet) - .expect("must have height"); - let test_height = NetworkUpgrade::Heartwood - .activation_height(&Network::Testnet) - .expect("must have height"); - let height = std::cmp::max(main_height, test_height); + let height = Network::iter() + .map(|network| { + NetworkUpgrade::Heartwood + .activation_height(&network) + .expect("must have height") + }) + .max() + .expect("Network::iter() must return non-empty iterator"); PreparedChain { ledger_strategy: Some(LedgerState::height_strategy( diff --git a/zebra-state/src/service/finalized_state/disk_format/tests/snapshot.rs b/zebra-state/src/service/finalized_state/disk_format/tests/snapshot.rs index 4605faf7d..14a8dd6c2 100644 --- a/zebra-state/src/service/finalized_state/disk_format/tests/snapshot.rs +++ b/zebra-state/src/service/finalized_state/disk_format/tests/snapshot.rs @@ -29,11 +29,7 @@ use std::{collections::BTreeMap, sync::Arc}; -use zebra_chain::{ - block::Block, - parameters::Network::{self, *}, - serialization::ZcashDeserializeInto, -}; +use zebra_chain::{block::Block, parameters::Network, serialization::ZcashDeserializeInto}; use crate::{ service::finalized_state::{ @@ -50,9 +46,9 @@ use crate::{ #[test] fn test_raw_rocksdb_column_families() { let _init_guard = zebra_test::init(); - - test_raw_rocksdb_column_families_with_network(Mainnet); - test_raw_rocksdb_column_families_with_network(Testnet); + for network in Network::iter() { + test_raw_rocksdb_column_families_with_network(network); + } } /// Snapshot raw column families for `network`. diff --git a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs index f91ea5843..2d72c9785 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs +++ b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs @@ -37,7 +37,7 @@ use serde::Serialize; use zebra_chain::{ block::{self, Block, Height, SerializedBlock}, orchard, - parameters::Network::{self, *}, + parameters::Network, sapling, serialization::{ZcashDeserializeInto, ZcashSerialize}, transaction::{self, Transaction}, @@ -153,9 +153,9 @@ impl TransactionData { #[test] fn test_block_and_transaction_data() { let _init_guard = zebra_test::init(); - - test_block_and_transaction_data_with_network(Mainnet); - test_block_and_transaction_data_with_network(Testnet); + for network in Network::iter() { + test_block_and_transaction_data_with_network(network); + } } /// Snapshot finalized block and transaction data for `network`. diff --git a/zebra-state/src/service/finalized_state/zebra_db/block/tests/vectors.rs b/zebra-state/src/service/finalized_state/zebra_db/block/tests/vectors.rs index 136121db3..1b4f7db9a 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/block/tests/vectors.rs +++ b/zebra-state/src/service/finalized_state/zebra_db/block/tests/vectors.rs @@ -43,7 +43,7 @@ fn test_block_db_round_trip() { .map(|(_height, block)| block.zcash_deserialize_into().unwrap()); test_block_db_round_trip_with(&Mainnet, mainnet_test_cases); - test_block_db_round_trip_with(&Testnet, testnet_test_cases); + test_block_db_round_trip_with(&Network::new_default_testnet(), testnet_test_cases); // It doesn't matter if these blocks are mainnet or testnet, // because there is no validation at this level of the database. diff --git a/zebra-state/src/service/non_finalized_state/tests/vectors.rs b/zebra-state/src/service/non_finalized_state/tests/vectors.rs index 00159e4c9..6f908f080 100644 --- a/zebra-state/src/service/non_finalized_state/tests/vectors.rs +++ b/zebra-state/src/service/non_finalized_state/tests/vectors.rs @@ -134,8 +134,9 @@ fn ord_matches_work() -> Result<()> { fn best_chain_wins() -> Result<()> { let _init_guard = zebra_test::init(); - best_chain_wins_for_network(Network::Mainnet)?; - best_chain_wins_for_network(Network::Testnet)?; + for network in Network::iter() { + best_chain_wins_for_network(network)?; + } Ok(()) } @@ -172,8 +173,9 @@ fn best_chain_wins_for_network(network: Network) -> Result<()> { fn finalize_pops_from_best_chain() -> Result<()> { let _init_guard = zebra_test::init(); - finalize_pops_from_best_chain_for_network(Network::Mainnet)?; - finalize_pops_from_best_chain_for_network(Network::Testnet)?; + for network in Network::iter() { + finalize_pops_from_best_chain_for_network(network)?; + } Ok(()) } @@ -219,8 +221,9 @@ fn finalize_pops_from_best_chain_for_network(network: Network) -> Result<()> { fn commit_block_extending_best_chain_doesnt_drop_worst_chains() -> Result<()> { let _init_guard = zebra_test::init(); - commit_block_extending_best_chain_doesnt_drop_worst_chains_for_network(Network::Mainnet)?; - commit_block_extending_best_chain_doesnt_drop_worst_chains_for_network(Network::Testnet)?; + for network in Network::iter() { + commit_block_extending_best_chain_doesnt_drop_worst_chains_for_network(network)?; + } Ok(()) } @@ -264,10 +267,9 @@ fn commit_block_extending_best_chain_doesnt_drop_worst_chains_for_network( #[test] fn shorter_chain_can_be_best_chain() -> Result<()> { let _init_guard = zebra_test::init(); - - shorter_chain_can_be_best_chain_for_network(Network::Mainnet)?; - shorter_chain_can_be_best_chain_for_network(Network::Testnet)?; - + for network in Network::iter() { + shorter_chain_can_be_best_chain_for_network(network)?; + } Ok(()) } @@ -307,9 +309,9 @@ fn shorter_chain_can_be_best_chain_for_network(network: Network) -> Result<()> { #[test] fn longer_chain_with_more_work_wins() -> Result<()> { let _init_guard = zebra_test::init(); - - longer_chain_with_more_work_wins_for_network(Network::Mainnet)?; - longer_chain_with_more_work_wins_for_network(Network::Testnet)?; + for network in Network::iter() { + longer_chain_with_more_work_wins_for_network(network)?; + } Ok(()) } @@ -355,8 +357,9 @@ fn longer_chain_with_more_work_wins_for_network(network: Network) -> Result<()> fn equal_length_goes_to_more_work() -> Result<()> { let _init_guard = zebra_test::init(); - equal_length_goes_to_more_work_for_network(Network::Mainnet)?; - equal_length_goes_to_more_work_for_network(Network::Testnet)?; + for network in Network::iter() { + equal_length_goes_to_more_work_for_network(network)?; + } Ok(()) } @@ -394,8 +397,9 @@ fn equal_length_goes_to_more_work_for_network(network: Network) -> Result<()> { #[test] fn history_tree_is_updated() -> Result<()> { - history_tree_is_updated_for_network_upgrade(Network::Mainnet, NetworkUpgrade::Heartwood)?; - history_tree_is_updated_for_network_upgrade(Network::Testnet, NetworkUpgrade::Heartwood)?; + for network in Network::iter() { + history_tree_is_updated_for_network_upgrade(network, NetworkUpgrade::Heartwood)?; + } // TODO: we can't test other upgrades until we have a method for creating a FinalizedState // with a HistoryTree. Ok(()) @@ -497,8 +501,9 @@ fn history_tree_is_updated_for_network_upgrade( #[test] fn commitment_is_validated() { - commitment_is_validated_for_network_upgrade(Network::Mainnet, NetworkUpgrade::Heartwood); - commitment_is_validated_for_network_upgrade(Network::Testnet, NetworkUpgrade::Heartwood); + for network in Network::iter() { + commitment_is_validated_for_network_upgrade(network, NetworkUpgrade::Heartwood); + } // TODO: we can't test other upgrades until we have a method for creating a FinalizedState // with a HistoryTree. } diff --git a/zebra-state/tests/basic.rs b/zebra-state/tests/basic.rs index 49d3346f0..01b90a11e 100644 --- a/zebra-state/tests/basic.rs +++ b/zebra-state/tests/basic.rs @@ -63,7 +63,7 @@ async fn check_transcripts_mainnet() -> Result<(), Report> { #[tokio::test(flavor = "multi_thread")] async fn check_transcripts_testnet() -> Result<(), Report> { - check_transcripts(Network::Testnet).await + check_transcripts(Network::new_default_testnet()).await } #[spandoc::spandoc] diff --git a/zebrad/src/components/mempool/storage/tests/vectors.rs b/zebrad/src/components/mempool/storage/tests/vectors.rs index 48bb3cb8e..4dd128278 100644 --- a/zebrad/src/components/mempool/storage/tests/vectors.rs +++ b/zebrad/src/components/mempool/storage/tests/vectors.rs @@ -60,8 +60,9 @@ fn mempool_storage_basic() -> Result<()> { // Test multiple times to catch intermittent bugs since eviction is randomized for _ in 0..10 { - mempool_storage_basic_for_network(Network::Mainnet)?; - mempool_storage_basic_for_network(Network::Testnet)?; + for network in Network::iter() { + mempool_storage_basic_for_network(network)?; + } } Ok(()) @@ -236,10 +237,9 @@ fn mempool_storage_crud_same_effects_mainnet() { #[test] fn mempool_expired_basic() -> Result<()> { let _init_guard = zebra_test::init(); - - mempool_expired_basic_for_network(Network::Mainnet)?; - mempool_expired_basic_for_network(Network::Testnet)?; - + for network in Network::iter() { + mempool_expired_basic_for_network(network)?; + } Ok(()) } diff --git a/zebrad/tests/acceptance.rs b/zebrad/tests/acceptance.rs index 6ae68ce43..656ef9a21 100644 --- a/zebrad/tests/acceptance.rs +++ b/zebrad/tests/acceptance.rs @@ -1051,7 +1051,7 @@ fn sync_one_checkpoint_mainnet() -> Result<()> { fn sync_one_checkpoint_testnet() -> Result<()> { sync_until( TINY_CHECKPOINT_TEST_HEIGHT, - &Testnet, + &Network::new_default_testnet(), STOP_AT_HEIGHT_REGEX, TINY_CHECKPOINT_TIMEOUT, None, @@ -1271,7 +1271,7 @@ fn sync_to_mandatory_checkpoint_mainnet() -> Result<()> { #[cfg_attr(feature = "test_sync_to_mandatory_checkpoint_testnet", test)] fn sync_to_mandatory_checkpoint_testnet() -> Result<()> { let _init_guard = zebra_test::init(); - let network = Testnet; + let network = Network::new_default_testnet(); create_cached_database(network) } @@ -1297,7 +1297,7 @@ fn sync_past_mandatory_checkpoint_mainnet() -> Result<()> { #[cfg_attr(feature = "test_sync_past_mandatory_checkpoint_testnet", test)] fn sync_past_mandatory_checkpoint_testnet() -> Result<()> { let _init_guard = zebra_test::init(); - let network = Testnet; + let network = Network::new_default_testnet(); sync_past_mandatory_checkpoint(network) } @@ -1322,7 +1322,10 @@ fn full_sync_mainnet() -> Result<()> { #[ignore] fn full_sync_testnet() -> Result<()> { // TODO: add "ZEBRA" at the start of this env var, to avoid clashes - full_sync_test(Testnet, "FULL_SYNC_TESTNET_TIMEOUT_MINUTES") + full_sync_test( + Network::new_default_testnet(), + "FULL_SYNC_TESTNET_TIMEOUT_MINUTES", + ) } #[cfg(feature = "prometheus")] @@ -2529,14 +2532,14 @@ async fn generate_checkpoints_mainnet() -> Result<()> { #[ignore] #[cfg(feature = "zebra-checkpoints")] async fn generate_checkpoints_testnet() -> Result<()> { - common::checkpoints::run(Testnet).await + common::checkpoints::run(Network::new_default_testnet()).await } /// Check that new states are created with the current state format version, /// and that restarting `zebrad` doesn't change the format version. #[tokio::test] async fn new_state_format() -> Result<()> { - for network in [Mainnet, Testnet] { + for network in Network::iter() { state_format_test("new_state_format_test", &network, 2, None).await?; } @@ -2554,7 +2557,7 @@ async fn update_state_format() -> Result<()> { fake_version.minor = 0; fake_version.patch = 0; - for network in [Mainnet, Testnet] { + for network in Network::iter() { state_format_test("update_state_format_test", &network, 3, Some(&fake_version)).await?; } @@ -2571,7 +2574,7 @@ async fn downgrade_state_format() -> Result<()> { fake_version.minor = u16::MAX.into(); fake_version.patch = 0; - for network in [Mainnet, Testnet] { + for network in Network::iter() { state_format_test( "downgrade_state_format_test", &network, diff --git a/zebrad/tests/common/checkpoints.rs b/zebrad/tests/common/checkpoints.rs index f59dd8fb2..602525fd9 100644 --- a/zebrad/tests/common/checkpoints.rs +++ b/zebrad/tests/common/checkpoints.rs @@ -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_a_test_network() { let state_version_message = wait_for_state_version_message(&mut zebrad)?; // Before we write a cached state image, wait for a database upgrade. From 06cf9474cea1cfc7eae3124977e04aaab9746380 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 21 Mar 2024 21:16:46 -0400 Subject: [PATCH 04/51] Adds `NetworkKind` and uses it instead of `Network` in `HistoryTreeParts` and `transparent::Address` --- zebra-chain/src/parameters.rs | 2 +- zebra-chain/src/parameters/network.rs | 101 ++++++++++++------ zebra-chain/src/primitives/address.rs | 42 ++++---- .../src/primitives/zcash_primitives.rs | 19 +++- zebra-chain/src/transparent/address.rs | 46 ++++---- .../src/methods/get_block_template_rpcs.rs | 6 +- .../tests/snapshot/get_block_template_rpcs.rs | 5 +- zebra-rpc/src/methods/tests/vectors.rs | 8 +- .../finalized_state/disk_format/chain.rs | 64 ++++++----- .../disk_format/transparent.rs | 21 ++-- .../service/finalized_state/zebra_db/chain.rs | 50 +++++---- 11 files changed, 220 insertions(+), 144 deletions(-) diff --git a/zebra-chain/src/parameters.rs b/zebra-chain/src/parameters.rs index 545d132ca..1fea059cd 100644 --- a/zebra-chain/src/parameters.rs +++ b/zebra-chain/src/parameters.rs @@ -23,7 +23,7 @@ pub mod arbitrary; pub use error::*; pub use genesis::*; -pub use network::Network; +pub use network::{Network, NetworkKind}; pub use network_upgrade::*; pub use transaction::*; diff --git a/zebra-chain/src/parameters/network.rs b/zebra-chain/src/parameters/network.rs index a35719484..c608ca2ec 100644 --- a/zebra-chain/src/parameters/network.rs +++ b/zebra-chain/src/parameters/network.rs @@ -66,6 +66,17 @@ impl NetworkParameters { } } +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))] +/// An enum describing the kind of network, whether it's the production mainnet or a testnet. +pub enum NetworkKind { + /// The production mainnet. + Mainnet, + + /// A test network. + Testnet, +} + /// An enum describing the possible network choices. #[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Serialize)] #[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))] @@ -106,41 +117,50 @@ impl<'de> Deserialize<'de> for Network { } } -impl Network { +impl NetworkKind { /// 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] { - ::try_from(self) - // This prefix is the same for Testnet and Regtest in zcashd. - // TODO: Use the constants directly when implementing `Parameters` for `Network` (#8365) - .unwrap_or(ZcashPrimitivesNetwork::TestNetwork) - .b58_pubkey_address_prefix() + pub fn b58_pubkey_address_prefix(self) -> [u8; 2] { + ::from(self).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] { - ::try_from(self) - // This prefix is the same for Testnet and Regtest in zcashd. - // TODO: Use the constants directly when implementing `Parameters` for `Network` (#8365) - .unwrap_or(ZcashPrimitivesNetwork::TestNetwork) - .b58_script_address_prefix() + pub fn b58_script_address_prefix(self) -> [u8; 2] { + ::from(self).b58_script_address_prefix() } - /// Returns true if the maximum block time rule is active for `network` and `height`. - /// - /// Always returns true if `network` is the Mainnet. - /// If `network` is the Testnet, the `height` should be at least - /// TESTNET_MAX_TIME_START_HEIGHT to return true. - /// Returns false otherwise. - /// - /// Part of the consensus rules at - pub fn is_max_block_time_enforced(&self, height: block::Height) -> bool { + + /// Return the network name as defined in + /// [BIP70](https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#paymentdetailspaymentrequest) + pub fn bip70_network_name(&self) -> String { match self { - Network::Mainnet => true, - // TODO: Move `TESTNET_MAX_TIME_START_HEIGHT` to a field on NetworkParameters (#8364) - Network::Testnet(_params) => height >= super::TESTNET_MAX_TIME_START_HEIGHT, + Self::Mainnet => "main".to_string(), + Self::Testnet => "test".to_string(), } } + + /// Converts a [`zcash_address::Network`] to a [`NetworkKind`]. + pub fn from_zcash_address(network: zcash_address::Network) -> Self { + match network { + zcash_address::Network::Main => NetworkKind::Mainnet, + zcash_address::Network::Test | zcash_address::Network::Regtest => NetworkKind::Testnet, + } + } +} + +impl From for &'static str { + fn from(network: NetworkKind) -> &'static str { + match network { + NetworkKind::Mainnet => "MainnetKind", + NetworkKind::Testnet => "TestnetKind", + } + } +} + +impl fmt::Display for NetworkKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str((*self).into()) + } } impl From<&Network> for &'static str { @@ -178,17 +198,41 @@ impl Network { } } + /// Returns the [`NetworkKind`] for this network. + pub fn kind(&self) -> NetworkKind { + match self { + Network::Mainnet => NetworkKind::Mainnet, + Network::Testnet(_) => NetworkKind::Testnet, + } + } + /// Returns an iterator over [`Network`] variants. pub fn iter() -> impl Iterator { // TODO: Use default values of `Testnet` variant when adding fields for #7845. [Self::Mainnet, Self::new_default_testnet()].into_iter() } + /// Returns true if the maximum block time rule is active for `network` and `height`. + /// + /// Always returns true if `network` is the Mainnet. + /// If `network` is the Testnet, the `height` should be at least + /// TESTNET_MAX_TIME_START_HEIGHT to return true. + /// Returns false otherwise. + /// + /// Part of the consensus rules at + pub fn is_max_block_time_enforced(&self, height: block::Height) -> bool { + match self { + Network::Mainnet => true, + // TODO: Move `TESTNET_MAX_TIME_START_HEIGHT` to a field on NetworkParameters (#8364) + Network::Testnet(_params) => height >= super::TESTNET_MAX_TIME_START_HEIGHT, + } + } + /// Get the default port associated to this network. pub fn default_port(&self) -> u16 { match self { Network::Mainnet => 8233, - // TODO: Add a `default_port` field to `NetworkParameters` to return here. + // TODO: Add a `default_port` field to `NetworkParameters` to return here. (zcashd uses 18344 for Regtest) Network::Testnet(_params) => 18233, } } @@ -214,10 +258,7 @@ impl Network { /// Return the network name as defined in /// [BIP70](https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#paymentdetailspaymentrequest) pub fn bip70_network_name(&self) -> String { - match self { - Network::Mainnet => "main".to_string(), - Network::Testnet(_params) => "test".to_string(), - } + self.kind().bip70_network_name() } /// Return the lowercase network name. diff --git a/zebra-chain/src/primitives/address.rs b/zebra-chain/src/primitives/address.rs index 2e7890c29..1cf20c0d0 100644 --- a/zebra-chain/src/primitives/address.rs +++ b/zebra-chain/src/primitives/address.rs @@ -6,7 +6,7 @@ use zcash_address::unified::{self, Container}; use zcash_primitives::sapling; use crate::{ - parameters::{Network, UnsupportedNetwork}, + parameters::{Network, NetworkKind, UnsupportedNetwork}, transparent, BoxError, }; @@ -17,8 +17,8 @@ pub enum Address { /// Sapling address Sapling { - /// Address' network - network: Network, + /// Address' network kind + network: NetworkKind, /// Sapling address address: sapling::PaymentAddress, @@ -26,8 +26,8 @@ pub enum Address { /// Unified address Unified { - /// Address' network - network: Network, + /// Address' network kind + network: NetworkKind, /// Unified address unified_address: zcash_address::unified::Address, @@ -73,6 +73,15 @@ impl TryFrom<&Network> for zcash_address::Network { } } +impl From for zcash_address::Network { + fn from(network: NetworkKind) -> Self { + match network { + NetworkKind::Mainnet => zcash_address::Network::Main, + NetworkKind::Testnet => zcash_address::Network::Test, + } + } +} + impl zcash_address::TryFromAddress for Address { // TODO: crate::serialization::SerializationError type Error = BoxError; @@ -82,7 +91,7 @@ impl zcash_address::TryFromAddress for Address { data: [u8; 20], ) -> Result> { Ok(Self::Transparent(transparent::Address::from_pub_key_hash( - &network.try_into()?, + NetworkKind::from_zcash_address(network), data, ))) } @@ -92,7 +101,7 @@ impl zcash_address::TryFromAddress for Address { data: [u8; 20], ) -> Result> { Ok(Self::Transparent(transparent::Address::from_script_hash( - &network.try_into()?, + NetworkKind::from_zcash_address(network), data, ))) } @@ -101,7 +110,7 @@ impl zcash_address::TryFromAddress for Address { network: zcash_address::Network, data: [u8; 43], ) -> Result> { - let network = network.try_into()?; + let network = NetworkKind::from_zcash_address(network); sapling::PaymentAddress::from_bytes(&data) .map(|address| Self::Sapling { address, network }) .ok_or_else(|| BoxError::from("not a valid sapling address").into()) @@ -111,7 +120,7 @@ impl zcash_address::TryFromAddress for Address { network: zcash_address::Network, unified_address: zcash_address::unified::Address, ) -> Result> { - let network = &network.try_into()?; + let network = NetworkKind::from_zcash_address(network); let mut orchard = None; let mut sapling = None; let mut transparent = None; @@ -155,7 +164,7 @@ impl zcash_address::TryFromAddress for Address { } Ok(Self::Unified { - network: network.clone(), + network, unified_address, orchard, sapling, @@ -166,10 +175,10 @@ impl zcash_address::TryFromAddress for Address { impl Address { /// Returns the network for the address. - pub fn network(&self) -> Network { + pub fn network(&self) -> NetworkKind { match &self { Self::Transparent(address) => address.network(), - Self::Sapling { network, .. } | Self::Unified { network, .. } => network.clone(), + Self::Sapling { network, .. } | Self::Unified { network, .. } => *network, } } @@ -196,14 +205,7 @@ impl Address { Self::Transparent(address) => Some(address.to_string()), Self::Sapling { address, network } => { let data = address.to_bytes(); - let network = network - .try_into() - .map_err(|err| { - warn!(?err, "could not convert address network to zcash network") - }) - .ok()?; - - let address = ZcashAddress::from_sapling(network, data); + let address = ZcashAddress::from_sapling((*network).into(), data); Some(address.encode()) } Self::Unified { .. } => None, diff --git a/zebra-chain/src/primitives/zcash_primitives.rs b/zebra-chain/src/primitives/zcash_primitives.rs index af54a05a4..6a9a0fc47 100644 --- a/zebra-chain/src/primitives/zcash_primitives.rs +++ b/zebra-chain/src/primitives/zcash_primitives.rs @@ -7,7 +7,7 @@ use zcash_primitives::transaction as zp_tx; use crate::{ amount::{Amount, NonNegative}, - parameters::{Network, NetworkUpgrade, UnsupportedNetwork}, + parameters::{Network, NetworkKind, NetworkUpgrade, UnsupportedNetwork}, serialization::ZcashSerialize, transaction::{AuthDigest, HashType, SigHash, Transaction}, transparent::{self, Script}, @@ -328,11 +328,11 @@ pub(crate) fn transparent_output_address( match alt_addr { Some(zcash_primitives::legacy::TransparentAddress::PublicKey(pub_key_hash)) => Some( - transparent::Address::from_pub_key_hash(network, pub_key_hash), + transparent::Address::from_pub_key_hash(network.kind(), pub_key_hash), + ), + Some(zcash_primitives::legacy::TransparentAddress::Script(script_hash)) => Some( + transparent::Address::from_script_hash(network.kind(), script_hash), ), - Some(zcash_primitives::legacy::TransparentAddress::Script(script_hash)) => { - Some(transparent::Address::from_script_hash(network, script_hash)) - } None => None, } } @@ -356,6 +356,15 @@ impl TryFrom<&Network> for zcash_primitives::consensus::Network { } } +impl From for zcash_primitives::consensus::Network { + fn from(network: NetworkKind) -> Self { + match network { + NetworkKind::Mainnet => zcash_primitives::consensus::Network::MainNetwork, + NetworkKind::Testnet => zcash_primitives::consensus::Network::TestNetwork, + } + } +} + impl From for Network { fn from(network: zcash_primitives::consensus::Network) -> Self { match network { diff --git a/zebra-chain/src/transparent/address.rs b/zebra-chain/src/transparent/address.rs index 754ea6de8..3e90aa6d6 100644 --- a/zebra-chain/src/transparent/address.rs +++ b/zebra-chain/src/transparent/address.rs @@ -7,7 +7,7 @@ use secp256k1::PublicKey; use sha2::Sha256; use crate::{ - parameters::Network, + parameters::NetworkKind, serialization::{SerializationError, ZcashDeserialize, ZcashSerialize}, transparent::{opcodes::OpCode, Script}, }; @@ -37,7 +37,7 @@ pub enum Address { /// P2SH (Pay to Script Hash) addresses PayToScriptHash { /// Production, test, or other network - network: Network, + network: NetworkKind, /// 20 bytes specifying a script hash. script_hash: [u8; 20], }, @@ -45,7 +45,7 @@ pub enum Address { /// P2PKH (Pay to Public Key Hash) addresses PayToPublicKeyHash { /// Production, test, or other network - network: Network, + network: NetworkKind, /// 20 bytes specifying a public key hash, which is a RIPEMD-160 /// hash of a SHA-256 hash of a compressed ECDSA key encoding. pub_key_hash: [u8; 20], @@ -131,27 +131,25 @@ impl ZcashDeserialize for Address { match version_bytes { zcash_primitives::constants::mainnet::B58_SCRIPT_ADDRESS_PREFIX => { Ok(Address::PayToScriptHash { - network: Network::Mainnet, + network: NetworkKind::Mainnet, script_hash: hash_bytes, }) } zcash_primitives::constants::testnet::B58_SCRIPT_ADDRESS_PREFIX => { Ok(Address::PayToScriptHash { - // TODO: Replace `network` field on `Address` with the prefix and a method for checking if it's the mainnet prefix. - network: Network::new_default_testnet(), + network: NetworkKind::Testnet, script_hash: hash_bytes, }) } zcash_primitives::constants::mainnet::B58_PUBKEY_ADDRESS_PREFIX => { Ok(Address::PayToPublicKeyHash { - network: Network::Mainnet, + network: NetworkKind::Mainnet, pub_key_hash: hash_bytes, }) } zcash_primitives::constants::testnet::B58_PUBKEY_ADDRESS_PREFIX => { Ok(Address::PayToPublicKeyHash { - // TODO: Replace `network` field on `Address` with the prefix and a method for checking if it's the mainnet prefix. - network: Network::new_default_testnet(), + network: NetworkKind::Testnet, pub_key_hash: hash_bytes, }) } @@ -162,11 +160,11 @@ impl ZcashDeserialize for Address { trait ToAddressWithNetwork { /// Convert `self` to an `Address`, given the current `network`. - fn to_address(&self, network: Network) -> Address; + fn to_address(&self, network: NetworkKind) -> Address; } impl ToAddressWithNetwork for Script { - fn to_address(&self, network: Network) -> Address { + fn to_address(&self, network: NetworkKind) -> Address { Address::PayToScriptHash { network, script_hash: Address::hash_payload(self.as_raw_bytes()), @@ -175,7 +173,7 @@ impl ToAddressWithNetwork for Script { } impl ToAddressWithNetwork for PublicKey { - fn to_address(&self, network: Network) -> Address { + fn to_address(&self, network: NetworkKind) -> Address { Address::PayToPublicKeyHash { network, pub_key_hash: Address::hash_payload(&self.serialize()[..]), @@ -185,26 +183,26 @@ impl ToAddressWithNetwork for PublicKey { impl Address { /// Create an address for the given public key hash and network. - pub fn from_pub_key_hash(network: &Network, pub_key_hash: [u8; 20]) -> Self { + pub fn from_pub_key_hash(network: NetworkKind, pub_key_hash: [u8; 20]) -> Self { Self::PayToPublicKeyHash { - network: network.clone(), + network, pub_key_hash, } } /// Create an address for the given script hash and network. - pub fn from_script_hash(network: &Network, script_hash: [u8; 20]) -> Self { + pub fn from_script_hash(network: NetworkKind, script_hash: [u8; 20]) -> Self { Self::PayToScriptHash { - network: network.clone(), + network, script_hash, } } - /// Returns the network for this address. - pub fn network(&self) -> Network { + /// Returns the network kind for this address. + pub fn network(&self) -> NetworkKind { match self { - Address::PayToScriptHash { network, .. } => network.clone(), - Address::PayToPublicKeyHash { network, .. } => network.clone(), + Address::PayToScriptHash { network, .. } => *network, + Address::PayToPublicKeyHash { network, .. } => *network, } } @@ -285,7 +283,7 @@ mod tests { ]) .expect("A PublicKey from slice"); - let t_addr = pub_key.to_address(Network::Mainnet); + let t_addr = pub_key.to_address(NetworkKind::Mainnet); assert_eq!(format!("{t_addr}"), "t1bmMa1wJDFdbc2TiURQP5BbBz6jHjUBuHq"); } @@ -300,7 +298,7 @@ mod tests { ]) .expect("A PublicKey from slice"); - let t_addr = pub_key.to_address(Network::new_default_testnet()); + let t_addr = pub_key.to_address(NetworkKind::Testnet); assert_eq!(format!("{t_addr}"), "tmTc6trRhbv96kGfA99i7vrFwb5p7BVFwc3"); } @@ -311,7 +309,7 @@ mod tests { let script = Script::new(&[0u8; 20]); - let t_addr = script.to_address(Network::Mainnet); + let t_addr = script.to_address(NetworkKind::Mainnet); assert_eq!(format!("{t_addr}"), "t3Y5pHwfgHbS6pDjj1HLuMFxhFFip1fcJ6g"); } @@ -322,7 +320,7 @@ mod tests { let script = Script::new(&[0; 20]); - let t_addr = script.to_address(Network::new_default_testnet()); + let t_addr = script.to_address(NetworkKind::Testnet); assert_eq!(format!("{t_addr}"), "t2L51LcmpA43UMvKTw2Lwtt9LMjwyqU2V1P"); } diff --git a/zebra-rpc/src/methods/get_block_template_rpcs.rs b/zebra-rpc/src/methods/get_block_template_rpcs.rs index 9dba00aa0..6e424a591 100644 --- a/zebra-rpc/src/methods/get_block_template_rpcs.rs +++ b/zebra-rpc/src/methods/get_block_template_rpcs.rs @@ -451,7 +451,7 @@ where if let Some(miner_address) = mining_config.miner_address.clone() { assert_eq!( miner_address.network(), - network.clone(), + network.kind(), "incorrect miner address config: {miner_address} \ network.network {network} and miner address network {} must match", miner_address.network(), @@ -1084,7 +1084,7 @@ where return Ok(validate_address::Response::invalid()); } - if address.network() == network { + if address.network() == network.kind() { Ok(validate_address::Response { address: Some(raw_address), is_valid: true, @@ -1124,7 +1124,7 @@ where } }; - if address.network() == network { + if address.network() == network.kind() { Ok(z_validate_address::Response { is_valid: true, address: Some(raw_address), diff --git a/zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs b/zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs index 3c2da6573..cc7d0b908 100644 --- a/zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs +++ b/zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs @@ -94,7 +94,10 @@ pub async fn test_responses( #[allow(clippy::unnecessary_struct_initialization)] let mining_config = crate::config::mining::Config { - miner_address: Some(transparent::Address::from_script_hash(network, [0xad; 20])), + miner_address: Some(transparent::Address::from_script_hash( + network.kind(), + [0xad; 20], + )), extra_coinbase_data: None, debug_like_zcashd: true, // TODO: Use default field values when optional features are enabled in tests #8183 diff --git a/zebra-rpc/src/methods/tests/vectors.rs b/zebra-rpc/src/methods/tests/vectors.rs index d2828b085..4ff87a92c 100644 --- a/zebra-rpc/src/methods/tests/vectors.rs +++ b/zebra-rpc/src/methods/tests/vectors.rs @@ -1193,6 +1193,7 @@ async fn rpc_getblocktemplate_mining_address(use_p2pkh: bool) { amount::NonNegative, block::{Hash, MAX_BLOCK_BYTES, ZCASH_BLOCK_VERSION}, chain_sync_status::MockSyncStatus, + parameters::NetworkKind, serialization::DateTime32, transaction::{zip317, VerifiedUnminedTx}, work::difficulty::{CompactDifficulty, ExpandedDifficulty, U256}, @@ -1223,11 +1224,10 @@ async fn rpc_getblocktemplate_mining_address(use_p2pkh: bool) { let mut mock_sync_status = MockSyncStatus::default(); mock_sync_status.set_is_close_to_tip(true); + let network = NetworkKind::Mainnet; let miner_address = match use_p2pkh { - false => Some(transparent::Address::from_script_hash(&Mainnet, [0x7e; 20])), - true => Some(transparent::Address::from_pub_key_hash( - &Mainnet, [0x7e; 20], - )), + false => Some(transparent::Address::from_script_hash(network, [0x7e; 20])), + true => Some(transparent::Address::from_pub_key_hash(network, [0x7e; 20])), }; #[allow(clippy::unnecessary_struct_initialization)] diff --git a/zebra-state/src/service/finalized_state/disk_format/chain.rs b/zebra-state/src/service/finalized_state/disk_format/chain.rs index cf1cbe7dc..88010608d 100644 --- a/zebra-state/src/service/finalized_state/disk_format/chain.rs +++ b/zebra-state/src/service/finalized_state/disk_format/chain.rs @@ -10,8 +10,12 @@ use std::collections::BTreeMap; use bincode::Options; use zebra_chain::{ - amount::NonNegative, block::Height, history_tree::NonEmptyHistoryTree, parameters::Network, - primitives::zcash_history, value_balance::ValueBalance, + amount::NonNegative, + block::Height, + history_tree::{HistoryTreeError, NonEmptyHistoryTree}, + parameters::{Network, NetworkKind}, + primitives::zcash_history, + value_balance::ValueBalance, }; use crate::service::finalized_state::disk_format::{FromDisk, IntoDisk}; @@ -39,42 +43,54 @@ impl FromDisk for ValueBalance { // https://docs.rs/bincode/1.3.3/bincode/config/index.html#options-struct-vs-bincode-functions #[derive(serde::Serialize, serde::Deserialize)] -struct HistoryTreeParts { - network: Network, +pub struct HistoryTreeParts { + network: NetworkKind, size: u32, peaks: BTreeMap, current_height: Height, } -impl IntoDisk for NonEmptyHistoryTree { +impl HistoryTreeParts { + /// Converts [`HistoryTreeParts`] to a [`NonEmptyHistoryTree`]. + pub(crate) fn with_network( + self, + network: &Network, + ) -> Result { + assert_eq!( + self.network, + network.kind(), + "history tree network kind should match current network" + ); + + NonEmptyHistoryTree::from_cache(network, self.size, self.peaks, self.current_height) + } +} + +impl From<&NonEmptyHistoryTree> for HistoryTreeParts { + fn from(history_tree: &NonEmptyHistoryTree) -> Self { + HistoryTreeParts { + network: history_tree.network().kind(), + size: history_tree.size(), + peaks: history_tree.peaks().clone(), + current_height: history_tree.current_height(), + } + } +} + +impl IntoDisk for HistoryTreeParts { type Bytes = Vec; fn as_bytes(&self) -> Self::Bytes { - let data = HistoryTreeParts { - network: self.network(), - size: self.size(), - peaks: self.peaks().clone(), - current_height: self.current_height(), - }; bincode::DefaultOptions::new() - .serialize(&data) + .serialize(self) .expect("serialization to vec doesn't fail") } } -impl FromDisk for NonEmptyHistoryTree { +impl FromDisk for HistoryTreeParts { fn from_bytes(bytes: impl AsRef<[u8]>) -> Self { - let parts: HistoryTreeParts = bincode::DefaultOptions::new() + bincode::DefaultOptions::new() .deserialize(bytes.as_ref()) - .expect( - "deserialization format should match the serialization format used by IntoDisk", - ); - NonEmptyHistoryTree::from_cache( - &parts.network, - parts.size, - parts.peaks, - parts.current_height, - ) - .expect("deserialization format should match the serialization format used by IntoDisk") + .expect("deserialization format should match the serialization format used by IntoDisk") } } diff --git a/zebra-state/src/service/finalized_state/disk_format/transparent.rs b/zebra-state/src/service/finalized_state/disk_format/transparent.rs index c6f80a6d1..e543498e0 100644 --- a/zebra-state/src/service/finalized_state/disk_format/transparent.rs +++ b/zebra-state/src/service/finalized_state/disk_format/transparent.rs @@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize}; use zebra_chain::{ amount::{self, Amount, NonNegative}, block::Height, - parameters::Network::{self, *}, + parameters::NetworkKind, serialization::{ZcashDeserializeInto, ZcashSerialize}, transparent::{self, Address::*}, }; @@ -502,13 +502,10 @@ fn address_variant(address: &transparent::Address) -> u8 { // // (This probably doesn't matter, but it might help slightly with data compression.) match (address.network(), address) { - (Mainnet, PayToPublicKeyHash { .. }) => 0, - (Mainnet, PayToScriptHash { .. }) => 1, - (Testnet(params), PayToPublicKeyHash { .. }) if params.is_default_testnet() => 2, - (Testnet(params), PayToScriptHash { .. }) if params.is_default_testnet() => 3, - // TODO: Use 4 and 5 for `Regtest` (#7839) - (Testnet(_params), PayToPublicKeyHash { .. }) => 6, - (Testnet(_params), PayToScriptHash { .. }) => 7, + (NetworkKind::Mainnet, PayToPublicKeyHash { .. }) => 0, + (NetworkKind::Mainnet, PayToScriptHash { .. }) => 1, + (NetworkKind::Testnet, PayToPublicKeyHash { .. }) => 2, + (NetworkKind::Testnet, PayToScriptHash { .. }) => 3, } } @@ -532,16 +529,16 @@ impl FromDisk for transparent::Address { let hash_bytes = hash_bytes.try_into().unwrap(); let network = if address_variant < 2 { - Mainnet + NetworkKind::Mainnet } else { // TODO: Replace `network` field on `Address` with the prefix and a method for checking if it's the mainnet prefix. - Network::new_default_testnet() + NetworkKind::Testnet }; if address_variant % 2 == 0 { - transparent::Address::from_pub_key_hash(&network, hash_bytes) + transparent::Address::from_pub_key_hash(network, hash_bytes) } else { - transparent::Address::from_script_hash(&network, hash_bytes) + transparent::Address::from_script_hash(network, hash_bytes) } } } diff --git a/zebra-state/src/service/finalized_state/zebra_db/chain.rs b/zebra-state/src/service/finalized_state/zebra_db/chain.rs index 842fc9453..93c65322d 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/chain.rs +++ b/zebra-state/src/service/finalized_state/zebra_db/chain.rs @@ -18,17 +18,17 @@ use std::{ }; use zebra_chain::{ - amount::NonNegative, - block::Height, - history_tree::{HistoryTree, NonEmptyHistoryTree}, - transparent, + amount::NonNegative, block::Height, history_tree::HistoryTree, transparent, value_balance::ValueBalance, }; use crate::{ request::FinalizedBlock, service::finalized_state::{ - disk_db::DiskWriteBatch, disk_format::RawBytes, zebra_db::ZebraDb, TypedColumnFamily, + disk_db::DiskWriteBatch, + disk_format::{chain::HistoryTreeParts, RawBytes}, + zebra_db::ZebraDb, + TypedColumnFamily, }, BoxError, }; @@ -42,15 +42,15 @@ pub const HISTORY_TREE: &str = "history_tree"; /// /// This constant should be used so the compiler can detect incorrectly typed accesses to the /// column family. -pub type HistoryTreeCf<'cf> = TypedColumnFamily<'cf, (), NonEmptyHistoryTree>; +pub type HistoryTreePartsCf<'cf> = TypedColumnFamily<'cf, (), HistoryTreeParts>; /// The legacy (1.3.0 and earlier) type for reading history trees from the database. /// This type should not be used in new code. -pub type LegacyHistoryTreeCf<'cf> = TypedColumnFamily<'cf, Height, NonEmptyHistoryTree>; +pub type LegacyHistoryTreePartsCf<'cf> = TypedColumnFamily<'cf, Height, HistoryTreeParts>; /// A generic raw key type for reading history trees from the database, regardless of the database version. /// This type should not be used in new code. -pub type RawHistoryTreeCf<'cf> = TypedColumnFamily<'cf, RawBytes, NonEmptyHistoryTree>; +pub type RawHistoryTreePartsCf<'cf> = TypedColumnFamily<'cf, RawBytes, HistoryTreeParts>; /// The name of the chain value pools column family. /// @@ -67,22 +67,22 @@ impl ZebraDb { // Column family convenience methods /// Returns a typed handle to the `history_tree` column family. - pub(crate) fn history_tree_cf(&self) -> HistoryTreeCf { - HistoryTreeCf::new(&self.db, HISTORY_TREE) + pub(crate) fn history_tree_cf(&self) -> HistoryTreePartsCf { + HistoryTreePartsCf::new(&self.db, HISTORY_TREE) .expect("column family was created when database was created") } /// Returns a legacy typed handle to the `history_tree` column family. /// This should not be used in new code. - pub(crate) fn legacy_history_tree_cf(&self) -> LegacyHistoryTreeCf { - LegacyHistoryTreeCf::new(&self.db, HISTORY_TREE) + pub(crate) fn legacy_history_tree_cf(&self) -> LegacyHistoryTreePartsCf { + LegacyHistoryTreePartsCf::new(&self.db, HISTORY_TREE) .expect("column family was created when database was created") } /// Returns a generic raw key typed handle to the `history_tree` column family. /// This should not be used in new code. - pub(crate) fn raw_history_tree_cf(&self) -> RawHistoryTreeCf { - RawHistoryTreeCf::new(&self.db, HISTORY_TREE) + pub(crate) fn raw_history_tree_cf(&self) -> RawHistoryTreePartsCf { + RawHistoryTreePartsCf::new(&self.db, HISTORY_TREE) .expect("column family was created when database was created") } @@ -115,19 +115,24 @@ impl ZebraDb { // // So we use the empty key `()`. Since the key has a constant value, we will always read // the latest tree. - let mut history_tree = history_tree_cf.zs_get(&()); + let mut history_tree_parts = history_tree_cf.zs_get(&()); - if history_tree.is_none() { + if history_tree_parts.is_none() { let legacy_history_tree_cf = self.legacy_history_tree_cf(); // In Zebra 1.4.0 and later, we only update the history tip tree when it has changed (for every block after heartwood). // But we write with a `()` key, not a height key. // So we need to look for the most recent update height if the `()` key has never been written. - history_tree = legacy_history_tree_cf + history_tree_parts = legacy_history_tree_cf .zs_last_key_value() .map(|(_height_key, tree_value)| tree_value); } + let history_tree = history_tree_parts.map(|parts| { + parts.with_network(&self.db.network()).expect( + "deserialization format should match the serialization format used by IntoDisk", + ) + }); Arc::new(HistoryTree::from(history_tree)) } @@ -139,7 +144,12 @@ impl ZebraDb { raw_history_tree_cf .zs_forward_range_iter(..) - .map(|(raw_key, history_tree)| (raw_key, Arc::new(HistoryTree::from(history_tree)))) + .map(|(raw_key, history_tree_parts)| { + let history_tree = history_tree_parts.with_network(&self.db.network()).expect( + "deserialization format should match the serialization format used by IntoDisk", + ); + (raw_key, Arc::new(HistoryTree::from(history_tree))) + }) .collect() } @@ -164,9 +174,9 @@ impl DiskWriteBatch { pub fn update_history_tree(&mut self, db: &ZebraDb, tree: &HistoryTree) { let history_tree_cf = db.history_tree_cf().with_batch_for_writing(self); - if let Some(tree) = tree.as_ref().as_ref() { + if let Some(tree) = tree.as_ref() { // The batch is modified by this method and written by the caller. - let _ = history_tree_cf.zs_insert(&(), tree); + let _ = history_tree_cf.zs_insert(&(), &HistoryTreeParts::from(tree)); } } From 4900a20cadd2becc4ca960a04018aa35d5a41440 Mon Sep 17 00:00:00 2001 From: Arya Date: Fri, 22 Mar 2024 19:35:00 -0400 Subject: [PATCH 05/51] Adds a [network.testnet_parameters] section to the config, uses `NetworkKind` as zebra_network::Config::network field type, and converts 'Network' to `NetworkKind` before serializing --- zebra-chain/src/parameters.rs | 2 +- zebra-chain/src/parameters/network.rs | 54 +++++++--------- zebra-network/src/config.rs | 23 +++++-- zebrad/tests/common/configs/v1.7.0.toml | 84 +++++++++++++++++++++++++ 4 files changed, 127 insertions(+), 36 deletions(-) create mode 100644 zebrad/tests/common/configs/v1.7.0.toml diff --git a/zebra-chain/src/parameters.rs b/zebra-chain/src/parameters.rs index 1fea059cd..1bf5d5095 100644 --- a/zebra-chain/src/parameters.rs +++ b/zebra-chain/src/parameters.rs @@ -23,7 +23,7 @@ pub mod arbitrary; pub use error::*; pub use genesis::*; -pub use network::{Network, NetworkKind}; +pub use network::{Network, NetworkKind, NetworkParameters}; pub use network_upgrade::*; pub use transaction::*; diff --git a/zebra-chain/src/parameters/network.rs b/zebra-chain/src/parameters/network.rs index c608ca2ec..87acebec2 100644 --- a/zebra-chain/src/parameters/network.rs +++ b/zebra-chain/src/parameters/network.rs @@ -2,7 +2,6 @@ use std::{fmt, str::FromStr, sync::Arc}; -use serde::{Deserialize, Deserializer}; use thiserror::Error; use zcash_primitives::consensus::{Network as ZcashPrimitivesNetwork, Parameters as _}; @@ -66,20 +65,28 @@ impl NetworkParameters { } } -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] #[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))] /// An enum describing the kind of network, whether it's the production mainnet or a testnet. pub enum NetworkKind { /// The production mainnet. + #[default] Mainnet, /// A test network. Testnet, } +impl From for NetworkKind { + fn from(network: Network) -> Self { + network.kind() + } +} + /// An enum describing the possible network choices. #[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Serialize)] #[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))] +#[serde(into = "NetworkKind")] pub enum Network { /// The production mainnet. #[default] @@ -89,34 +96,6 @@ pub enum Network { Testnet(Arc), } -impl<'de> Deserialize<'de> for Network { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - #[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Serialize, Deserialize)] - #[serde(deny_unknown_fields)] - enum DNetwork { - #[default] - Mainnet, - #[serde(alias = "Testnet")] - DefaultTestnet, - Regtest, - #[serde(untagged)] - ConfiguredTestnet(NetworkParameters), - } - - let network = match DNetwork::deserialize(deserializer)? { - DNetwork::Mainnet => Network::Mainnet, - DNetwork::DefaultTestnet => Network::new_default_testnet(), - DNetwork::Regtest => unimplemented!("Regtest is not yet implemented"), - DNetwork::ConfiguredTestnet(params) => Network::Testnet(Arc::new(params)), - }; - - Ok(network) - } -} - impl NetworkKind { /// Returns the human-readable prefix for Base58Check-encoded transparent /// pay-to-public-key-hash payment addresses for the network. @@ -184,11 +163,16 @@ impl fmt::Display for Network { } impl Network { - /// Creates a new [`Network::Testnet`] with the default Testnet network parameters. + /// Creates a new [`Network::Testnet`] with the default Testnet [`NetworkParameters`]. pub fn new_default_testnet() -> Self { Self::Testnet(Arc::new(NetworkParameters::default())) } + /// Creates a new configured [`Network::Testnet`] with the provided Testnet [`NetworkParameters`]. + pub fn new_configured_testnet(params: NetworkParameters) -> Self { + Self::Testnet(Arc::new(params)) + } + /// Returns true if the network is the default Testnet, or false otherwise. pub fn is_default_testnet(&self) -> bool { if let Self::Testnet(params) = self { @@ -206,6 +190,14 @@ impl Network { } } + /// Returns the default [`Network`] for a [`NetworkKind`]. + pub fn from_kind(kind: NetworkKind) -> Self { + match kind { + NetworkKind::Mainnet => Self::Mainnet, + NetworkKind::Testnet => Self::new_default_testnet(), + } + } + /// Returns an iterator over [`Network`] variants. pub fn iter() -> impl Iterator { // TODO: Use default values of `Testnet` variant when adding fields for #7845. diff --git a/zebra-network/src/config.rs b/zebra-network/src/config.rs index 33d12eb02..28a3d55d3 100644 --- a/zebra-network/src/config.rs +++ b/zebra-network/src/config.rs @@ -17,7 +17,7 @@ use tracing::Span; use lazy_static::lazy_static; -use zebra_chain::parameters::Network; +use zebra_chain::parameters::{Network, NetworkKind, NetworkParameters}; use crate::{ constants::{ @@ -633,7 +633,8 @@ impl<'de> Deserialize<'de> for Config { #[serde(deny_unknown_fields, default)] struct DConfig { listen_addr: String, - network: Network, + network: NetworkKind, + testnet_parameters: Option, initial_mainnet_peers: IndexSet, initial_testnet_peers: IndexSet, cache_dir: CacheDir, @@ -648,7 +649,8 @@ impl<'de> Deserialize<'de> for Config { let config = Config::default(); Self { listen_addr: "0.0.0.0".to_string(), - network: config.network, + network: Default::default(), + testnet_parameters: None, initial_mainnet_peers: config.initial_mainnet_peers, initial_testnet_peers: config.initial_testnet_peers, cache_dir: config.cache_dir, @@ -661,7 +663,8 @@ impl<'de> Deserialize<'de> for Config { let DConfig { listen_addr, - network, + network: network_kind, + testnet_parameters, initial_mainnet_peers, initial_testnet_peers, cache_dir, @@ -670,6 +673,18 @@ impl<'de> Deserialize<'de> for Config { max_connections_per_ip, } = DConfig::deserialize(deserializer)?; + let network = if let Some(network_params) = testnet_parameters { + assert_eq!( + network_kind, + NetworkKind::Testnet, + "set network to 'Testnet' to use testnet parameters" + ); + + Network::new_configured_testnet(network_params) + } else { + Network::from_kind(network_kind) + }; + let listen_addr = match listen_addr.parse::() { Ok(socket) => Ok(socket), Err(_) => match listen_addr.parse::() { diff --git a/zebrad/tests/common/configs/v1.7.0.toml b/zebrad/tests/common/configs/v1.7.0.toml new file mode 100644 index 000000000..cbb3a0200 --- /dev/null +++ b/zebrad/tests/common/configs/v1.7.0.toml @@ -0,0 +1,84 @@ +# Default configuration for zebrad. +# +# This file can be used as a skeleton for custom configs. +# +# Unspecified fields use default values. Optional fields are Some(field) if the +# field is present and None if it is absent. +# +# This file is generated as an example using zebrad's current defaults. +# You should set only the config options you want to keep, and delete the rest. +# Only a subset of fields are present in the skeleton, since optional values +# whose default is None are omitted. +# +# The config format (including a complete list of sections and fields) is +# documented here: +# https://docs.rs/zebrad/latest/zebrad/config/struct.ZebradConfig.html +# +# zebrad attempts to load configs in the following order: +# +# 1. The -c flag on the command line, e.g., `zebrad -c myconfig.toml start`; +# 2. The file `zebrad.toml` in the users's preference directory (platform-dependent); +# 3. The default config. +# +# The user's preference directory and the default path to the `zebrad` config are platform dependent, +# based on `dirs::preference_dir`, see https://docs.rs/dirs/latest/dirs/fn.preference_dir.html : +# +# | Platform | Value | Example | +# | -------- | ------------------------------------- | ---------------------------------------------- | +# | Linux | `$XDG_CONFIG_HOME` or `$HOME/.config` | `/home/alice/.config/zebrad.toml` | +# | macOS | `$HOME/Library/Preferences` | `/Users/Alice/Library/Preferences/zebrad.toml` | +# | Windows | `{FOLDERID_RoamingAppData}` | `C:\Users\Alice\AppData\Local\zebrad.toml` | + +[consensus] +checkpoint_sync = true + +[mempool] +eviction_memory_time = "1h" +tx_cost_limit = 80000000 + +[metrics] + +[mining] +debug_like_zcashd = true + +[network] +cache_dir = true +crawl_new_peer_interval = "1m 1s" +initial_mainnet_peers = [ + "dnsseed.z.cash:8233", + "dnsseed.str4d.xyz:8233", + "mainnet.seeder.zfnd.org:8233", + "mainnet.is.yolo.money:8233", +] +initial_testnet_peers = [ + "dnsseed.testnet.z.cash:18233", + "testnet.seeder.zfnd.org:18233", + "testnet.is.yolo.money:18233", +] +listen_addr = "0.0.0.0:8233" +max_connections_per_ip = 1 +network = "Testnet" +peerset_initial_target_size = 25 + +[network.testnet_parameters] + +[rpc] +debug_force_finished_sync = false +parallel_cpu_threads = 0 + +[state] +cache_dir = "cache_dir" +delete_old_database = true +ephemeral = false + +[sync] +checkpoint_verify_concurrency_limit = 1000 +download_concurrency_limit = 50 +full_verify_concurrency_limit = 20 +parallel_cpu_threads = 0 + +[tracing] +buffer_limit = 128000 +force_use_color = false +use_color = true +use_journald = false \ No newline at end of file From cb8726b38aa5c770fe46c88018690e15381803ae Mon Sep 17 00:00:00 2001 From: Arya Date: Mon, 1 Apr 2024 18:12:55 -0400 Subject: [PATCH 06/51] Applies some suggestions from code review --- zebra-chain/src/parameters/error.rs | 15 ++----- zebra-chain/src/parameters/network.rs | 39 ++++++++----------- zebra-chain/src/primitives/address.rs | 39 +++++++++++++------ .../src/primitives/zcash_primitives.rs | 9 ++++- zebra-chain/src/transparent/address.rs | 4 -- zebra-chain/src/transparent/arbitrary.rs | 28 ++++++++++++- zebra-network/src/config.rs | 7 +++- .../disk_format/transparent.rs | 12 +++--- 8 files changed, 94 insertions(+), 59 deletions(-) diff --git a/zebra-chain/src/parameters/error.rs b/zebra-chain/src/parameters/error.rs index 0c9ce6306..12dc1851c 100644 --- a/zebra-chain/src/parameters/error.rs +++ b/zebra-chain/src/parameters/error.rs @@ -1,15 +1,8 @@ //! Error types for zebra-chain parameters -use std::fmt; +use thiserror::Error; /// An error indicating that Zebra network is not supported in type conversions. -#[derive(Debug)] -pub struct UnsupportedNetwork; - -impl fmt::Display for UnsupportedNetwork { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "unsupported Zcash network parameters") - } -} - -impl std::error::Error for UnsupportedNetwork {} +#[derive(Clone, Debug, Error)] +#[error("Unsupported Zcash network parameters: {0}")] +pub struct UnsupportedNetwork(pub String); diff --git a/zebra-chain/src/parameters/network.rs b/zebra-chain/src/parameters/network.rs index 87acebec2..6930ebb50 100644 --- a/zebra-chain/src/parameters/network.rs +++ b/zebra-chain/src/parameters/network.rs @@ -66,8 +66,10 @@ impl NetworkParameters { } #[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] -#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))] /// An enum describing the kind of network, whether it's the production mainnet or a testnet. +// Note: The order of these variants is important for correct bincode (de)serialization +// of history trees in the db format. +// TODO: Replace bincode (de)serialization of `HistoryTreeParts` in a db format upgrade? pub enum NetworkKind { /// The production mainnet. #[default] @@ -75,6 +77,9 @@ pub enum NetworkKind { /// A test network. Testnet, + + /// Regtest mode + Regtest, } impl From for NetworkKind { @@ -92,7 +97,8 @@ pub enum Network { #[default] Mainnet, - /// The oldest public test network. + /// A test network such as the default public testnet, + /// a configured testnet, or Regtest. Testnet(Arc), } @@ -112,26 +118,21 @@ impl NetworkKind { /// Return the network name as defined in /// [BIP70](https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#paymentdetailspaymentrequest) pub fn bip70_network_name(&self) -> String { - match self { - Self::Mainnet => "main".to_string(), - Self::Testnet => "test".to_string(), - } - } - - /// Converts a [`zcash_address::Network`] to a [`NetworkKind`]. - pub fn from_zcash_address(network: zcash_address::Network) -> Self { - match network { - zcash_address::Network::Main => NetworkKind::Mainnet, - zcash_address::Network::Test | zcash_address::Network::Regtest => NetworkKind::Testnet, + if *self == Self::Mainnet { + "main".to_string() + } else { + "test".to_string() } } } impl From for &'static str { fn from(network: NetworkKind) -> &'static str { + // These should be different from the `Display` impl for `Network` match network { NetworkKind::Mainnet => "MainnetKind", NetworkKind::Testnet => "TestnetKind", + NetworkKind::Regtest => "RegtestKind", } } } @@ -148,8 +149,7 @@ impl From<&Network> for &'static str { Network::Mainnet => "Mainnet", // TODO: // - Add a `name` field to use here instead of checking `is_default_testnet()` - // - Find out what zcashd calls the regtest cache dir for the `Network::new_regtest()` method, or - // if it always uses an ephemeral db, and do the same for Regtest in Zebra (#8327). + // - zcashd calls the Regtest cache dir 'regtest' (#8327). Network::Testnet(params) if params.is_default_testnet() => "Testnet", Network::Testnet(_params) => "UnknownTestnet", } @@ -186,18 +186,11 @@ impl Network { pub fn kind(&self) -> NetworkKind { match self { Network::Mainnet => NetworkKind::Mainnet, + // TODO: Return `NetworkKind::Regtest` if the parameters match the default Regtest params Network::Testnet(_) => NetworkKind::Testnet, } } - /// Returns the default [`Network`] for a [`NetworkKind`]. - pub fn from_kind(kind: NetworkKind) -> Self { - match kind { - NetworkKind::Mainnet => Self::Mainnet, - NetworkKind::Testnet => Self::new_default_testnet(), - } - } - /// Returns an iterator over [`Network`] variants. pub fn iter() -> impl Iterator { // TODO: Use default values of `Testnet` variant when adding fields for #7845. diff --git a/zebra-chain/src/primitives/address.rs b/zebra-chain/src/primitives/address.rs index 1cf20c0d0..0fe166577 100644 --- a/zebra-chain/src/primitives/address.rs +++ b/zebra-chain/src/primitives/address.rs @@ -68,16 +68,9 @@ impl TryFrom<&Network> for zcash_address::Network { // TODO: If the network parameters match `Regtest` parameters, convert to // `zcash_address::Network::Regtest instead of returning `UnsupportedAddress` error. // (#7119, #7839) - Network::Testnet(_params) => Err(UnsupportedNetwork), - } - } -} - -impl From for zcash_address::Network { - fn from(network: NetworkKind) -> Self { - match network { - NetworkKind::Mainnet => zcash_address::Network::Main, - NetworkKind::Testnet => zcash_address::Network::Test, + Network::Testnet(_params) => Err(UnsupportedNetwork( + "could not convert configured testnet to zcash_address::Network".to_string(), + )), } } } @@ -205,10 +198,34 @@ 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.to_zcash_address(), data); Some(address.encode()) } Self::Unified { .. } => None, } } } + +impl NetworkKind { + /// Converts a [`zcash_address::Network`] to a [`NetworkKind`]. + /// + /// This method is meant to be used for decoding Zcash addresses in zebra-rpc methods. + fn from_zcash_address(network: zcash_address::Network) -> Self { + match network { + zcash_address::Network::Main => NetworkKind::Mainnet, + zcash_address::Network::Test => NetworkKind::Testnet, + zcash_address::Network::Regtest => NetworkKind::Regtest, + } + } + + /// Converts a [`zcash_address::Network`] to a [`NetworkKind`]. + /// + /// This method is meant to be used for encoding Zcash addresses in zebra-rpc methods. + fn to_zcash_address(self) -> zcash_address::Network { + match self { + NetworkKind::Mainnet => zcash_address::Network::Main, + NetworkKind::Testnet => zcash_address::Network::Test, + NetworkKind::Regtest => zcash_address::Network::Regtest, + } + } +} diff --git a/zebra-chain/src/primitives/zcash_primitives.rs b/zebra-chain/src/primitives/zcash_primitives.rs index 6a9a0fc47..c00d056e2 100644 --- a/zebra-chain/src/primitives/zcash_primitives.rs +++ b/zebra-chain/src/primitives/zcash_primitives.rs @@ -351,7 +351,10 @@ impl TryFrom<&Network> for zcash_primitives::consensus::Network { Network::Testnet(_params) if network.is_default_testnet() => { Ok(zcash_primitives::consensus::Network::TestNetwork) } - Network::Testnet(_params) => Err(UnsupportedNetwork), + Network::Testnet(_params) => Err(UnsupportedNetwork( + "could not convert configured testnet to zcash_primitives::consensus::Network" + .to_string(), + )), } } } @@ -360,7 +363,9 @@ impl From for zcash_primitives::consensus::Network { fn from(network: NetworkKind) -> Self { match network { NetworkKind::Mainnet => zcash_primitives::consensus::Network::MainNetwork, - NetworkKind::Testnet => zcash_primitives::consensus::Network::TestNetwork, + NetworkKind::Testnet | NetworkKind::Regtest => { + zcash_primitives::consensus::Network::TestNetwork + } } } } diff --git a/zebra-chain/src/transparent/address.rs b/zebra-chain/src/transparent/address.rs index 3e90aa6d6..3e2f896d3 100644 --- a/zebra-chain/src/transparent/address.rs +++ b/zebra-chain/src/transparent/address.rs @@ -29,10 +29,6 @@ use proptest::prelude::*; #[derive( Clone, Eq, PartialEq, Hash, serde_with::SerializeDisplay, serde_with::DeserializeFromStr, )] -#[cfg_attr( - any(test, feature = "proptest-impl"), - derive(proptest_derive::Arbitrary) -)] pub enum Address { /// P2SH (Pay to Script Hash) addresses PayToScriptHash { diff --git a/zebra-chain/src/transparent/arbitrary.rs b/zebra-chain/src/transparent/arbitrary.rs index f7678bdc8..d275b8e9d 100644 --- a/zebra-chain/src/transparent/arbitrary.rs +++ b/zebra-chain/src/transparent/arbitrary.rs @@ -1,8 +1,8 @@ use proptest::{arbitrary::any, collection::vec, prelude::*}; -use crate::{block, LedgerState}; +use crate::{block, parameters::NetworkKind, LedgerState}; -use super::{CoinbaseData, Input, OutPoint, Script, GENESIS_COINBASE_DATA}; +use super::{Address, CoinbaseData, Input, OutPoint, Script, GENESIS_COINBASE_DATA}; impl Input { /// Construct a strategy for creating valid-ish vecs of Inputs. @@ -46,3 +46,27 @@ impl Arbitrary for Input { type Strategy = BoxedStrategy; } + +impl Arbitrary for Address { + type Parameters = (); + + fn arbitrary_with(_args: ()) -> Self::Strategy { + any::<(bool, bool, [u8; 20])>() + .prop_map(|(is_mainnet, is_p2pkh, hash_bytes)| { + let network = if is_mainnet { + NetworkKind::Mainnet + } else { + NetworkKind::Testnet + }; + + if is_p2pkh { + Address::from_pub_key_hash(network, hash_bytes) + } else { + Address::from_script_hash(network, hash_bytes) + } + }) + .boxed() + } + + type Strategy = BoxedStrategy; +} diff --git a/zebra-network/src/config.rs b/zebra-network/src/config.rs index 28a3d55d3..1252a2160 100644 --- a/zebra-network/src/config.rs +++ b/zebra-network/src/config.rs @@ -682,7 +682,12 @@ impl<'de> Deserialize<'de> for Config { Network::new_configured_testnet(network_params) } else { - Network::from_kind(network_kind) + // Convert to default `Network` for a `NetworkKind` if there are no testnet parameters. + match network_kind { + NetworkKind::Mainnet => Network::Mainnet, + NetworkKind::Testnet => Network::new_default_testnet(), + NetworkKind::Regtest => unimplemented!("Regtest is not yet implemented in Zebra"), + } }; let listen_addr = match listen_addr.parse::() { diff --git a/zebra-state/src/service/finalized_state/disk_format/transparent.rs b/zebra-state/src/service/finalized_state/disk_format/transparent.rs index e543498e0..bcf70ff23 100644 --- a/zebra-state/src/service/finalized_state/disk_format/transparent.rs +++ b/zebra-state/src/service/finalized_state/disk_format/transparent.rs @@ -498,14 +498,17 @@ impl AddressTransaction { /// Returns a byte representing the [`transparent::Address`] variant. fn address_variant(address: &transparent::Address) -> u8 { + use NetworkKind::*; // Return smaller values for more common variants. // // (This probably doesn't matter, but it might help slightly with data compression.) match (address.network(), address) { - (NetworkKind::Mainnet, PayToPublicKeyHash { .. }) => 0, - (NetworkKind::Mainnet, PayToScriptHash { .. }) => 1, - (NetworkKind::Testnet, PayToPublicKeyHash { .. }) => 2, - (NetworkKind::Testnet, PayToScriptHash { .. }) => 3, + (Mainnet, PayToPublicKeyHash { .. }) => 0, + (Mainnet, PayToScriptHash { .. }) => 1, + // There's no way to distinguish between Regtest and Testnet for encoded transparent addresses, + // so the network kind should always be `Mainnet` or `Testnet`. + (Testnet | Regtest, PayToPublicKeyHash { .. }) => 2, + (Testnet | Regtest, PayToScriptHash { .. }) => 3, } } @@ -531,7 +534,6 @@ impl FromDisk for transparent::Address { let network = if address_variant < 2 { NetworkKind::Mainnet } else { - // TODO: Replace `network` field on `Address` with the prefix and a method for checking if it's the mainnet prefix. NetworkKind::Testnet }; From d5a807a508d5bebabe0f4fca3fa0d4226624b81e Mon Sep 17 00:00:00 2001 From: Arya Date: Mon, 1 Apr 2024 20:17:33 -0400 Subject: [PATCH 07/51] Applies suggestions from code review --- zebra-chain/src/parameters/network.rs | 3 ++- zebra-chain/src/primitives/address.rs | 1 + .../src/primitives/zcash_primitives.rs | 22 +++++++------------ 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/zebra-chain/src/parameters/network.rs b/zebra-chain/src/parameters/network.rs index 6930ebb50..9d7cd3822 100644 --- a/zebra-chain/src/parameters/network.rs +++ b/zebra-chain/src/parameters/network.rs @@ -78,7 +78,8 @@ pub enum NetworkKind { /// A test network. Testnet, - /// Regtest mode + /// Regtest mode, not yet implemented + // TODO: Add `new_regtest()` and `is_regtest` methods on `Network`. Regtest, } diff --git a/zebra-chain/src/primitives/address.rs b/zebra-chain/src/primitives/address.rs index 0fe166577..54f584243 100644 --- a/zebra-chain/src/primitives/address.rs +++ b/zebra-chain/src/primitives/address.rs @@ -51,6 +51,7 @@ impl TryFrom for Network { match network { zcash_address::Network::Main => Ok(Network::Mainnet), zcash_address::Network::Test => Ok(Network::new_default_testnet()), + // TODO: Add conversion to regtest (#7839) zcash_address::Network::Regtest => Err("unsupported Zcash network parameters".into()), } } diff --git a/zebra-chain/src/primitives/zcash_primitives.rs b/zebra-chain/src/primitives/zcash_primitives.rs index c00d056e2..d786389c3 100644 --- a/zebra-chain/src/primitives/zcash_primitives.rs +++ b/zebra-chain/src/primitives/zcash_primitives.rs @@ -343,11 +343,14 @@ impl TryFrom<&Network> for zcash_primitives::consensus::Network { fn try_from(network: &Network) -> Result { match network { Network::Mainnet => Ok(zcash_primitives::consensus::Network::MainNetwork), - // Note: There are differences between the `TestNetwork` parameters and those returned by - // `CRegTestParams()` in zcashd, so this function can't return `TestNetwork` unless - // Zebra is using the default public Testnet. - // TODO: Try to remove this conversion, if possible, by implementing `zcash_primitives::consensus::Parameters` - // on `Network` (#8365). + // # Correctness: + // + // There are differences between the `TestNetwork` parameters and those returned by + // `CRegTestParams()` in zcashd, so this function can't return `TestNetwork` unless + // Zebra is using the default public Testnet. + // + // TODO: Remove this conversion by implementing `zcash_primitives::consensus::Parameters` + // for `Network` (#8365). Network::Testnet(_params) if network.is_default_testnet() => { Ok(zcash_primitives::consensus::Network::TestNetwork) } @@ -369,12 +372,3 @@ impl From for zcash_primitives::consensus::Network { } } } - -impl From 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::new_default_testnet(), - } - } -} From d108ec6355c36967eea30923a9cde566d190e073 Mon Sep 17 00:00:00 2001 From: Arya Date: Mon, 1 Apr 2024 20:23:55 -0400 Subject: [PATCH 08/51] returns b58 prefix constants directly to remove From impl for zcash_primitives::consensus::Network --- zebra-chain/src/parameters/network.rs | 12 +++++++++--- zebra-chain/src/primitives/zcash_primitives.rs | 13 +------------ 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/zebra-chain/src/parameters/network.rs b/zebra-chain/src/parameters/network.rs index 9d7cd3822..e74177eef 100644 --- a/zebra-chain/src/parameters/network.rs +++ b/zebra-chain/src/parameters/network.rs @@ -4,7 +4,7 @@ use std::{fmt, str::FromStr, sync::Arc}; use thiserror::Error; -use zcash_primitives::consensus::{Network as ZcashPrimitivesNetwork, Parameters as _}; +use zcash_primitives::constants; use crate::{ block::{self, Height, HeightDiff}, @@ -107,13 +107,19 @@ impl NetworkKind { /// 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] { - ::from(self).b58_pubkey_address_prefix() + match self { + Self::Mainnet => constants::mainnet::B58_PUBKEY_ADDRESS_PREFIX, + Self::Testnet | Self::Regtest => constants::testnet::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] { - ::from(self).b58_script_address_prefix() + match self { + Self::Mainnet => constants::mainnet::B58_SCRIPT_ADDRESS_PREFIX, + Self::Testnet | Self::Regtest => constants::testnet::B58_SCRIPT_ADDRESS_PREFIX, + } } /// Return the network name as defined in diff --git a/zebra-chain/src/primitives/zcash_primitives.rs b/zebra-chain/src/primitives/zcash_primitives.rs index d786389c3..389deeb89 100644 --- a/zebra-chain/src/primitives/zcash_primitives.rs +++ b/zebra-chain/src/primitives/zcash_primitives.rs @@ -7,7 +7,7 @@ use zcash_primitives::transaction as zp_tx; use crate::{ amount::{Amount, NonNegative}, - parameters::{Network, NetworkKind, NetworkUpgrade, UnsupportedNetwork}, + parameters::{Network, NetworkUpgrade, UnsupportedNetwork}, serialization::ZcashSerialize, transaction::{AuthDigest, HashType, SigHash, Transaction}, transparent::{self, Script}, @@ -361,14 +361,3 @@ impl TryFrom<&Network> for zcash_primitives::consensus::Network { } } } - -impl From for zcash_primitives::consensus::Network { - fn from(network: NetworkKind) -> Self { - match network { - NetworkKind::Mainnet => zcash_primitives::consensus::Network::MainNetwork, - NetworkKind::Testnet | NetworkKind::Regtest => { - zcash_primitives::consensus::Network::TestNetwork - } - } - } -} From 574b1f9487f851854005f3b304acc157ee853e22 Mon Sep 17 00:00:00 2001 From: Arya Date: Mon, 1 Apr 2024 21:03:06 -0400 Subject: [PATCH 09/51] Applies more suggestions from code review. --- zebra-chain/src/history_tree.rs | 4 ++-- .../src/block/subsidy/funding_streams.rs | 8 +++----- .../block/subsidy/funding_streams/tests.rs | 6 ++---- zebra-consensus/src/checkpoint/list.rs | 6 +++--- zebra-consensus/src/parameters/subsidy.rs | 15 ++++++++------- zebra-network/src/config.rs | 19 ++++++++----------- zebra-network/src/constants.rs | 7 ++++--- zebra-network/src/protocol/external/types.rs | 2 +- .../zebra_db/block/tests/snapshot.rs | 2 +- 9 files changed, 32 insertions(+), 37 deletions(-) diff --git a/zebra-chain/src/history_tree.rs b/zebra-chain/src/history_tree.rs index 8666ba49e..ca19c5892 100644 --- a/zebra-chain/src/history_tree.rs +++ b/zebra-chain/src/history_tree.rs @@ -372,8 +372,8 @@ impl NonEmptyHistoryTree { } /// Return the network where this tree is used. - pub fn network(&self) -> Network { - self.network.clone() + pub fn network(&self) -> &Network { + &self.network } } diff --git a/zebra-consensus/src/block/subsidy/funding_streams.rs b/zebra-consensus/src/block/subsidy/funding_streams.rs index ad43b6156..c3389782b 100644 --- a/zebra-consensus/src/block/subsidy/funding_streams.rs +++ b/zebra-consensus/src/block/subsidy/funding_streams.rs @@ -29,9 +29,7 @@ pub fn funding_stream_values( let mut results = HashMap::new(); if height >= canopy_height { - let range = FUNDING_STREAM_HEIGHT_RANGES - .get(&network.bip70_network_name()) - .unwrap(); + let range = FUNDING_STREAM_HEIGHT_RANGES.get(&network.kind()).unwrap(); if range.contains(&height) { let block_subsidy = block_subsidy(height, network)?; for (&receiver, &numerator) in FUNDING_STREAM_RECEIVER_NUMERATORS.iter() { @@ -86,7 +84,7 @@ fn funding_stream_address_index(height: Height, network: &Network) -> usize { .expect("no overflow should happen in this sum") .checked_sub(funding_stream_address_period( FUNDING_STREAM_HEIGHT_RANGES - .get(&network.bip70_network_name()) + .get(&network.kind()) .unwrap() .start, network, @@ -110,7 +108,7 @@ pub fn funding_stream_address( ) -> transparent::Address { let index = funding_stream_address_index(height, network); let address = &FUNDING_STREAM_ADDRESSES - .get(&network.bip70_network_name()) + .get(&network.kind()) .expect("there is always another hash map as value for a given valid network") .get(&receiver) .expect("in the inner hash map there is always a vector of strings with addresses")[index]; diff --git a/zebra-consensus/src/block/subsidy/funding_streams/tests.rs b/zebra-consensus/src/block/subsidy/funding_streams/tests.rs index 0527c6b48..893fcc826 100644 --- a/zebra-consensus/src/block/subsidy/funding_streams/tests.rs +++ b/zebra-consensus/src/block/subsidy/funding_streams/tests.rs @@ -44,9 +44,7 @@ fn test_funding_stream_values() -> Result<(), Report> { ); // funding stream period is ending - let range = FUNDING_STREAM_HEIGHT_RANGES - .get(&network.bip70_network_name()) - .unwrap(); + let range = FUNDING_STREAM_HEIGHT_RANGES.get(&network.kind()).unwrap(); let end = range.end; let last = end - 1; @@ -70,7 +68,7 @@ fn test_funding_stream_addresses() -> Result<(), Report> { let address = transparent::Address::from_str(address).expect("address should deserialize"); assert_eq!( - &address.network().bip70_network_name(), + &address.network(), network, "incorrect network for {receiver:?} funding stream address constant: {address}", ); diff --git a/zebra-consensus/src/checkpoint/list.rs b/zebra-consensus/src/checkpoint/list.rs index 7b2a5ca0c..4b8cd2436 100644 --- a/zebra-consensus/src/checkpoint/list.rs +++ b/zebra-consensus/src/checkpoint/list.rs @@ -70,8 +70,7 @@ impl ParameterCheckpoint for Network { // parse calls CheckpointList::from_list // TODO: // - Add a `genesis_hash` field to `NetworkParameters` and return it here (#8366) - // - Consider adding a `CUSTOM_TESTNET_CHECKPOINTS` constant to enable building with another checkpoints list - // when using a configured testnet? + // - Try to disable checkpoints entirely for regtest and custom testnets let checkpoint_list: CheckpointList = match self { Network::Mainnet => MAINNET_CHECKPOINTS .parse() @@ -149,7 +148,8 @@ impl CheckpointList { // Check that the list starts with the correct genesis block match checkpoints.iter().next() { - // TODO: Move this check to `::checkpoint_list(&network)` method above (#8366), + // TODO: If required (we may not need checkpoints at all in Regtest or custom testnets): + // move this check to `::checkpoint_list(&network)` method above (#8366), // See Some((block::Height(0), hash)) if (hash == &Network::Mainnet.genesis_hash() diff --git a/zebra-consensus/src/parameters/subsidy.rs b/zebra-consensus/src/parameters/subsidy.rs index d8ac576f6..15fafd5ec 100644 --- a/zebra-consensus/src/parameters/subsidy.rs +++ b/zebra-consensus/src/parameters/subsidy.rs @@ -7,7 +7,7 @@ use lazy_static::lazy_static; use zebra_chain::{ amount::COIN, block::{Height, HeightDiff}, - parameters::{Network, NetworkUpgrade}, + parameters::{Network, NetworkKind, NetworkUpgrade}, }; /// An initial period from Genesis to this Height where the block subsidy is gradually incremented. [What is slow-start mining][slow-mining] @@ -105,16 +105,17 @@ lazy_static! { /// /// [7.10.1]: https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams // TODO: Move the value here to a field on `NetworkParameters` (#8367) - pub static ref FUNDING_STREAM_HEIGHT_RANGES: HashMap> = { + pub static ref FUNDING_STREAM_HEIGHT_RANGES: HashMap> = { let mut hash_map = HashMap::new(); - hash_map.insert(Network::Mainnet.bip70_network_name(), Height(1_046_400)..Height(2_726_400)); - hash_map.insert(Network::new_default_testnet().bip70_network_name(), Height(1_028_500)..Height(2_796_000)); + hash_map.insert(NetworkKind::Mainnet, Height(1_046_400)..Height(2_726_400)); + hash_map.insert(NetworkKind::Testnet, Height(1_028_500)..Height(2_796_000)); hash_map }; /// Convenient storage for all addresses, for all receivers and networks // TODO: Move the value here to a field on `NetworkParameters` (#8367) - pub static ref FUNDING_STREAM_ADDRESSES: HashMap>> = { + // There are no funding stream addresses on Regtest in zcashd, zebrad should do the same for compatibility. + pub static ref FUNDING_STREAM_ADDRESSES: HashMap>> = { let mut addresses_by_network = HashMap::with_capacity(2); // Mainnet addresses @@ -122,14 +123,14 @@ lazy_static! { mainnet_addresses.insert(FundingStreamReceiver::Ecc, FUNDING_STREAM_ECC_ADDRESSES_MAINNET.iter().map(|a| a.to_string()).collect()); mainnet_addresses.insert(FundingStreamReceiver::ZcashFoundation, FUNDING_STREAM_ZF_ADDRESSES_MAINNET.iter().map(|a| a.to_string()).collect()); mainnet_addresses.insert(FundingStreamReceiver::MajorGrants, FUNDING_STREAM_MG_ADDRESSES_MAINNET.iter().map(|a| a.to_string()).collect()); - addresses_by_network.insert(Network::Mainnet.bip70_network_name(), mainnet_addresses); + addresses_by_network.insert(NetworkKind::Mainnet, mainnet_addresses); // Testnet addresses let mut testnet_addresses = HashMap::with_capacity(3); 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::new_default_testnet().bip70_network_name(), testnet_addresses); + addresses_by_network.insert(NetworkKind::Testnet, testnet_addresses); addresses_by_network }; diff --git a/zebra-network/src/config.rs b/zebra-network/src/config.rs index 1252a2160..090fe35aa 100644 --- a/zebra-network/src/config.rs +++ b/zebra-network/src/config.rs @@ -15,8 +15,6 @@ use tempfile::NamedTempFile; use tokio::{fs, io::AsyncWriteExt}; use tracing::Span; -use lazy_static::lazy_static; - use zebra_chain::parameters::{Network, NetworkKind, NetworkParameters}; use crate::{ @@ -178,10 +176,6 @@ pub struct Config { pub max_connections_per_ip: usize, } -lazy_static! { - static ref EMPTY_INITIAL_REGTEST_PEERS: IndexSet = IndexSet::new(); -} - impl Config { /// The maximum number of outbound connections that Zebra will open at the same time. /// When this limit is reached, Zebra stops opening outbound connections. @@ -228,14 +222,16 @@ impl Config { } /// Returns the initial seed peer hostnames for the configured network. - pub fn initial_peer_hostnames(&self) -> &IndexSet { + pub fn initial_peer_hostnames(&self) -> IndexSet { match &self.network { - Network::Mainnet => &self.initial_mainnet_peers, - Network::Testnet(params) if params.is_default_testnet() => &self.initial_testnet_peers, + Network::Mainnet => self.initial_mainnet_peers.clone(), + Network::Testnet(params) if params.is_default_testnet() => { + self.initial_testnet_peers.clone() + } // TODO: Check if the network is an incompatible custom testnet (_not_ Regtest), then panic if `initial_testnet_peers` // contains any of the default testnet peers, or return `initial_testnet_peers` otherwise. See: // - Network::Testnet(_params) => &EMPTY_INITIAL_REGTEST_PEERS, + Network::Testnet(_params) => IndexSet::new(), } } @@ -674,10 +670,11 @@ impl<'de> Deserialize<'de> for Config { } = DConfig::deserialize(deserializer)?; let network = if let Some(network_params) = testnet_parameters { + // TODO: Panic here if the initial testnet peers are the default initial testnet peers. assert_eq!( network_kind, NetworkKind::Testnet, - "set network to 'Testnet' to use testnet parameters" + "set network to 'Testnet' to use configured testnet parameters" ); Network::new_configured_testnet(network_params) diff --git a/zebra-network/src/constants.rs b/zebra-network/src/constants.rs index c8ecc02fe..91b390949 100644 --- a/zebra-network/src/constants.rs +++ b/zebra-network/src/constants.rs @@ -16,6 +16,7 @@ use crate::protocol::external::types::*; use zebra_chain::{ parameters::{ Network::{self, *}, + NetworkKind, NetworkUpgrade::*, }, serialization::Duration32, @@ -393,11 +394,11 @@ lazy_static! { /// The minimum network protocol version typically changes after Mainnet and /// Testnet network upgrades. // TODO: Move the value here to a field on `NetworkParameters` (#8367) - pub static ref INITIAL_MIN_NETWORK_PROTOCOL_VERSION: HashMap = { + pub static ref INITIAL_MIN_NETWORK_PROTOCOL_VERSION: HashMap = { let mut hash_map = HashMap::new(); - hash_map.insert(Mainnet.bip70_network_name(), Version::min_specified_for_upgrade(&Mainnet, Nu5)); - hash_map.insert(Network::new_default_testnet().bip70_network_name(), Version::min_specified_for_upgrade(&Network::new_default_testnet(), Nu5)); + hash_map.insert(NetworkKind::Mainnet, Version::min_specified_for_upgrade(&Mainnet, Nu5)); + hash_map.insert(NetworkKind::Testnet, Version::min_specified_for_upgrade(&Network::new_default_testnet(), Nu5)); hash_map }; diff --git a/zebra-network/src/protocol/external/types.rs b/zebra-network/src/protocol/external/types.rs index c1b98523b..9d71755b1 100644 --- a/zebra-network/src/protocol/external/types.rs +++ b/zebra-network/src/protocol/external/types.rs @@ -81,7 +81,7 @@ impl Version { /// - after Zebra's local network is slow or shut down. fn initial_min_for_network(network: &Network) -> Version { *constants::INITIAL_MIN_NETWORK_PROTOCOL_VERSION - .get(&network.bip70_network_name()) + .get(&network.kind()) .expect("We always have a value for testnet or mainnet") } diff --git a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs index 2d72c9785..dbd0e7c1d 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs +++ b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs @@ -318,7 +318,7 @@ fn snapshot_block_and_transaction_data(state: &FinalizedState) { // Skip these checks for empty history trees. if let Some(history_tree_at_tip) = history_tree_at_tip.as_ref().as_ref() { assert_eq!(history_tree_at_tip.current_height(), max_height); - assert_eq!(history_tree_at_tip.network(), state.network()); + assert_eq!(history_tree_at_tip.network(), &state.network()); } } From 05e41a28037ca4288a61b7a7e9a4e0ce40e40b98 Mon Sep 17 00:00:00 2001 From: Arya Date: Mon, 1 Apr 2024 21:27:58 -0400 Subject: [PATCH 10/51] moves conversions to zcash_primitives::consensus::Network to where they're used. --- zebra-chain/src/parameters.rs | 2 - zebra-chain/src/parameters/error.rs | 8 ---- zebra-chain/src/primitives/address.rs | 38 +------------------ .../src/primitives/zcash_note_encryption.rs | 20 ++++++++-- .../src/primitives/zcash_primitives.rs | 27 +------------ zebra-scan/src/service/scan_task/scan.rs | 22 +++++++++-- .../src/bin/scanning-results-reader/main.rs | 3 +- 7 files changed, 39 insertions(+), 81 deletions(-) delete mode 100644 zebra-chain/src/parameters/error.rs diff --git a/zebra-chain/src/parameters.rs b/zebra-chain/src/parameters.rs index 1bf5d5095..fdb27c95c 100644 --- a/zebra-chain/src/parameters.rs +++ b/zebra-chain/src/parameters.rs @@ -12,7 +12,6 @@ //! Typically, consensus parameters are accessed via a function that takes a //! `Network` and `block::Height`. -mod error; mod genesis; mod network; mod network_upgrade; @@ -21,7 +20,6 @@ mod transaction; #[cfg(any(test, feature = "proptest-impl"))] pub mod arbitrary; -pub use error::*; pub use genesis::*; pub use network::{Network, NetworkKind, NetworkParameters}; pub use network_upgrade::*; diff --git a/zebra-chain/src/parameters/error.rs b/zebra-chain/src/parameters/error.rs deleted file mode 100644 index 12dc1851c..000000000 --- a/zebra-chain/src/parameters/error.rs +++ /dev/null @@ -1,8 +0,0 @@ -//! Error types for zebra-chain parameters - -use thiserror::Error; - -/// An error indicating that Zebra network is not supported in type conversions. -#[derive(Clone, Debug, Error)] -#[error("Unsupported Zcash network parameters: {0}")] -pub struct UnsupportedNetwork(pub String); diff --git a/zebra-chain/src/primitives/address.rs b/zebra-chain/src/primitives/address.rs index 54f584243..87e42bb29 100644 --- a/zebra-chain/src/primitives/address.rs +++ b/zebra-chain/src/primitives/address.rs @@ -5,10 +5,7 @@ use zcash_address::unified::{self, Container}; use zcash_primitives::sapling; -use crate::{ - parameters::{Network, NetworkKind, UnsupportedNetwork}, - transparent, BoxError, -}; +use crate::{parameters::NetworkKind, transparent, BoxError}; /// Zcash address variants pub enum Address { @@ -43,39 +40,6 @@ pub enum Address { }, } -impl TryFrom for Network { - // TODO: better error type - type Error = BoxError; - - fn try_from(network: zcash_address::Network) -> Result { - match network { - zcash_address::Network::Main => Ok(Network::Mainnet), - zcash_address::Network::Test => Ok(Network::new_default_testnet()), - // TODO: Add conversion to regtest (#7839) - zcash_address::Network::Regtest => Err("unsupported Zcash network parameters".into()), - } - } -} - -impl TryFrom<&Network> for zcash_address::Network { - type Error = UnsupportedNetwork; - - fn try_from(network: &Network) -> Result { - match network { - Network::Mainnet => Ok(zcash_address::Network::Main), - Network::Testnet(_params) if network.is_default_testnet() => { - Ok(zcash_address::Network::Test) - } - // TODO: If the network parameters match `Regtest` parameters, convert to - // `zcash_address::Network::Regtest instead of returning `UnsupportedAddress` error. - // (#7119, #7839) - Network::Testnet(_params) => Err(UnsupportedNetwork( - "could not convert configured testnet to zcash_address::Network".to_string(), - )), - } - } -} - impl zcash_address::TryFromAddress for Address { // TODO: crate::serialization::SerializationError type Error = BoxError; diff --git a/zebra-chain/src/primitives/zcash_note_encryption.rs b/zebra-chain/src/primitives/zcash_note_encryption.rs index e5a46955a..0fa44c26d 100644 --- a/zebra-chain/src/primitives/zcash_note_encryption.rs +++ b/zebra-chain/src/primitives/zcash_note_encryption.rs @@ -25,10 +25,24 @@ pub fn decrypts_successfully(transaction: &Transaction, network: &Network, heigh if let Some(bundle) = alt_tx.sapling_bundle() { for output in bundle.shielded_outputs().iter() { + let network = match network { + Network::Mainnet => zcash_primitives::consensus::Network::MainNetwork, + Network::Testnet(params) => { + // # Correctness: + // + // There are differences between the `TestNetwork` parameters and those returned by + // `CRegTestParams()` in zcashd, so this function can't return `TestNetwork` unless + // Zebra is using the default public Testnet. + // + // TODO: Remove this conversion by implementing `zcash_primitives::consensus::Parameters` + // for `Network` (#8365). + assert!(params.is_default_testnet(), "could not convert configured testnet to zcash_primitives::consensus::Network"); + zcash_primitives::consensus::Network::TestNetwork + } + }; + let recovery = zcash_primitives::sapling::note_encryption::try_sapling_output_recovery( - // TODO: Implement `Parameters` trait for `Network` and pass network here without conversion (#8365) - &::try_from(network) - .expect("network must match zcash_primitives network"), + &network, alt_height, &null_sapling_ovk, output, diff --git a/zebra-chain/src/primitives/zcash_primitives.rs b/zebra-chain/src/primitives/zcash_primitives.rs index 389deeb89..564afab09 100644 --- a/zebra-chain/src/primitives/zcash_primitives.rs +++ b/zebra-chain/src/primitives/zcash_primitives.rs @@ -7,7 +7,7 @@ use zcash_primitives::transaction as zp_tx; use crate::{ amount::{Amount, NonNegative}, - parameters::{Network, NetworkUpgrade, UnsupportedNetwork}, + parameters::{Network, NetworkUpgrade}, serialization::ZcashSerialize, transaction::{AuthDigest, HashType, SigHash, Transaction}, transparent::{self, Script}, @@ -336,28 +336,3 @@ pub(crate) fn transparent_output_address( None => None, } } - -impl TryFrom<&Network> for zcash_primitives::consensus::Network { - type Error = UnsupportedNetwork; - - fn try_from(network: &Network) -> Result { - match network { - Network::Mainnet => Ok(zcash_primitives::consensus::Network::MainNetwork), - // # Correctness: - // - // There are differences between the `TestNetwork` parameters and those returned by - // `CRegTestParams()` in zcashd, so this function can't return `TestNetwork` unless - // Zebra is using the default public Testnet. - // - // TODO: Remove this conversion by implementing `zcash_primitives::consensus::Parameters` - // for `Network` (#8365). - Network::Testnet(_params) if network.is_default_testnet() => { - Ok(zcash_primitives::consensus::Network::TestNetwork) - } - Network::Testnet(_params) => Err(UnsupportedNetwork( - "could not convert configured testnet to zcash_primitives::consensus::Network" - .to_string(), - )), - } - } -} diff --git a/zebra-scan/src/service/scan_task/scan.rs b/zebra-scan/src/service/scan_task/scan.rs index c24f744ce..c078e3260 100644 --- a/zebra-scan/src/service/scan_task/scan.rs +++ b/zebra-scan/src/service/scan_task/scan.rs @@ -384,10 +384,24 @@ pub fn scan_block( // TODO: Implement a check that returns early when the block height is below the Sapling // activation height. - // TODO: Implement `zcash_primitives::consensus::Parameters` for `Network` and remove this conversion (#8365) - let network: zcash_primitives::consensus::Network = network - .try_into() - .expect("must match zcash_primitives network"); + let network = match network { + Network::Mainnet => zcash_primitives::consensus::Network::MainNetwork, + Network::Testnet(params) => { + // # Correctness: + // + // There are differences between the `TestNetwork` parameters and those returned by + // `CRegTestParams()` in zcashd, so this function can't return `TestNetwork` unless + // Zebra is using the default public Testnet. + // + // TODO: Remove this conversion by implementing `zcash_primitives::consensus::Parameters` + // for `Network` (#8365). + assert!( + params.is_default_testnet(), + "could not convert configured testnet to zcash_primitives::consensus::Network" + ); + zcash_primitives::consensus::Network::TestNetwork + } + }; let chain_metadata = ChainMetadata { sapling_commitment_tree_size: sapling_tree_size, diff --git a/zebra-utils/src/bin/scanning-results-reader/main.rs b/zebra-utils/src/bin/scanning-results-reader/main.rs index cd31f55cf..66b48e0ac 100644 --- a/zebra-utils/src/bin/scanning-results-reader/main.rs +++ b/zebra-utils/src/bin/scanning-results-reader/main.rs @@ -41,8 +41,9 @@ use zebra_scan::{storage::Storage, Config}; /// - The transaction fetched via RPC cannot be deserialized from raw bytes. #[allow(clippy::print_stdout)] pub fn main() { + // TODO: Implement `zcash_primitives::consensus::Parameters` for `Network` and remove this variable (#8365). let network = zcash_primitives::consensus::Network::MainNetwork; - let zebra_network: zebra_chain::parameters::Network = network.into(); + let zebra_network = zebra_chain::parameters::Network::Mainnet; let storage = Storage::new(&Config::default(), &zebra_network, true); // If the first memo is empty, it doesn't get printed. But we never print empty memos anyway. let mut prev_memo = "".to_owned(); From fd3d50572c95871a20ec36711f53da5b1e7fe698 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 11 Apr 2024 12:36:30 -0400 Subject: [PATCH 11/51] Apply suggestions from code review Co-authored-by: Marek --- zebra-chain/src/parameters/network.rs | 2 +- zebra-consensus/src/parameters/subsidy.rs | 1 - .../src/service/finalized_state/disk_format/transparent.rs | 3 ++- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/zebra-chain/src/parameters/network.rs b/zebra-chain/src/parameters/network.rs index e74177eef..3e370020a 100644 --- a/zebra-chain/src/parameters/network.rs +++ b/zebra-chain/src/parameters/network.rs @@ -65,11 +65,11 @@ impl NetworkParameters { } } -#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] /// An enum describing the kind of network, whether it's the production mainnet or a testnet. // Note: The order of these variants is important for correct bincode (de)serialization // of history trees in the db format. // TODO: Replace bincode (de)serialization of `HistoryTreeParts` in a db format upgrade? +#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] pub enum NetworkKind { /// The production mainnet. #[default] diff --git a/zebra-consensus/src/parameters/subsidy.rs b/zebra-consensus/src/parameters/subsidy.rs index 15fafd5ec..8ab4c8148 100644 --- a/zebra-consensus/src/parameters/subsidy.rs +++ b/zebra-consensus/src/parameters/subsidy.rs @@ -231,7 +231,6 @@ impl ParameterSubsidy for Network { Network::Mainnet => NetworkUpgrade::Canopy .activation_height(self) .expect("canopy activation height should be available"), - // TODO: Check what zcashd does here, consider adding a field to `NetworkParamters` to make this configurable. Network::Testnet(_params) => FIRST_HALVING_TESTNET, } diff --git a/zebra-state/src/service/finalized_state/disk_format/transparent.rs b/zebra-state/src/service/finalized_state/disk_format/transparent.rs index bcf70ff23..b09423a38 100644 --- a/zebra-state/src/service/finalized_state/disk_format/transparent.rs +++ b/zebra-state/src/service/finalized_state/disk_format/transparent.rs @@ -506,7 +506,8 @@ fn address_variant(address: &transparent::Address) -> u8 { (Mainnet, PayToPublicKeyHash { .. }) => 0, (Mainnet, PayToScriptHash { .. }) => 1, // There's no way to distinguish between Regtest and Testnet for encoded transparent addresses, - // so the network kind should always be `Mainnet` or `Testnet`. + // we can consider `Regtest` to use `Testnet` transparent addresses, so it's okay to use the `Testnet` + // address variant for `Regtest` transparent addresses in the db format (Testnet | Regtest, PayToPublicKeyHash { .. }) => 2, (Testnet | Regtest, PayToScriptHash { .. }) => 3, } From f04b94b13afc061bcedee9b44d54b467c0e71fc6 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 11 Apr 2024 12:43:11 -0400 Subject: [PATCH 12/51] rename `network` variables and method names typed as NetworkKind to `network_kind` --- zebra-chain/src/primitives/address.rs | 2 +- zebra-chain/src/transparent/address.rs | 52 +++++++++---------- .../block/subsidy/funding_streams/tests.rs | 2 +- zebra-consensus/src/parameters/subsidy.rs | 2 +- .../src/methods/get_block_template_rpcs.rs | 4 +- .../disk_format/transparent.rs | 2 +- 6 files changed, 32 insertions(+), 32 deletions(-) diff --git a/zebra-chain/src/primitives/address.rs b/zebra-chain/src/primitives/address.rs index 87e42bb29..7e5f76138 100644 --- a/zebra-chain/src/primitives/address.rs +++ b/zebra-chain/src/primitives/address.rs @@ -135,7 +135,7 @@ impl Address { /// Returns the network for the address. pub fn network(&self) -> NetworkKind { match &self { - Self::Transparent(address) => address.network(), + Self::Transparent(address) => address.network_kind(), Self::Sapling { network, .. } | Self::Unified { network, .. } => *network, } } diff --git a/zebra-chain/src/transparent/address.rs b/zebra-chain/src/transparent/address.rs index 3e2f896d3..474041289 100644 --- a/zebra-chain/src/transparent/address.rs +++ b/zebra-chain/src/transparent/address.rs @@ -33,7 +33,7 @@ pub enum Address { /// P2SH (Pay to Script Hash) addresses PayToScriptHash { /// Production, test, or other network - network: NetworkKind, + network_kind: NetworkKind, /// 20 bytes specifying a script hash. script_hash: [u8; 20], }, @@ -41,7 +41,7 @@ pub enum Address { /// P2PKH (Pay to Public Key Hash) addresses PayToPublicKeyHash { /// Production, test, or other network - network: NetworkKind, + network_kind: NetworkKind, /// 20 bytes specifying a public key hash, which is a RIPEMD-160 /// hash of a SHA-256 hash of a compressed ECDSA key encoding. pub_key_hash: [u8; 20], @@ -54,17 +54,17 @@ impl fmt::Debug for Address { match self { Address::PayToScriptHash { - network, + network_kind, script_hash, } => debug_struct - .field("network", network) + .field("network_kind", network_kind) .field("script_hash", &hex::encode(script_hash)) .finish(), Address::PayToPublicKeyHash { - network, + network_kind, pub_key_hash, } => debug_struct - .field("network", network) + .field("network_kind", network_kind) .field("pub_key_hash", &hex::encode(pub_key_hash)) .finish(), } @@ -97,17 +97,17 @@ impl ZcashSerialize for Address { fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { match self { Address::PayToScriptHash { - network, + network_kind, script_hash, } => { - writer.write_all(&network.b58_script_address_prefix())?; + writer.write_all(&network_kind.b58_script_address_prefix())?; writer.write_all(script_hash)? } Address::PayToPublicKeyHash { - network, + network_kind, pub_key_hash, } => { - writer.write_all(&network.b58_pubkey_address_prefix())?; + writer.write_all(&network_kind.b58_pubkey_address_prefix())?; writer.write_all(pub_key_hash)? } } @@ -127,25 +127,25 @@ impl ZcashDeserialize for Address { match version_bytes { zcash_primitives::constants::mainnet::B58_SCRIPT_ADDRESS_PREFIX => { Ok(Address::PayToScriptHash { - network: NetworkKind::Mainnet, + network_kind: NetworkKind::Mainnet, script_hash: hash_bytes, }) } zcash_primitives::constants::testnet::B58_SCRIPT_ADDRESS_PREFIX => { Ok(Address::PayToScriptHash { - network: NetworkKind::Testnet, + network_kind: NetworkKind::Testnet, script_hash: hash_bytes, }) } zcash_primitives::constants::mainnet::B58_PUBKEY_ADDRESS_PREFIX => { Ok(Address::PayToPublicKeyHash { - network: NetworkKind::Mainnet, + network_kind: NetworkKind::Mainnet, pub_key_hash: hash_bytes, }) } zcash_primitives::constants::testnet::B58_PUBKEY_ADDRESS_PREFIX => { Ok(Address::PayToPublicKeyHash { - network: NetworkKind::Testnet, + network_kind: NetworkKind::Testnet, pub_key_hash: hash_bytes, }) } @@ -160,18 +160,18 @@ trait ToAddressWithNetwork { } impl ToAddressWithNetwork for Script { - fn to_address(&self, network: NetworkKind) -> Address { + fn to_address(&self, network_kind: NetworkKind) -> Address { Address::PayToScriptHash { - network, + network_kind, script_hash: Address::hash_payload(self.as_raw_bytes()), } } } impl ToAddressWithNetwork for PublicKey { - fn to_address(&self, network: NetworkKind) -> Address { + fn to_address(&self, network_kind: NetworkKind) -> Address { Address::PayToPublicKeyHash { - network, + network_kind, pub_key_hash: Address::hash_payload(&self.serialize()[..]), } } @@ -179,26 +179,26 @@ impl ToAddressWithNetwork for PublicKey { impl Address { /// Create an address for the given public key hash and network. - pub fn from_pub_key_hash(network: NetworkKind, pub_key_hash: [u8; 20]) -> Self { + pub fn from_pub_key_hash(network_kind: NetworkKind, pub_key_hash: [u8; 20]) -> Self { Self::PayToPublicKeyHash { - network, + network_kind, pub_key_hash, } } /// Create an address for the given script hash and network. - pub fn from_script_hash(network: NetworkKind, script_hash: [u8; 20]) -> Self { + pub fn from_script_hash(network_kind: NetworkKind, script_hash: [u8; 20]) -> Self { Self::PayToScriptHash { - network, + network_kind, script_hash, } } /// Returns the network kind for this address. - pub fn network(&self) -> NetworkKind { + pub fn network_kind(&self) -> NetworkKind { match self { - Address::PayToScriptHash { network, .. } => *network, - Address::PayToPublicKeyHash { network, .. } => *network, + Address::PayToScriptHash { network_kind, .. } => *network_kind, + Address::PayToPublicKeyHash { network_kind, .. } => *network_kind, } } @@ -338,7 +338,7 @@ mod tests { assert_eq!( format!("{t_addr:?}"), - "TransparentAddress { network: Mainnet, script_hash: \"7d46a730d31f97b1930d3368a967c309bd4d136a\" }" + "TransparentAddress { network_kind: Mainnet, script_hash: \"7d46a730d31f97b1930d3368a967c309bd4d136a\" }" ); } } diff --git a/zebra-consensus/src/block/subsidy/funding_streams/tests.rs b/zebra-consensus/src/block/subsidy/funding_streams/tests.rs index 893fcc826..7f6011a42 100644 --- a/zebra-consensus/src/block/subsidy/funding_streams/tests.rs +++ b/zebra-consensus/src/block/subsidy/funding_streams/tests.rs @@ -68,7 +68,7 @@ fn test_funding_stream_addresses() -> Result<(), Report> { let address = transparent::Address::from_str(address).expect("address should deserialize"); assert_eq!( - &address.network(), + &address.network_kind(), network, "incorrect network for {receiver:?} funding stream address constant: {address}", ); diff --git a/zebra-consensus/src/parameters/subsidy.rs b/zebra-consensus/src/parameters/subsidy.rs index 8ab4c8148..6dcc172c8 100644 --- a/zebra-consensus/src/parameters/subsidy.rs +++ b/zebra-consensus/src/parameters/subsidy.rs @@ -231,7 +231,7 @@ impl ParameterSubsidy for Network { Network::Mainnet => NetworkUpgrade::Canopy .activation_height(self) .expect("canopy activation height should be available"), - // TODO: Check what zcashd does here, consider adding a field to `NetworkParamters` to make this configurable. + // TODO: Check what zcashd does here, consider adding a field to `NetworkParameters` to make this configurable. Network::Testnet(_params) => FIRST_HALVING_TESTNET, } } diff --git a/zebra-rpc/src/methods/get_block_template_rpcs.rs b/zebra-rpc/src/methods/get_block_template_rpcs.rs index 6e424a591..de47bba3b 100644 --- a/zebra-rpc/src/methods/get_block_template_rpcs.rs +++ b/zebra-rpc/src/methods/get_block_template_rpcs.rs @@ -450,11 +450,11 @@ where // Prevent loss of miner funds due to an unsupported or incorrect address type. if let Some(miner_address) = mining_config.miner_address.clone() { assert_eq!( - miner_address.network(), + miner_address.network_kind(), network.kind(), "incorrect miner address config: {miner_address} \ network.network {network} and miner address network {} must match", - miner_address.network(), + miner_address.network_kind(), ); } diff --git a/zebra-state/src/service/finalized_state/disk_format/transparent.rs b/zebra-state/src/service/finalized_state/disk_format/transparent.rs index b09423a38..b45c211bc 100644 --- a/zebra-state/src/service/finalized_state/disk_format/transparent.rs +++ b/zebra-state/src/service/finalized_state/disk_format/transparent.rs @@ -502,7 +502,7 @@ 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) { + match (address.network_kind(), address) { (Mainnet, PayToPublicKeyHash { .. }) => 0, (Mainnet, PayToScriptHash { .. }) => 1, // There's no way to distinguish between Regtest and Testnet for encoded transparent addresses, From ea2d001fdcf3fc145081e3570b49fa6804566a29 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 11 Apr 2024 13:38:53 -0400 Subject: [PATCH 13/51] use only test block heights for the network associated with them --- zebra-consensus/src/checkpoint/tests.rs | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/zebra-consensus/src/checkpoint/tests.rs b/zebra-consensus/src/checkpoint/tests.rs index b8f41cc6f..b60540d27 100644 --- a/zebra-consensus/src/checkpoint/tests.rs +++ b/zebra-consensus/src/checkpoint/tests.rs @@ -213,22 +213,15 @@ async fn continuous_blockchain_no_restart() -> Result<(), Report> { #[tokio::test(flavor = "multi_thread")] async fn continuous_blockchain_restart() -> Result<(), Report> { - for network in Network::iter() { - for height in 0..zebra_test::vectors::CONTINUOUS_MAINNET_BLOCKS.len() { - continuous_blockchain( - Some(block::Height(height.try_into().unwrap())), - network.clone(), - ) - .await?; - } - - for height in 0..zebra_test::vectors::CONTINUOUS_TESTNET_BLOCKS.len() { - continuous_blockchain( - Some(block::Height(height.try_into().unwrap())), - network.clone(), - ) - .await?; - } + for height in 0..zebra_test::vectors::CONTINUOUS_MAINNET_BLOCKS.len() { + continuous_blockchain(Some(block::Height(height.try_into().unwrap())), Mainnet).await?; + } + for height in 0..zebra_test::vectors::CONTINUOUS_TESTNET_BLOCKS.len() { + continuous_blockchain( + Some(block::Height(height.try_into().unwrap())), + Network::new_default_testnet(), + ) + .await?; } Ok(()) } From 86cd919135c40e5b2dd512c1ed14aa0963465262 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 11 Apr 2024 13:39:04 -0400 Subject: [PATCH 14/51] Applies more suggestions from code review. --- zebra-chain/src/parameters/network.rs | 5 ++- .../src/primitives/zcash_note_encryption.rs | 34 ++++++++++--------- .../finalized_state/disk_format/chain.rs | 6 ++-- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/zebra-chain/src/parameters/network.rs b/zebra-chain/src/parameters/network.rs index 3e370020a..400f7c66e 100644 --- a/zebra-chain/src/parameters/network.rs +++ b/zebra-chain/src/parameters/network.rs @@ -135,7 +135,9 @@ impl NetworkKind { impl From for &'static str { fn from(network: NetworkKind) -> &'static str { - // These should be different from the `Display` impl for `Network` + // These should be different from the `Display` impl for `Network` so that its lowercase form + // can't be parsed as the default Testnet in the `Network` `FromStr` impl, it's easy to + // distinguish them in logs, and so it's generally harder to confuse the two. match network { NetworkKind::Mainnet => "MainnetKind", NetworkKind::Testnet => "TestnetKind", @@ -271,6 +273,7 @@ impl Network { } } +// This is used for parsing a command-line argument for the `TipHeight` command in zebrad. impl FromStr for Network { type Err = InvalidNetworkError; diff --git a/zebra-chain/src/primitives/zcash_note_encryption.rs b/zebra-chain/src/primitives/zcash_note_encryption.rs index 0fa44c26d..cde0879d8 100644 --- a/zebra-chain/src/primitives/zcash_note_encryption.rs +++ b/zebra-chain/src/primitives/zcash_note_encryption.rs @@ -22,25 +22,27 @@ pub fn decrypts_successfully(transaction: &Transaction, network: &Network, heigh let alt_height = height.0.into(); let null_sapling_ovk = zcash_primitives::keys::OutgoingViewingKey([0u8; 32]); + let network = match network { + Network::Mainnet => zcash_primitives::consensus::Network::MainNetwork, + Network::Testnet(params) => { + // # Correctness: + // + // There are differences between the `TestNetwork` parameters and those returned by + // `CRegTestParams()` in zcashd, so this function can't return `TestNetwork` unless + // Zebra is using the default public Testnet. + // + // TODO: Remove this conversion by implementing `zcash_primitives::consensus::Parameters` + // for `Network` (#8365). + assert!( + params.is_default_testnet(), + "could not convert configured testnet to zcash_primitives::consensus::Network" + ); + zcash_primitives::consensus::Network::TestNetwork + } + }; if let Some(bundle) = alt_tx.sapling_bundle() { for output in bundle.shielded_outputs().iter() { - let network = match network { - Network::Mainnet => zcash_primitives::consensus::Network::MainNetwork, - Network::Testnet(params) => { - // # Correctness: - // - // There are differences between the `TestNetwork` parameters and those returned by - // `CRegTestParams()` in zcashd, so this function can't return `TestNetwork` unless - // Zebra is using the default public Testnet. - // - // TODO: Remove this conversion by implementing `zcash_primitives::consensus::Parameters` - // for `Network` (#8365). - assert!(params.is_default_testnet(), "could not convert configured testnet to zcash_primitives::consensus::Network"); - zcash_primitives::consensus::Network::TestNetwork - } - }; - let recovery = zcash_primitives::sapling::note_encryption::try_sapling_output_recovery( &network, alt_height, diff --git a/zebra-state/src/service/finalized_state/disk_format/chain.rs b/zebra-state/src/service/finalized_state/disk_format/chain.rs index 88010608d..2f04b5369 100644 --- a/zebra-state/src/service/finalized_state/disk_format/chain.rs +++ b/zebra-state/src/service/finalized_state/disk_format/chain.rs @@ -44,7 +44,7 @@ impl FromDisk for ValueBalance { #[derive(serde::Serialize, serde::Deserialize)] pub struct HistoryTreeParts { - network: NetworkKind, + network_kind: NetworkKind, size: u32, peaks: BTreeMap, current_height: Height, @@ -57,7 +57,7 @@ impl HistoryTreeParts { network: &Network, ) -> Result { assert_eq!( - self.network, + self.network_kind, network.kind(), "history tree network kind should match current network" ); @@ -69,7 +69,7 @@ impl HistoryTreeParts { impl From<&NonEmptyHistoryTree> for HistoryTreeParts { fn from(history_tree: &NonEmptyHistoryTree) -> Self { HistoryTreeParts { - network: history_tree.network().kind(), + network_kind: history_tree.network().kind(), size: history_tree.size(), peaks: history_tree.peaks().clone(), current_height: history_tree.current_height(), From 365b58ea3f874d09f54de898f3c1dc8455f2bb77 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 11 Apr 2024 13:56:43 -0400 Subject: [PATCH 15/51] Rename `NetworkParameters` to `Parameters` and move it a new `testnet` module --- zebra-chain/src/parameters.rs | 2 +- zebra-chain/src/parameters/network.rs | 28 ++++++------------- zebra-chain/src/parameters/network/testnet.rs | 16 +++++++++++ zebra-chain/src/parameters/network_upgrade.rs | 4 +-- zebra-chain/src/work/difficulty.rs | 2 +- zebra-consensus/src/checkpoint/list.rs | 4 +-- zebra-consensus/src/parameters/subsidy.rs | 6 ++-- zebra-network/src/config.rs | 4 +-- zebra-network/src/constants.rs | 2 +- zebra-network/src/protocol/external/types.rs | 2 +- 10 files changed, 38 insertions(+), 32 deletions(-) create mode 100644 zebra-chain/src/parameters/network/testnet.rs diff --git a/zebra-chain/src/parameters.rs b/zebra-chain/src/parameters.rs index fdb27c95c..2974bd78c 100644 --- a/zebra-chain/src/parameters.rs +++ b/zebra-chain/src/parameters.rs @@ -21,7 +21,7 @@ mod transaction; pub mod arbitrary; pub use genesis::*; -pub use network::{Network, NetworkKind, NetworkParameters}; +pub use network::{testnet, Network, NetworkKind}; pub use network_upgrade::*; pub use transaction::*; diff --git a/zebra-chain/src/parameters/network.rs b/zebra-chain/src/parameters/network.rs index 400f7c66e..8c5ecaf9e 100644 --- a/zebra-chain/src/parameters/network.rs +++ b/zebra-chain/src/parameters/network.rs @@ -11,6 +11,8 @@ use crate::{ parameters::NetworkUpgrade::Canopy, }; +pub mod testnet; + #[cfg(any(test, feature = "proptest-impl"))] use proptest_derive::Arbitrary; @@ -53,18 +55,6 @@ 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(Clone, Debug, Default, Eq, PartialEq, Hash, Serialize, Deserialize)] -#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))] -pub struct NetworkParameters {} - -impl NetworkParameters { - /// Returns true if the instance of [`NetworkParameters`] represents the default public Testnet. - pub fn is_default_testnet(&self) -> bool { - self == &Self::default() - } -} - /// An enum describing the kind of network, whether it's the production mainnet or a testnet. // Note: The order of these variants is important for correct bincode (de)serialization // of history trees in the db format. @@ -100,7 +90,7 @@ pub enum Network { /// A test network such as the default public testnet, /// a configured testnet, or Regtest. - Testnet(Arc), + Testnet(Arc), } impl NetworkKind { @@ -172,13 +162,13 @@ impl fmt::Display for Network { } impl Network { - /// Creates a new [`Network::Testnet`] with the default Testnet [`NetworkParameters`]. + /// Creates a new [`Network::Testnet`] with the default Testnet [`testnet::Parameters`]. pub fn new_default_testnet() -> Self { - Self::Testnet(Arc::new(NetworkParameters::default())) + Self::Testnet(Arc::new(testnet::Parameters::default())) } - /// Creates a new configured [`Network::Testnet`] with the provided Testnet [`NetworkParameters`]. - pub fn new_configured_testnet(params: NetworkParameters) -> Self { + /// Creates a new configured [`Network::Testnet`] with the provided Testnet [`testnet::Parameters`]. + pub fn new_configured_testnet(params: testnet::Parameters) -> Self { Self::Testnet(Arc::new(params)) } @@ -217,7 +207,7 @@ impl Network { pub fn is_max_block_time_enforced(&self, height: block::Height) -> bool { match self { Network::Mainnet => true, - // TODO: Move `TESTNET_MAX_TIME_START_HEIGHT` to a field on NetworkParameters (#8364) + // TODO: Move `TESTNET_MAX_TIME_START_HEIGHT` to a field on testnet::Parameters (#8364) Network::Testnet(_params) => height >= super::TESTNET_MAX_TIME_START_HEIGHT, } } @@ -226,7 +216,7 @@ impl Network { pub fn default_port(&self) -> u16 { match self { Network::Mainnet => 8233, - // TODO: Add a `default_port` field to `NetworkParameters` to return here. (zcashd uses 18344 for Regtest) + // TODO: Add a `default_port` field to `testnet::Parameters` to return here. (zcashd uses 18344 for Regtest) Network::Testnet(_params) => 18233, } } diff --git a/zebra-chain/src/parameters/network/testnet.rs b/zebra-chain/src/parameters/network/testnet.rs new file mode 100644 index 000000000..d759be94b --- /dev/null +++ b/zebra-chain/src/parameters/network/testnet.rs @@ -0,0 +1,16 @@ +//! Types and implementation for Testnet consensus parameters + +#[cfg(any(test, feature = "proptest-impl"))] +use proptest_derive::Arbitrary; + +/// Network consensus parameters for test networks such as Regtest and the default Testnet. +#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))] +pub struct Parameters {} + +impl Parameters { + /// Returns true if the instance of [`Parameters`] represents the default public Testnet. + pub fn is_default_testnet(&self) -> bool { + self == &Self::default() + } +} diff --git a/zebra-chain/src/parameters/network_upgrade.rs b/zebra-chain/src/parameters/network_upgrade.rs index a5613d1ea..6c114062b 100644 --- a/zebra-chain/src/parameters/network_upgrade.rs +++ b/zebra-chain/src/parameters/network_upgrade.rs @@ -271,7 +271,7 @@ impl Network { }; match self { Mainnet => mainnet_heights, - // TODO: Add an `activation_heights` field to `NetworkParameters` to return here. (#7970) + // TODO: Add an `activation_heights` field to `testnet::Parameters` to return here. (#7970) Testnet(_params) => testnet_heights, } .iter() @@ -395,7 +395,7 @@ impl NetworkUpgrade { height: block::Height, ) -> Option { match (network, height) { - // TODO: Move `TESTNET_MINIMUM_DIFFICULTY_START_HEIGHT` to a field on NetworkParameters (#8364) + // TODO: Move `TESTNET_MINIMUM_DIFFICULTY_START_HEIGHT` to a field on testnet::Parameters (#8364) (Network::Testnet(_params), height) if height < TESTNET_MINIMUM_DIFFICULTY_START_HEIGHT => { diff --git a/zebra-chain/src/work/difficulty.rs b/zebra-chain/src/work/difficulty.rs index 2f5e46821..c13210923 100644 --- a/zebra-chain/src/work/difficulty.rs +++ b/zebra-chain/src/work/difficulty.rs @@ -699,7 +699,7 @@ impl ParameterDifficulty for Network { /* 2^243 - 1 */ Network::Mainnet => (U256::one() << 243) - 1, /* 2^251 - 1 */ - // TODO: Add a `target_difficulty_limit` field to `NetworkParameters` to return here. + // TODO: Add a `target_difficulty_limit` field to `testnet::Parameters` to return here. Network::Testnet(_params) => (U256::one() << 251) - 1, }; diff --git a/zebra-consensus/src/checkpoint/list.rs b/zebra-consensus/src/checkpoint/list.rs index 4b8cd2436..887998482 100644 --- a/zebra-consensus/src/checkpoint/list.rs +++ b/zebra-consensus/src/checkpoint/list.rs @@ -57,7 +57,7 @@ impl ParameterCheckpoint for Network { // zcash-cli getblockhash 0 Network::Mainnet => "00040fe8ec8471911baa1db1266ea15dd06b4a8a5c453883c000b031973dce08", // zcash-cli -testnet getblockhash 0 - // TODO: Add a `genesis_hash` field to `NetworkParameters` and return it here (#8366) + // TODO: Add a `genesis_hash` field to `testnet::Parameters` and return it here (#8366) Network::Testnet(_params) => { "05a60a92d99d85997cce3b87616c089f6124d7342af37106edc76126334a2c38" } @@ -69,7 +69,7 @@ impl ParameterCheckpoint for Network { fn checkpoint_list(&self) -> CheckpointList { // parse calls CheckpointList::from_list // TODO: - // - Add a `genesis_hash` field to `NetworkParameters` and return it here (#8366) + // - Add a `genesis_hash` field to `testnet::Parameters` and return it here (#8366) // - Try to disable checkpoints entirely for regtest and custom testnets let checkpoint_list: CheckpointList = match self { Network::Mainnet => MAINNET_CHECKPOINTS diff --git a/zebra-consensus/src/parameters/subsidy.rs b/zebra-consensus/src/parameters/subsidy.rs index 6dcc172c8..e7cf8f91b 100644 --- a/zebra-consensus/src/parameters/subsidy.rs +++ b/zebra-consensus/src/parameters/subsidy.rs @@ -104,7 +104,7 @@ lazy_static! { /// as described in [protocol specification §7.10.1][7.10.1]. /// /// [7.10.1]: https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams - // TODO: Move the value here to a field on `NetworkParameters` (#8367) + // TODO: Move the value here to a field on `testnet::Parameters` (#8367) pub static ref FUNDING_STREAM_HEIGHT_RANGES: HashMap> = { let mut hash_map = HashMap::new(); hash_map.insert(NetworkKind::Mainnet, Height(1_046_400)..Height(2_726_400)); @@ -113,7 +113,7 @@ lazy_static! { }; /// Convenient storage for all addresses, for all receivers and networks - // TODO: Move the value here to a field on `NetworkParameters` (#8367) + // TODO: Move the value here to a field on `testnet::Parameters` (#8367) // There are no funding stream addresses on Regtest in zcashd, zebrad should do the same for compatibility. pub static ref FUNDING_STREAM_ADDRESSES: HashMap>> = { let mut addresses_by_network = HashMap::with_capacity(2); @@ -231,7 +231,7 @@ impl ParameterSubsidy for Network { Network::Mainnet => NetworkUpgrade::Canopy .activation_height(self) .expect("canopy activation height should be available"), - // TODO: Check what zcashd does here, consider adding a field to `NetworkParameters` to make this configurable. + // TODO: Check what zcashd does here, consider adding a field to `testnet::Parameters` to make this configurable. Network::Testnet(_params) => FIRST_HALVING_TESTNET, } } diff --git a/zebra-network/src/config.rs b/zebra-network/src/config.rs index 090fe35aa..2ee59c883 100644 --- a/zebra-network/src/config.rs +++ b/zebra-network/src/config.rs @@ -15,7 +15,7 @@ use tempfile::NamedTempFile; use tokio::{fs, io::AsyncWriteExt}; use tracing::Span; -use zebra_chain::parameters::{Network, NetworkKind, NetworkParameters}; +use zebra_chain::parameters::{testnet, Network, NetworkKind}; use crate::{ constants::{ @@ -630,7 +630,7 @@ impl<'de> Deserialize<'de> for Config { struct DConfig { listen_addr: String, network: NetworkKind, - testnet_parameters: Option, + testnet_parameters: Option, initial_mainnet_peers: IndexSet, initial_testnet_peers: IndexSet, cache_dir: CacheDir, diff --git a/zebra-network/src/constants.rs b/zebra-network/src/constants.rs index 91b390949..363c05717 100644 --- a/zebra-network/src/constants.rs +++ b/zebra-network/src/constants.rs @@ -393,7 +393,7 @@ lazy_static! { /// /// The minimum network protocol version typically changes after Mainnet and /// Testnet network upgrades. - // TODO: Move the value here to a field on `NetworkParameters` (#8367) + // TODO: Move the value here to a field on `testnet::Parameters` (#8367) pub static ref INITIAL_MIN_NETWORK_PROTOCOL_VERSION: HashMap = { let mut hash_map = HashMap::new(); diff --git a/zebra-network/src/protocol/external/types.rs b/zebra-network/src/protocol/external/types.rs index 9d71755b1..0e35698a7 100644 --- a/zebra-network/src/protocol/external/types.rs +++ b/zebra-network/src/protocol/external/types.rs @@ -31,7 +31,7 @@ impl ParameterMagic for Network { fn magic_value(&self) -> Magic { match self { Network::Mainnet => magics::MAINNET, - // TODO: Move `Magic` struct definition to `zebra-chain`, add it as a field in `NetworkParameters`, and return it here. + // TODO: Move `Magic` struct definition to `zebra-chain`, add it as a field in `testnet::Parameters`, and return it here. Network::Testnet(_params) => magics::TESTNET, } } From 60248e3f1f3887ec4a40db65265a3616258433f8 Mon Sep 17 00:00:00 2001 From: Arya Date: Tue, 2 Apr 2024 19:29:00 -0400 Subject: [PATCH 16/51] adds activation heights field --- zebra-chain/src/parameters/arbitrary.rs | 12 ++++++- zebra-chain/src/parameters/network.rs | 8 ++--- zebra-chain/src/parameters/network/testnet.rs | 30 +++++++++++++---- zebra-chain/src/parameters/network_upgrade.rs | 32 +++++++------------ 4 files changed, 48 insertions(+), 34 deletions(-) diff --git a/zebra-chain/src/parameters/arbitrary.rs b/zebra-chain/src/parameters/arbitrary.rs index 12440946d..63e678b3d 100644 --- a/zebra-chain/src/parameters/arbitrary.rs +++ b/zebra-chain/src/parameters/arbitrary.rs @@ -2,7 +2,7 @@ use proptest::prelude::*; -use super::NetworkUpgrade; +use super::{Network, NetworkUpgrade}; impl NetworkUpgrade { /// Generates network upgrades. @@ -32,3 +32,13 @@ impl NetworkUpgrade { .boxed() } } + +impl Arbitrary for Network { + type Parameters = (); + + fn arbitrary_with(_args: ()) -> Self::Strategy { + prop_oneof![Just(Self::Mainnet), Just(Self::new_default_testnet())].boxed() + } + + type Strategy = BoxedStrategy; +} diff --git a/zebra-chain/src/parameters/network.rs b/zebra-chain/src/parameters/network.rs index 8c5ecaf9e..26e993464 100644 --- a/zebra-chain/src/parameters/network.rs +++ b/zebra-chain/src/parameters/network.rs @@ -8,14 +8,11 @@ use zcash_primitives::constants; use crate::{ block::{self, Height, HeightDiff}, - parameters::NetworkUpgrade::Canopy, + parameters::NetworkUpgrade, }; pub mod testnet; -#[cfg(any(test, feature = "proptest-impl"))] -use proptest_derive::Arbitrary; - #[cfg(test)] mod tests; @@ -81,7 +78,6 @@ impl From for NetworkKind { /// An enum describing the possible network choices. #[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Serialize)] -#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))] #[serde(into = "NetworkKind")] pub enum Network { /// The production mainnet. @@ -231,7 +227,7 @@ impl Network { // // See the `ZIP_212_GRACE_PERIOD_DURATION` documentation for more information. - let canopy_activation = Canopy + let canopy_activation = NetworkUpgrade::Canopy .activation_height(self) .expect("Canopy activation height must be present for both networks"); diff --git a/zebra-chain/src/parameters/network/testnet.rs b/zebra-chain/src/parameters/network/testnet.rs index d759be94b..654d4a3b5 100644 --- a/zebra-chain/src/parameters/network/testnet.rs +++ b/zebra-chain/src/parameters/network/testnet.rs @@ -1,15 +1,33 @@ //! Types and implementation for Testnet consensus parameters +use std::collections::BTreeMap; -#[cfg(any(test, feature = "proptest-impl"))] -use proptest_derive::Arbitrary; +use crate::{ + block::Height, + parameters::{network_upgrade::TESTNET_ACTIVATION_HEIGHTS, NetworkUpgrade}, +}; /// Network consensus parameters for test networks such as Regtest and the default Testnet. -#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Serialize, Deserialize)] -#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))] -pub struct Parameters {} +#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] +pub struct Parameters { + /// The network upgrade activation heights for this network. + /// + /// Note: This value is ignored by [`Network::activation_list()`] when `zebra-chain` is + /// compiled with the `zebra-test` feature flag AND the `TEST_FAKE_ACTIVATION_HEIGHTS` + /// environment variable is set. + pub activation_heights: BTreeMap, +} + +impl Default for Parameters { + /// Returns an instance of the default public testnet [`NetworkParameters`]. + fn default() -> Self { + Self { + activation_heights: TESTNET_ACTIVATION_HEIGHTS.iter().cloned().collect(), + } + } +} impl Parameters { - /// Returns true if the instance of [`Parameters`] represents the default public Testnet. + /// Returns true if the instance of [`NetworkParameters`] represents the default public Testnet. pub fn is_default_testnet(&self) -> bool { self == &Self::default() } diff --git a/zebra-chain/src/parameters/network_upgrade.rs b/zebra-chain/src/parameters/network_upgrade.rs index 6c114062b..724a412b8 100644 --- a/zebra-chain/src/parameters/network_upgrade.rs +++ b/zebra-chain/src/parameters/network_upgrade.rs @@ -242,12 +242,7 @@ impl Network { /// and it's a test build, this returns a list of fake activation heights /// used by some tests. pub fn activation_list(&self) -> BTreeMap { - let (mainnet_heights, testnet_heights) = { - #[cfg(not(feature = "zebra-test"))] - { - (MAINNET_ACTIVATION_HEIGHTS, TESTNET_ACTIVATION_HEIGHTS) - } - + match self { // To prevent accidentally setting this somehow, only check the env var // when being compiled for tests. We can't use cfg(test) since the // test that uses this is in zebra-state, and cfg(test) is not @@ -260,25 +255,20 @@ impl Network { // feature should only be enabled for tests: // https://doc.rust-lang.org/cargo/reference/features.html#resolver-version-2-command-line-flags #[cfg(feature = "zebra-test")] - if std::env::var_os("TEST_FAKE_ACTIVATION_HEIGHTS").is_some() { - ( - FAKE_MAINNET_ACTIVATION_HEIGHTS, - FAKE_TESTNET_ACTIVATION_HEIGHTS, - ) - } else { - (MAINNET_ACTIVATION_HEIGHTS, TESTNET_ACTIVATION_HEIGHTS) + Mainnet if std::env::var_os("TEST_FAKE_ACTIVATION_HEIGHTS").is_some() => { + FAKE_MAINNET_ACTIVATION_HEIGHTS.iter().cloned().collect() } - }; - match self { - Mainnet => mainnet_heights, - // TODO: Add an `activation_heights` field to `testnet::Parameters` to return here. (#7970) - Testnet(_params) => testnet_heights, + #[cfg(feature = "zebra-test")] + Testnet(_) if std::env::var_os("TEST_FAKE_ACTIVATION_HEIGHTS").is_some() => { + FAKE_TESTNET_ACTIVATION_HEIGHTS.iter().cloned().collect() + } + + Mainnet => MAINNET_ACTIVATION_HEIGHTS.iter().cloned().collect(), + Testnet(params) => params.activation_heights.clone(), } - .iter() - .cloned() - .collect() } } + impl NetworkUpgrade { /// Returns the current network upgrade for `network` and `height`. pub fn current(network: &Network, height: block::Height) -> NetworkUpgrade { From b9bdf0527ccdebbd23685dc013cce13ad3e5b90c Mon Sep 17 00:00:00 2001 From: Arya Date: Tue, 2 Apr 2024 20:24:36 -0400 Subject: [PATCH 17/51] updates stored test config, adds a quicker test for checking that stored configs can be parsed, adds an intermediate representation of activation heights --- zebra-network/src/config.rs | 24 +++++++-- zebrad/tests/acceptance.rs | 65 +++++++++++++++++++++++++ zebrad/tests/common/configs/v1.7.0.toml | 10 ++++ 3 files changed, 95 insertions(+), 4 deletions(-) diff --git a/zebra-network/src/config.rs b/zebra-network/src/config.rs index 2ee59c883..caf28b543 100644 --- a/zebra-network/src/config.rs +++ b/zebra-network/src/config.rs @@ -15,7 +15,7 @@ use tempfile::NamedTempFile; use tokio::{fs, io::AsyncWriteExt}; use tracing::Span; -use zebra_chain::parameters::{testnet, Network, NetworkKind}; +use zebra_chain::parameters::{testnet, Network, NetworkKind, NetworkUpgrade}; use crate::{ constants::{ @@ -625,12 +625,18 @@ impl<'de> Deserialize<'de> for Config { where D: Deserializer<'de>, { + /// Network consensus parameters for test networks such as Regtest and the default Testnet. + #[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] + pub struct DTestnetParameters { + pub(super) activation_heights: Vec<(u32, NetworkUpgrade)>, + } + #[derive(Deserialize)] #[serde(deny_unknown_fields, default)] struct DConfig { listen_addr: String, network: NetworkKind, - testnet_parameters: Option, + testnet_parameters: Option, initial_mainnet_peers: IndexSet, initial_testnet_peers: IndexSet, cache_dir: CacheDir, @@ -669,7 +675,7 @@ impl<'de> Deserialize<'de> for Config { max_connections_per_ip, } = DConfig::deserialize(deserializer)?; - let network = if let Some(network_params) = testnet_parameters { + let network = if let Some(DTestnetParameters { activation_heights }) = testnet_parameters { // TODO: Panic here if the initial testnet peers are the default initial testnet peers. assert_eq!( network_kind, @@ -677,7 +683,17 @@ impl<'de> Deserialize<'de> for Config { "set network to 'Testnet' to use configured testnet parameters" ); - Network::new_configured_testnet(network_params) + let activation_heights = activation_heights + .into_iter() + .map(|(height, network_upgrade)| { + ( + height.try_into().expect("activation height must be valid"), + network_upgrade, + ) + }) + .collect(); + + Network::new_configured_testnet(testnet::Parameters { activation_heights }) } else { // Convert to default `Network` for a `NetworkKind` if there are no testnet parameters. match network_kind { diff --git a/zebrad/tests/acceptance.rs b/zebrad/tests/acceptance.rs index 656ef9a21..ffb3ffb28 100644 --- a/zebrad/tests/acceptance.rs +++ b/zebrad/tests/acceptance.rs @@ -922,6 +922,71 @@ fn invalid_generated_config() -> Result<()> { Ok(()) } +/// Test all versions of `zebrad.toml` we have stored can be parsed by the latest `zebrad`. +#[tracing::instrument] +#[test] +fn stored_configs_parsed_correctly() -> Result<()> { + let old_configs_dir = configs_dir(); + use abscissa_core::Application; + use zebrad::application::ZebradApp; + + tracing::info!(?old_configs_dir, "testing older config parsing"); + + for config_file in old_configs_dir + .read_dir() + .expect("read_dir call failed") + .flatten() + { + let config_file_path = config_file.path(); + let config_file_name = config_file_path + .file_name() + .expect("config files must have a file name") + .to_str() + .expect("config file names are valid unicode"); + + if config_file_name.starts_with('.') || config_file_name.starts_with('#') { + // Skip editor files and other invalid config paths + tracing::info!( + ?config_file_path, + "skipping hidden/temporary config file path" + ); + continue; + } + + // ignore files starting with getblocktemplate prefix + // if we were not built with the getblocktemplate-rpcs feature. + #[cfg(not(feature = "getblocktemplate-rpcs"))] + if config_file_name.starts_with(GET_BLOCK_TEMPLATE_CONFIG_PREFIX) { + tracing::info!( + ?config_file_path, + "skipping getblocktemplate-rpcs config file path" + ); + continue; + } + + // ignore files starting with shieldedscan prefix + // if we were not built with the shielded-scan feature. + #[cfg(not(feature = "shielded-scan"))] + if config_file_name.starts_with(SHIELDED_SCAN_CONFIG_PREFIX) { + tracing::info!(?config_file_path, "skipping shielded-scan config file path"); + continue; + } + + let stored_config_path = config_file_full_path(config_file.path()); + + tracing::info!( + ?stored_config_path, + "testing old config can be parsed by current zebrad" + ); + + ZebradApp::default() + .load_config(&stored_config_path) + .expect("config should parse"); + } + + Ok(()) +} + /// Test all versions of `zebrad.toml` we have stored can be parsed by the latest `zebrad`. #[tracing::instrument] fn stored_configs_work() -> Result<()> { diff --git a/zebrad/tests/common/configs/v1.7.0.toml b/zebrad/tests/common/configs/v1.7.0.toml index cbb3a0200..44540ed73 100644 --- a/zebrad/tests/common/configs/v1.7.0.toml +++ b/zebrad/tests/common/configs/v1.7.0.toml @@ -61,6 +61,16 @@ network = "Testnet" peerset_initial_target_size = 25 [network.testnet_parameters] +activation_heights = [ + [0, "Genesis"], + [1, "BeforeOverwinter"], + [207_500, "Overwinter"], + [280_000, "Sapling"], + [584_000, "Blossom"], + [903_800, "Heartwood"], + [1_028_500, "Canopy"], + [1_842_420, "NU5"], +] [rpc] debug_force_finished_sync = false From ad4e7dcf8ad4c300bf27ce6b5169a0fa637eb4e4 Mon Sep 17 00:00:00 2001 From: Arya Date: Tue, 2 Apr 2024 21:05:13 -0400 Subject: [PATCH 18/51] implement Parameters for Network --- zebra-chain/src/parameters/network.rs | 96 +++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 5 deletions(-) diff --git a/zebra-chain/src/parameters/network.rs b/zebra-chain/src/parameters/network.rs index 26e993464..5ab9a4cd7 100644 --- a/zebra-chain/src/parameters/network.rs +++ b/zebra-chain/src/parameters/network.rs @@ -4,7 +4,10 @@ use std::{fmt, str::FromStr, sync::Arc}; use thiserror::Error; -use zcash_primitives::constants; +use zcash_primitives::{ + consensus::{self as zp_consensus, Parameters}, + constants as zp_constants, +}; use crate::{ block::{self, Height, HeightDiff}, @@ -94,8 +97,8 @@ impl NetworkKind { /// pay-to-public-key-hash payment addresses for the network. pub fn b58_pubkey_address_prefix(self) -> [u8; 2] { match self { - Self::Mainnet => constants::mainnet::B58_PUBKEY_ADDRESS_PREFIX, - Self::Testnet | Self::Regtest => constants::testnet::B58_PUBKEY_ADDRESS_PREFIX, + Self::Mainnet => zp_constants::mainnet::B58_PUBKEY_ADDRESS_PREFIX, + Self::Testnet | Self::Regtest => zp_constants::testnet::B58_PUBKEY_ADDRESS_PREFIX, } } @@ -103,8 +106,8 @@ impl NetworkKind { /// payment addresses for the network. pub fn b58_script_address_prefix(self) -> [u8; 2] { match self { - Self::Mainnet => constants::mainnet::B58_SCRIPT_ADDRESS_PREFIX, - Self::Testnet | Self::Regtest => constants::testnet::B58_SCRIPT_ADDRESS_PREFIX, + Self::Mainnet => zp_constants::mainnet::B58_SCRIPT_ADDRESS_PREFIX, + Self::Testnet | Self::Regtest => zp_constants::testnet::B58_SCRIPT_ADDRESS_PREFIX, } } @@ -275,3 +278,86 @@ impl FromStr for Network { #[derive(Clone, Debug, Error)] #[error("Invalid network: {0}")] pub struct InvalidNetworkError(String); + +impl Parameters for Network { + fn activation_height( + &self, + nu: zcash_primitives::consensus::NetworkUpgrade, + ) -> Option { + let target_nu = match nu { + zp_consensus::NetworkUpgrade::Overwinter => NetworkUpgrade::Overwinter, + zp_consensus::NetworkUpgrade::Sapling => NetworkUpgrade::Sapling, + zp_consensus::NetworkUpgrade::Blossom => NetworkUpgrade::Blossom, + zp_consensus::NetworkUpgrade::Heartwood => NetworkUpgrade::Heartwood, + zp_consensus::NetworkUpgrade::Canopy => NetworkUpgrade::Canopy, + zp_consensus::NetworkUpgrade::Nu5 => NetworkUpgrade::Nu5, + }; + + self.activation_list() + .into_iter() + .find(|(_, nu)| *nu == target_nu) + .map(|(Height(height), _)| height) + // Heights are hard-coded below Height::MAX or checked when the config is parsed. + .map(zp_consensus::BlockHeight::from_u32) + } + + fn coin_type(&self) -> u32 { + match self { + Network::Mainnet => zp_constants::mainnet::COIN_TYPE, + // The regtest cointype reuses the testnet cointype, + // See + Network::Testnet(_) => zp_constants::testnet::COIN_TYPE, + } + } + + fn address_network(&self) -> Option { + match self { + Network::Mainnet => Some(zcash_address::Network::Main), + Network::Testnet(params) if params.is_default_testnet() => { + Some(zcash_address::Network::Test) + } + _other => None, + } + } + + fn hrp_sapling_extended_spending_key(&self) -> &str { + match self { + Network::Mainnet => zp_constants::mainnet::HRP_SAPLING_EXTENDED_SPENDING_KEY, + Network::Testnet(params) if params.is_default_testnet() => { + zp_constants::testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY + } + // TODO: Add this as a field to testnet params + _other => "secret-extended-key-unknown-test", + } + } + + fn hrp_sapling_extended_full_viewing_key(&self) -> &str { + match self { + Network::Mainnet => zp_constants::mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, + Network::Testnet(params) if params.is_default_testnet() => { + zp_constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY + } + // TODO: Add this as a field to testnet params + _other => "zxviewunknowntestsapling", + } + } + + fn hrp_sapling_payment_address(&self) -> &str { + match self { + Network::Mainnet => zp_constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, + Network::Testnet(params) if params.is_default_testnet() => { + zp_constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS + } + // TODO: Add this as a field to testnet params + _other => "zunknowntestsapling", + } + } + + fn b58_pubkey_address_prefix(&self) -> [u8; 2] { + self.kind().b58_pubkey_address_prefix() + } + + fn b58_script_address_prefix(&self) -> [u8; 2] { + self.kind().b58_script_address_prefix() + } +} From 4347a80c24aabe41e42d81d4c5ab58fa2bda9843 Mon Sep 17 00:00:00 2001 From: Arya Date: Tue, 2 Apr 2024 21:09:36 -0400 Subject: [PATCH 19/51] Passes &Network directly instead of converting to zp_consensus::Network where there were conversions --- .../src/primitives/zcash_note_encryption.rs | 2 +- zebra-scan/src/service/scan_task/scan.rs | 21 +------------------ .../src/bin/scanning-results-reader/main.rs | 8 +++---- 3 files changed, 5 insertions(+), 26 deletions(-) diff --git a/zebra-chain/src/primitives/zcash_note_encryption.rs b/zebra-chain/src/primitives/zcash_note_encryption.rs index cde0879d8..a194ead0b 100644 --- a/zebra-chain/src/primitives/zcash_note_encryption.rs +++ b/zebra-chain/src/primitives/zcash_note_encryption.rs @@ -44,7 +44,7 @@ pub fn decrypts_successfully(transaction: &Transaction, network: &Network, heigh 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( - &network, + network, alt_height, &null_sapling_ovk, output, diff --git a/zebra-scan/src/service/scan_task/scan.rs b/zebra-scan/src/service/scan_task/scan.rs index c078e3260..12bbb86a1 100644 --- a/zebra-scan/src/service/scan_task/scan.rs +++ b/zebra-scan/src/service/scan_task/scan.rs @@ -384,25 +384,6 @@ pub fn scan_block( // TODO: Implement a check that returns early when the block height is below the Sapling // activation height. - let network = match network { - Network::Mainnet => zcash_primitives::consensus::Network::MainNetwork, - Network::Testnet(params) => { - // # Correctness: - // - // There are differences between the `TestNetwork` parameters and those returned by - // `CRegTestParams()` in zcashd, so this function can't return `TestNetwork` unless - // Zebra is using the default public Testnet. - // - // TODO: Remove this conversion by implementing `zcash_primitives::consensus::Parameters` - // for `Network` (#8365). - assert!( - params.is_default_testnet(), - "could not convert configured testnet to zcash_primitives::consensus::Network" - ); - zcash_primitives::consensus::Network::TestNetwork - } - }; - let chain_metadata = ChainMetadata { sapling_commitment_tree_size: sapling_tree_size, // Orchard is not supported at the moment so the tree size can be 0. @@ -417,7 +398,7 @@ pub fn scan_block( .collect(); zcash_client_backend::scanning::scan_block( - &network, + network, block_to_compact(block, chain_metadata), scanning_keys.as_slice(), // Ignore whether notes are change from a viewer's own spends for now. diff --git a/zebra-utils/src/bin/scanning-results-reader/main.rs b/zebra-utils/src/bin/scanning-results-reader/main.rs index 66b48e0ac..1bfa15d36 100644 --- a/zebra-utils/src/bin/scanning-results-reader/main.rs +++ b/zebra-utils/src/bin/scanning-results-reader/main.rs @@ -41,15 +41,13 @@ use zebra_scan::{storage::Storage, Config}; /// - The transaction fetched via RPC cannot be deserialized from raw bytes. #[allow(clippy::print_stdout)] pub fn main() { - // TODO: Implement `zcash_primitives::consensus::Parameters` for `Network` and remove this variable (#8365). - let network = zcash_primitives::consensus::Network::MainNetwork; - let zebra_network = zebra_chain::parameters::Network::Mainnet; - let storage = Storage::new(&Config::default(), &zebra_network, true); + let network = zebra_chain::parameters::Network::Mainnet; + let storage = Storage::new(&Config::default(), &network, true); // If the first memo is empty, it doesn't get printed. But we never print empty memos anyway. let mut prev_memo = "".to_owned(); for (key, _) in storage.sapling_keys_last_heights().iter() { - let dfvk = sapling_key_to_scan_block_keys(key, &zebra_network) + let dfvk = sapling_key_to_scan_block_keys(key, &network) .expect("Scanning key from the storage should be valid") .0 .into_iter() From 080affd6d7eebba2089fb5332dfab98081521ef8 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 11 Apr 2024 15:52:20 -0400 Subject: [PATCH 20/51] fixes a bad merge (removes a network conversion in zcash_note_encryption) --- .../src/primitives/zcash_note_encryption.rs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/zebra-chain/src/primitives/zcash_note_encryption.rs b/zebra-chain/src/primitives/zcash_note_encryption.rs index a194ead0b..f8e47fbad 100644 --- a/zebra-chain/src/primitives/zcash_note_encryption.rs +++ b/zebra-chain/src/primitives/zcash_note_encryption.rs @@ -22,24 +22,6 @@ pub fn decrypts_successfully(transaction: &Transaction, network: &Network, heigh let alt_height = height.0.into(); let null_sapling_ovk = zcash_primitives::keys::OutgoingViewingKey([0u8; 32]); - let network = match network { - Network::Mainnet => zcash_primitives::consensus::Network::MainNetwork, - Network::Testnet(params) => { - // # Correctness: - // - // There are differences between the `TestNetwork` parameters and those returned by - // `CRegTestParams()` in zcashd, so this function can't return `TestNetwork` unless - // Zebra is using the default public Testnet. - // - // TODO: Remove this conversion by implementing `zcash_primitives::consensus::Parameters` - // for `Network` (#8365). - assert!( - params.is_default_testnet(), - "could not convert configured testnet to zcash_primitives::consensus::Network" - ); - zcash_primitives::consensus::Network::TestNetwork - } - }; if let Some(bundle) = alt_tx.sapling_bundle() { for output in bundle.shielded_outputs().iter() { From 2493e7ec76be6eb5188037b35d6ddf07e1f79cd0 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 11 Apr 2024 16:37:13 -0400 Subject: [PATCH 21/51] Adds a test for the Parameters impl for zebra_chain::Network --- zebra-chain/src/parameters/network.rs | 7 +- zebra-chain/src/parameters/network/tests.rs | 1 + .../src/parameters/network/tests/vectors.rs | 83 +++++++++++++++++++ 3 files changed, 86 insertions(+), 5 deletions(-) create mode 100644 zebra-chain/src/parameters/network/tests/vectors.rs diff --git a/zebra-chain/src/parameters/network.rs b/zebra-chain/src/parameters/network.rs index 5ab9a4cd7..15f80aa78 100644 --- a/zebra-chain/src/parameters/network.rs +++ b/zebra-chain/src/parameters/network.rs @@ -4,10 +4,7 @@ use std::{fmt, str::FromStr, sync::Arc}; use thiserror::Error; -use zcash_primitives::{ - consensus::{self as zp_consensus, Parameters}, - constants as zp_constants, -}; +use zcash_primitives::{consensus as zp_consensus, constants as zp_constants}; use crate::{ block::{self, Height, HeightDiff}, @@ -279,7 +276,7 @@ impl FromStr for Network { #[error("Invalid network: {0}")] pub struct InvalidNetworkError(String); -impl Parameters for Network { +impl zp_consensus::Parameters for Network { fn activation_height( &self, nu: zcash_primitives::consensus::NetworkUpgrade, diff --git a/zebra-chain/src/parameters/network/tests.rs b/zebra-chain/src/parameters/network/tests.rs index 2bf82ef4e..cc95d9d45 100644 --- a/zebra-chain/src/parameters/network/tests.rs +++ b/zebra-chain/src/parameters/network/tests.rs @@ -1 +1,2 @@ mod prop; +mod vectors; diff --git a/zebra-chain/src/parameters/network/tests/vectors.rs b/zebra-chain/src/parameters/network/tests/vectors.rs new file mode 100644 index 000000000..f95f41e5d --- /dev/null +++ b/zebra-chain/src/parameters/network/tests/vectors.rs @@ -0,0 +1,83 @@ +//! Fixed test vectors for the network consensus parameters. + +use zcash_primitives::consensus::{self as zp_consensus, Parameters}; + +use crate::parameters::Network; + +#[test] +fn check_parameters_impl() { + let zp_network_upgrades = [ + zp_consensus::NetworkUpgrade::Overwinter, + zp_consensus::NetworkUpgrade::Sapling, + zp_consensus::NetworkUpgrade::Blossom, + zp_consensus::NetworkUpgrade::Heartwood, + zp_consensus::NetworkUpgrade::Canopy, + zp_consensus::NetworkUpgrade::Nu5, + ]; + + for (network, zp_network) in [ + (Network::Mainnet, zp_consensus::Network::MainNetwork), + ( + Network::new_default_testnet(), + zp_consensus::Network::TestNetwork, + ), + ] { + for nu in zp_network_upgrades { + let activation_height = network + .activation_height(nu) + .expect("must have activation height for past network upgrades"); + + assert_eq!( + activation_height, + zp_network + .activation_height(nu) + .expect("must have activation height for past network upgrades"), + "Parameters::activation_heights() outputs must match" + ); + + let activation_height: u32 = activation_height.into(); + + for height in (activation_height - 1)..=(activation_height + 1) { + for nu in zp_network_upgrades { + let height = zp_consensus::BlockHeight::from_u32(height); + assert_eq!( + network.is_nu_active(nu, height), + zp_network.is_nu_active(nu, height), + "Parameters::is_nu_active() outputs must match", + ); + } + } + } + + assert_eq!( + network.coin_type(), + zp_network.coin_type(), + "Parameters::coin_type() outputs must match" + ); + assert_eq!( + network.hrp_sapling_extended_spending_key(), + zp_network.hrp_sapling_extended_spending_key(), + "Parameters::hrp_sapling_extended_spending_key() outputs must match" + ); + assert_eq!( + network.hrp_sapling_extended_full_viewing_key(), + zp_network.hrp_sapling_extended_full_viewing_key(), + "Parameters::hrp_sapling_extended_full_viewing_key() outputs must match" + ); + assert_eq!( + network.hrp_sapling_payment_address(), + zp_network.hrp_sapling_payment_address(), + "Parameters::hrp_sapling_payment_address() outputs must match" + ); + assert_eq!( + network.b58_pubkey_address_prefix(), + zp_network.b58_pubkey_address_prefix(), + "Parameters::b58_pubkey_address_prefix() outputs must match" + ); + assert_eq!( + network.b58_script_address_prefix(), + zp_network.b58_script_address_prefix(), + "Parameters::b58_script_address_prefix() outputs must match" + ); + } +} From 997e26563607ecf2790b3b67a83e87f07fc999a1 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 11 Apr 2024 16:47:19 -0400 Subject: [PATCH 22/51] fixes doc links --- zebra-chain/src/parameters/network/testnet.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zebra-chain/src/parameters/network/testnet.rs b/zebra-chain/src/parameters/network/testnet.rs index 654d4a3b5..8b18f45ef 100644 --- a/zebra-chain/src/parameters/network/testnet.rs +++ b/zebra-chain/src/parameters/network/testnet.rs @@ -11,14 +11,14 @@ use crate::{ pub struct Parameters { /// The network upgrade activation heights for this network. /// - /// Note: This value is ignored by [`Network::activation_list()`] when `zebra-chain` is + /// Note: This value is ignored by `Network::activation_list()` when `zebra-chain` is /// compiled with the `zebra-test` feature flag AND the `TEST_FAKE_ACTIVATION_HEIGHTS` /// environment variable is set. pub activation_heights: BTreeMap, } impl Default for Parameters { - /// Returns an instance of the default public testnet [`NetworkParameters`]. + /// Returns an instance of the default public testnet [`Parameters`]. fn default() -> Self { Self { activation_heights: TESTNET_ACTIVATION_HEIGHTS.iter().cloned().collect(), @@ -27,7 +27,7 @@ impl Default for Parameters { } impl Parameters { - /// Returns true if the instance of [`NetworkParameters`] represents the default public Testnet. + /// Returns true if the instance of [`Parameters`] represents the default public Testnet. pub fn is_default_testnet(&self) -> bool { self == &Self::default() } From 93c7a58330bd3e7d033837769f1c6dbb61c93d39 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 11 Apr 2024 22:07:20 -0400 Subject: [PATCH 23/51] - Makes the `activation_heights` config field optional by adding a #[serde(default)] - Panics if a non-zero activation height is provided for the `Genesis` network upgrade - Always sets the `Genesis` and `BeforeOverwinter` network upgrade activation heights to 0 and 1, `BeforeOverwinter` could be overwritten by a later network upgrade - Makes the `activation_heights` field on `Parameters` private, adds/uses an accessor method instead, and adds a builder struct and `build()` method --- zebra-chain/src/parameters/network/testnet.rs | 66 +++++++++++++++++-- zebra-chain/src/parameters/network_upgrade.rs | 2 +- zebra-network/src/config.rs | 17 +++-- zebrad/tests/common/configs/v1.7.0.toml | 1 - 4 files changed, 76 insertions(+), 10 deletions(-) diff --git a/zebra-chain/src/parameters/network/testnet.rs b/zebra-chain/src/parameters/network/testnet.rs index 8b18f45ef..f869a2873 100644 --- a/zebra-chain/src/parameters/network/testnet.rs +++ b/zebra-chain/src/parameters/network/testnet.rs @@ -6,6 +6,54 @@ use crate::{ parameters::{network_upgrade::TESTNET_ACTIVATION_HEIGHTS, NetworkUpgrade}, }; +/// Builder for the [`Parameters`] struct. +#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] +pub struct ParametersBuilder { + /// The network upgrade activation heights for this network, see [`Parameters::activation_heights`] for more details. + activation_heights: BTreeMap, +} + +impl Default for ParametersBuilder { + fn default() -> Self { + Self { + // # Correctness + // + // `Genesis` network upgrade activation height must always be 0 + // TODO: Find out if `BeforeOverwinter` must always be active at height 1 + activation_heights: [ + (Height(0), NetworkUpgrade::Genesis), + (Height(1), NetworkUpgrade::BeforeOverwinter), + ] + .into_iter() + .collect(), + } + } +} + +impl ParametersBuilder { + /// Extends network upgrade activation heights with the provided activation heights. + pub fn activation_heights( + mut self, + new_activation_heights: Vec<(Height, NetworkUpgrade)>, + ) -> Self { + let new_activation_heights: BTreeMap<_, _> = + new_activation_heights.iter().cloned().collect(); + + // # Correctness + // + // Height(0) must be reserved for the `NetworkUpgrade::Genesis`. + self.activation_heights + .extend(new_activation_heights.range(Height(1)..)); + self + } + + /// Converts the builder to a [`Parameters`] struct + pub fn finish(self) -> Parameters { + let Self { activation_heights } = self; + Parameters { activation_heights } + } +} + /// Network consensus parameters for test networks such as Regtest and the default Testnet. #[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct Parameters { @@ -14,21 +62,31 @@ pub struct Parameters { /// Note: This value is ignored by `Network::activation_list()` when `zebra-chain` is /// compiled with the `zebra-test` feature flag AND the `TEST_FAKE_ACTIVATION_HEIGHTS` /// environment variable is set. - pub activation_heights: BTreeMap, + activation_heights: BTreeMap, } impl Default for Parameters { /// Returns an instance of the default public testnet [`Parameters`]. fn default() -> Self { - Self { - activation_heights: TESTNET_ACTIVATION_HEIGHTS.iter().cloned().collect(), - } + Self::build() + .activation_heights(TESTNET_ACTIVATION_HEIGHTS.to_vec()) + .finish() } } impl Parameters { + /// Creates a new [`ParametersBuilder`]. + pub fn build() -> ParametersBuilder { + ParametersBuilder::default() + } + /// Returns true if the instance of [`Parameters`] represents the default public Testnet. pub fn is_default_testnet(&self) -> bool { self == &Self::default() } + + /// Returns a reference to the network upgrade activation heights + pub fn activation_heights(&self) -> &BTreeMap { + &self.activation_heights + } } diff --git a/zebra-chain/src/parameters/network_upgrade.rs b/zebra-chain/src/parameters/network_upgrade.rs index 724a412b8..2a4f9ef7b 100644 --- a/zebra-chain/src/parameters/network_upgrade.rs +++ b/zebra-chain/src/parameters/network_upgrade.rs @@ -264,7 +264,7 @@ impl Network { } Mainnet => MAINNET_ACTIVATION_HEIGHTS.iter().cloned().collect(), - Testnet(params) => params.activation_heights.clone(), + Testnet(params) => params.activation_heights().clone(), } } } diff --git a/zebra-network/src/config.rs b/zebra-network/src/config.rs index caf28b543..6358dff40 100644 --- a/zebra-network/src/config.rs +++ b/zebra-network/src/config.rs @@ -625,9 +625,9 @@ impl<'de> Deserialize<'de> for Config { where D: Deserializer<'de>, { - /// Network consensus parameters for test networks such as Regtest and the default Testnet. - #[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] - pub struct DTestnetParameters { + #[derive(Deserialize)] + struct DTestnetParameters { + #[serde(default)] pub(super) activation_heights: Vec<(u32, NetworkUpgrade)>, } @@ -686,6 +686,11 @@ impl<'de> Deserialize<'de> for Config { let activation_heights = activation_heights .into_iter() .map(|(height, network_upgrade)| { + assert!( + network_upgrade != NetworkUpgrade::Genesis || height == 0, + "Genesis network upgrade activation height is not configurable" + ); + ( height.try_into().expect("activation height must be valid"), network_upgrade, @@ -693,7 +698,11 @@ impl<'de> Deserialize<'de> for Config { }) .collect(); - Network::new_configured_testnet(testnet::Parameters { activation_heights }) + let testnet_parameters = testnet::Parameters::build() + .activation_heights(activation_heights) + .finish(); + + Network::new_configured_testnet(testnet_parameters) } else { // Convert to default `Network` for a `NetworkKind` if there are no testnet parameters. match network_kind { diff --git a/zebrad/tests/common/configs/v1.7.0.toml b/zebrad/tests/common/configs/v1.7.0.toml index 44540ed73..05a9def3f 100644 --- a/zebrad/tests/common/configs/v1.7.0.toml +++ b/zebrad/tests/common/configs/v1.7.0.toml @@ -62,7 +62,6 @@ peerset_initial_target_size = 25 [network.testnet_parameters] activation_heights = [ - [0, "Genesis"], [1, "BeforeOverwinter"], [207_500, "Overwinter"], [280_000, "Sapling"], From 0fd4158dad98ded60e28a397398b4c38cc4bcd9b Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 11 Apr 2024 22:29:52 -0400 Subject: [PATCH 24/51] small refactor of activation_heights() method --- zebra-chain/src/parameters/network.rs | 10 ++++------ zebra-chain/src/parameters/network_upgrade.rs | 3 +-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/zebra-chain/src/parameters/network.rs b/zebra-chain/src/parameters/network.rs index 15f80aa78..0ae7e3170 100644 --- a/zebra-chain/src/parameters/network.rs +++ b/zebra-chain/src/parameters/network.rs @@ -290,12 +290,10 @@ impl zp_consensus::Parameters for Network { zp_consensus::NetworkUpgrade::Nu5 => NetworkUpgrade::Nu5, }; - self.activation_list() - .into_iter() - .find(|(_, nu)| *nu == target_nu) - .map(|(Height(height), _)| height) - // Heights are hard-coded below Height::MAX or checked when the config is parsed. - .map(zp_consensus::BlockHeight::from_u32) + // Heights are hard-coded below Height::MAX or checked when the config is parsed. + target_nu + .activation_height(self) + .map(|Height(h)| zp_consensus::BlockHeight::from_u32(h)) } fn coin_type(&self) -> u32 { diff --git a/zebra-chain/src/parameters/network_upgrade.rs b/zebra-chain/src/parameters/network_upgrade.rs index 2a4f9ef7b..fe0e50b6f 100644 --- a/zebra-chain/src/parameters/network_upgrade.rs +++ b/zebra-chain/src/parameters/network_upgrade.rs @@ -300,9 +300,8 @@ impl NetworkUpgrade { network .activation_list() .iter() - .filter(|(_, nu)| nu == &self) + .find(|(_, nu)| nu == &self) .map(|(height, _)| *height) - .next() } /// Returns `true` if `height` is the activation height of any network upgrade From 3952caa0221afd8ff0fb60765c177796f91db195 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 11 Apr 2024 23:06:53 -0400 Subject: [PATCH 25/51] check that activation heights are in the right order --- zebra-chain/src/parameters/network/testnet.rs | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/zebra-chain/src/parameters/network/testnet.rs b/zebra-chain/src/parameters/network/testnet.rs index f869a2873..194714f30 100644 --- a/zebra-chain/src/parameters/network/testnet.rs +++ b/zebra-chain/src/parameters/network/testnet.rs @@ -19,9 +19,9 @@ impl Default for ParametersBuilder { // # Correctness // // `Genesis` network upgrade activation height must always be 0 - // TODO: Find out if `BeforeOverwinter` must always be active at height 1 activation_heights: [ (Height(0), NetworkUpgrade::Genesis), + // TODO: Find out if `BeforeOverwinter` must always be active at Height(1), remove it here if it's not required. (Height(1), NetworkUpgrade::BeforeOverwinter), ] .into_iter() @@ -31,19 +31,35 @@ impl Default for ParametersBuilder { } impl ParametersBuilder { - /// Extends network upgrade activation heights with the provided activation heights. - pub fn activation_heights( - mut self, - new_activation_heights: Vec<(Height, NetworkUpgrade)>, - ) -> Self { - let new_activation_heights: BTreeMap<_, _> = - new_activation_heights.iter().cloned().collect(); + /// Checks that the provided network upgrade activation heights are in the correct order, then + /// sets them as the new network upgrade activation heights. + pub fn activation_heights(mut self, activation_heights: Vec<(Height, NetworkUpgrade)>) -> Self { + let activation_heights: BTreeMap<_, _> = activation_heights + .into_iter() + // TODO: Find out if `BeforeOverwinter` is required at Height(1), remove this filter if it's not required to be at Height(1) + .filter(|&(_, nu)| nu != NetworkUpgrade::BeforeOverwinter) + .collect(); + + // Check that the provided network upgrade activation heights are in the same order by height as the default testnet activation heights + let network_upgrades: Vec<_> = activation_heights.iter().map(|(_h, &nu)| nu).collect(); + let mut activation_heights_iter = activation_heights.iter(); + for expected_network_upgrade in TESTNET_ACTIVATION_HEIGHTS.iter().map(|&(_, nu)| nu) { + if !network_upgrades.contains(&expected_network_upgrade) { + continue; + } else if let Some((_h, &network_upgrade)) = activation_heights_iter.next() { + assert!( + network_upgrade == expected_network_upgrade, + "network upgrades must be in order" + ); + } + } // # Correctness // // Height(0) must be reserved for the `NetworkUpgrade::Genesis`. - self.activation_heights - .extend(new_activation_heights.range(Height(1)..)); + self.activation_heights.split_off(&Height(2)); + self.activation_heights.extend(activation_heights); + self } From e697d36dee359805dd436f6c5ec7c0e560700593 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 11 Apr 2024 23:38:51 -0400 Subject: [PATCH 26/51] Updates `NetworkUpgrade::activation_height()` to return the height of the next NetworkUpgrade if it doesn't find the activation height of `&self` --- zebra-chain/src/parameters/network_upgrade.rs | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/zebra-chain/src/parameters/network_upgrade.rs b/zebra-chain/src/parameters/network_upgrade.rs index fe0e50b6f..635f48e69 100644 --- a/zebra-chain/src/parameters/network_upgrade.rs +++ b/zebra-chain/src/parameters/network_upgrade.rs @@ -7,7 +7,6 @@ use crate::parameters::{Network, Network::*}; use std::collections::{BTreeMap, HashMap}; use std::fmt; -use std::ops::Bound::*; use chrono::{DateTime, Duration, Utc}; use hex::{FromHex, ToHex}; @@ -280,11 +279,28 @@ impl NetworkUpgrade { .expect("every height has a current network upgrade") } + /// Returns the next expected network upgrade after this network upgrade + pub fn next_upgrade(self) -> Option { + match self { + Genesis => Some(BeforeOverwinter), + BeforeOverwinter => Some(Overwinter), + Overwinter => Some(Sapling), + Sapling => Some(Blossom), + Blossom => Some(Heartwood), + Heartwood => Some(Canopy), + Canopy => Some(Nu5), + Nu5 => None, + } + } + /// Returns the next network upgrade for `network` and `height`. /// /// Returns None if the next upgrade has not been implemented in Zebra /// yet. + #[cfg(test)] pub fn next(network: &Network, height: block::Height) -> Option { + use std::ops::Bound::*; + network .activation_list() .range((Excluded(height), Unbounded)) @@ -302,6 +318,10 @@ impl NetworkUpgrade { .iter() .find(|(_, nu)| nu == &self) .map(|(height, _)| *height) + .or_else(|| { + self.next_upgrade() + .and_then(|next_nu| next_nu.activation_height(network)) + }) } /// Returns `true` if `height` is the activation height of any network upgrade From 325a6c615491d79fdc699f7bdf3cddbcb9c06a71 Mon Sep 17 00:00:00 2001 From: Arya Date: Fri, 12 Apr 2024 15:04:51 -0400 Subject: [PATCH 27/51] checks that the miner address is of TestnetKind on Regtest and update assertion message --- .../src/methods/get_block_template_rpcs.rs | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/zebra-rpc/src/methods/get_block_template_rpcs.rs b/zebra-rpc/src/methods/get_block_template_rpcs.rs index de47bba3b..fe293000d 100644 --- a/zebra-rpc/src/methods/get_block_template_rpcs.rs +++ b/zebra-rpc/src/methods/get_block_template_rpcs.rs @@ -14,7 +14,7 @@ use zebra_chain::{ block::{self, Block, Height, TryIntoHeight}, chain_sync_status::ChainSyncStatus, chain_tip::ChainTip, - parameters::{Network, POW_AVERAGING_WINDOW}, + parameters::{Network, NetworkKind, POW_AVERAGING_WINDOW}, primitives, serialization::ZcashDeserializeInto, transparent::{ @@ -449,13 +449,23 @@ where ) -> Self { // Prevent loss of miner funds due to an unsupported or incorrect address type. if let Some(miner_address) = mining_config.miner_address.clone() { - assert_eq!( - miner_address.network_kind(), - network.kind(), - "incorrect miner address config: {miner_address} \ - network.network {network} and miner address network {} must match", - miner_address.network_kind(), - ); + match network.kind() { + network_kind @ NetworkKind::Mainnet => assert_eq!( + miner_address.network_kind(), + NetworkKind::Mainnet, + "Incorrect mining address config: Zebra is configured to run on a {network_kind} network, \ + but the configured mining address is for {}. The network kinds must match.", + miner_address.network_kind(), + ), + // `Regtest` uses `Testnet` transparent addresses. + network_kind @ (NetworkKind::Testnet | NetworkKind::Regtest) => assert_eq!( + miner_address.network_kind(), + NetworkKind::Testnet, + "Incorrect mining address config: Zebra is configured to run on a {network_kind} network, \ + but the configured mining address is for {}. The network kinds must match.", + miner_address.network_kind(), + ), + } } // A limit on the configured extra coinbase data, regardless of the current block height. From f724c0188f7ac033f08079fc922cde8665049c36 Mon Sep 17 00:00:00 2001 From: Arya Date: Fri, 12 Apr 2024 16:18:41 -0400 Subject: [PATCH 28/51] Adds a DNetworkUpgradeActivationHeights struct for better control over how activation heights can be configured --- zebra-network/src/config.rs | 52 ++++++++++++++++++------- zebrad/tests/common/configs/v1.7.0.toml | 20 +++++----- 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/zebra-network/src/config.rs b/zebra-network/src/config.rs index 6358dff40..e128f277f 100644 --- a/zebra-network/src/config.rs +++ b/zebra-network/src/config.rs @@ -625,10 +625,23 @@ impl<'de> Deserialize<'de> for Config { where D: Deserializer<'de>, { + #[derive(Deserialize, Default)] + #[serde(rename_all = "PascalCase")] + struct DNetworkUpgradeActivationHeights { + before_overwinter: Option, + overwinter: Option, + sapling: Option, + blossom: Option, + heartwood: Option, + canopy: Option, + #[serde(rename = "NU5")] + nu5: Option, + } + #[derive(Deserialize)] struct DTestnetParameters { #[serde(default)] - pub(super) activation_heights: Vec<(u32, NetworkUpgrade)>, + pub(super) activation_heights: DNetworkUpgradeActivationHeights, } #[derive(Deserialize)] @@ -675,7 +688,21 @@ impl<'de> Deserialize<'de> for Config { max_connections_per_ip, } = DConfig::deserialize(deserializer)?; - let network = if let Some(DTestnetParameters { activation_heights }) = testnet_parameters { + let network = if let Some(DTestnetParameters { + activation_heights: + DNetworkUpgradeActivationHeights { + before_overwinter, + overwinter, + sapling, + blossom, + heartwood, + canopy, + nu5, + }, + }) = testnet_parameters + { + use NetworkUpgrade::*; + // TODO: Panic here if the initial testnet peers are the default initial testnet peers. assert_eq!( network_kind, @@ -683,19 +710,16 @@ impl<'de> Deserialize<'de> for Config { "set network to 'Testnet' to use configured testnet parameters" ); - let activation_heights = activation_heights + let activation_heights = before_overwinter .into_iter() - .map(|(height, network_upgrade)| { - assert!( - network_upgrade != NetworkUpgrade::Genesis || height == 0, - "Genesis network upgrade activation height is not configurable" - ); - - ( - height.try_into().expect("activation height must be valid"), - network_upgrade, - ) - }) + .map(|h| (h, BeforeOverwinter)) + .chain(overwinter.into_iter().map(|h| (h, Overwinter))) + .chain(sapling.into_iter().map(|h| (h, Sapling))) + .chain(blossom.into_iter().map(|h| (h, Blossom))) + .chain(heartwood.into_iter().map(|h| (h, Heartwood))) + .chain(canopy.into_iter().map(|h| (h, Canopy))) + .chain(nu5.into_iter().map(|h| (h, Nu5))) + .map(|(h, nu)| (h.try_into().expect("activation height must be valid"), nu)) .collect(); let testnet_parameters = testnet::Parameters::build() diff --git a/zebrad/tests/common/configs/v1.7.0.toml b/zebrad/tests/common/configs/v1.7.0.toml index 05a9def3f..58aed232f 100644 --- a/zebrad/tests/common/configs/v1.7.0.toml +++ b/zebrad/tests/common/configs/v1.7.0.toml @@ -60,16 +60,14 @@ max_connections_per_ip = 1 network = "Testnet" peerset_initial_target_size = 25 -[network.testnet_parameters] -activation_heights = [ - [1, "BeforeOverwinter"], - [207_500, "Overwinter"], - [280_000, "Sapling"], - [584_000, "Blossom"], - [903_800, "Heartwood"], - [1_028_500, "Canopy"], - [1_842_420, "NU5"], -] +[network.testnet_parameters.activation_heights] +BeforeOverwinter = 1 +Overwinter = 207_500 +Sapling = 280_000 +Blossom = 584_000 +Heartwood = 903_800 +Canopy = 1_028_500 +NU5 = 1_842_420 [rpc] debug_force_finished_sync = false @@ -90,4 +88,4 @@ parallel_cpu_threads = 0 buffer_limit = 128000 force_use_color = false use_color = true -use_journald = false \ No newline at end of file +use_journald = false From a37510a67b1ff59bd24c078d41124e80f037dc10 Mon Sep 17 00:00:00 2001 From: Arya Date: Fri, 12 Apr 2024 17:19:34 -0400 Subject: [PATCH 29/51] moves all ordered network upgrades to a constant, adds a no_duplicates test, moves struct with activation heights outside deserialization impl and accepts it in `ParametersBuilder::activation_heights()` instead of a Vec --- zebra-chain/src/parameters/network/testnet.rs | 74 ++++++++++++++++--- .../src/parameters/network/tests/vectors.rs | 70 +++++++++++++++++- zebra-chain/src/parameters/network_upgrade.rs | 12 +++ zebra-network/src/config.rs | 56 +++----------- 4 files changed, 154 insertions(+), 58 deletions(-) diff --git a/zebra-chain/src/parameters/network/testnet.rs b/zebra-chain/src/parameters/network/testnet.rs index 194714f30..7805212bc 100644 --- a/zebra-chain/src/parameters/network/testnet.rs +++ b/zebra-chain/src/parameters/network/testnet.rs @@ -3,9 +3,33 @@ use std::collections::BTreeMap; use crate::{ block::Height, - parameters::{network_upgrade::TESTNET_ACTIVATION_HEIGHTS, NetworkUpgrade}, + parameters::{ + network_upgrade::TESTNET_ACTIVATION_HEIGHTS, Network, NetworkUpgrade, + NETWORK_UPGRADES_IN_ORDER, + }, }; +/// Configurable activation heights for Regtest and configured Testnets. +#[derive(Deserialize, Default)] +#[serde(rename_all = "PascalCase")] +pub struct ConfiguredActivationHeights { + /// Activation height for `BeforeOverwinter` network upgrade. + pub before_overwinter: Option, + /// Activation height for `Overwinter` network upgrade. + pub overwinter: Option, + /// Activation height for `Sapling` network upgrade. + pub sapling: Option, + /// Activation height for `Blossom` network upgrade. + pub blossom: Option, + /// Activation height for `Heartwood` network upgrade. + pub heartwood: Option, + /// Activation height for `Canopy` network upgrade. + pub canopy: Option, + /// Activation height for `NU5` network upgrade. + #[serde(rename = "NU5")] + pub nu5: Option, +} + /// Builder for the [`Parameters`] struct. #[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct ParametersBuilder { @@ -33,23 +57,48 @@ impl Default for ParametersBuilder { impl ParametersBuilder { /// Checks that the provided network upgrade activation heights are in the correct order, then /// sets them as the new network upgrade activation heights. - pub fn activation_heights(mut self, activation_heights: Vec<(Height, NetworkUpgrade)>) -> Self { - let activation_heights: BTreeMap<_, _> = activation_heights + pub fn activation_heights( + mut self, + ConfiguredActivationHeights { + before_overwinter, + overwinter, + sapling, + blossom, + heartwood, + canopy, + nu5, + }: ConfiguredActivationHeights, + ) -> Self { + use NetworkUpgrade::*; + + // # Correctness + // + // These must be in order so that later network upgrades overwrite prior ones + // if multiple network upgrades are configured with the same activation height. + let activation_heights: BTreeMap<_, _> = before_overwinter .into_iter() - // TODO: Find out if `BeforeOverwinter` is required at Height(1), remove this filter if it's not required to be at Height(1) + .map(|h| (h, BeforeOverwinter)) + .chain(overwinter.into_iter().map(|h| (h, Overwinter))) + .chain(sapling.into_iter().map(|h| (h, Sapling))) + .chain(blossom.into_iter().map(|h| (h, Blossom))) + .chain(heartwood.into_iter().map(|h| (h, Heartwood))) + .chain(canopy.into_iter().map(|h| (h, Canopy))) + .chain(nu5.into_iter().map(|h| (h, Nu5))) // TODO: Find out if `BeforeOverwinter` is required at Height(1), remove this filter if it's not required to be at Height(1) .filter(|&(_, nu)| nu != NetworkUpgrade::BeforeOverwinter) + .map(|(h, nu)| (h.try_into().expect("activation height must be valid"), nu)) .collect(); - // Check that the provided network upgrade activation heights are in the same order by height as the default testnet activation heights let network_upgrades: Vec<_> = activation_heights.iter().map(|(_h, &nu)| nu).collect(); + + // Check that the provided network upgrade activation heights are in the same order by height as the default testnet activation heights let mut activation_heights_iter = activation_heights.iter(); - for expected_network_upgrade in TESTNET_ACTIVATION_HEIGHTS.iter().map(|&(_, nu)| nu) { + for expected_network_upgrade in NETWORK_UPGRADES_IN_ORDER { if !network_upgrades.contains(&expected_network_upgrade) { continue; } else if let Some((_h, &network_upgrade)) = activation_heights_iter.next() { assert!( network_upgrade == expected_network_upgrade, - "network upgrades must be in order" + "network upgrades must be activated in order, the correct order is {NETWORK_UPGRADES_IN_ORDER:?}" ); } } @@ -68,6 +117,11 @@ impl ParametersBuilder { let Self { activation_heights } = self; Parameters { activation_heights } } + + /// Converts the builder to a configured [`Network::Testnet`] + pub fn to_network(self) -> Network { + Network::new_configured_testnet(self.finish()) + } } /// Network consensus parameters for test networks such as Regtest and the default Testnet. @@ -84,9 +138,9 @@ pub struct Parameters { impl Default for Parameters { /// Returns an instance of the default public testnet [`Parameters`]. fn default() -> Self { - Self::build() - .activation_heights(TESTNET_ACTIVATION_HEIGHTS.to_vec()) - .finish() + Self { + activation_heights: TESTNET_ACTIVATION_HEIGHTS.iter().cloned().collect(), + } } } diff --git a/zebra-chain/src/parameters/network/tests/vectors.rs b/zebra-chain/src/parameters/network/tests/vectors.rs index f95f41e5d..9fda5b55f 100644 --- a/zebra-chain/src/parameters/network/tests/vectors.rs +++ b/zebra-chain/src/parameters/network/tests/vectors.rs @@ -2,8 +2,16 @@ use zcash_primitives::consensus::{self as zp_consensus, Parameters}; -use crate::parameters::Network; +use crate::{ + block::Height, + parameters::{ + testnet::{self, ConfiguredActivationHeights}, + Network, NetworkUpgrade, NETWORK_UPGRADES_IN_ORDER, + }, +}; +/// Checks that every method in the `Parameters` impl for `zebra_chain::Network` has the same output +/// as the Parameters impl for `zcash_primitives::consensus::Network` on Mainnet and the default Testnet. #[test] fn check_parameters_impl() { let zp_network_upgrades = [ @@ -81,3 +89,63 @@ fn check_parameters_impl() { ); } } + +/// Checks that `NetworkUpgrade::activation_height()` returns the activation height of the next +/// network upgrade if it doesn't find an activation height for a prior network upgrade, and that the +/// `Genesis` upgrade is always at `Height(0)`. +#[test] +fn activates_network_upgrades_correctly() { + let expected_activation_height = 1; + let network = testnet::Parameters::build() + .activation_heights(ConfiguredActivationHeights { + nu5: Some(expected_activation_height), + ..Default::default() + }) + .to_network(); + + let genesis_activation_height = NetworkUpgrade::Genesis + .activation_height(&network) + .expect("must return an activation height"); + + assert_eq!( + genesis_activation_height, + Height(0), + "activation height for all networks after Genesis and BeforeOverwinter should match NU5 activation height" + ); + + for nu in NETWORK_UPGRADES_IN_ORDER.into_iter().skip(1) { + let activation_height = nu + .activation_height(&network) + .expect("must return an activation height"); + + assert_eq!( + activation_height, Height(expected_activation_height), + "activation height for all networks after Genesis and BeforeOverwinter \ + should match NU5 activation height, network_upgrade: {nu}, activation_height: {activation_height:?}" + ); + } +} + +/// Checks that there are no duplicate activation heights when using configured activation heights. +// TODO: Convert this to a proptest. +#[test] +fn no_duplicate_activation_heights() { + let network = testnet::Parameters::build() + .activation_heights(ConfiguredActivationHeights { + overwinter: Some(2), + ..Default::default() + }) + .to_network(); + + for target_nu in NETWORK_UPGRADES_IN_ORDER.into_iter().skip(1) { + assert!( + network + .activation_list() + .into_iter() + .filter(|&(_h, nu)| nu == target_nu) + .count() + <= 1, + "there should be at most 1 activation height per possible network upgrade" + ); + } +} diff --git a/zebra-chain/src/parameters/network_upgrade.rs b/zebra-chain/src/parameters/network_upgrade.rs index 635f48e69..178c2961a 100644 --- a/zebra-chain/src/parameters/network_upgrade.rs +++ b/zebra-chain/src/parameters/network_upgrade.rs @@ -14,6 +14,18 @@ use hex::{FromHex, ToHex}; #[cfg(any(test, feature = "proptest-impl"))] use proptest_derive::Arbitrary; +/// A list of network upgrades in the order that they must be activated. +pub const NETWORK_UPGRADES_IN_ORDER: [NetworkUpgrade; 8] = [ + Genesis, + BeforeOverwinter, + Overwinter, + Sapling, + Blossom, + Heartwood, + Canopy, + Nu5, +]; + /// A Zcash network upgrade. /// /// Network upgrades can change the Zcash network protocol or consensus rules in diff --git a/zebra-network/src/config.rs b/zebra-network/src/config.rs index e128f277f..51395d55f 100644 --- a/zebra-network/src/config.rs +++ b/zebra-network/src/config.rs @@ -15,7 +15,10 @@ use tempfile::NamedTempFile; use tokio::{fs, io::AsyncWriteExt}; use tracing::Span; -use zebra_chain::parameters::{testnet, Network, NetworkKind, NetworkUpgrade}; +use zebra_chain::parameters::{ + testnet::{self, ConfiguredActivationHeights}, + Network, NetworkKind, +}; use crate::{ constants::{ @@ -625,23 +628,10 @@ impl<'de> Deserialize<'de> for Config { where D: Deserializer<'de>, { - #[derive(Deserialize, Default)] - #[serde(rename_all = "PascalCase")] - struct DNetworkUpgradeActivationHeights { - before_overwinter: Option, - overwinter: Option, - sapling: Option, - blossom: Option, - heartwood: Option, - canopy: Option, - #[serde(rename = "NU5")] - nu5: Option, - } - #[derive(Deserialize)] struct DTestnetParameters { #[serde(default)] - pub(super) activation_heights: DNetworkUpgradeActivationHeights, + pub(super) activation_heights: ConfiguredActivationHeights, } #[derive(Deserialize)] @@ -688,45 +678,17 @@ impl<'de> Deserialize<'de> for Config { max_connections_per_ip, } = DConfig::deserialize(deserializer)?; - let network = if let Some(DTestnetParameters { - activation_heights: - DNetworkUpgradeActivationHeights { - before_overwinter, - overwinter, - sapling, - blossom, - heartwood, - canopy, - nu5, - }, - }) = testnet_parameters - { - use NetworkUpgrade::*; - - // TODO: Panic here if the initial testnet peers are the default initial testnet peers. + // TODO: Panic here if the initial testnet peers are the default initial testnet peers. + let network = if let Some(DTestnetParameters { activation_heights }) = testnet_parameters { assert_eq!( network_kind, NetworkKind::Testnet, "set network to 'Testnet' to use configured testnet parameters" ); - let activation_heights = before_overwinter - .into_iter() - .map(|h| (h, BeforeOverwinter)) - .chain(overwinter.into_iter().map(|h| (h, Overwinter))) - .chain(sapling.into_iter().map(|h| (h, Sapling))) - .chain(blossom.into_iter().map(|h| (h, Blossom))) - .chain(heartwood.into_iter().map(|h| (h, Heartwood))) - .chain(canopy.into_iter().map(|h| (h, Canopy))) - .chain(nu5.into_iter().map(|h| (h, Nu5))) - .map(|(h, nu)| (h.try_into().expect("activation height must be valid"), nu)) - .collect(); - - let testnet_parameters = testnet::Parameters::build() + testnet::Parameters::build() .activation_heights(activation_heights) - .finish(); - - Network::new_configured_testnet(testnet_parameters) + .to_network() } else { // Convert to default `Network` for a `NetworkKind` if there are no testnet parameters. match network_kind { From 0abe87312bef5d7883b6aced4d3056f354a8f8ff Mon Sep 17 00:00:00 2001 From: Arya Date: Fri, 12 Apr 2024 17:36:07 -0400 Subject: [PATCH 30/51] panics if any network upgrades are configured to activate at Height(0) because it's reserved for Genesis --- zebra-chain/src/parameters/network/testnet.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/zebra-chain/src/parameters/network/testnet.rs b/zebra-chain/src/parameters/network/testnet.rs index 7805212bc..4d6196f8f 100644 --- a/zebra-chain/src/parameters/network/testnet.rs +++ b/zebra-chain/src/parameters/network/testnet.rs @@ -95,7 +95,13 @@ impl ParametersBuilder { for expected_network_upgrade in NETWORK_UPGRADES_IN_ORDER { if !network_upgrades.contains(&expected_network_upgrade) { continue; - } else if let Some((_h, &network_upgrade)) = activation_heights_iter.next() { + } else if let Some((&height, &network_upgrade)) = activation_heights_iter.next() { + assert_ne!( + height, + Height(0), + "Height(0) is reserved for the `Genesis` upgrade" + ); + assert!( network_upgrade == expected_network_upgrade, "network upgrades must be activated in order, the correct order is {NETWORK_UPGRADES_IN_ORDER:?}" From 1dfad530b1da0599f4360f3d948cb9ff89ac2130 Mon Sep 17 00:00:00 2001 From: Arya Date: Fri, 12 Apr 2024 18:15:52 -0400 Subject: [PATCH 31/51] Simplifies the `ParametersBuilder::activation_heights()` method and removes an unnecessary test. --- zebra-chain/src/parameters/network/testnet.rs | 10 ++++---- .../src/parameters/network/tests/vectors.rs | 24 ------------------- 2 files changed, 5 insertions(+), 29 deletions(-) diff --git a/zebra-chain/src/parameters/network/testnet.rs b/zebra-chain/src/parameters/network/testnet.rs index 4d6196f8f..c69c98ffc 100644 --- a/zebra-chain/src/parameters/network/testnet.rs +++ b/zebra-chain/src/parameters/network/testnet.rs @@ -60,7 +60,8 @@ impl ParametersBuilder { pub fn activation_heights( mut self, ConfiguredActivationHeights { - before_overwinter, + // TODO: Find out if `BeforeOverwinter` is required at Height(1), remove this filter if it's not required to be at Height(1) + before_overwinter: _, overwinter, sapling, blossom, @@ -75,15 +76,14 @@ impl ParametersBuilder { // // These must be in order so that later network upgrades overwrite prior ones // if multiple network upgrades are configured with the same activation height. - let activation_heights: BTreeMap<_, _> = before_overwinter + let activation_heights: BTreeMap<_, _> = overwinter .into_iter() - .map(|h| (h, BeforeOverwinter)) - .chain(overwinter.into_iter().map(|h| (h, Overwinter))) + .map(|h| (h, Overwinter)) .chain(sapling.into_iter().map(|h| (h, Sapling))) .chain(blossom.into_iter().map(|h| (h, Blossom))) .chain(heartwood.into_iter().map(|h| (h, Heartwood))) .chain(canopy.into_iter().map(|h| (h, Canopy))) - .chain(nu5.into_iter().map(|h| (h, Nu5))) // TODO: Find out if `BeforeOverwinter` is required at Height(1), remove this filter if it's not required to be at Height(1) + .chain(nu5.into_iter().map(|h| (h, Nu5))) .filter(|&(_, nu)| nu != NetworkUpgrade::BeforeOverwinter) .map(|(h, nu)| (h.try_into().expect("activation height must be valid"), nu)) .collect(); diff --git a/zebra-chain/src/parameters/network/tests/vectors.rs b/zebra-chain/src/parameters/network/tests/vectors.rs index 9fda5b55f..b6fca27c2 100644 --- a/zebra-chain/src/parameters/network/tests/vectors.rs +++ b/zebra-chain/src/parameters/network/tests/vectors.rs @@ -125,27 +125,3 @@ fn activates_network_upgrades_correctly() { ); } } - -/// Checks that there are no duplicate activation heights when using configured activation heights. -// TODO: Convert this to a proptest. -#[test] -fn no_duplicate_activation_heights() { - let network = testnet::Parameters::build() - .activation_heights(ConfiguredActivationHeights { - overwinter: Some(2), - ..Default::default() - }) - .to_network(); - - for target_nu in NETWORK_UPGRADES_IN_ORDER.into_iter().skip(1) { - assert!( - network - .activation_list() - .into_iter() - .filter(|&(_h, nu)| nu == target_nu) - .count() - <= 1, - "there should be at most 1 activation height per possible network upgrade" - ); - } -} From 0b835f1f05003fa4f17e6d5a2af89272c025472b Mon Sep 17 00:00:00 2001 From: Arya Date: Wed, 17 Apr 2024 18:39:16 -0400 Subject: [PATCH 32/51] Adds Sapling HRPs as fields on testnet params. (#8398) --- zebra-chain/src/parameters/network.rs | 24 ++------ zebra-chain/src/parameters/network/testnet.rs | 57 ++++++++++++++++++- 2 files changed, 59 insertions(+), 22 deletions(-) diff --git a/zebra-chain/src/parameters/network.rs b/zebra-chain/src/parameters/network.rs index 0ae7e3170..47bcf68e8 100644 --- a/zebra-chain/src/parameters/network.rs +++ b/zebra-chain/src/parameters/network.rs @@ -308,43 +308,29 @@ impl zp_consensus::Parameters for Network { fn address_network(&self) -> Option { match self { Network::Mainnet => Some(zcash_address::Network::Main), - Network::Testnet(params) if params.is_default_testnet() => { - Some(zcash_address::Network::Test) - } - _other => None, + // TODO: Check if network is `Regtest` first, and if it is, return `zcash_address::Network::Regtest` + Network::Testnet(_params) => Some(zcash_address::Network::Test), } } fn hrp_sapling_extended_spending_key(&self) -> &str { match self { Network::Mainnet => zp_constants::mainnet::HRP_SAPLING_EXTENDED_SPENDING_KEY, - Network::Testnet(params) if params.is_default_testnet() => { - zp_constants::testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY - } - // TODO: Add this as a field to testnet params - _other => "secret-extended-key-unknown-test", + Network::Testnet(params) => params.hrp_sapling_extended_spending_key(), } } fn hrp_sapling_extended_full_viewing_key(&self) -> &str { match self { Network::Mainnet => zp_constants::mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, - Network::Testnet(params) if params.is_default_testnet() => { - zp_constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY - } - // TODO: Add this as a field to testnet params - _other => "zxviewunknowntestsapling", + Network::Testnet(params) => params.hrp_sapling_extended_full_viewing_key(), } } fn hrp_sapling_payment_address(&self) -> &str { match self { Network::Mainnet => zp_constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, - Network::Testnet(params) if params.is_default_testnet() => { - zp_constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS - } - // TODO: Add this as a field to testnet params - _other => "zunknowntestsapling", + Network::Testnet(params) => params.hrp_sapling_payment_address(), } } diff --git a/zebra-chain/src/parameters/network/testnet.rs b/zebra-chain/src/parameters/network/testnet.rs index c69c98ffc..1720daab1 100644 --- a/zebra-chain/src/parameters/network/testnet.rs +++ b/zebra-chain/src/parameters/network/testnet.rs @@ -1,6 +1,8 @@ //! Types and implementation for Testnet consensus parameters use std::collections::BTreeMap; +use zcash_primitives::constants as zp_constants; + use crate::{ block::Height, parameters::{ @@ -35,6 +37,12 @@ pub struct ConfiguredActivationHeights { pub struct ParametersBuilder { /// The network upgrade activation heights for this network, see [`Parameters::activation_heights`] for more details. activation_heights: BTreeMap, + /// Sapling extended spending key human-readable prefix for this network + hrp_sapling_extended_spending_key: String, + /// Sapling extended full viewing key human-readable prefix for this network + hrp_sapling_extended_full_viewing_key: String, + /// Sapling payment address human-readable prefix for this network + hrp_sapling_payment_address: String, } impl Default for ParametersBuilder { @@ -50,6 +58,12 @@ impl Default for ParametersBuilder { ] .into_iter() .collect(), + hrp_sapling_extended_spending_key: + zp_constants::testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY.to_string(), + hrp_sapling_extended_full_viewing_key: + zp_constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY.to_string(), + hrp_sapling_payment_address: zp_constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS + .to_string(), } } } @@ -120,8 +134,18 @@ impl ParametersBuilder { /// Converts the builder to a [`Parameters`] struct pub fn finish(self) -> Parameters { - let Self { activation_heights } = self; - Parameters { activation_heights } + let Self { + activation_heights, + hrp_sapling_extended_spending_key, + hrp_sapling_extended_full_viewing_key, + hrp_sapling_payment_address, + } = self; + Parameters { + activation_heights, + hrp_sapling_extended_spending_key, + hrp_sapling_extended_full_viewing_key, + hrp_sapling_payment_address, + } } /// Converts the builder to a configured [`Network::Testnet`] @@ -139,6 +163,12 @@ pub struct Parameters { /// compiled with the `zebra-test` feature flag AND the `TEST_FAKE_ACTIVATION_HEIGHTS` /// environment variable is set. activation_heights: BTreeMap, + /// Sapling extended spending key human-readable prefix for this network + hrp_sapling_extended_spending_key: String, + /// Sapling extended full viewing key human-readable prefix for this network + hrp_sapling_extended_full_viewing_key: String, + /// Sapling payment address human-readable prefix for this network + hrp_sapling_payment_address: String, } impl Default for Parameters { @@ -146,6 +176,12 @@ impl Default for Parameters { fn default() -> Self { Self { activation_heights: TESTNET_ACTIVATION_HEIGHTS.iter().cloned().collect(), + hrp_sapling_extended_spending_key: + zp_constants::testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY.to_string(), + hrp_sapling_extended_full_viewing_key: + zp_constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY.to_string(), + hrp_sapling_payment_address: zp_constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS + .to_string(), } } } @@ -161,8 +197,23 @@ impl Parameters { self == &Self::default() } - /// Returns a reference to the network upgrade activation heights + /// Returns the network upgrade activation heights pub fn activation_heights(&self) -> &BTreeMap { &self.activation_heights } + + /// Returns the `hrp_sapling_extended_spending_key` field + pub fn hrp_sapling_extended_spending_key(&self) -> &str { + &self.hrp_sapling_extended_spending_key + } + + /// Returns the `hrp_sapling_extended_full_viewing_key` field + pub fn hrp_sapling_extended_full_viewing_key(&self) -> &str { + &self.hrp_sapling_extended_full_viewing_key + } + + /// Returns the `hrp_sapling_payment_address` field + pub fn hrp_sapling_payment_address(&self) -> &str { + &self.hrp_sapling_payment_address + } } From fb2d13a7062d5d65db49cf89a472425c9efd59bd Mon Sep 17 00:00:00 2001 From: Arya Date: Wed, 17 Apr 2024 18:57:01 -0400 Subject: [PATCH 33/51] Applies suggestions from code review. --- zebra-chain/src/parameters/network/testnet.rs | 3 ++- zebra-chain/src/parameters/network_upgrade.rs | 9 ++++++++- zebra-rpc/src/methods/get_block_template_rpcs.rs | 12 +++++++----- zebrad/tests/acceptance.rs | 6 ++---- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/zebra-chain/src/parameters/network/testnet.rs b/zebra-chain/src/parameters/network/testnet.rs index 1720daab1..1a1b47001 100644 --- a/zebra-chain/src/parameters/network/testnet.rs +++ b/zebra-chain/src/parameters/network/testnet.rs @@ -74,7 +74,8 @@ impl ParametersBuilder { pub fn activation_heights( mut self, ConfiguredActivationHeights { - // TODO: Find out if `BeforeOverwinter` is required at Height(1), remove this filter if it's not required to be at Height(1) + // TODO: Find out if `BeforeOverwinter` is required at Height(1), allow for + // configuring its activation height if it's not required to be at Height(1) before_overwinter: _, overwinter, sapling, diff --git a/zebra-chain/src/parameters/network_upgrade.rs b/zebra-chain/src/parameters/network_upgrade.rs index c8da7911e..9035633ee 100644 --- a/zebra-chain/src/parameters/network_upgrade.rs +++ b/zebra-chain/src/parameters/network_upgrade.rs @@ -319,10 +319,17 @@ impl NetworkUpgrade { .next() } - /// Returns the activation height for this network upgrade on `network`. + /// Returns the activation height for this network upgrade on `network`, or + /// + /// Returns the activation height of the first network upgrade that relies + /// on this network upgrade if there is no activation height for this network upgrade + /// such as on Regtest or a configured Testnet where multiple network upgrades have the + /// same activation height, or if one is omitted when others that rely on it are included. /// /// Returns None if this network upgrade is a future upgrade, and its /// activation height has not been set yet. + /// + /// Returns None if this network upgrade has not been configured on a Testnet or Regtest. pub fn activation_height(&self, network: &Network) -> Option { network .activation_list() diff --git a/zebra-rpc/src/methods/get_block_template_rpcs.rs b/zebra-rpc/src/methods/get_block_template_rpcs.rs index fe293000d..737d33659 100644 --- a/zebra-rpc/src/methods/get_block_template_rpcs.rs +++ b/zebra-rpc/src/methods/get_block_template_rpcs.rs @@ -450,19 +450,21 @@ where // Prevent loss of miner funds due to an unsupported or incorrect address type. if let Some(miner_address) = mining_config.miner_address.clone() { match network.kind() { - network_kind @ NetworkKind::Mainnet => assert_eq!( + NetworkKind::Mainnet => assert_eq!( miner_address.network_kind(), NetworkKind::Mainnet, - "Incorrect mining address config: Zebra is configured to run on a {network_kind} network, \ - but the configured mining address is for {}. The network kinds must match.", + "Incorrect config: Zebra is configured to run on a Mainnet network, \ + which implies the configured mining address needs to be for Mainnet, \ + but the provided address is for {}.", miner_address.network_kind(), ), // `Regtest` uses `Testnet` transparent addresses. network_kind @ (NetworkKind::Testnet | NetworkKind::Regtest) => assert_eq!( miner_address.network_kind(), NetworkKind::Testnet, - "Incorrect mining address config: Zebra is configured to run on a {network_kind} network, \ - but the configured mining address is for {}. The network kinds must match.", + "Incorrect config: Zebra is configured to run on a {network_kind} network, \ + which implies the configured mining address needs to be for Testnet, \ + but the provided address is for {}.", miner_address.network_kind(), ), } diff --git a/zebrad/tests/acceptance.rs b/zebrad/tests/acceptance.rs index ffb3ffb28..df1ed4179 100644 --- a/zebrad/tests/acceptance.rs +++ b/zebrad/tests/acceptance.rs @@ -972,15 +972,13 @@ fn stored_configs_parsed_correctly() -> Result<()> { continue; } - let stored_config_path = config_file_full_path(config_file.path()); - tracing::info!( - ?stored_config_path, + ?config_file_path, "testing old config can be parsed by current zebrad" ); ZebradApp::default() - .load_config(&stored_config_path) + .load_config(&config_file_path) .expect("config should parse"); } From fea2162419b5953f2263b4887dcb1dc317c2f7fb Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 18 Apr 2024 10:13:53 -0400 Subject: [PATCH 34/51] Update zebra-chain/src/parameters/network_upgrade.rs Co-authored-by: Marek --- zebra-chain/src/parameters/network_upgrade.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zebra-chain/src/parameters/network_upgrade.rs b/zebra-chain/src/parameters/network_upgrade.rs index 9035633ee..de546e3b4 100644 --- a/zebra-chain/src/parameters/network_upgrade.rs +++ b/zebra-chain/src/parameters/network_upgrade.rs @@ -321,10 +321,10 @@ impl NetworkUpgrade { /// Returns the activation height for this network upgrade on `network`, or /// - /// Returns the activation height of the first network upgrade that relies - /// on this network upgrade if there is no activation height for this network upgrade + /// Returns the activation height of the first network upgrade that follows + /// this network upgrade if there is no activation height for this network upgrade /// such as on Regtest or a configured Testnet where multiple network upgrades have the - /// same activation height, or if one is omitted when others that rely on it are included. + /// same activation height, or if one is omitted when others that follow it are included. /// /// Returns None if this network upgrade is a future upgrade, and its /// activation height has not been set yet. From a94dcabaca4f782123338a8ee8118af871849f02 Mon Sep 17 00:00:00 2001 From: Arya Date: Wed, 17 Apr 2024 21:10:09 -0400 Subject: [PATCH 35/51] Adds `network_name` field and accessor method on `Parameters`, uses it in the `Display` impl for `Network` --- zebra-chain/src/parameters/network.rs | 8 +++--- zebra-chain/src/parameters/network/testnet.rs | 25 ++++++++++++++----- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/zebra-chain/src/parameters/network.rs b/zebra-chain/src/parameters/network.rs index 47bcf68e8..e346e28e6 100644 --- a/zebra-chain/src/parameters/network.rs +++ b/zebra-chain/src/parameters/network.rs @@ -138,15 +138,13 @@ impl fmt::Display for NetworkKind { } } -impl From<&Network> for &'static str { - fn from(network: &Network) -> &'static str { +impl<'a> From<&'a Network> for &'a str { + fn from(network: &'a Network) -> &'a str { match network { Network::Mainnet => "Mainnet", // TODO: - // - Add a `name` field to use here instead of checking `is_default_testnet()` // - zcashd calls the Regtest cache dir 'regtest' (#8327). - Network::Testnet(params) if params.is_default_testnet() => "Testnet", - Network::Testnet(_params) => "UnknownTestnet", + Network::Testnet(params) => params.network_name(), } } } diff --git a/zebra-chain/src/parameters/network/testnet.rs b/zebra-chain/src/parameters/network/testnet.rs index 1a1b47001..86e39add6 100644 --- a/zebra-chain/src/parameters/network/testnet.rs +++ b/zebra-chain/src/parameters/network/testnet.rs @@ -35,6 +35,8 @@ pub struct ConfiguredActivationHeights { /// Builder for the [`Parameters`] struct. #[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct ParametersBuilder { + /// The name of this network to be used by the `Display` trait impl. + network_name: String, /// The network upgrade activation heights for this network, see [`Parameters::activation_heights`] for more details. activation_heights: BTreeMap, /// Sapling extended spending key human-readable prefix for this network @@ -48,6 +50,7 @@ pub struct ParametersBuilder { impl Default for ParametersBuilder { fn default() -> Self { Self { + network_name: "Testnet".to_string(), // # Correctness // // `Genesis` network upgrade activation height must always be 0 @@ -69,6 +72,12 @@ impl Default for ParametersBuilder { } impl ParametersBuilder { + /// Sets the network name to be used in the [`Parameters`] being built. + pub fn network_name(mut self, network_name: String) -> Self { + self.network_name = network_name; + self + } + /// Checks that the provided network upgrade activation heights are in the correct order, then /// sets them as the new network upgrade activation heights. pub fn activation_heights( @@ -136,12 +145,14 @@ impl ParametersBuilder { /// Converts the builder to a [`Parameters`] struct pub fn finish(self) -> Parameters { let Self { + network_name, activation_heights, hrp_sapling_extended_spending_key, hrp_sapling_extended_full_viewing_key, hrp_sapling_payment_address, } = self; Parameters { + network_name, activation_heights, hrp_sapling_extended_spending_key, hrp_sapling_extended_full_viewing_key, @@ -158,6 +169,8 @@ impl ParametersBuilder { /// Network consensus parameters for test networks such as Regtest and the default Testnet. #[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct Parameters { + /// The name of this network to be used by the `Display` trait impl. + network_name: String, /// The network upgrade activation heights for this network. /// /// Note: This value is ignored by `Network::activation_list()` when `zebra-chain` is @@ -177,12 +190,7 @@ impl Default for Parameters { fn default() -> Self { Self { activation_heights: TESTNET_ACTIVATION_HEIGHTS.iter().cloned().collect(), - hrp_sapling_extended_spending_key: - zp_constants::testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY.to_string(), - hrp_sapling_extended_full_viewing_key: - zp_constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY.to_string(), - hrp_sapling_payment_address: zp_constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS - .to_string(), + ..Self::build().finish() } } } @@ -198,6 +206,11 @@ impl Parameters { self == &Self::default() } + /// Returns the network upgrade activation heights + pub fn network_name(&self) -> &str { + &self.network_name + } + /// Returns the network upgrade activation heights pub fn activation_heights(&self) -> &BTreeMap { &self.activation_heights From 1173faf8949677d08cd897db543bd0f59588ba81 Mon Sep 17 00:00:00 2001 From: Arya Date: Wed, 17 Apr 2024 21:10:30 -0400 Subject: [PATCH 36/51] Removes unnecessary `.filter()` --- zebra-chain/src/parameters/network/testnet.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/zebra-chain/src/parameters/network/testnet.rs b/zebra-chain/src/parameters/network/testnet.rs index 86e39add6..4c99fe18a 100644 --- a/zebra-chain/src/parameters/network/testnet.rs +++ b/zebra-chain/src/parameters/network/testnet.rs @@ -108,7 +108,6 @@ impl ParametersBuilder { .chain(heartwood.into_iter().map(|h| (h, Heartwood))) .chain(canopy.into_iter().map(|h| (h, Canopy))) .chain(nu5.into_iter().map(|h| (h, Nu5))) - .filter(|&(_, nu)| nu != NetworkUpgrade::BeforeOverwinter) .map(|(h, nu)| (h.try_into().expect("activation height must be valid"), nu)) .collect(); From 1608472f5af89145ee27909c67d19e96c6114008 Mon Sep 17 00:00:00 2001 From: Arya Date: Wed, 17 Apr 2024 22:41:11 -0400 Subject: [PATCH 37/51] adds constraints on valid network names and a test --- zebra-chain/src/parameters/network/testnet.rs | 33 +++++++++-- .../src/parameters/network/tests/vectors.rs | 59 ++++++++++++++++++- 2 files changed, 87 insertions(+), 5 deletions(-) diff --git a/zebra-chain/src/parameters/network/testnet.rs b/zebra-chain/src/parameters/network/testnet.rs index 4c99fe18a..6fd57e615 100644 --- a/zebra-chain/src/parameters/network/testnet.rs +++ b/zebra-chain/src/parameters/network/testnet.rs @@ -1,5 +1,5 @@ //! Types and implementation for Testnet consensus parameters -use std::collections::BTreeMap; +use std::{collections::BTreeMap, fmt}; use zcash_primitives::constants as zp_constants; @@ -11,6 +11,12 @@ use crate::{ }, }; +/// Reserved network names that should not be allowed for configured Testnets. +pub const RESERVED_NETWORK_NAMES: [&str; 3] = ["Mainnet", "Testnet", "Regtest"]; + +/// Maximum length for a configured network name. +pub const MAX_NETWORK_NAME_LENGTH: usize = 30; + /// Configurable activation heights for Regtest and configured Testnets. #[derive(Deserialize, Default)] #[serde(rename_all = "PascalCase")] @@ -50,7 +56,7 @@ pub struct ParametersBuilder { impl Default for ParametersBuilder { fn default() -> Self { Self { - network_name: "Testnet".to_string(), + network_name: "UnknownTestnet".to_string(), // # Correctness // // `Genesis` network upgrade activation height must always be 0 @@ -73,8 +79,26 @@ impl Default for ParametersBuilder { impl ParametersBuilder { /// Sets the network name to be used in the [`Parameters`] being built. - pub fn network_name(mut self, network_name: String) -> Self { - self.network_name = network_name; + pub fn network_name(mut self, network_name: impl fmt::Display) -> Self { + self.network_name = network_name.to_string(); + + assert!( + !RESERVED_NETWORK_NAMES.contains(&self.network_name.as_str()), + "cannot use reserved network name '{network_name}' as configured Testnet name, reserved names: {RESERVED_NETWORK_NAMES:?}" + ); + + assert!( + self.network_name.len() <= MAX_NETWORK_NAME_LENGTH, + "network name {network_name} is too long, must be {MAX_NETWORK_NAME_LENGTH} characters or less" + ); + + assert!( + self.network_name + .chars() + .all(|x| x.is_alphanumeric() || x == '_'), + "network name must include only alphanumeric characters or '_'" + ); + self } @@ -188,6 +212,7 @@ impl Default for Parameters { /// Returns an instance of the default public testnet [`Parameters`]. fn default() -> Self { Self { + network_name: "Testnet".to_string(), activation_heights: TESTNET_ACTIVATION_HEIGHTS.iter().cloned().collect(), ..Self::build().finish() } diff --git a/zebra-chain/src/parameters/network/tests/vectors.rs b/zebra-chain/src/parameters/network/tests/vectors.rs index b6fca27c2..881501905 100644 --- a/zebra-chain/src/parameters/network/tests/vectors.rs +++ b/zebra-chain/src/parameters/network/tests/vectors.rs @@ -5,7 +5,9 @@ use zcash_primitives::consensus::{self as zp_consensus, Parameters}; use crate::{ block::Height, parameters::{ - testnet::{self, ConfiguredActivationHeights}, + testnet::{ + self, ConfiguredActivationHeights, MAX_NETWORK_NAME_LENGTH, RESERVED_NETWORK_NAMES, + }, Network, NetworkUpgrade, NETWORK_UPGRADES_IN_ORDER, }, }; @@ -125,3 +127,58 @@ fn activates_network_upgrades_correctly() { ); } } + +/// Checks that configured testnet names are validated and used correctly. +#[test] +fn check_network_name() { + // Sets a no-op panic hook to avoid long output. + std::panic::set_hook(Box::new(|_| {})); + + // Checks that reserved network names cannot be used for configured testnets. + for reserved_network_name in RESERVED_NETWORK_NAMES { + std::panic::catch_unwind(|| { + testnet::Parameters::build().network_name(reserved_network_name) + }) + .expect_err("should panic when attempting to set network name as a reserved name"); + } + + // Check that max length is enforced, and that network names may only contain alphanumeric characters and '_'. + for invalid_network_name in [ + "a".repeat(MAX_NETWORK_NAME_LENGTH + 1), + "!!!!non-alphanumeric-name".to_string(), + ] { + std::panic::catch_unwind(|| { + testnet::Parameters::build().network_name(invalid_network_name) + }) + .expect_err("should panic when setting network name that's too long or contains non-alphanumeric characters (except '_')"); + } + + // Checks that network names are displayed correctly + assert_eq!( + Network::new_default_testnet().to_string(), + "Testnet", + "default testnet should be displayed as 'Testnet'" + ); + assert_eq!( + Network::Mainnet.to_string(), + "Mainnet", + "Mainnet should be displayed as 'Mainnet'" + ); + + // TODO: Check Regtest + + // Check that network name can contain alphanumeric characters and '_'. + let expected_name = "ConfiguredTestnet_1"; + let network = testnet::Parameters::build() + // Check that network name can contain `MAX_NETWORK_NAME_LENGTH` characters + .network_name("a".repeat(MAX_NETWORK_NAME_LENGTH)) + .network_name(expected_name) + .to_network(); + + // Check that configured network name is displayed + assert_eq!( + network.to_string(), + expected_name, + "network must be displayed as configured network name" + ); +} From 08f0838079aa09ff715a5fe6c62779890f987d62 Mon Sep 17 00:00:00 2001 From: Arya Date: Wed, 17 Apr 2024 22:49:03 -0400 Subject: [PATCH 38/51] adds config field for setting network name, adds "with_" prefix to ParameterBuilder setter methods --- zebra-chain/src/parameters/network/testnet.rs | 4 ++-- .../src/parameters/network/tests/vectors.rs | 10 +++++----- zebra-network/src/config.rs | 19 +++++++++++++++---- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/zebra-chain/src/parameters/network/testnet.rs b/zebra-chain/src/parameters/network/testnet.rs index 6fd57e615..0b01c493b 100644 --- a/zebra-chain/src/parameters/network/testnet.rs +++ b/zebra-chain/src/parameters/network/testnet.rs @@ -79,7 +79,7 @@ impl Default for ParametersBuilder { impl ParametersBuilder { /// Sets the network name to be used in the [`Parameters`] being built. - pub fn network_name(mut self, network_name: impl fmt::Display) -> Self { + pub fn with_network_name(mut self, network_name: impl fmt::Display) -> Self { self.network_name = network_name.to_string(); assert!( @@ -104,7 +104,7 @@ impl ParametersBuilder { /// Checks that the provided network upgrade activation heights are in the correct order, then /// sets them as the new network upgrade activation heights. - pub fn activation_heights( + pub fn with_activation_heights( mut self, ConfiguredActivationHeights { // TODO: Find out if `BeforeOverwinter` is required at Height(1), allow for diff --git a/zebra-chain/src/parameters/network/tests/vectors.rs b/zebra-chain/src/parameters/network/tests/vectors.rs index 881501905..1d619aa5e 100644 --- a/zebra-chain/src/parameters/network/tests/vectors.rs +++ b/zebra-chain/src/parameters/network/tests/vectors.rs @@ -99,7 +99,7 @@ fn check_parameters_impl() { fn activates_network_upgrades_correctly() { let expected_activation_height = 1; let network = testnet::Parameters::build() - .activation_heights(ConfiguredActivationHeights { + .with_activation_heights(ConfiguredActivationHeights { nu5: Some(expected_activation_height), ..Default::default() }) @@ -137,7 +137,7 @@ fn check_network_name() { // Checks that reserved network names cannot be used for configured testnets. for reserved_network_name in RESERVED_NETWORK_NAMES { std::panic::catch_unwind(|| { - testnet::Parameters::build().network_name(reserved_network_name) + testnet::Parameters::build().with_network_name(reserved_network_name) }) .expect_err("should panic when attempting to set network name as a reserved name"); } @@ -148,7 +148,7 @@ fn check_network_name() { "!!!!non-alphanumeric-name".to_string(), ] { std::panic::catch_unwind(|| { - testnet::Parameters::build().network_name(invalid_network_name) + testnet::Parameters::build().with_network_name(invalid_network_name) }) .expect_err("should panic when setting network name that's too long or contains non-alphanumeric characters (except '_')"); } @@ -171,8 +171,8 @@ fn check_network_name() { let expected_name = "ConfiguredTestnet_1"; let network = testnet::Parameters::build() // Check that network name can contain `MAX_NETWORK_NAME_LENGTH` characters - .network_name("a".repeat(MAX_NETWORK_NAME_LENGTH)) - .network_name(expected_name) + .with_network_name("a".repeat(MAX_NETWORK_NAME_LENGTH)) + .with_network_name(expected_name) .to_network(); // Check that configured network name is displayed diff --git a/zebra-network/src/config.rs b/zebra-network/src/config.rs index 51395d55f..71ca3974c 100644 --- a/zebra-network/src/config.rs +++ b/zebra-network/src/config.rs @@ -630,8 +630,9 @@ impl<'de> Deserialize<'de> for Config { { #[derive(Deserialize)] struct DTestnetParameters { + network_name: Option, #[serde(default)] - pub(super) activation_heights: ConfiguredActivationHeights, + activation_heights: ConfiguredActivationHeights, } #[derive(Deserialize)] @@ -679,15 +680,25 @@ impl<'de> Deserialize<'de> for Config { } = DConfig::deserialize(deserializer)?; // TODO: Panic here if the initial testnet peers are the default initial testnet peers. - let network = if let Some(DTestnetParameters { activation_heights }) = testnet_parameters { + let network = if let Some(DTestnetParameters { + network_name, + activation_heights, + }) = testnet_parameters + { assert_eq!( network_kind, NetworkKind::Testnet, "set network to 'Testnet' to use configured testnet parameters" ); - testnet::Parameters::build() - .activation_heights(activation_heights) + let mut params_builder = testnet::Parameters::build(); + + if let Some(network_name) = network_name { + params_builder = params_builder.with_network_name(network_name) + } + + params_builder + .with_activation_heights(activation_heights) .to_network() } else { // Convert to default `Network` for a `NetworkKind` if there are no testnet parameters. From 0e97b539370e7d825b557b48cab96b84a6b61c71 Mon Sep 17 00:00:00 2001 From: Arya Date: Wed, 17 Apr 2024 22:57:04 -0400 Subject: [PATCH 39/51] Adds `MainnetKind`, `TestnetKind`, and `RegtestKind` to reserved network names --- zebra-chain/src/parameters/network/testnet.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/zebra-chain/src/parameters/network/testnet.rs b/zebra-chain/src/parameters/network/testnet.rs index 0b01c493b..eeb8f17c9 100644 --- a/zebra-chain/src/parameters/network/testnet.rs +++ b/zebra-chain/src/parameters/network/testnet.rs @@ -12,7 +12,14 @@ use crate::{ }; /// Reserved network names that should not be allowed for configured Testnets. -pub const RESERVED_NETWORK_NAMES: [&str; 3] = ["Mainnet", "Testnet", "Regtest"]; +pub const RESERVED_NETWORK_NAMES: [&str; 6] = [ + "Mainnet", + "Testnet", + "Regtest", + "MainnetKind", + "TestnetKind", + "RegtestKind", +]; /// Maximum length for a configured network name. pub const MAX_NETWORK_NAME_LENGTH: usize = 30; From a842c12d550a2dee85e5d346406cee1d26542b35 Mon Sep 17 00:00:00 2001 From: Arya Date: Wed, 17 Apr 2024 23:03:15 -0400 Subject: [PATCH 40/51] updates stored test configs and fixes `network_name()` docs --- zebra-chain/src/parameters/network/testnet.rs | 2 +- zebrad/tests/common/configs/v1.7.0.toml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/zebra-chain/src/parameters/network/testnet.rs b/zebra-chain/src/parameters/network/testnet.rs index eeb8f17c9..7cc936927 100644 --- a/zebra-chain/src/parameters/network/testnet.rs +++ b/zebra-chain/src/parameters/network/testnet.rs @@ -237,7 +237,7 @@ impl Parameters { self == &Self::default() } - /// Returns the network upgrade activation heights + /// Returns the network name pub fn network_name(&self) -> &str { &self.network_name } diff --git a/zebrad/tests/common/configs/v1.7.0.toml b/zebrad/tests/common/configs/v1.7.0.toml index 58aed232f..00438213d 100644 --- a/zebrad/tests/common/configs/v1.7.0.toml +++ b/zebrad/tests/common/configs/v1.7.0.toml @@ -60,6 +60,9 @@ max_connections_per_ip = 1 network = "Testnet" peerset_initial_target_size = 25 +[network.testnet_parameters] +network_name = "ConfiguredTestnet_1" + [network.testnet_parameters.activation_heights] BeforeOverwinter = 1 Overwinter = 207_500 From 6e297278616b2616f89a4d07f2224a293997eafe Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 18 Apr 2024 01:10:23 -0400 Subject: [PATCH 41/51] Adds a `new_regtest()` method on `Network` and `testnet::Parameters`, updates config deserialization to return an error if the initial_testnet_peers include any default initial peers AND contain configured activation heights --- zebra-chain/src/parameters/network.rs | 7 +++ zebra-chain/src/parameters/network/testnet.rs | 23 +++++--- .../src/parameters/network/tests/vectors.rs | 7 ++- zebra-network/src/config.rs | 54 ++++++++++++++----- zebrad/tests/common/configs/v1.7.0.toml | 7 +-- 5 files changed, 71 insertions(+), 27 deletions(-) diff --git a/zebra-chain/src/parameters/network.rs b/zebra-chain/src/parameters/network.rs index e346e28e6..805db9f89 100644 --- a/zebra-chain/src/parameters/network.rs +++ b/zebra-chain/src/parameters/network.rs @@ -11,6 +11,8 @@ use crate::{ parameters::NetworkUpgrade, }; +use self::testnet::ConfiguredActivationHeights; + pub mod testnet; #[cfg(test)] @@ -166,6 +168,11 @@ impl Network { Self::Testnet(Arc::new(params)) } + /// Creates a new [`Network::Testnet`] with `Regtest` parameters and the provided network upgrade activation heights. + pub fn new_regtest(activation_heights: ConfiguredActivationHeights) -> Self { + Self::new_configured_testnet(testnet::Parameters::new_regtest(activation_heights)) + } + /// Returns true if the network is the default Testnet, or false otherwise. pub fn is_default_testnet(&self) -> bool { if let Self::Testnet(params) = self { diff --git a/zebra-chain/src/parameters/network/testnet.rs b/zebra-chain/src/parameters/network/testnet.rs index 7cc936927..bb6acaf9e 100644 --- a/zebra-chain/src/parameters/network/testnet.rs +++ b/zebra-chain/src/parameters/network/testnet.rs @@ -67,13 +67,7 @@ impl Default for ParametersBuilder { // # Correctness // // `Genesis` network upgrade activation height must always be 0 - activation_heights: [ - (Height(0), NetworkUpgrade::Genesis), - // TODO: Find out if `BeforeOverwinter` must always be active at Height(1), remove it here if it's not required. - (Height(1), NetworkUpgrade::BeforeOverwinter), - ] - .into_iter() - .collect(), + activation_heights: TESTNET_ACTIVATION_HEIGHTS.iter().cloned().collect(), hrp_sapling_extended_spending_key: zp_constants::testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY.to_string(), hrp_sapling_extended_full_viewing_key: @@ -166,6 +160,7 @@ impl ParametersBuilder { // # Correctness // // Height(0) must be reserved for the `NetworkUpgrade::Genesis`. + // TODO: Find out if `BeforeOverwinter` must always be active at Height(1), remove it here if it's not required. self.activation_heights.split_off(&Height(2)); self.activation_heights.extend(activation_heights); @@ -220,7 +215,6 @@ impl Default for Parameters { fn default() -> Self { Self { network_name: "Testnet".to_string(), - activation_heights: TESTNET_ACTIVATION_HEIGHTS.iter().cloned().collect(), ..Self::build().finish() } } @@ -232,6 +226,19 @@ impl Parameters { ParametersBuilder::default() } + /// Accepts a [`ConfiguredActivationHeights`]. + /// + /// Creates an instance of [`Parameters`] with `Regtest` values. + pub fn new_regtest(activation_heights: ConfiguredActivationHeights) -> Self { + Self { + network_name: "Regtest".to_string(), + ..Self::build() + // Removes default Testnet activation heights, most network upgrades are disabled by default for Regtest + .with_activation_heights(activation_heights) + .finish() + } + } + /// Returns true if the instance of [`Parameters`] represents the default public Testnet. pub fn is_default_testnet(&self) -> bool { self == &Self::default() diff --git a/zebra-chain/src/parameters/network/tests/vectors.rs b/zebra-chain/src/parameters/network/tests/vectors.rs index 1d619aa5e..0e69b9de8 100644 --- a/zebra-chain/src/parameters/network/tests/vectors.rs +++ b/zebra-chain/src/parameters/network/tests/vectors.rs @@ -164,8 +164,11 @@ fn check_network_name() { "Mainnet", "Mainnet should be displayed as 'Mainnet'" ); - - // TODO: Check Regtest + assert_eq!( + Network::new_regtest(Default::default()).to_string(), + "Regtest", + "Regtest should be displayed as 'Regtest'" + ); // Check that network name can contain alphanumeric characters and '_'. let expected_name = "ConfiguredTestnet_1"; diff --git a/zebra-network/src/config.rs b/zebra-network/src/config.rs index e32f6c138..53e77c119 100644 --- a/zebra-network/src/config.rs +++ b/zebra-network/src/config.rs @@ -630,8 +630,7 @@ impl<'de> Deserialize<'de> for Config { #[derive(Deserialize)] struct DTestnetParameters { network_name: Option, - #[serde(default)] - activation_heights: ConfiguredActivationHeights, + activation_heights: Option, } #[derive(Deserialize)] @@ -640,6 +639,7 @@ impl<'de> Deserialize<'de> for Config { listen_addr: String, network: NetworkKind, testnet_parameters: Option, + regtest_activation_heights: ConfiguredActivationHeights, initial_mainnet_peers: IndexSet, initial_testnet_peers: IndexSet, cache_dir: CacheDir, @@ -656,6 +656,7 @@ impl<'de> Deserialize<'de> for Config { listen_addr: "0.0.0.0".to_string(), network: Default::default(), testnet_parameters: None, + regtest_activation_heights: ConfiguredActivationHeights::default(), initial_mainnet_peers: config.initial_mainnet_peers, initial_testnet_peers: config.initial_testnet_peers, cache_dir: config.cache_dir, @@ -670,6 +671,7 @@ impl<'de> Deserialize<'de> for Config { listen_addr, network: network_kind, testnet_parameters, + regtest_activation_heights, initial_mainnet_peers, initial_testnet_peers, cache_dir, @@ -678,17 +680,33 @@ impl<'de> Deserialize<'de> for Config { max_connections_per_ip, } = DConfig::deserialize(deserializer)?; - // TODO: Panic here if the initial testnet peers are the default initial testnet peers. + /// Accepts an [`IndexSet`] of initial peers, + /// + /// Returns true if any of them are the default Testnet or Mainnet initial peers. + fn contains_default_initial_peers(initial_peers: &IndexSet) -> bool { + let Config { + initial_mainnet_peers: mut default_initial_peers, + initial_testnet_peers: default_initial_testnet_peers, + .. + } = Config::default(); + default_initial_peers.extend(default_initial_testnet_peers); + + initial_peers + .intersection(&default_initial_peers) + .next() + .is_some() + } + let network = if let Some(DTestnetParameters { network_name, activation_heights, }) = testnet_parameters { - assert_eq!( - network_kind, - NetworkKind::Testnet, - "set network to 'Testnet' to use configured testnet parameters" - ); + if network_kind != NetworkKind::Testnet { + return Err(de::Error::custom( + "network must be 'Testnet' to use configured testnet parameters", + )); + } let mut params_builder = testnet::Parameters::build(); @@ -696,15 +714,27 @@ impl<'de> Deserialize<'de> for Config { params_builder = params_builder.with_network_name(network_name) } - params_builder - .with_activation_heights(activation_heights) - .to_network() + // Retain default Testnet activation heights unless there's an empty [testnet_parameters.activation_heights] section. + if let Some(activation_heights) = activation_heights { + // Return an error if the initial testnet peers includes any of the default initial Mainnet or Testnet + // peers while activation heights are configured. + // TODO: Check that the network magic is different from the default Mainnet/Testnet network magic too? + if contains_default_initial_peers(&initial_testnet_peers) { + return Err(de::Error::custom( + "cannot use default initial testnet peers with configured activation heights", + )); + } + + params_builder = params_builder.with_activation_heights(activation_heights) + } + + params_builder.to_network() } else { // Convert to default `Network` for a `NetworkKind` if there are no testnet parameters. match network_kind { NetworkKind::Mainnet => Network::Mainnet, NetworkKind::Testnet => Network::new_default_testnet(), - NetworkKind::Regtest => unimplemented!("Regtest is not yet implemented in Zebra"), + NetworkKind::Regtest => Network::new_regtest(regtest_activation_heights), } }; diff --git a/zebrad/tests/common/configs/v1.7.0.toml b/zebrad/tests/common/configs/v1.7.0.toml index 00438213d..a0499258c 100644 --- a/zebrad/tests/common/configs/v1.7.0.toml +++ b/zebrad/tests/common/configs/v1.7.0.toml @@ -57,13 +57,10 @@ initial_testnet_peers = [ ] listen_addr = "0.0.0.0:8233" max_connections_per_ip = 1 -network = "Testnet" +network = "Regtest" peerset_initial_target_size = 25 -[network.testnet_parameters] -network_name = "ConfiguredTestnet_1" - -[network.testnet_parameters.activation_heights] +[network.regtest_activation_heights] BeforeOverwinter = 1 Overwinter = 207_500 Sapling = 280_000 From 32d075870205dc8589db48ff2fadad2dcaada12a Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 18 Apr 2024 02:01:14 -0400 Subject: [PATCH 42/51] Updates `activates_network_upgrades_correctly` test to check Regtest activation heights (and default Mainnet/Testnet) --- .../src/parameters/network/tests/vectors.rs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/zebra-chain/src/parameters/network/tests/vectors.rs b/zebra-chain/src/parameters/network/tests/vectors.rs index 0e69b9de8..3adfa3706 100644 --- a/zebra-chain/src/parameters/network/tests/vectors.rs +++ b/zebra-chain/src/parameters/network/tests/vectors.rs @@ -8,7 +8,8 @@ use crate::{ testnet::{ self, ConfiguredActivationHeights, MAX_NETWORK_NAME_LENGTH, RESERVED_NETWORK_NAMES, }, - Network, NetworkUpgrade, NETWORK_UPGRADES_IN_ORDER, + Network, NetworkUpgrade, MAINNET_ACTIVATION_HEIGHTS, NETWORK_UPGRADES_IN_ORDER, + TESTNET_ACTIVATION_HEIGHTS, }, }; @@ -126,6 +127,26 @@ fn activates_network_upgrades_correctly() { should match NU5 activation height, network_upgrade: {nu}, activation_height: {activation_height:?}" ); } + + let expected_default_regtest_activation_heights = &[ + (Height(0), NetworkUpgrade::Genesis), + (Height(1), NetworkUpgrade::BeforeOverwinter), + ]; + + for (network, expected_activation_heights) in [ + (Network::Mainnet, MAINNET_ACTIVATION_HEIGHTS), + (Network::new_default_testnet(), TESTNET_ACTIVATION_HEIGHTS), + ( + Network::new_regtest(Default::default()), + expected_default_regtest_activation_heights, + ), + ] { + assert_eq!( + network.activation_list(), + expected_activation_heights.iter().cloned().collect(), + "network activation list should match expected activation heights" + ); + } } /// Checks that configured testnet names are validated and used correctly. From e816c6616cfb5772bfecdf9ad28535ef50725642 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 18 Apr 2024 02:02:41 -0400 Subject: [PATCH 43/51] Refactors if-let & match statement into general match statement, removes constraint against including `testnet_parameters` field/section in the config when using `Mainnet` or `Regtest` --- zebra-network/src/config.rs | 62 +++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/zebra-network/src/config.rs b/zebra-network/src/config.rs index 53e77c119..b630c30c2 100644 --- a/zebra-network/src/config.rs +++ b/zebra-network/src/config.rs @@ -697,44 +697,38 @@ impl<'de> Deserialize<'de> for Config { .is_some() } - let network = if let Some(DTestnetParameters { - network_name, - activation_heights, - }) = testnet_parameters - { - if network_kind != NetworkKind::Testnet { - return Err(de::Error::custom( - "network must be 'Testnet' to use configured testnet parameters", - )); - } + let network = match (network_kind, testnet_parameters) { + (NetworkKind::Mainnet, _) => Network::Mainnet, + (NetworkKind::Testnet, None) => Network::new_default_testnet(), + (NetworkKind::Regtest, _) => Network::new_regtest(regtest_activation_heights), + ( + NetworkKind::Testnet, + Some(DTestnetParameters { + network_name, + activation_heights, + }), + ) => { + let mut params_builder = testnet::Parameters::build(); - let mut params_builder = testnet::Parameters::build(); - - if let Some(network_name) = network_name { - params_builder = params_builder.with_network_name(network_name) - } - - // Retain default Testnet activation heights unless there's an empty [testnet_parameters.activation_heights] section. - if let Some(activation_heights) = activation_heights { - // Return an error if the initial testnet peers includes any of the default initial Mainnet or Testnet - // peers while activation heights are configured. - // TODO: Check that the network magic is different from the default Mainnet/Testnet network magic too? - if contains_default_initial_peers(&initial_testnet_peers) { - return Err(de::Error::custom( - "cannot use default initial testnet peers with configured activation heights", - )); + if let Some(network_name) = network_name { + params_builder = params_builder.with_network_name(network_name) } - params_builder = params_builder.with_activation_heights(activation_heights) - } + // Retain default Testnet activation heights unless there's an empty [testnet_parameters.activation_heights] section. + if let Some(activation_heights) = activation_heights { + // Return an error if the initial testnet peers includes any of the default initial Mainnet or Testnet + // peers while activation heights are configured. + // TODO: Check that the network magic is different from the default Mainnet/Testnet network magic too? + if contains_default_initial_peers(&initial_testnet_peers) { + return Err(de::Error::custom( + "cannot use default initial testnet peers with configured activation heights", + )); + } - params_builder.to_network() - } else { - // Convert to default `Network` for a `NetworkKind` if there are no testnet parameters. - match network_kind { - NetworkKind::Mainnet => Network::Mainnet, - NetworkKind::Testnet => Network::new_default_testnet(), - NetworkKind::Regtest => Network::new_regtest(regtest_activation_heights), + params_builder = params_builder.with_activation_heights(activation_heights) + } + + params_builder.to_network() } }; From 32e4fbbc51594f3690e78ffa6383173c5e983928 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 18 Apr 2024 14:25:13 -0400 Subject: [PATCH 44/51] Removes outdated TODO --- zebra-chain/src/parameters/network.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/zebra-chain/src/parameters/network.rs b/zebra-chain/src/parameters/network.rs index 805db9f89..c12e1f82d 100644 --- a/zebra-chain/src/parameters/network.rs +++ b/zebra-chain/src/parameters/network.rs @@ -144,8 +144,6 @@ impl<'a> From<&'a Network> for &'a str { fn from(network: &'a Network) -> &'a str { match network { Network::Mainnet => "Mainnet", - // TODO: - // - zcashd calls the Regtest cache dir 'regtest' (#8327). Network::Testnet(params) => params.network_name(), } } From 63623ae53f5b9c839143e6641458c8b1b43c2b8d Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 18 Apr 2024 15:00:27 -0400 Subject: [PATCH 45/51] Restores `testnet_parameters` section of the latest stored config. --- zebrad/tests/common/configs/v1.7.0.toml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/zebrad/tests/common/configs/v1.7.0.toml b/zebrad/tests/common/configs/v1.7.0.toml index a0499258c..f29e39378 100644 --- a/zebrad/tests/common/configs/v1.7.0.toml +++ b/zebrad/tests/common/configs/v1.7.0.toml @@ -60,6 +60,18 @@ max_connections_per_ip = 1 network = "Regtest" peerset_initial_target_size = 25 +[network.testnet_parameters] +network_name = "ConfiguredTestnet_1" + +[network.testnet_parameters.activation_heights] +BeforeOverwinter = 1 +Overwinter = 207_500 +Sapling = 280_000 +Blossom = 584_000 +Heartwood = 903_800 +Canopy = 1_028_500 +NU5 = 1_842_420 + [network.regtest_activation_heights] BeforeOverwinter = 1 Overwinter = 207_500 From d8715909e2cf4ca11ba8b2bff53ad1c28a8b14d8 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 18 Apr 2024 16:23:23 -0400 Subject: [PATCH 46/51] Adds `with_sapling_hrps()` method and uses it to set the Regtest HRPs in `new_regtest()`. Adds a test for Sapling HRP validation --- zebra-chain/src/parameters/network/testnet.rs | 49 ++++++++++++++- .../src/parameters/network/tests/vectors.rs | 63 ++++++++++++++++++- 2 files changed, 110 insertions(+), 2 deletions(-) diff --git a/zebra-chain/src/parameters/network/testnet.rs b/zebra-chain/src/parameters/network/testnet.rs index bb6acaf9e..c9c8cd483 100644 --- a/zebra-chain/src/parameters/network/testnet.rs +++ b/zebra-chain/src/parameters/network/testnet.rs @@ -24,6 +24,9 @@ pub const RESERVED_NETWORK_NAMES: [&str; 6] = [ /// Maximum length for a configured network name. pub const MAX_NETWORK_NAME_LENGTH: usize = 30; +/// Maximum length for a configured human-readable prefix. +pub const MAX_HRP_LENGTH: usize = 30; + /// Configurable activation heights for Regtest and configured Testnets. #[derive(Deserialize, Default)] #[serde(rename_all = "PascalCase")] @@ -103,6 +106,44 @@ impl ParametersBuilder { self } + /// Checks that the provided Sapling human-readable prefixes (HRPs) are valid and unique, then + /// sets the Sapling HRPs to be used in the [`Parameters`] being built. + pub fn with_sapling_hrps( + mut self, + hrp_sapling_extended_spending_key: impl fmt::Display, + hrp_sapling_extended_full_viewing_key: impl fmt::Display, + hrp_sapling_payment_address: impl fmt::Display, + ) -> Self { + self.hrp_sapling_extended_spending_key = hrp_sapling_extended_spending_key.to_string(); + self.hrp_sapling_extended_full_viewing_key = + hrp_sapling_extended_full_viewing_key.to_string(); + self.hrp_sapling_payment_address = hrp_sapling_payment_address.to_string(); + + let sapling_hrps = [ + &self.hrp_sapling_extended_spending_key, + &self.hrp_sapling_extended_full_viewing_key, + &self.hrp_sapling_payment_address, + ]; + + for sapling_hrp in sapling_hrps { + assert!(sapling_hrp.len() <= MAX_HRP_LENGTH, "Sapling human-readable prefix {sapling_hrp} is too long, must be {MAX_HRP_LENGTH} characters or less"); + assert!( + sapling_hrp.chars().all(|c| c.is_ascii_lowercase() || c == '-'), + "human-readable prefixes should contain only lowercase ASCII characters and dashes, hrp: {sapling_hrp}" + ); + assert_eq!( + sapling_hrps + .iter() + .filter(|&&hrp| hrp == sapling_hrp) + .count(), + 1, + "Sapling human-readable prefixes must be unique, repeated Sapling HRP: {sapling_hrp}" + ); + } + + self + } + /// Checks that the provided network upgrade activation heights are in the correct order, then /// sets them as the new network upgrade activation heights. pub fn with_activation_heights( @@ -233,7 +274,13 @@ impl Parameters { Self { network_name: "Regtest".to_string(), ..Self::build() - // Removes default Testnet activation heights, most network upgrades are disabled by default for Regtest + .with_sapling_hrps( + zp_constants::regtest::HRP_SAPLING_EXTENDED_SPENDING_KEY, + zp_constants::regtest::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, + zp_constants::regtest::HRP_SAPLING_PAYMENT_ADDRESS, + ) + // Removes default Testnet activation heights if not configured, + // most network upgrades are disabled by default for Regtest in zcashd .with_activation_heights(activation_heights) .finish() } diff --git a/zebra-chain/src/parameters/network/tests/vectors.rs b/zebra-chain/src/parameters/network/tests/vectors.rs index 3adfa3706..843122d31 100644 --- a/zebra-chain/src/parameters/network/tests/vectors.rs +++ b/zebra-chain/src/parameters/network/tests/vectors.rs @@ -151,7 +151,7 @@ fn activates_network_upgrades_correctly() { /// Checks that configured testnet names are validated and used correctly. #[test] -fn check_network_name() { +fn check_configured_network_name() { // Sets a no-op panic hook to avoid long output. std::panic::set_hook(Box::new(|_| {})); @@ -206,3 +206,64 @@ fn check_network_name() { "network must be displayed as configured network name" ); } + +/// Checks that configured Sapling human-readable prefixes (HRPs) are validated and used correctly. +#[test] +fn check_configured_sapling_hrps() { + // Sets a no-op panic hook to avoid long output. + std::panic::set_hook(Box::new(|_| {})); + + // Check that configured Sapling HRPs must be unique. + std::panic::catch_unwind(|| { + testnet::Parameters::build().with_sapling_hrps("", "", ""); + }) + .expect_err("should panic when setting non-unique Sapling HRPs"); + + // Check that max length is enforced, and that network names may only contain lowecase ASCII characters and dashes. + for invalid_hrp in [ + "a".repeat(MAX_NETWORK_NAME_LENGTH + 1), + "!!!!non-alphabetical-name".to_string(), + "A".to_string(), + ] { + std::panic::catch_unwind(|| { + testnet::Parameters::build().with_sapling_hrps(invalid_hrp, "dummy-hrp-a", "dummy-hrp-b"); + }) + .expect_err("should panic when setting Sapling HRPs that are too long or contain non-alphanumeric characters (except '-')"); + } + + // Check that Sapling HRPs can contain lowercase ascii characters and dashes. + let expected_hrp_sapling_extended_spending_key = "sapling-hrp-a"; + let expected_hrp_sapling_extended_full_viewing_key = "sapling-hrp-b"; + let expected_hrp_sapling_payment_address = "sapling-hrp-c"; + + let network = testnet::Parameters::build() + // Check that Sapling HRPs can contain `MAX_NETWORK_NAME_LENGTH` characters + .with_sapling_hrps( + "a".repeat(MAX_NETWORK_NAME_LENGTH), + "dummy-hrp-a", + "dummy-hrp-b", + ) + .with_sapling_hrps( + expected_hrp_sapling_extended_spending_key, + expected_hrp_sapling_extended_full_viewing_key, + expected_hrp_sapling_payment_address, + ) + .to_network(); + + // Check that configured Sapling HRPs are returned by `Parameters` trait methods + assert_eq!( + network.hrp_sapling_extended_spending_key(), + expected_hrp_sapling_extended_spending_key, + "should return expected Sapling extended spending key HRP" + ); + assert_eq!( + network.hrp_sapling_extended_full_viewing_key(), + expected_hrp_sapling_extended_full_viewing_key, + "should return expected Sapling EFVK HRP" + ); + assert_eq!( + network.hrp_sapling_payment_address(), + expected_hrp_sapling_payment_address, + "should return expected Sapling payment address HRP" + ); +} From 4671efe98c94f40eba6f12b09ec2fe17b69c17d9 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 18 Apr 2024 16:34:11 -0400 Subject: [PATCH 47/51] Checks that default Mainnet/Testnet/Regtest Sapling HRPs pass validation in `with_sapling_hrps()` --- .../src/parameters/network/tests/vectors.rs | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/zebra-chain/src/parameters/network/tests/vectors.rs b/zebra-chain/src/parameters/network/tests/vectors.rs index 843122d31..b9511dc91 100644 --- a/zebra-chain/src/parameters/network/tests/vectors.rs +++ b/zebra-chain/src/parameters/network/tests/vectors.rs @@ -1,6 +1,9 @@ //! Fixed test vectors for the network consensus parameters. -use zcash_primitives::consensus::{self as zp_consensus, Parameters}; +use zcash_primitives::{ + consensus::{self as zp_consensus, Parameters}, + constants as zp_constants, +}; use crate::{ block::Height, @@ -266,4 +269,23 @@ fn check_configured_sapling_hrps() { expected_hrp_sapling_payment_address, "should return expected Sapling payment address HRP" ); + + // Check that default Mainnet, Testnet, and Regtest HRPs are valid, these calls will panic + // if any of the values fail validation. + testnet::Parameters::build() + .with_sapling_hrps( + zp_constants::mainnet::HRP_SAPLING_EXTENDED_SPENDING_KEY, + zp_constants::mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, + zp_constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, + ) + .with_sapling_hrps( + zp_constants::testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY, + zp_constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, + zp_constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS, + ) + .with_sapling_hrps( + zp_constants::regtest::HRP_SAPLING_EXTENDED_SPENDING_KEY, + zp_constants::regtest::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY, + zp_constants::regtest::HRP_SAPLING_PAYMENT_ADDRESS, + ); } From 9b41cba5a37ba5231b14b56daaaca1ccbf5287b2 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 18 Apr 2024 16:38:01 -0400 Subject: [PATCH 48/51] Uses the correct constant in test --- zebra-chain/src/parameters/network/tests/vectors.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/zebra-chain/src/parameters/network/tests/vectors.rs b/zebra-chain/src/parameters/network/tests/vectors.rs index b9511dc91..e54fa4d7c 100644 --- a/zebra-chain/src/parameters/network/tests/vectors.rs +++ b/zebra-chain/src/parameters/network/tests/vectors.rs @@ -9,7 +9,8 @@ use crate::{ block::Height, parameters::{ testnet::{ - self, ConfiguredActivationHeights, MAX_NETWORK_NAME_LENGTH, RESERVED_NETWORK_NAMES, + self, ConfiguredActivationHeights, MAX_HRP_LENGTH, MAX_NETWORK_NAME_LENGTH, + RESERVED_NETWORK_NAMES, }, Network, NetworkUpgrade, MAINNET_ACTIVATION_HEIGHTS, NETWORK_UPGRADES_IN_ORDER, TESTNET_ACTIVATION_HEIGHTS, @@ -240,12 +241,8 @@ fn check_configured_sapling_hrps() { let expected_hrp_sapling_payment_address = "sapling-hrp-c"; let network = testnet::Parameters::build() - // Check that Sapling HRPs can contain `MAX_NETWORK_NAME_LENGTH` characters - .with_sapling_hrps( - "a".repeat(MAX_NETWORK_NAME_LENGTH), - "dummy-hrp-a", - "dummy-hrp-b", - ) + // Check that Sapling HRPs can contain `MAX_HRP_LENGTH` characters + .with_sapling_hrps("a".repeat(MAX_HRP_LENGTH), "dummy-hrp-a", "dummy-hrp-b") .with_sapling_hrps( expected_hrp_sapling_extended_spending_key, expected_hrp_sapling_extended_full_viewing_key, From b38dbfd2521c785a648a77e854f6653528def4c7 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 18 Apr 2024 21:05:32 -0400 Subject: [PATCH 49/51] Adds `is_regtest()` methods --- zebra-chain/src/parameters/network.rs | 9 +++++++++ zebra-chain/src/parameters/network/testnet.rs | 16 ++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/zebra-chain/src/parameters/network.rs b/zebra-chain/src/parameters/network.rs index c12e1f82d..94fd30150 100644 --- a/zebra-chain/src/parameters/network.rs +++ b/zebra-chain/src/parameters/network.rs @@ -180,6 +180,15 @@ impl Network { } } + /// Returns true if the network is Regtest, or false otherwise. + pub fn is_regtest(&self) -> bool { + if let Self::Testnet(params) = self { + params.is_regtest() + } else { + false + } + } + /// Returns the [`NetworkKind`] for this network. pub fn kind(&self) -> NetworkKind { match self { diff --git a/zebra-chain/src/parameters/network/testnet.rs b/zebra-chain/src/parameters/network/testnet.rs index c9c8cd483..295f51813 100644 --- a/zebra-chain/src/parameters/network/testnet.rs +++ b/zebra-chain/src/parameters/network/testnet.rs @@ -291,6 +291,22 @@ impl Parameters { self == &Self::default() } + /// Returns true if the instance of [`Parameters`] represents Regtest. + pub fn is_regtest(&self) -> bool { + let Self { + network_name, + hrp_sapling_extended_spending_key, + hrp_sapling_extended_full_viewing_key, + hrp_sapling_payment_address, + .. + } = Self::new_regtest(ConfiguredActivationHeights::default()); + + self.network_name == network_name + && self.hrp_sapling_extended_spending_key == hrp_sapling_extended_spending_key + && self.hrp_sapling_extended_full_viewing_key == hrp_sapling_extended_full_viewing_key + && self.hrp_sapling_payment_address == hrp_sapling_payment_address + } + /// Returns the network name pub fn network_name(&self) -> &str { &self.network_name From 13d59add1f202a19e2571aff68439b316e91c9d4 Mon Sep 17 00:00:00 2001 From: Arya Date: Wed, 24 Apr 2024 11:08:51 -0400 Subject: [PATCH 50/51] Updates test docs --- zebra-chain/src/parameters/network/tests/vectors.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/zebra-chain/src/parameters/network/tests/vectors.rs b/zebra-chain/src/parameters/network/tests/vectors.rs index 691060dbd..5ff8ebe5f 100644 --- a/zebra-chain/src/parameters/network/tests/vectors.rs +++ b/zebra-chain/src/parameters/network/tests/vectors.rs @@ -98,8 +98,9 @@ fn check_parameters_impl() { } /// Checks that `NetworkUpgrade::activation_height()` returns the activation height of the next -/// network upgrade if it doesn't find an activation height for a prior network upgrade, and that the -/// `Genesis` upgrade is always at `Height(0)`. +/// network upgrade if it doesn't find an activation height for a prior network upgrade, that the +/// `Genesis` upgrade is always at `Height(0)`, and that the default Mainnet/Testnet/Regtest activation +/// heights are what's expected. #[test] fn activates_network_upgrades_correctly() { let expected_activation_height = 1; From 3b5a7f5f10cae7c9dfffb5c751276075abff0b42 Mon Sep 17 00:00:00 2001 From: Arya Date: Wed, 24 Apr 2024 12:25:57 -0400 Subject: [PATCH 51/51] drop custom panic hooks after expected panics --- zebra-chain/src/parameters/network/tests/vectors.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/zebra-chain/src/parameters/network/tests/vectors.rs b/zebra-chain/src/parameters/network/tests/vectors.rs index 5ff8ebe5f..d794b0471 100644 --- a/zebra-chain/src/parameters/network/tests/vectors.rs +++ b/zebra-chain/src/parameters/network/tests/vectors.rs @@ -179,6 +179,8 @@ fn check_configured_network_name() { .expect_err("should panic when setting network name that's too long or contains non-alphanumeric characters (except '_')"); } + drop(std::panic::take_hook()); + // Checks that network names are displayed correctly assert_eq!( Network::new_default_testnet().to_string(), @@ -236,6 +238,8 @@ fn check_configured_sapling_hrps() { .expect_err("should panic when setting Sapling HRPs that are too long or contain non-alphanumeric characters (except '-')"); } + drop(std::panic::take_hook()); + // Check that Sapling HRPs can contain lowercase ascii characters and dashes. let expected_hrp_sapling_extended_spending_key = "sapling-hrp-a"; let expected_hrp_sapling_extended_full_viewing_key = "sapling-hrp-b"; @@ -313,6 +317,8 @@ fn check_network_name() { .expect_err("should panic when setting network name that's too long or contains non-alphanumeric characters (except '_')"); } + drop(std::panic::take_hook()); + // Checks that network names are displayed correctly assert_eq!( Network::new_default_testnet().to_string(),