- Makes the `activation_heights` config field optional by adding a #[serde(default)]

- Panics if a non-zero activation height is provided for the `Genesis` network upgrade
- Always sets the `Genesis` and `BeforeOverwinter` network upgrade activation heights to 0 and 1, `BeforeOverwinter` could be overwritten by a later network upgrade
- Makes the `activation_heights` field on `Parameters` private, adds/uses an accessor method instead, and adds a builder struct and `build()` method
This commit is contained in:
Arya 2024-04-11 22:07:20 -04:00
parent 997e265636
commit 93c7a58330
4 changed files with 76 additions and 10 deletions

View File

@ -6,6 +6,54 @@ use crate::{
parameters::{network_upgrade::TESTNET_ACTIVATION_HEIGHTS, NetworkUpgrade},
};
/// Builder for the [`Parameters`] struct.
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct ParametersBuilder {
/// The network upgrade activation heights for this network, see [`Parameters::activation_heights`] for more details.
activation_heights: BTreeMap<Height, NetworkUpgrade>,
}
impl Default for ParametersBuilder {
fn default() -> Self {
Self {
// # Correctness
//
// `Genesis` network upgrade activation height must always be 0
// TODO: Find out if `BeforeOverwinter` must always be active at height 1
activation_heights: [
(Height(0), NetworkUpgrade::Genesis),
(Height(1), NetworkUpgrade::BeforeOverwinter),
]
.into_iter()
.collect(),
}
}
}
impl ParametersBuilder {
/// Extends network upgrade activation heights with the provided activation heights.
pub fn activation_heights(
mut self,
new_activation_heights: Vec<(Height, NetworkUpgrade)>,
) -> Self {
let new_activation_heights: BTreeMap<_, _> =
new_activation_heights.iter().cloned().collect();
// # Correctness
//
// Height(0) must be reserved for the `NetworkUpgrade::Genesis`.
self.activation_heights
.extend(new_activation_heights.range(Height(1)..));
self
}
/// Converts the builder to a [`Parameters`] struct
pub fn finish(self) -> Parameters {
let Self { activation_heights } = self;
Parameters { activation_heights }
}
}
/// Network consensus parameters for test networks such as Regtest and the default Testnet.
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct Parameters {
@ -14,21 +62,31 @@ pub struct Parameters {
/// Note: This value is ignored by `Network::activation_list()` when `zebra-chain` is
/// compiled with the `zebra-test` feature flag AND the `TEST_FAKE_ACTIVATION_HEIGHTS`
/// environment variable is set.
pub activation_heights: BTreeMap<Height, NetworkUpgrade>,
activation_heights: BTreeMap<Height, NetworkUpgrade>,
}
impl Default for Parameters {
/// Returns an instance of the default public testnet [`Parameters`].
fn default() -> Self {
Self {
activation_heights: TESTNET_ACTIVATION_HEIGHTS.iter().cloned().collect(),
}
Self::build()
.activation_heights(TESTNET_ACTIVATION_HEIGHTS.to_vec())
.finish()
}
}
impl Parameters {
/// Creates a new [`ParametersBuilder`].
pub fn build() -> ParametersBuilder {
ParametersBuilder::default()
}
/// Returns true if the instance of [`Parameters`] represents the default public Testnet.
pub fn is_default_testnet(&self) -> bool {
self == &Self::default()
}
/// Returns a reference to the network upgrade activation heights
pub fn activation_heights(&self) -> &BTreeMap<Height, NetworkUpgrade> {
&self.activation_heights
}
}

View File

@ -264,7 +264,7 @@ impl Network {
}
Mainnet => MAINNET_ACTIVATION_HEIGHTS.iter().cloned().collect(),
Testnet(params) => params.activation_heights.clone(),
Testnet(params) => params.activation_heights().clone(),
}
}
}

View File

@ -625,9 +625,9 @@ impl<'de> Deserialize<'de> for Config {
where
D: Deserializer<'de>,
{
/// Network consensus parameters for test networks such as Regtest and the default Testnet.
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct DTestnetParameters {
#[derive(Deserialize)]
struct DTestnetParameters {
#[serde(default)]
pub(super) activation_heights: Vec<(u32, NetworkUpgrade)>,
}
@ -686,6 +686,11 @@ impl<'de> Deserialize<'de> for Config {
let activation_heights = activation_heights
.into_iter()
.map(|(height, network_upgrade)| {
assert!(
network_upgrade != NetworkUpgrade::Genesis || height == 0,
"Genesis network upgrade activation height is not configurable"
);
(
height.try_into().expect("activation height must be valid"),
network_upgrade,
@ -693,7 +698,11 @@ impl<'de> Deserialize<'de> for Config {
})
.collect();
Network::new_configured_testnet(testnet::Parameters { activation_heights })
let testnet_parameters = testnet::Parameters::build()
.activation_heights(activation_heights)
.finish();
Network::new_configured_testnet(testnet_parameters)
} else {
// Convert to default `Network` for a `NetworkKind` if there are no testnet parameters.
match network_kind {

View File

@ -62,7 +62,6 @@ peerset_initial_target_size = 25
[network.testnet_parameters]
activation_heights = [
[0, "Genesis"],
[1, "BeforeOverwinter"],
[207_500, "Overwinter"],
[280_000, "Sapling"],