zebra/zebra-chain/src/parameters/network/tests/vectors.rs

445 lines
16 KiB
Rust

//! Fixed test vectors for the network consensus parameters.
use zcash_primitives::consensus::{self as zp_consensus, Parameters};
use zcash_protocol::consensus::NetworkConstants as _;
use crate::{
block::Height,
parameters::{
subsidy::{
FundingStreamReceiver, FUNDING_STREAM_ECC_ADDRESSES_MAINNET,
FUNDING_STREAM_ECC_ADDRESSES_TESTNET, POST_NU6_FUNDING_STREAMS_TESTNET,
PRE_NU6_FUNDING_STREAMS_TESTNET,
},
testnet::{
self, ConfiguredActivationHeights, ConfiguredFundingStreamRecipient,
ConfiguredFundingStreams, MAX_NETWORK_NAME_LENGTH, RESERVED_NETWORK_NAMES,
},
Network, NetworkUpgrade, MAINNET_ACTIVATION_HEIGHTS, NETWORK_UPGRADES_IN_ORDER,
TESTNET_ACTIVATION_HEIGHTS,
},
};
/// Checks that every method in the `Parameters` impl for `zebra_chain::Network` has the same output
/// as the Parameters impl for `zcash_primitives::consensus::Network` on Mainnet and the default Testnet.
#[test]
fn check_parameters_impl() {
let zp_network_upgrades = [
zp_consensus::NetworkUpgrade::Overwinter,
zp_consensus::NetworkUpgrade::Sapling,
zp_consensus::NetworkUpgrade::Blossom,
zp_consensus::NetworkUpgrade::Heartwood,
zp_consensus::NetworkUpgrade::Canopy,
zp_consensus::NetworkUpgrade::Nu5,
];
for (network, zp_network) in [
(Network::Mainnet, zp_consensus::Network::MainNetwork),
(
Network::new_default_testnet(),
zp_consensus::Network::TestNetwork,
),
] {
for nu in zp_network_upgrades {
let activation_height = network
.activation_height(nu)
.expect("must have activation height for past network upgrades");
assert_eq!(
activation_height,
zp_network
.activation_height(nu)
.expect("must have activation height for past network upgrades"),
"Parameters::activation_heights() outputs must match"
);
let activation_height: u32 = activation_height.into();
for height in (activation_height - 1)..=(activation_height + 1) {
for nu in zp_network_upgrades {
let height = zp_consensus::BlockHeight::from_u32(height);
assert_eq!(
network.is_nu_active(nu, height),
zp_network.is_nu_active(nu, height),
"Parameters::is_nu_active() outputs must match",
);
}
}
}
assert_eq!(
network.coin_type(),
zp_network.coin_type(),
"Parameters::coin_type() outputs must match"
);
assert_eq!(
network.hrp_sapling_extended_spending_key(),
zp_network.hrp_sapling_extended_spending_key(),
"Parameters::hrp_sapling_extended_spending_key() outputs must match"
);
assert_eq!(
network.hrp_sapling_extended_full_viewing_key(),
zp_network.hrp_sapling_extended_full_viewing_key(),
"Parameters::hrp_sapling_extended_full_viewing_key() outputs must match"
);
assert_eq!(
network.hrp_sapling_payment_address(),
zp_network.hrp_sapling_payment_address(),
"Parameters::hrp_sapling_payment_address() outputs must match"
);
assert_eq!(
network.b58_pubkey_address_prefix(),
zp_network.b58_pubkey_address_prefix(),
"Parameters::b58_pubkey_address_prefix() outputs must match"
);
assert_eq!(
network.b58_script_address_prefix(),
zp_network.b58_script_address_prefix(),
"Parameters::b58_script_address_prefix() outputs must match"
);
}
}
/// Checks that `NetworkUpgrade::activation_height()` returns the activation height of the next
/// network upgrade if it doesn't find an activation height for a prior network upgrade, that the
/// `Genesis` upgrade is always at `Height(0)`, and that the default Mainnet/Testnet/Regtest activation
/// heights are what's expected.
#[test]
fn activates_network_upgrades_correctly() {
let expected_activation_height = 1;
let network = testnet::Parameters::build()
.with_activation_heights(ConfiguredActivationHeights {
nu6: Some(expected_activation_height),
..Default::default()
})
.to_network();
let genesis_activation_height = NetworkUpgrade::Genesis
.activation_height(&network)
.expect("must return an activation height");
assert_eq!(
genesis_activation_height,
Height(0),
"activation height for all networks after Genesis and BeforeOverwinter should match NU5 activation height"
);
for nu in NETWORK_UPGRADES_IN_ORDER.into_iter().skip(1) {
let activation_height = nu
.activation_height(&network)
.expect("must return an activation height");
assert_eq!(
activation_height, Height(expected_activation_height),
"activation height for all networks after Genesis and BeforeOverwinter \
should match NU5 activation height, network_upgrade: {nu}, activation_height: {activation_height:?}"
);
}
let expected_default_regtest_activation_heights = &[
(Height(0), NetworkUpgrade::Genesis),
(Height(1), NetworkUpgrade::Canopy),
// TODO: Remove this once the testnet parameters are being serialized.
(Height(100), NetworkUpgrade::Nu5),
];
for (network, expected_activation_heights) in [
(Network::Mainnet, MAINNET_ACTIVATION_HEIGHTS),
(Network::new_default_testnet(), TESTNET_ACTIVATION_HEIGHTS),
(
Network::new_regtest(None, None),
expected_default_regtest_activation_heights,
),
] {
assert_eq!(
network.activation_list(),
expected_activation_heights.iter().cloned().collect(),
"network activation list should match expected activation heights"
);
}
}
/// Checks that configured testnet names are validated and used correctly.
#[test]
fn check_configured_network_name() {
// Sets a no-op panic hook to avoid long output.
std::panic::set_hook(Box::new(|_| {}));
// Checks that reserved network names cannot be used for configured testnets.
for reserved_network_name in RESERVED_NETWORK_NAMES {
std::panic::catch_unwind(|| {
testnet::Parameters::build().with_network_name(reserved_network_name)
})
.expect_err("should panic when attempting to set network name as a reserved name");
}
// Check that max length is enforced, and that network names may only contain alphanumeric characters and '_'.
for invalid_network_name in [
"a".repeat(MAX_NETWORK_NAME_LENGTH + 1),
"!!!!non-alphanumeric-name".to_string(),
] {
std::panic::catch_unwind(|| {
testnet::Parameters::build().with_network_name(invalid_network_name)
})
.expect_err("should panic when setting network name that's too long or contains non-alphanumeric characters (except '_')");
}
drop(std::panic::take_hook());
// Checks that network names are displayed correctly
assert_eq!(
Network::new_default_testnet().to_string(),
"Testnet",
"default testnet should be displayed as 'Testnet'"
);
assert_eq!(
Network::Mainnet.to_string(),
"Mainnet",
"Mainnet should be displayed as 'Mainnet'"
);
assert_eq!(
Network::new_regtest(None, None).to_string(),
"Regtest",
"Regtest should be displayed as 'Regtest'"
);
// Check that network name can contain alphanumeric characters and '_'.
let expected_name = "ConfiguredTestnet_1";
let network = testnet::Parameters::build()
// Check that network name can contain `MAX_NETWORK_NAME_LENGTH` characters
.with_network_name("a".repeat(MAX_NETWORK_NAME_LENGTH))
.with_network_name(expected_name)
.to_network();
// Check that configured network name is displayed
assert_eq!(
network.to_string(),
expected_name,
"network must be displayed as configured network name"
);
}
/// Checks that configured testnet names are validated and used correctly.
#[test]
fn check_network_name() {
// Sets a no-op panic hook to avoid long output.
std::panic::set_hook(Box::new(|_| {}));
// Checks that reserved network names cannot be used for configured testnets.
for reserved_network_name in RESERVED_NETWORK_NAMES {
std::panic::catch_unwind(|| {
testnet::Parameters::build().with_network_name(reserved_network_name)
})
.expect_err("should panic when attempting to set network name as a reserved name");
}
// Check that max length is enforced, and that network names may only contain alphanumeric characters and '_'.
for invalid_network_name in [
"a".repeat(MAX_NETWORK_NAME_LENGTH + 1),
"!!!!non-alphanumeric-name".to_string(),
] {
std::panic::catch_unwind(|| {
testnet::Parameters::build().with_network_name(invalid_network_name)
})
.expect_err("should panic when setting network name that's too long or contains non-alphanumeric characters (except '_')");
}
// Restore the regular panic hook for any unexpected panics
drop(std::panic::take_hook());
// Checks that network names are displayed correctly
assert_eq!(
Network::new_default_testnet().to_string(),
"Testnet",
"default testnet should be displayed as 'Testnet'"
);
assert_eq!(
Network::Mainnet.to_string(),
"Mainnet",
"Mainnet should be displayed as 'Mainnet'"
);
// TODO: Check Regtest
// Check that network name can contain alphanumeric characters and '_'.
let expected_name = "ConfiguredTestnet_1";
let network = testnet::Parameters::build()
// Check that network name can contain `MAX_NETWORK_NAME_LENGTH` characters
.with_network_name("a".repeat(MAX_NETWORK_NAME_LENGTH))
.with_network_name(expected_name)
.to_network();
// Check that configured network name is displayed
assert_eq!(
network.to_string(),
expected_name,
"network must be displayed as configured network name"
);
}
#[test]
fn check_full_activation_list() {
let network = testnet::Parameters::build()
.with_activation_heights(ConfiguredActivationHeights {
nu5: Some(1),
..Default::default()
})
.to_network();
// We expect the first 8 network upgrades to be included, up to NU5
let expected_network_upgrades = &NETWORK_UPGRADES_IN_ORDER[..8];
let full_activation_list_network_upgrades: Vec<_> = network
.full_activation_list()
.into_iter()
.map(|(_, nu)| nu)
.collect();
for expected_network_upgrade in expected_network_upgrades {
assert!(
full_activation_list_network_upgrades.contains(expected_network_upgrade),
"full activation list should contain expected network upgrade"
);
}
}
/// Tests that a set of constraints are enforced when building Testnet parameters,
/// and that funding stream configurations that should be valid can be built.
#[test]
fn check_configured_funding_stream_constraints() {
let configured_funding_streams = [
Default::default(),
ConfiguredFundingStreams {
height_range: Some(Height(2_000_000)..Height(2_200_000)),
..Default::default()
},
ConfiguredFundingStreams {
height_range: Some(Height(20)..Height(30)),
recipients: None,
},
ConfiguredFundingStreams {
recipients: Some(vec![ConfiguredFundingStreamRecipient {
receiver: FundingStreamReceiver::Ecc,
numerator: 20,
addresses: FUNDING_STREAM_ECC_ADDRESSES_TESTNET
.map(Into::into)
.to_vec(),
}]),
..Default::default()
},
ConfiguredFundingStreams {
recipients: Some(vec![ConfiguredFundingStreamRecipient {
receiver: FundingStreamReceiver::Ecc,
numerator: 100,
addresses: FUNDING_STREAM_ECC_ADDRESSES_TESTNET
.map(Into::into)
.to_vec(),
}]),
..Default::default()
},
];
for configured_funding_streams in configured_funding_streams {
for is_pre_nu6 in [false, true] {
let (network_funding_streams, default_funding_streams) = if is_pre_nu6 {
(
testnet::Parameters::build()
.with_pre_nu6_funding_streams(configured_funding_streams.clone())
.to_network()
.pre_nu6_funding_streams()
.clone(),
PRE_NU6_FUNDING_STREAMS_TESTNET.clone(),
)
} else {
(
testnet::Parameters::build()
.with_post_nu6_funding_streams(configured_funding_streams.clone())
.to_network()
.post_nu6_funding_streams()
.clone(),
POST_NU6_FUNDING_STREAMS_TESTNET.clone(),
)
};
let expected_height_range = configured_funding_streams
.height_range
.clone()
.unwrap_or(default_funding_streams.height_range().clone());
assert_eq!(
network_funding_streams.height_range().clone(),
expected_height_range,
"should use default start height when unconfigured"
);
let expected_recipients = configured_funding_streams
.recipients
.clone()
.map(|recipients| {
recipients
.into_iter()
.map(ConfiguredFundingStreamRecipient::into_recipient)
.collect()
})
.unwrap_or(default_funding_streams.recipients().clone());
assert_eq!(
network_funding_streams.recipients().clone(),
expected_recipients,
"should use default start height when unconfigured"
);
}
}
std::panic::set_hook(Box::new(|_| {}));
// should panic when there are fewer addresses than the max funding stream address index.
let expected_panic_num_addresses = std::panic::catch_unwind(|| {
testnet::Parameters::build().with_pre_nu6_funding_streams(ConfiguredFundingStreams {
recipients: Some(vec![ConfiguredFundingStreamRecipient {
receiver: FundingStreamReceiver::Ecc,
numerator: 10,
addresses: vec![],
}]),
..Default::default()
});
});
// should panic when sum of numerators is greater than funding stream denominator.
let expected_panic_numerator = std::panic::catch_unwind(|| {
testnet::Parameters::build().with_pre_nu6_funding_streams(ConfiguredFundingStreams {
recipients: Some(vec![ConfiguredFundingStreamRecipient {
receiver: FundingStreamReceiver::Ecc,
numerator: 101,
addresses: FUNDING_STREAM_ECC_ADDRESSES_TESTNET
.map(Into::into)
.to_vec(),
}]),
..Default::default()
});
});
// should panic when recipient addresses are for Mainnet.
let expected_panic_wrong_addr_network = std::panic::catch_unwind(|| {
testnet::Parameters::build().with_pre_nu6_funding_streams(ConfiguredFundingStreams {
recipients: Some(vec![ConfiguredFundingStreamRecipient {
receiver: FundingStreamReceiver::Ecc,
numerator: 10,
addresses: FUNDING_STREAM_ECC_ADDRESSES_MAINNET
.map(Into::into)
.to_vec(),
}]),
..Default::default()
});
});
// drop panic hook before expecting errors.
let _ = std::panic::take_hook();
expected_panic_num_addresses.expect_err("should panic when there are too few addresses");
expected_panic_numerator.expect_err(
"should panic when sum of numerators is greater than funding stream denominator",
);
expected_panic_wrong_addr_network
.expect_err("should panic when recipient addresses are for Mainnet");
}