add(consensus/network): Add an empty `Parameters` struct in `Network::Testnet` (#8368)

* minor cleanup and rename

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

* Updates tests

* Adds `NetworkKind` and uses it instead of `Network` in `HistoryTreeParts` and `transparent::Address`

* 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

* Applies some suggestions from code review

* Applies suggestions from code review

* returns b58 prefix constants directly to remove From<NetworkKind> impl for zcash_primitives::consensus::Network

* Applies more suggestions from code review.

* moves conversions to zcash_primitives::consensus::Network to where they're used.

* Apply suggestions from code review

Co-authored-by: Marek <mail@marek.onl>

* rename `network` variables and method names typed as NetworkKind to `network_kind`

* use only test block heights for the network associated with them

* Applies more suggestions from code review.

* Rename `NetworkParameters` to `Parameters` and move it a new `testnet` module

---------

Co-authored-by: Marek <mail@marek.onl>
This commit is contained in:
Arya 2024-04-16 22:20:34 -04:00 committed by GitHub
parent 5fd15a022c
commit 16a39f89d0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
59 changed files with 813 additions and 447 deletions

View File

@ -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.

View File

@ -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
}
}

View File

@ -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(())
}

View File

@ -21,7 +21,7 @@ mod transaction;
pub mod arbitrary;
pub use genesis::*;
pub use network::Network;
pub use network::{testnet, Network, NetworkKind};
pub use network_upgrade::*;
pub use transaction::*;

View File

@ -1,14 +1,18 @@
//! Consensus parameters for each Zcash network.
use std::{fmt, str::FromStr};
use std::{fmt, str::FromStr, sync::Arc};
use thiserror::Error;
use zcash_primitives::constants;
use crate::{
block::{self, Height, HeightDiff},
parameters::NetworkUpgrade::Canopy,
};
pub mod testnet;
#[cfg(any(test, feature = "proptest-impl"))]
use proptest_derive::Arbitrary;
@ -51,52 +55,102 @@ mod tests;
/// after the grace period.
const ZIP_212_GRACE_PERIOD_DURATION: HeightDiff = 32_256;
/// 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]
Mainnet,
/// A test network.
Testnet,
/// Regtest mode, not yet implemented
// TODO: Add `new_regtest()` and `is_regtest` methods on `Network`.
Regtest,
}
impl From<Network> for NetworkKind {
fn from(network: Network) -> Self {
network.kind()
}
}
/// An enum describing the possible network choices.
#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[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]
Mainnet,
/// The oldest public test network.
Testnet,
/// A test network such as the default public testnet,
/// a configured testnet, or Regtest.
Testnet(Arc<testnet::Parameters>),
}
use zcash_primitives::consensus::{Network as ZcashPrimitivesNetwork, Parameters as _};
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] {
<ZcashPrimitivesNetwork>::from(self).b58_pubkey_address_prefix()
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,
}
}
/// Returns the human-readable prefix for Base58Check-encoded transparent pay-to-script-hash
/// payment addresses for the network.
pub fn b58_script_address_prefix(&self) -> [u8; 2] {
<ZcashPrimitivesNetwork>::from(self).b58_script_address_prefix()
}
/// 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 <https://zips.z.cash/protocol/protocol.pdf#blockheader>
pub fn is_max_block_time_enforced(&self, height: block::Height) -> bool {
pub fn b58_script_address_prefix(self) -> [u8; 2] {
match self {
Network::Mainnet => true,
Network::Testnet => height >= super::TESTNET_MAX_TIME_START_HEIGHT,
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
/// [BIP70](https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#paymentdetailspaymentrequest)
pub fn bip70_network_name(&self) -> String {
if *self == Self::Mainnet {
"main".to_string()
} else {
"test".to_string()
}
}
}
impl From<NetworkKind> for &'static str {
fn from(network: NetworkKind) -> &'static str {
// 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",
NetworkKind::Regtest => "RegtestKind",
}
}
}
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 {
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()`
// - zcashd calls the Regtest cache dir 'regtest' (#8327).
Network::Testnet(params) if params.is_default_testnet() => "Testnet",
Network::Testnet(_params) => "UnknownTestnet",
}
}
}
@ -108,17 +162,62 @@ impl fmt::Display for Network {
}
impl Network {
/// Creates a new [`Network::Testnet`] with the default Testnet [`testnet::Parameters`].
pub fn new_default_testnet() -> Self {
Self::Testnet(Arc::new(testnet::Parameters::default()))
}
/// 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))
}
/// 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 the [`NetworkKind`] for this 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 an iterator over [`Network`] variants.
pub fn iter() -> impl Iterator<Item = Self> {
// TODO: Use default values of `Testnet` variant when adding fields for #7845.
[Self::Mainnet, Self::Testnet].into_iter()
[Self::Mainnet, Self::new_default_testnet()].into_iter()
}
/// 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 <https://zips.z.cash/protocol/protocol.pdf#blockheader>
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 testnet::Parameters (#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,
Network::Testnet => 18233,
// TODO: Add a `default_port` field to `testnet::Parameters` to return here. (zcashd uses 18344 for Regtest)
Network::Testnet(_params) => 18233,
}
}
@ -143,10 +242,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 => "test".to_string(),
}
self.kind().bip70_network_name()
}
/// Return the lowercase network name.
@ -167,13 +263,14 @@ impl Network {
}
}
// This is used for parsing a command-line argument for the `TipHeight` command in zebrad.
impl FromStr for Network {
type Err = InvalidNetworkError;
fn from_str(string: &str) -> Result<Self, Self::Err> {
match string.to_lowercase().as_str() {
"mainnet" => Ok(Network::Mainnet),
"testnet" => Ok(Network::Testnet),
"testnet" => Ok(Network::new_default_testnet()),
_ => Err(InvalidNetworkError(string.to_owned())),
}
}

View File

@ -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()
}
}

View File

@ -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);
}
}

View File

@ -271,7 +271,8 @@ impl Network {
};
match self {
Mainnet => mainnet_heights,
Testnet => testnet_heights,
// TODO: Add an `activation_heights` field to `testnet::Parameters` to return here. (#7970)
Testnet(_params) => testnet_heights,
}
.iter()
.cloned()
@ -394,9 +395,14 @@ impl NetworkUpgrade {
height: block::Height,
) -> Option<Duration> {
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 testnet::Parameters (#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)
}

View File

@ -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`.

View File

@ -5,7 +5,7 @@
use zcash_address::unified::{self, Container};
use zcash_primitives::sapling;
use crate::{parameters::Network, transparent, BoxError};
use crate::{parameters::NetworkKind, transparent, BoxError};
/// Zcash address variants
pub enum Address {
@ -14,8 +14,8 @@ pub enum Address {
/// Sapling address
Sapling {
/// Address' network
network: Network,
/// Address' network kind
network: NetworkKind,
/// Sapling address
address: sapling::PaymentAddress,
@ -23,8 +23,8 @@ pub enum Address {
/// Unified address
Unified {
/// Address' network
network: Network,
/// Address' network kind
network: NetworkKind,
/// Unified address
unified_address: zcash_address::unified::Address,
@ -40,28 +40,6 @@ pub enum Address {
},
}
impl TryFrom<zcash_address::Network> for Network {
// TODO: better error type
type Error = BoxError;
fn try_from(network: zcash_address::Network) -> Result<Self, Self::Error> {
match network {
zcash_address::Network::Main => Ok(Network::Mainnet),
zcash_address::Network::Test => Ok(Network::Testnet),
zcash_address::Network::Regtest => Err("unsupported Zcash network parameters".into()),
}
}
}
impl From<&Network> for zcash_address::Network {
fn from(network: &Network) -> Self {
match network {
Network::Mainnet => zcash_address::Network::Main,
Network::Testnet => zcash_address::Network::Test,
}
}
}
impl zcash_address::TryFromAddress for Address {
// TODO: crate::serialization::SerializationError
type Error = BoxError;
@ -71,7 +49,7 @@ impl zcash_address::TryFromAddress for Address {
data: [u8; 20],
) -> Result<Self, zcash_address::ConversionError<Self::Error>> {
Ok(Self::Transparent(transparent::Address::from_pub_key_hash(
&network.try_into()?,
NetworkKind::from_zcash_address(network),
data,
)))
}
@ -81,7 +59,7 @@ impl zcash_address::TryFromAddress for Address {
data: [u8; 20],
) -> Result<Self, zcash_address::ConversionError<Self::Error>> {
Ok(Self::Transparent(transparent::Address::from_script_hash(
&network.try_into()?,
NetworkKind::from_zcash_address(network),
data,
)))
}
@ -90,7 +68,7 @@ impl zcash_address::TryFromAddress for Address {
network: zcash_address::Network,
data: [u8; 43],
) -> Result<Self, zcash_address::ConversionError<Self::Error>> {
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())
@ -100,7 +78,7 @@ impl zcash_address::TryFromAddress for Address {
network: zcash_address::Network,
unified_address: zcash_address::unified::Address,
) -> Result<Self, zcash_address::ConversionError<Self::Error>> {
let network = &network.try_into()?;
let network = NetworkKind::from_zcash_address(network);
let mut orchard = None;
let mut sapling = None;
let mut transparent = None;
@ -144,7 +122,7 @@ impl zcash_address::TryFromAddress for Address {
}
Ok(Self::Unified {
network: network.clone(),
network,
unified_address,
orchard,
sapling,
@ -155,10 +133,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::Transparent(address) => address.network_kind(),
Self::Sapling { network, .. } | Self::Unified { network, .. } => *network,
}
}
@ -185,10 +163,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,
}
}
}

View File

@ -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(())
}

View File

@ -22,11 +22,29 @@ 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 recovery = zcash_primitives::sapling::note_encryption::try_sapling_output_recovery(
&<zcash_primitives::consensus::Network>::from(network),
&network,
alt_height,
&null_sapling_ovk,
output,

View File

@ -328,29 +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,
}
}
impl From<&Network> for zcash_primitives::consensus::Network {
fn from(network: &Network) -> Self {
match network {
Network::Mainnet => zcash_primitives::consensus::Network::MainNetwork,
Network::Testnet => zcash_primitives::consensus::Network::TestNetwork,
}
}
}
impl From<zcash_primitives::consensus::Network> for Network {
fn from(network: zcash_primitives::consensus::Network) -> Self {
match network {
zcash_primitives::consensus::Network::MainNetwork => Network::Mainnet,
zcash_primitives::consensus::Network::TestNetwork => Network::Testnet,
}
}
}

View File

@ -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(())
}

View File

@ -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(())
}

View File

@ -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) {

View File

@ -7,7 +7,7 @@ use secp256k1::PublicKey;
use sha2::Sha256;
use crate::{
parameters::Network,
parameters::NetworkKind,
serialization::{SerializationError, ZcashDeserialize, ZcashSerialize},
transparent::{opcodes::OpCode, Script},
};
@ -29,15 +29,11 @@ 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 {
/// Production, test, or other network
network: Network,
network_kind: NetworkKind,
/// 20 bytes specifying a script hash.
script_hash: [u8; 20],
},
@ -45,7 +41,7 @@ pub enum Address {
/// P2PKH (Pay to Public Key Hash) addresses
PayToPublicKeyHash {
/// Production, test, or other network
network: Network,
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],
@ -58,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(),
}
@ -101,17 +97,17 @@ impl ZcashSerialize for Address {
fn zcash_serialize<W: io::Write>(&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)?
}
}
@ -131,25 +127,25 @@ impl ZcashDeserialize for Address {
match version_bytes {
zcash_primitives::constants::mainnet::B58_SCRIPT_ADDRESS_PREFIX => {
Ok(Address::PayToScriptHash {
network: Network::Mainnet,
network_kind: NetworkKind::Mainnet,
script_hash: hash_bytes,
})
}
zcash_primitives::constants::testnet::B58_SCRIPT_ADDRESS_PREFIX => {
Ok(Address::PayToScriptHash {
network: Network::Testnet,
network_kind: NetworkKind::Testnet,
script_hash: hash_bytes,
})
}
zcash_primitives::constants::mainnet::B58_PUBKEY_ADDRESS_PREFIX => {
Ok(Address::PayToPublicKeyHash {
network: Network::Mainnet,
network_kind: NetworkKind::Mainnet,
pub_key_hash: hash_bytes,
})
}
zcash_primitives::constants::testnet::B58_PUBKEY_ADDRESS_PREFIX => {
Ok(Address::PayToPublicKeyHash {
network: Network::Testnet,
network_kind: NetworkKind::Testnet,
pub_key_hash: hash_bytes,
})
}
@ -160,22 +156,22 @@ 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_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: Network) -> Address {
fn to_address(&self, network_kind: NetworkKind) -> Address {
Address::PayToPublicKeyHash {
network,
network_kind,
pub_key_hash: Address::hash_payload(&self.serialize()[..]),
}
}
@ -183,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: &Network, 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.clone(),
network_kind,
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_kind: NetworkKind, script_hash: [u8; 20]) -> Self {
Self::PayToScriptHash {
network: network.clone(),
network_kind,
script_hash,
}
}
/// Returns the network for this address.
pub fn network(&self) -> Network {
/// Returns the network kind for this address.
pub fn network_kind(&self) -> NetworkKind {
match self {
Address::PayToScriptHash { network, .. } => network.clone(),
Address::PayToPublicKeyHash { network, .. } => network.clone(),
Address::PayToScriptHash { network_kind, .. } => *network_kind,
Address::PayToPublicKeyHash { network_kind, .. } => *network_kind,
}
}
@ -283,7 +279,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");
}
@ -298,7 +294,7 @@ mod tests {
])
.expect("A PublicKey from slice");
let t_addr = pub_key.to_address(Network::Testnet);
let t_addr = pub_key.to_address(NetworkKind::Testnet);
assert_eq!(format!("{t_addr}"), "tmTc6trRhbv96kGfA99i7vrFwb5p7BVFwc3");
}
@ -309,7 +305,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");
}
@ -320,7 +316,7 @@ mod tests {
let script = Script::new(&[0; 20]);
let t_addr = script.to_address(Network::Testnet);
let t_addr = script.to_address(NetworkKind::Testnet);
assert_eq!(format!("{t_addr}"), "t2L51LcmpA43UMvKTw2Lwtt9LMjwyqU2V1P");
}
@ -342,7 +338,7 @@ mod tests {
assert_eq!(
format!("{t_addr:?}"),
"TransparentAddress { network: Mainnet, script_hash: \"7d46a730d31f97b1930d3368a967c309bd4d136a\" }"
"TransparentAddress { network_kind: Mainnet, script_hash: \"7d46a730d31f97b1930d3368a967c309bd4d136a\" }"
);
}
}

View File

@ -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<Self>;
}
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<Self>;
}

View File

@ -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,

View File

@ -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 `testnet::Parameters` to return here.
Network::Testnet(_params) => (U256::one() << 251) - 1,
};
// `zcashd` converts the PoWLimit into a compact representation before

View File

@ -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"
);
}

View File

@ -29,7 +29,7 @@ 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.kind()).unwrap();
if range.contains(&height) {
let block_subsidy = block_subsidy(height, network)?;
for (&receiver, &numerator) in FUNDING_STREAM_RECEIVER_NUMERATORS.iter() {
@ -83,7 +83,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.kind())
.unwrap()
.start,
network,
))
.expect("no overflow should happen in this sub") as usize;
@ -105,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)
.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];

View File

@ -44,7 +44,7 @@ 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.kind()).unwrap();
let end = range.end;
let last = end - 1;
@ -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}",
);

View File

@ -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(())
}

View File

@ -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(())
}

View File

@ -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 `testnet::Parameters` and return it here (#8366)
Network::Testnet(_params) => {
"05a60a92d99d85997cce3b87616c089f6124d7342af37106edc76126334a2c38"
}
}
.parse()
.expect("hard-coded hash parses")
@ -65,11 +68,14 @@ impl ParameterCheckpoint for Network {
fn checkpoint_list(&self) -> CheckpointList {
// parse calls CheckpointList::from_list
// TODO:
// - 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
.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 +148,12 @@ impl CheckpointList {
// Check that the list starts with the correct genesis block
match checkpoints.iter().next() {
// TODO: If required (we may not need checkpoints at all in Regtest or custom testnets):
// move this check to `<Network as ParameterCheckpoint>::checkpoint_list(&network)` method above (#8366),
// See <https://github.com/ZcashFoundation/zebra/pull/7924#discussion_r1385865347>
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")?
}

View File

@ -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`],

View File

@ -205,8 +205,9 @@ 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(())
}
@ -216,7 +217,11 @@ async fn continuous_blockchain_restart() -> Result<(), Report> {
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?;
continuous_blockchain(
Some(block::Height(height.try_into().unwrap())),
Network::new_default_testnet(),
)
.await?;
}
Ok(())
}

View File

@ -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]
@ -104,15 +104,18 @@ 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<Network, std::ops::Range<Height>> = {
// TODO: Move the value here to a field on `testnet::Parameters` (#8367)
pub static ref FUNDING_STREAM_HEIGHT_RANGES: HashMap<NetworkKind, std::ops::Range<Height>> = {
let mut hash_map = HashMap::new();
hash_map.insert(Network::Mainnet, Height(1_046_400)..Height(2_726_400));
hash_map.insert(Network::Testnet, Height(1_028_500)..Height(2_796_000));
hash_map.insert(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
pub static ref FUNDING_STREAM_ADDRESSES: HashMap<Network, HashMap<FundingStreamReceiver, Vec<String>>> = {
// 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<NetworkKind, HashMap<FundingStreamReceiver, Vec<String>>> = {
let mut addresses_by_network = HashMap::with_capacity(2);
// Mainnet addresses
@ -120,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, 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::Testnet, testnet_addresses);
addresses_by_network.insert(NetworkKind::Testnet, testnet_addresses);
addresses_by_network
};
@ -215,9 +218,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 +231,8 @@ 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 `testnet::Parameters` to make this configurable.
Network::Testnet(_params) => FIRST_HALVING_TESTNET,
}
}
}

View File

@ -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.
//
// <https://zips.z.cash/protocol/protocol.pdf#blockchain>
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() {

View File

@ -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

View File

@ -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()
),
);

View File

@ -15,7 +15,7 @@ use tempfile::NamedTempFile;
use tokio::{fs, io::AsyncWriteExt};
use tracing::Span;
use zebra_chain::parameters::Network;
use zebra_chain::parameters::{testnet, Network, NetworkKind};
use crate::{
constants::{
@ -222,10 +222,16 @@ impl Config {
}
/// Returns the initial seed peer hostnames for the configured network.
pub fn initial_peer_hostnames(&self) -> &IndexSet<String> {
match self.network {
Network::Mainnet => &self.initial_mainnet_peers,
Network::Testnet => &self.initial_testnet_peers,
pub fn initial_peer_hostnames(&self) -> IndexSet<String> {
match &self.network {
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:
// <https://github.com/ZcashFoundation/zebra/pull/7924#discussion_r1385881828>
Network::Testnet(_params) => IndexSet::new(),
}
}
@ -623,7 +629,8 @@ impl<'de> Deserialize<'de> for Config {
#[serde(deny_unknown_fields, default)]
struct DConfig {
listen_addr: String,
network: Network,
network: NetworkKind,
testnet_parameters: Option<testnet::Parameters>,
initial_mainnet_peers: IndexSet<String>,
initial_testnet_peers: IndexSet<String>,
cache_dir: CacheDir,
@ -638,7 +645,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,
@ -651,7 +659,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,
@ -660,6 +669,24 @@ impl<'de> Deserialize<'de> for Config {
max_connections_per_ip,
} = 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 configured testnet parameters"
);
Network::new_configured_testnet(network_params)
} 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"),
}
};
let listen_addr = match listen_addr.parse::<SocketAddr>() {
Ok(socket) => Ok(socket),
Err(_) => match listen_addr.parse::<IpAddr>() {

View File

@ -16,6 +16,7 @@ use crate::protocol::external::types::*;
use zebra_chain::{
parameters::{
Network::{self, *},
NetworkKind,
NetworkUpgrade::*,
},
serialization::Duration32,
@ -392,11 +393,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<Network, Version> = {
// TODO: Move the value here to a field on `testnet::Parameters` (#8367)
pub static ref INITIAL_MIN_NETWORK_PROTOCOL_VERSION: HashMap<NetworkKind, Version> = {
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(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
};

View File

@ -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) {

View File

@ -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;

View File

@ -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 `testnet::Parameters`, 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.kind())
.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,
})
}
}
@ -201,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
@ -229,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

View File

@ -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(),
network.clone(),
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(),
);
}
@ -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),

View File

@ -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),
);
}

View File

@ -94,7 +94,10 @@ pub async fn test_responses<State, ReadState>(
#[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

View File

@ -706,7 +706,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<Arc<Block>> = network
.blockchain_map()
.iter()
@ -1245,6 +1245,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},
@ -1275,11 +1276,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)]

View File

@ -384,7 +384,24 @@ pub fn scan_block<K: ScanningKey>(
// TODO: Implement a check that returns early when the block height is below the Sapling
// activation height.
let network: zcash_primitives::consensus::Network = network.into();
let network = 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,

View File

@ -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`.

View File

@ -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(

View File

@ -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 {

View File

@ -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<NonNegative> {
// 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_kind: NetworkKind,
size: u32,
peaks: BTreeMap<u32, zcash_history::Entry>,
current_height: Height,
}
impl IntoDisk for NonEmptyHistoryTree {
impl HistoryTreeParts {
/// Converts [`HistoryTreeParts`] to a [`NonEmptyHistoryTree`].
pub(crate) fn with_network(
self,
network: &Network,
) -> Result<NonEmptyHistoryTree, HistoryTreeError> {
assert_eq!(
self.network_kind,
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_kind: 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<u8>;
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")
}
}

View File

@ -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`.

View File

@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize};
use zebra_chain::{
amount::{self, Amount, NonNegative},
block::Height,
parameters::Network::*,
parameters::NetworkKind,
serialization::{ZcashDeserializeInto, ZcashSerialize},
transparent::{self, Address::*},
};
@ -498,14 +498,18 @@ 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) {
match (address.network_kind(), address) {
(Mainnet, PayToPublicKeyHash { .. }) => 0,
(Mainnet, PayToScriptHash { .. }) => 1,
(Testnet, PayToPublicKeyHash { .. }) => 2,
(Testnet, PayToScriptHash { .. }) => 3,
// There's no way to distinguish between Regtest and Testnet for encoded transparent addresses,
// 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,
}
}
@ -529,15 +533,15 @@ impl FromDisk for transparent::Address {
let hash_bytes = hash_bytes.try_into().unwrap();
let network = if address_variant < 2 {
Mainnet
NetworkKind::Mainnet
} else {
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)
}
}
}

View File

@ -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.

View File

@ -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`.
@ -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());
}
}

View File

@ -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.

View File

@ -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));
}
}

View File

@ -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.
}

View File

@ -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]

View File

@ -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();

View File

@ -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(())
}

View File

@ -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,

View File

@ -15,7 +15,7 @@ use tempfile::TempDir;
use zebra_chain::{
block::{Height, HeightDiff, TryIntoHeight},
parameters::Network::{self, *},
parameters::Network,
transparent::MIN_TRANSPARENT_COINBASE_MATURITY,
};
use zebra_consensus::MAX_CHECKPOINT_HEIGHT_GAP;
@ -82,7 +82,7 @@ pub async fn run(network: Network) -> Result<()> {
// Wait for the upgrade if needed.
// Currently we only write an image for testnet, which is quick.
// (Mainnet would need to wait at the end of this function, if the upgrade is long.)
if network == Testnet {
if network.is_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.

View File

@ -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