moves all ordered network upgrades to a constant, adds a no_duplicates test, moves struct with activation heights outside deserialization impl and accepts it in `ParametersBuilder::activation_heights()` instead of a Vec
This commit is contained in:
parent
f724c0188f
commit
a37510a67b
|
@ -3,9 +3,33 @@ use std::collections::BTreeMap;
|
|||
|
||||
use crate::{
|
||||
block::Height,
|
||||
parameters::{network_upgrade::TESTNET_ACTIVATION_HEIGHTS, NetworkUpgrade},
|
||||
parameters::{
|
||||
network_upgrade::TESTNET_ACTIVATION_HEIGHTS, Network, NetworkUpgrade,
|
||||
NETWORK_UPGRADES_IN_ORDER,
|
||||
},
|
||||
};
|
||||
|
||||
/// Configurable activation heights for Regtest and configured Testnets.
|
||||
#[derive(Deserialize, Default)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct ConfiguredActivationHeights {
|
||||
/// Activation height for `BeforeOverwinter` network upgrade.
|
||||
pub before_overwinter: Option<u32>,
|
||||
/// Activation height for `Overwinter` network upgrade.
|
||||
pub overwinter: Option<u32>,
|
||||
/// Activation height for `Sapling` network upgrade.
|
||||
pub sapling: Option<u32>,
|
||||
/// Activation height for `Blossom` network upgrade.
|
||||
pub blossom: Option<u32>,
|
||||
/// Activation height for `Heartwood` network upgrade.
|
||||
pub heartwood: Option<u32>,
|
||||
/// Activation height for `Canopy` network upgrade.
|
||||
pub canopy: Option<u32>,
|
||||
/// Activation height for `NU5` network upgrade.
|
||||
#[serde(rename = "NU5")]
|
||||
pub nu5: Option<u32>,
|
||||
}
|
||||
|
||||
/// Builder for the [`Parameters`] struct.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||
pub struct ParametersBuilder {
|
||||
|
@ -33,23 +57,48 @@ impl Default for ParametersBuilder {
|
|||
impl ParametersBuilder {
|
||||
/// Checks that the provided network upgrade activation heights are in the correct order, then
|
||||
/// sets them as the new network upgrade activation heights.
|
||||
pub fn activation_heights(mut self, activation_heights: Vec<(Height, NetworkUpgrade)>) -> Self {
|
||||
let activation_heights: BTreeMap<_, _> = activation_heights
|
||||
pub fn activation_heights(
|
||||
mut self,
|
||||
ConfiguredActivationHeights {
|
||||
before_overwinter,
|
||||
overwinter,
|
||||
sapling,
|
||||
blossom,
|
||||
heartwood,
|
||||
canopy,
|
||||
nu5,
|
||||
}: ConfiguredActivationHeights,
|
||||
) -> Self {
|
||||
use NetworkUpgrade::*;
|
||||
|
||||
// # Correctness
|
||||
//
|
||||
// These must be in order so that later network upgrades overwrite prior ones
|
||||
// if multiple network upgrades are configured with the same activation height.
|
||||
let activation_heights: BTreeMap<_, _> = before_overwinter
|
||||
.into_iter()
|
||||
// TODO: Find out if `BeforeOverwinter` is required at Height(1), remove this filter if it's not required to be at Height(1)
|
||||
.map(|h| (h, BeforeOverwinter))
|
||||
.chain(overwinter.into_iter().map(|h| (h, Overwinter)))
|
||||
.chain(sapling.into_iter().map(|h| (h, Sapling)))
|
||||
.chain(blossom.into_iter().map(|h| (h, Blossom)))
|
||||
.chain(heartwood.into_iter().map(|h| (h, Heartwood)))
|
||||
.chain(canopy.into_iter().map(|h| (h, Canopy)))
|
||||
.chain(nu5.into_iter().map(|h| (h, Nu5))) // TODO: Find out if `BeforeOverwinter` is required at Height(1), remove this filter if it's not required to be at Height(1)
|
||||
.filter(|&(_, nu)| nu != NetworkUpgrade::BeforeOverwinter)
|
||||
.map(|(h, nu)| (h.try_into().expect("activation height must be valid"), nu))
|
||||
.collect();
|
||||
|
||||
// Check that the provided network upgrade activation heights are in the same order by height as the default testnet activation heights
|
||||
let network_upgrades: Vec<_> = activation_heights.iter().map(|(_h, &nu)| nu).collect();
|
||||
|
||||
// Check that the provided network upgrade activation heights are in the same order by height as the default testnet activation heights
|
||||
let mut activation_heights_iter = activation_heights.iter();
|
||||
for expected_network_upgrade in TESTNET_ACTIVATION_HEIGHTS.iter().map(|&(_, nu)| nu) {
|
||||
for expected_network_upgrade in NETWORK_UPGRADES_IN_ORDER {
|
||||
if !network_upgrades.contains(&expected_network_upgrade) {
|
||||
continue;
|
||||
} else if let Some((_h, &network_upgrade)) = activation_heights_iter.next() {
|
||||
assert!(
|
||||
network_upgrade == expected_network_upgrade,
|
||||
"network upgrades must be in order"
|
||||
"network upgrades must be activated in order, the correct order is {NETWORK_UPGRADES_IN_ORDER:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -68,6 +117,11 @@ impl ParametersBuilder {
|
|||
let Self { activation_heights } = self;
|
||||
Parameters { activation_heights }
|
||||
}
|
||||
|
||||
/// Converts the builder to a configured [`Network::Testnet`]
|
||||
pub fn to_network(self) -> Network {
|
||||
Network::new_configured_testnet(self.finish())
|
||||
}
|
||||
}
|
||||
|
||||
/// Network consensus parameters for test networks such as Regtest and the default Testnet.
|
||||
|
@ -84,9 +138,9 @@ pub struct Parameters {
|
|||
impl Default for Parameters {
|
||||
/// Returns an instance of the default public testnet [`Parameters`].
|
||||
fn default() -> Self {
|
||||
Self::build()
|
||||
.activation_heights(TESTNET_ACTIVATION_HEIGHTS.to_vec())
|
||||
.finish()
|
||||
Self {
|
||||
activation_heights: TESTNET_ACTIVATION_HEIGHTS.iter().cloned().collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,8 +2,16 @@
|
|||
|
||||
use zcash_primitives::consensus::{self as zp_consensus, Parameters};
|
||||
|
||||
use crate::parameters::Network;
|
||||
use crate::{
|
||||
block::Height,
|
||||
parameters::{
|
||||
testnet::{self, ConfiguredActivationHeights},
|
||||
Network, NetworkUpgrade, NETWORK_UPGRADES_IN_ORDER,
|
||||
},
|
||||
};
|
||||
|
||||
/// Checks that every method in the `Parameters` impl for `zebra_chain::Network` has the same output
|
||||
/// as the Parameters impl for `zcash_primitives::consensus::Network` on Mainnet and the default Testnet.
|
||||
#[test]
|
||||
fn check_parameters_impl() {
|
||||
let zp_network_upgrades = [
|
||||
|
@ -81,3 +89,63 @@ fn check_parameters_impl() {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks that `NetworkUpgrade::activation_height()` returns the activation height of the next
|
||||
/// network upgrade if it doesn't find an activation height for a prior network upgrade, and that the
|
||||
/// `Genesis` upgrade is always at `Height(0)`.
|
||||
#[test]
|
||||
fn activates_network_upgrades_correctly() {
|
||||
let expected_activation_height = 1;
|
||||
let network = testnet::Parameters::build()
|
||||
.activation_heights(ConfiguredActivationHeights {
|
||||
nu5: Some(expected_activation_height),
|
||||
..Default::default()
|
||||
})
|
||||
.to_network();
|
||||
|
||||
let genesis_activation_height = NetworkUpgrade::Genesis
|
||||
.activation_height(&network)
|
||||
.expect("must return an activation height");
|
||||
|
||||
assert_eq!(
|
||||
genesis_activation_height,
|
||||
Height(0),
|
||||
"activation height for all networks after Genesis and BeforeOverwinter should match NU5 activation height"
|
||||
);
|
||||
|
||||
for nu in NETWORK_UPGRADES_IN_ORDER.into_iter().skip(1) {
|
||||
let activation_height = nu
|
||||
.activation_height(&network)
|
||||
.expect("must return an activation height");
|
||||
|
||||
assert_eq!(
|
||||
activation_height, Height(expected_activation_height),
|
||||
"activation height for all networks after Genesis and BeforeOverwinter \
|
||||
should match NU5 activation height, network_upgrade: {nu}, activation_height: {activation_height:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks that there are no duplicate activation heights when using configured activation heights.
|
||||
// TODO: Convert this to a proptest.
|
||||
#[test]
|
||||
fn no_duplicate_activation_heights() {
|
||||
let network = testnet::Parameters::build()
|
||||
.activation_heights(ConfiguredActivationHeights {
|
||||
overwinter: Some(2),
|
||||
..Default::default()
|
||||
})
|
||||
.to_network();
|
||||
|
||||
for target_nu in NETWORK_UPGRADES_IN_ORDER.into_iter().skip(1) {
|
||||
assert!(
|
||||
network
|
||||
.activation_list()
|
||||
.into_iter()
|
||||
.filter(|&(_h, nu)| nu == target_nu)
|
||||
.count()
|
||||
<= 1,
|
||||
"there should be at most 1 activation height per possible network upgrade"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,18 @@ use hex::{FromHex, ToHex};
|
|||
#[cfg(any(test, feature = "proptest-impl"))]
|
||||
use proptest_derive::Arbitrary;
|
||||
|
||||
/// A list of network upgrades in the order that they must be activated.
|
||||
pub const NETWORK_UPGRADES_IN_ORDER: [NetworkUpgrade; 8] = [
|
||||
Genesis,
|
||||
BeforeOverwinter,
|
||||
Overwinter,
|
||||
Sapling,
|
||||
Blossom,
|
||||
Heartwood,
|
||||
Canopy,
|
||||
Nu5,
|
||||
];
|
||||
|
||||
/// A Zcash network upgrade.
|
||||
///
|
||||
/// Network upgrades can change the Zcash network protocol or consensus rules in
|
||||
|
|
|
@ -15,7 +15,10 @@ use tempfile::NamedTempFile;
|
|||
use tokio::{fs, io::AsyncWriteExt};
|
||||
use tracing::Span;
|
||||
|
||||
use zebra_chain::parameters::{testnet, Network, NetworkKind, NetworkUpgrade};
|
||||
use zebra_chain::parameters::{
|
||||
testnet::{self, ConfiguredActivationHeights},
|
||||
Network, NetworkKind,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
constants::{
|
||||
|
@ -625,23 +628,10 @@ impl<'de> Deserialize<'de> for Config {
|
|||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize, Default)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
struct DNetworkUpgradeActivationHeights {
|
||||
before_overwinter: Option<u32>,
|
||||
overwinter: Option<u32>,
|
||||
sapling: Option<u32>,
|
||||
blossom: Option<u32>,
|
||||
heartwood: Option<u32>,
|
||||
canopy: Option<u32>,
|
||||
#[serde(rename = "NU5")]
|
||||
nu5: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct DTestnetParameters {
|
||||
#[serde(default)]
|
||||
pub(super) activation_heights: DNetworkUpgradeActivationHeights,
|
||||
pub(super) activation_heights: ConfiguredActivationHeights,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
@ -688,45 +678,17 @@ impl<'de> Deserialize<'de> for Config {
|
|||
max_connections_per_ip,
|
||||
} = DConfig::deserialize(deserializer)?;
|
||||
|
||||
let network = if let Some(DTestnetParameters {
|
||||
activation_heights:
|
||||
DNetworkUpgradeActivationHeights {
|
||||
before_overwinter,
|
||||
overwinter,
|
||||
sapling,
|
||||
blossom,
|
||||
heartwood,
|
||||
canopy,
|
||||
nu5,
|
||||
},
|
||||
}) = testnet_parameters
|
||||
{
|
||||
use NetworkUpgrade::*;
|
||||
|
||||
// TODO: Panic here if the initial testnet peers are the default initial testnet peers.
|
||||
// TODO: Panic here if the initial testnet peers are the default initial testnet peers.
|
||||
let network = if let Some(DTestnetParameters { activation_heights }) = testnet_parameters {
|
||||
assert_eq!(
|
||||
network_kind,
|
||||
NetworkKind::Testnet,
|
||||
"set network to 'Testnet' to use configured testnet parameters"
|
||||
);
|
||||
|
||||
let activation_heights = before_overwinter
|
||||
.into_iter()
|
||||
.map(|h| (h, BeforeOverwinter))
|
||||
.chain(overwinter.into_iter().map(|h| (h, Overwinter)))
|
||||
.chain(sapling.into_iter().map(|h| (h, Sapling)))
|
||||
.chain(blossom.into_iter().map(|h| (h, Blossom)))
|
||||
.chain(heartwood.into_iter().map(|h| (h, Heartwood)))
|
||||
.chain(canopy.into_iter().map(|h| (h, Canopy)))
|
||||
.chain(nu5.into_iter().map(|h| (h, Nu5)))
|
||||
.map(|(h, nu)| (h.try_into().expect("activation height must be valid"), nu))
|
||||
.collect();
|
||||
|
||||
let testnet_parameters = testnet::Parameters::build()
|
||||
testnet::Parameters::build()
|
||||
.activation_heights(activation_heights)
|
||||
.finish();
|
||||
|
||||
Network::new_configured_testnet(testnet_parameters)
|
||||
.to_network()
|
||||
} else {
|
||||
// Convert to default `Network` for a `NetworkKind` if there are no testnet parameters.
|
||||
match network_kind {
|
||||
|
|
Loading…
Reference in New Issue