diff --git a/zebra-chain/src/parameters/network.rs b/zebra-chain/src/parameters/network.rs index e346e28e6..805db9f89 100644 --- a/zebra-chain/src/parameters/network.rs +++ b/zebra-chain/src/parameters/network.rs @@ -11,6 +11,8 @@ use crate::{ parameters::NetworkUpgrade, }; +use self::testnet::ConfiguredActivationHeights; + pub mod testnet; #[cfg(test)] @@ -166,6 +168,11 @@ impl Network { Self::Testnet(Arc::new(params)) } + /// Creates a new [`Network::Testnet`] with `Regtest` parameters and the provided network upgrade activation heights. + pub fn new_regtest(activation_heights: ConfiguredActivationHeights) -> Self { + Self::new_configured_testnet(testnet::Parameters::new_regtest(activation_heights)) + } + /// Returns true if the network is the default Testnet, or false otherwise. pub fn is_default_testnet(&self) -> bool { if let Self::Testnet(params) = self { diff --git a/zebra-chain/src/parameters/network/testnet.rs b/zebra-chain/src/parameters/network/testnet.rs index 7cc936927..bb6acaf9e 100644 --- a/zebra-chain/src/parameters/network/testnet.rs +++ b/zebra-chain/src/parameters/network/testnet.rs @@ -67,13 +67,7 @@ impl Default for ParametersBuilder { // # Correctness // // `Genesis` network upgrade activation height must always be 0 - activation_heights: [ - (Height(0), NetworkUpgrade::Genesis), - // TODO: Find out if `BeforeOverwinter` must always be active at Height(1), remove it here if it's not required. - (Height(1), NetworkUpgrade::BeforeOverwinter), - ] - .into_iter() - .collect(), + activation_heights: TESTNET_ACTIVATION_HEIGHTS.iter().cloned().collect(), hrp_sapling_extended_spending_key: zp_constants::testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY.to_string(), hrp_sapling_extended_full_viewing_key: @@ -166,6 +160,7 @@ impl ParametersBuilder { // # Correctness // // Height(0) must be reserved for the `NetworkUpgrade::Genesis`. + // TODO: Find out if `BeforeOverwinter` must always be active at Height(1), remove it here if it's not required. self.activation_heights.split_off(&Height(2)); self.activation_heights.extend(activation_heights); @@ -220,7 +215,6 @@ impl Default for Parameters { fn default() -> Self { Self { network_name: "Testnet".to_string(), - activation_heights: TESTNET_ACTIVATION_HEIGHTS.iter().cloned().collect(), ..Self::build().finish() } } @@ -232,6 +226,19 @@ impl Parameters { ParametersBuilder::default() } + /// Accepts a [`ConfiguredActivationHeights`]. + /// + /// Creates an instance of [`Parameters`] with `Regtest` values. + pub fn new_regtest(activation_heights: ConfiguredActivationHeights) -> Self { + Self { + network_name: "Regtest".to_string(), + ..Self::build() + // Removes default Testnet activation heights, most network upgrades are disabled by default for Regtest + .with_activation_heights(activation_heights) + .finish() + } + } + /// Returns true if the instance of [`Parameters`] represents the default public Testnet. pub fn is_default_testnet(&self) -> bool { self == &Self::default() diff --git a/zebra-chain/src/parameters/network/tests/vectors.rs b/zebra-chain/src/parameters/network/tests/vectors.rs index 1d619aa5e..0e69b9de8 100644 --- a/zebra-chain/src/parameters/network/tests/vectors.rs +++ b/zebra-chain/src/parameters/network/tests/vectors.rs @@ -164,8 +164,11 @@ fn check_network_name() { "Mainnet", "Mainnet should be displayed as 'Mainnet'" ); - - // TODO: Check Regtest + assert_eq!( + Network::new_regtest(Default::default()).to_string(), + "Regtest", + "Regtest should be displayed as 'Regtest'" + ); // Check that network name can contain alphanumeric characters and '_'. let expected_name = "ConfiguredTestnet_1"; diff --git a/zebra-network/src/config.rs b/zebra-network/src/config.rs index e32f6c138..53e77c119 100644 --- a/zebra-network/src/config.rs +++ b/zebra-network/src/config.rs @@ -630,8 +630,7 @@ impl<'de> Deserialize<'de> for Config { #[derive(Deserialize)] struct DTestnetParameters { network_name: Option, - #[serde(default)] - activation_heights: ConfiguredActivationHeights, + activation_heights: Option, } #[derive(Deserialize)] @@ -640,6 +639,7 @@ impl<'de> Deserialize<'de> for Config { listen_addr: String, network: NetworkKind, testnet_parameters: Option, + regtest_activation_heights: ConfiguredActivationHeights, initial_mainnet_peers: IndexSet, initial_testnet_peers: IndexSet, cache_dir: CacheDir, @@ -656,6 +656,7 @@ impl<'de> Deserialize<'de> for Config { listen_addr: "0.0.0.0".to_string(), network: Default::default(), testnet_parameters: None, + regtest_activation_heights: ConfiguredActivationHeights::default(), initial_mainnet_peers: config.initial_mainnet_peers, initial_testnet_peers: config.initial_testnet_peers, cache_dir: config.cache_dir, @@ -670,6 +671,7 @@ impl<'de> Deserialize<'de> for Config { listen_addr, network: network_kind, testnet_parameters, + regtest_activation_heights, initial_mainnet_peers, initial_testnet_peers, cache_dir, @@ -678,17 +680,33 @@ impl<'de> Deserialize<'de> for Config { max_connections_per_ip, } = DConfig::deserialize(deserializer)?; - // TODO: Panic here if the initial testnet peers are the default initial testnet peers. + /// Accepts an [`IndexSet`] of initial peers, + /// + /// Returns true if any of them are the default Testnet or Mainnet initial peers. + fn contains_default_initial_peers(initial_peers: &IndexSet) -> bool { + let Config { + initial_mainnet_peers: mut default_initial_peers, + initial_testnet_peers: default_initial_testnet_peers, + .. + } = Config::default(); + default_initial_peers.extend(default_initial_testnet_peers); + + initial_peers + .intersection(&default_initial_peers) + .next() + .is_some() + } + let network = if let Some(DTestnetParameters { network_name, activation_heights, }) = testnet_parameters { - assert_eq!( - network_kind, - NetworkKind::Testnet, - "set network to 'Testnet' to use configured testnet parameters" - ); + if network_kind != NetworkKind::Testnet { + return Err(de::Error::custom( + "network must be 'Testnet' to use configured testnet parameters", + )); + } let mut params_builder = testnet::Parameters::build(); @@ -696,15 +714,27 @@ impl<'de> Deserialize<'de> for Config { params_builder = params_builder.with_network_name(network_name) } - params_builder - .with_activation_heights(activation_heights) - .to_network() + // Retain default Testnet activation heights unless there's an empty [testnet_parameters.activation_heights] section. + if let Some(activation_heights) = activation_heights { + // Return an error if the initial testnet peers includes any of the default initial Mainnet or Testnet + // peers while activation heights are configured. + // TODO: Check that the network magic is different from the default Mainnet/Testnet network magic too? + if contains_default_initial_peers(&initial_testnet_peers) { + return Err(de::Error::custom( + "cannot use default initial testnet peers with configured activation heights", + )); + } + + params_builder = params_builder.with_activation_heights(activation_heights) + } + + params_builder.to_network() } else { // Convert to default `Network` for a `NetworkKind` if there are no testnet parameters. match network_kind { NetworkKind::Mainnet => Network::Mainnet, NetworkKind::Testnet => Network::new_default_testnet(), - NetworkKind::Regtest => unimplemented!("Regtest is not yet implemented in Zebra"), + NetworkKind::Regtest => Network::new_regtest(regtest_activation_heights), } }; diff --git a/zebrad/tests/common/configs/v1.7.0.toml b/zebrad/tests/common/configs/v1.7.0.toml index 00438213d..a0499258c 100644 --- a/zebrad/tests/common/configs/v1.7.0.toml +++ b/zebrad/tests/common/configs/v1.7.0.toml @@ -57,13 +57,10 @@ initial_testnet_peers = [ ] listen_addr = "0.0.0.0:8233" max_connections_per_ip = 1 -network = "Testnet" +network = "Regtest" peerset_initial_target_size = 25 -[network.testnet_parameters] -network_name = "ConfiguredTestnet_1" - -[network.testnet_parameters.activation_heights] +[network.regtest_activation_heights] BeforeOverwinter = 1 Overwinter = 207_500 Sapling = 280_000