Skip to content

Commit

Permalink
Updates new_regtest() method to return a Testnet without funding st…
Browse files Browse the repository at this point in the history
…reams, updates funding stream setter methods to set a flag indicating that parameters affecting the funding stream address period should be locked, updates the setter methods for parameters that affect the funding stream address period to panic if those parameters should be locked.
  • Loading branch information
arya2 committed Oct 10, 2024
1 parent ca7ecf0 commit 95c6c84
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 90 deletions.
186 changes: 105 additions & 81 deletions zebra-chain/src/parameters/network/testnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ use crate::{
use super::{
magic::Magic,
subsidy::{
FundingStreamReceiver, FundingStreamRecipient, FundingStreams, ParameterSubsidy,
BLOSSOM_POW_TARGET_SPACING_RATIO, FIRST_HALVING_TESTNET,
FUNDING_STREAM_ADDRESS_CHANGE_INTERVAL, POST_BLOSSOM_HALVING_INTERVAL,
FundingStreamReceiver, FundingStreamRecipient, FundingStreams,
BLOSSOM_POW_TARGET_SPACING_RATIO, POST_BLOSSOM_HALVING_INTERVAL,
POST_NU6_FUNDING_STREAMS_MAINNET, POST_NU6_FUNDING_STREAMS_TESTNET,
PRE_BLOSSOM_HALVING_INTERVAL, PRE_NU6_FUNDING_STREAMS_MAINNET,
PRE_NU6_FUNDING_STREAMS_TESTNET,
Expand Down Expand Up @@ -57,26 +56,6 @@ const TESTNET_GENESIS_HASH: &str =
/// [zcashd regtest halving interval](https://github.com/zcash/zcash/blob/v5.10.0/src/consensus/params.h#L252)
const PRE_BLOSSOM_REGTEST_HALVING_INTERVAL: HeightDiff = 144;

/// Used to validate number of funding stream recipient addresses on configured Testnets.
struct TestnetParameterSubsidyImpl;

impl ParameterSubsidy for TestnetParameterSubsidyImpl {
fn height_for_first_halving(&self) -> Height {
FIRST_HALVING_TESTNET
}
fn post_blossom_halving_interval(&self) -> block::HeightDiff {
POST_BLOSSOM_HALVING_INTERVAL
}

fn pre_blossom_halving_interval(&self) -> block::HeightDiff {
PRE_BLOSSOM_HALVING_INTERVAL
}

fn funding_stream_address_change_interval(&self) -> HeightDiff {
FUNDING_STREAM_ADDRESS_CHANGE_INTERVAL
}
}

/// Configurable funding stream recipient for configured Testnets.
#[derive(Deserialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
Expand Down Expand Up @@ -112,7 +91,12 @@ pub struct ConfiguredFundingStreams {
impl ConfiguredFundingStreams {
/// Converts a [`ConfiguredFundingStreams`] to a [`FundingStreams`], using the provided default values
/// if `height_range` or `recipients` are None.
fn convert_with_default(self, default_funding_streams: FundingStreams) -> FundingStreams {
fn convert_with_default(
self,
default_funding_streams: FundingStreams,
parameters_builder: &ParametersBuilder,
) -> FundingStreams {
let network = parameters_builder.to_network_unchecked();
let height_range = self
.height_range
.unwrap_or(default_funding_streams.height_range().clone());
Expand All @@ -134,43 +118,7 @@ impl ConfiguredFundingStreams {

let funding_streams = FundingStreams::new(height_range.clone(), recipients);

// check that receivers have enough addresses.

let expected_min_num_addresses =
1u32.checked_add(funding_stream_address_period(
height_range
.end
.previous()
.expect("end height must be above start height and genesis height"),
&TestnetParameterSubsidyImpl,
))
.expect("no overflow should happen in this sum")
.checked_sub(funding_stream_address_period(
height_range.start,
&TestnetParameterSubsidyImpl,
))
.expect("no overflow should happen in this sub") as usize;

for (&receiver, recipient) in funding_streams.recipients() {
if receiver == FundingStreamReceiver::Deferred {
// The `Deferred` receiver doesn't need any addresses.
continue;
}

assert!(
recipient.addresses().len() >= expected_min_num_addresses,
"recipients must have a sufficient number of addresses for height range, \
minimum num addresses required: {expected_min_num_addresses}"
);

for address in recipient.addresses() {
assert_eq!(
address.network_kind(),
NetworkKind::Testnet,
"configured funding stream addresses must be for Testnet"
);
}
}
check_funding_stream_address_period(&funding_streams, &network);

// check that sum of receiver numerators is valid.

Expand All @@ -190,6 +138,44 @@ impl ConfiguredFundingStreams {
}
}

/// Checks that the provided [`FundingStreams`] has sufficient recipient addresses for the
/// funding stream address period of the provided [`Network`].
fn check_funding_stream_address_period(funding_streams: &FundingStreams, network: &Network) {
let height_range = funding_streams.height_range();
let expected_min_num_addresses =
1u32.checked_add(funding_stream_address_period(
height_range
.end
.previous()
.expect("end height must be above start height and genesis height"),
network,
))
.expect("no overflow should happen in this sum")
.checked_sub(funding_stream_address_period(height_range.start, network))
.expect("no overflow should happen in this sub") as usize;

for (&receiver, recipient) in funding_streams.recipients() {
if receiver == FundingStreamReceiver::Deferred {
// The `Deferred` receiver doesn't need any addresses.
continue;
}

assert!(
recipient.addresses().len() >= expected_min_num_addresses,
"recipients must have a sufficient number of addresses for height range, \
minimum num addresses required: {expected_min_num_addresses}"
);

for address in recipient.addresses() {
assert_eq!(
address.network_kind(),
NetworkKind::Testnet,
"configured funding stream addresses must be for Testnet"
);
}
}
}

/// Configurable activation heights for Regtest and configured Testnets.
#[derive(Deserialize, Default, Clone)]
#[serde(rename_all = "PascalCase", deny_unknown_fields)]
Expand Down Expand Up @@ -231,6 +217,9 @@ pub struct ParametersBuilder {
pre_nu6_funding_streams: FundingStreams,
/// Post-NU6 funding streams for this network
post_nu6_funding_streams: FundingStreams,
/// A flag indicating whether to allow changes to fields that affect
/// the funding stream address period.
should_lock_funding_stream_address_period: bool,
/// Target difficulty limit for this network
target_difficulty_limit: ExpandedDifficulty,
/// A flag for disabling proof-of-work checks when Zebra is validating blocks
Expand Down Expand Up @@ -271,6 +260,7 @@ impl Default for ParametersBuilder {
disable_pow: false,
pre_nu6_funding_streams: PRE_NU6_FUNDING_STREAMS_TESTNET.clone(),
post_nu6_funding_streams: POST_NU6_FUNDING_STREAMS_TESTNET.clone(),
should_lock_funding_stream_address_period: false,
pre_blossom_halving_interval: PRE_BLOSSOM_HALVING_INTERVAL,
post_blossom_halving_interval: POST_BLOSSOM_HALVING_INTERVAL,
}
Expand Down Expand Up @@ -342,6 +332,10 @@ impl ParametersBuilder {
) -> Self {
use NetworkUpgrade::*;

if self.should_lock_funding_stream_address_period {
panic!("activation heights on ParametersBuilder must not be set after setting funding streams");
}

// # Correctness
//
// These must be in order so that later network upgrades overwrite prior ones
Expand Down Expand Up @@ -401,7 +395,8 @@ impl ParametersBuilder {
funding_streams: ConfiguredFundingStreams,
) -> Self {
self.pre_nu6_funding_streams =
funding_streams.convert_with_default(PRE_NU6_FUNDING_STREAMS_TESTNET.clone());
funding_streams.convert_with_default(PRE_NU6_FUNDING_STREAMS_TESTNET.clone(), &self);
self.should_lock_funding_stream_address_period = true;
self
}

Expand All @@ -411,7 +406,8 @@ impl ParametersBuilder {
funding_streams: ConfiguredFundingStreams,
) -> Self {
self.post_nu6_funding_streams =
funding_streams.convert_with_default(POST_NU6_FUNDING_STREAMS_TESTNET.clone());
funding_streams.convert_with_default(POST_NU6_FUNDING_STREAMS_TESTNET.clone(), &self);
self.should_lock_funding_stream_address_period = true;
self
}

Expand All @@ -437,14 +433,18 @@ impl ParametersBuilder {

/// Sets the pre and post Blosssom halving intervals to be used in the [`Parameters`] being built.
pub fn with_halving_interval(mut self, pre_blossom_halving_interval: HeightDiff) -> Self {
if self.should_lock_funding_stream_address_period {
panic!("halving interval on ParametersBuilder must not be set after setting funding streams");
}

self.pre_blossom_halving_interval = pre_blossom_halving_interval;
self.post_blossom_halving_interval =
self.pre_blossom_halving_interval * (BLOSSOM_POW_TARGET_SPACING_RATIO as HeightDiff);
self
}

/// Converts the builder to a [`Parameters`] struct
pub fn finish(self) -> Parameters {
fn finish(self) -> Parameters {
let Self {
network_name,
network_magic,
Expand All @@ -453,6 +453,7 @@ impl ParametersBuilder {
slow_start_interval,
pre_nu6_funding_streams,
post_nu6_funding_streams,
should_lock_funding_stream_address_period: _,
target_difficulty_limit,
disable_pow,
pre_blossom_halving_interval,
Expand All @@ -475,8 +476,23 @@ impl ParametersBuilder {
}

/// Converts the builder to a configured [`Network::Testnet`]
fn to_network_unchecked(&self) -> Network {
Network::new_configured_testnet(self.clone().finish())
}

/// Checks funding streams and converts the builder to a configured [`Network::Testnet`]
pub fn to_network(self) -> Network {
Network::new_configured_testnet(self.finish())
let network = self.to_network_unchecked();

// Final check that the configured funding streams will be valid for these Testnet parameters.
// TODO: Always check funding stream address period once the testnet parameters are being serialized (#8920).
#[cfg(not(any(test, feature = "proptest-impl")))]
{
check_funding_stream_address_period(&self.pre_nu6_funding_streams, &network);
check_funding_stream_address_period(&self.post_nu6_funding_streams, &network);
}

network
}

/// Returns true if these [`Parameters`] should be compatible with the default Testnet parameters.
Expand All @@ -489,6 +505,7 @@ impl ParametersBuilder {
slow_start_interval,
pre_nu6_funding_streams,
post_nu6_funding_streams,
should_lock_funding_stream_address_period: _,
target_difficulty_limit,
disable_pow,
pre_blossom_halving_interval,
Expand Down Expand Up @@ -567,25 +584,32 @@ impl Parameters {
#[cfg(any(test, feature = "proptest-impl"))]
let nu5_activation_height = nu5_activation_height.or(Some(100));

let parameters = Self::build()
.with_genesis_hash(REGTEST_GENESIS_HASH)
// This value is chosen to match zcashd, see: <https://github.com/zcash/zcash/blob/master/src/chainparams.cpp#L654>
.with_target_difficulty_limit(U256::from_big_endian(&[0x0f; 32]))
.with_disable_pow(true)
.with_slow_start_interval(Height::MIN)
// Removes default Testnet activation heights if not configured,
// most network upgrades are disabled by default for Regtest in zcashd
.with_activation_heights(ConfiguredActivationHeights {
canopy: Some(1),
nu5: nu5_activation_height,
nu6: nu6_activation_height,
..Default::default()
})
.with_halving_interval(PRE_BLOSSOM_REGTEST_HALVING_INTERVAL);

// TODO: Always clear funding streams on Regtest once the testnet parameters are being serialized (#8920).
#[cfg(not(any(test, feature = "proptest-impl")))]
let parameters = parameters
.with_pre_nu6_funding_streams(ConfiguredFundingStreams::default())
.with_post_nu6_funding_streams(ConfiguredFundingStreams::default());

Self {
network_name: "Regtest".to_string(),
network_magic: magics::REGTEST,
..Self::build()
.with_genesis_hash(REGTEST_GENESIS_HASH)
// This value is chosen to match zcashd, see: <https://github.com/zcash/zcash/blob/master/src/chainparams.cpp#L654>
.with_target_difficulty_limit(U256::from_big_endian(&[0x0f; 32]))
.with_disable_pow(true)
.with_slow_start_interval(Height::MIN)
// Removes default Testnet activation heights if not configured,
// most network upgrades are disabled by default for Regtest in zcashd
.with_activation_heights(ConfiguredActivationHeights {
canopy: Some(1),
nu5: nu5_activation_height,
nu6: nu6_activation_height,
..Default::default()
})
.with_halving_interval(PRE_BLOSSOM_REGTEST_HALVING_INTERVAL)
.finish()
..parameters.finish()
}
}

Expand Down
2 changes: 1 addition & 1 deletion zebra-chain/src/parameters/network/tests/vectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ fn activates_network_upgrades_correctly() {
let expected_default_regtest_activation_heights = &[
(Height(0), NetworkUpgrade::Genesis),
(Height(1), NetworkUpgrade::Canopy),
// TODO: Remove this once the testnet parameters are being serialized.
// TODO: Remove this once the testnet parameters are being serialized (#8920).
(Height(100), NetworkUpgrade::Nu5),
];

Expand Down
18 changes: 10 additions & 8 deletions zebra-network/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -710,14 +710,6 @@ impl<'de> Deserialize<'de> for Config {
);
}

if let Some(funding_streams) = pre_nu6_funding_streams {
params_builder = params_builder.with_pre_nu6_funding_streams(funding_streams);
}

if let Some(funding_streams) = post_nu6_funding_streams {
params_builder = params_builder.with_post_nu6_funding_streams(funding_streams);
}

if let Some(target_difficulty_limit) = target_difficulty_limit.clone() {
params_builder = params_builder.with_target_difficulty_limit(
target_difficulty_limit
Expand All @@ -739,6 +731,16 @@ impl<'de> Deserialize<'de> for Config {
params_builder = params_builder.with_halving_interval(halving_interval.into())
}

// Set configured funding streams after setting any parameters that affect the funding stream address period.

if let Some(funding_streams) = pre_nu6_funding_streams {
params_builder = params_builder.with_pre_nu6_funding_streams(funding_streams);
}

if let Some(funding_streams) = post_nu6_funding_streams {
params_builder = params_builder.with_post_nu6_funding_streams(funding_streams);
}

// Return an error if the initial testnet peers includes any of the default initial Mainnet or Testnet
// peers and the configured network parameters are incompatible with the default public Testnet.
if !params_builder.is_compatible_with_default_parameters()
Expand Down

0 comments on commit 95c6c84

Please sign in to comment.