diff --git a/zebra-chain/src/parameters.rs b/zebra-chain/src/parameters.rs index bfe806556a4..ebfa401d7b7 100644 --- a/zebra-chain/src/parameters.rs +++ b/zebra-chain/src/parameters.rs @@ -22,7 +22,7 @@ mod transaction; pub mod arbitrary; pub use genesis::*; -pub use network::{magic::Magic, testnet, Network, NetworkKind}; +pub use network::{magic::Magic, subsidy, testnet, Network, NetworkKind}; pub use network_upgrade::*; pub use transaction::*; diff --git a/zebra-chain/src/parameters/network.rs b/zebra-chain/src/parameters/network.rs index 093b53bf36b..8cec6c16c1e 100644 --- a/zebra-chain/src/parameters/network.rs +++ b/zebra-chain/src/parameters/network.rs @@ -10,6 +10,7 @@ use crate::{ }; pub mod magic; +pub mod subsidy; pub mod testnet; #[cfg(test)] diff --git a/zebra-consensus/src/parameters/subsidy.rs b/zebra-chain/src/parameters/network/subsidy.rs similarity index 59% rename from zebra-consensus/src/parameters/subsidy.rs rename to zebra-chain/src/parameters/network/subsidy.rs index 268221c6669..7528058d5bd 100644 --- a/zebra-consensus/src/parameters/subsidy.rs +++ b/zebra-chain/src/parameters/network/subsidy.rs @@ -1,13 +1,26 @@ -//! Constants for Block Subsidy and Funding Streams +//! Constants and calculations for Block Subsidy and Funding Streams +//! +//! This module contains the consensus parameters which are required for +//! verification. +//! +//! Some consensus parameters change based on network upgrades. Each network +//! upgrade happens at a particular block height. Some parameters have a value +//! (or function) before the upgrade height, at the upgrade height, and after +//! the upgrade height. (For example, the value of the reserved field in the +//! block header during the Heartwood upgrade.) +//! +//! Typically, consensus parameters are accessed via a function that takes a +//! `Network` and `block::Height`. use std::collections::HashMap; use lazy_static::lazy_static; -use zebra_chain::{ +use crate::{ amount::COIN, block::{Height, HeightDiff}, - parameters::{Network, NetworkKind, NetworkUpgrade}, + parameters::{Network, NetworkUpgrade}, + transparent, }; /// The largest block subsidy, used before the first halving. @@ -38,9 +51,10 @@ pub const POST_BLOSSOM_HALVING_INTERVAL: HeightDiff = pub const FIRST_HALVING_TESTNET: Height = Height(1_116_000); /// The funding stream receiver categories. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Deserialize, Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum FundingStreamReceiver { /// The Electric Coin Company (Bootstrap Foundation) funding stream. + #[serde(rename = "ECC")] Ecc, /// The Zcash Foundation funding stream. @@ -50,6 +64,20 @@ pub enum FundingStreamReceiver { MajorGrants, } +impl FundingStreamReceiver { + /// The name for each funding stream receiver, as described in [ZIP-1014] and [`zcashd`]. + /// + /// [ZIP-1014]: https://zips.z.cash/zip-1014#abstract + /// [`zcashd`]: https://github.com/zcash/zcash/blob/3f09cfa00a3c90336580a127e0096d99e25a38d6/src/consensus/funding.cpp#L13-L32 + pub fn name(self) -> &'static str { + match self { + FundingStreamReceiver::Ecc => "Electric Coin Company", + FundingStreamReceiver::ZcashFoundation => "Zcash Foundation", + FundingStreamReceiver::MajorGrants => "Major Grants", + } + } +} + /// Denominator as described in [protocol specification §7.10.1][7.10.1]. /// /// [7.10.1]: https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams @@ -60,76 +88,149 @@ pub const FUNDING_STREAM_RECEIVER_DENOMINATOR: u64 = 100; /// [ZIP-214]: https://zips.z.cash/zip-0214 pub const FUNDING_STREAM_SPECIFICATION: &str = "https://zips.z.cash/zip-0214"; -// TODO: use a struct for the info for each funding stream, like zcashd does: -// https://github.com/zcash/zcash/blob/3f09cfa00a3c90336580a127e0096d99e25a38d6/src/consensus/funding.cpp#L13-L32 -lazy_static! { - /// The name for each funding stream receiver, as described in [ZIP-1014] and [`zcashd`]. +/// Funding stream recipients and height ranges. +#[derive(Deserialize, Clone, Debug, Eq, PartialEq)] +pub struct FundingStreams { + /// Start and end Heights for funding streams + /// as described in [protocol specification §7.10.1][7.10.1]. /// - /// [ZIP-1014]: https://zips.z.cash/zip-1014#abstract - /// [`zcashd`]: https://github.com/zcash/zcash/blob/3f09cfa00a3c90336580a127e0096d99e25a38d6/src/consensus/funding.cpp#L13-L32 - pub static ref FUNDING_STREAM_NAMES: HashMap = { - let mut hash_map = HashMap::new(); - hash_map.insert(FundingStreamReceiver::Ecc, "Electric Coin Company"); - hash_map.insert(FundingStreamReceiver::ZcashFoundation, "Zcash Foundation"); - hash_map.insert(FundingStreamReceiver::MajorGrants, "Major Grants"); - hash_map - }; + /// [7.10.1]: https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams + height_range: std::ops::Range, + /// Funding stream recipients by [`FundingStreamReceiver`]. + recipients: HashMap, +} + +impl FundingStreams { + /// Creates a new [`FundingStreams`]. + pub fn new( + height_range: std::ops::Range, + recipients: HashMap, + ) -> Self { + Self { + height_range, + recipients, + } + } + /// Returns height range where these [`FundingStreams`] should apply. + pub fn height_range(&self) -> &std::ops::Range { + &self.height_range + } + + /// Returns recipients of these [`FundingStreams`]. + pub fn recipients(&self) -> &HashMap { + &self.recipients + } + /// Returns a recipient with the provided receiver. + pub fn recipient(&self, receiver: FundingStreamReceiver) -> Option<&FundingStreamRecipient> { + self.recipients.get(&receiver) + } +} + +/// A funding stream recipient as specified in [protocol specification §7.10.1][7.10.1] +/// +/// [7.10.1]: https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams +#[derive(Deserialize, Clone, Debug, Eq, PartialEq)] +pub struct FundingStreamRecipient { /// The numerator for each funding stream receiver category /// as described in [protocol specification §7.10.1][7.10.1]. /// /// [7.10.1]: https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams - pub static ref FUNDING_STREAM_RECEIVER_NUMERATORS: HashMap = { - let mut hash_map = HashMap::new(); - hash_map.insert(FundingStreamReceiver::Ecc, 7); - hash_map.insert(FundingStreamReceiver::ZcashFoundation, 5); - hash_map.insert(FundingStreamReceiver::MajorGrants, 8); - hash_map + numerator: u64, + /// Addresses for the funding stream recipient + addresses: Vec, +} + +impl FundingStreamRecipient { + /// Creates a new [`FundingStreamRecipient`]. + pub fn new(numerator: u64, addresses: I) -> Self + where + T: ToString, + I: IntoIterator, + { + Self { + numerator, + addresses: addresses + .into_iter() + .map(|addr| { + let addr = addr.to_string(); + addr.parse() + .expect("funding stream address must deserialize") + }) + .collect(), + } + } + + /// Returns the numerator for this funding stream. + pub fn numerator(&self) -> u64 { + self.numerator + } + + /// Returns the receiver of this funding stream. + pub fn addresses(&self) -> &[transparent::Address] { + &self.addresses + } +} + +lazy_static! { + /// The pre-NU6 funding streams for Mainnet as described in [protocol specification §7.10.1][7.10.1] + /// [7.10.1]: https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams + pub static ref PRE_NU6_FUNDING_STREAMS_MAINNET: FundingStreams = FundingStreams { + height_range: Height(1_046_400)..Height(2_726_400), + recipients: [ + ( + FundingStreamReceiver::Ecc, + FundingStreamRecipient::new(7, FUNDING_STREAM_ECC_ADDRESSES_MAINNET.iter()), + ), + ( + FundingStreamReceiver::ZcashFoundation, + FundingStreamRecipient::new(5, FUNDING_STREAM_ZF_ADDRESSES_MAINNET), + ), + ( + FundingStreamReceiver::MajorGrants, + FundingStreamRecipient::new(8, FUNDING_STREAM_MG_ADDRESSES_MAINNET), + ), + ] + .into_iter() + .collect(), }; - /// Start and end Heights for funding streams - /// as described in [protocol specification §7.10.1][7.10.1]. - /// + /// The post-NU6 funding streams for Mainnet + // TODO: Add a reference to lockbox stream ZIP, this is currently based on https://zips.z.cash/draft-nuttycom-funding-allocation + pub static ref POST_NU6_FUNDING_STREAMS_MAINNET: FundingStreams = FundingStreams { + height_range: Height(2_726_400)..Height(3_146_400), + recipients: HashMap::new() + }; + + /// The pre-NU6 funding streams for Testnet as described in [protocol specification §7.10.1][7.10.1] /// [7.10.1]: https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams - // TODO: Move the value here to a field on `testnet::Parameters` (#8367) - pub static ref FUNDING_STREAM_HEIGHT_RANGES: HashMap> = { - let mut hash_map = HashMap::new(); - hash_map.insert(NetworkKind::Mainnet, Height(1_046_400)..Height(2_726_400)); - hash_map.insert(NetworkKind::Testnet, Height(1_028_500)..Height(2_796_000)); - hash_map.insert(NetworkKind::Regtest, Height(1_028_500)..Height(2_796_000)); - hash_map + pub static ref PRE_NU6_FUNDING_STREAMS_TESTNET: FundingStreams = FundingStreams { + height_range: Height(1_028_500)..Height(2_796_000), + recipients: [ + ( + FundingStreamReceiver::Ecc, + FundingStreamRecipient::new(7, FUNDING_STREAM_ECC_ADDRESSES_TESTNET), + ), + ( + FundingStreamReceiver::ZcashFoundation, + FundingStreamRecipient::new(5, FUNDING_STREAM_ZF_ADDRESSES_TESTNET), + ), + ( + FundingStreamReceiver::MajorGrants, + FundingStreamRecipient::new(8, FUNDING_STREAM_MG_ADDRESSES_TESTNET), + ), + ] + .into_iter() + .collect(), }; - /// Convenient storage for all addresses, for all receivers and networks - pub static ref FUNDING_STREAM_ADDRESSES: HashMap>> = { - let mut addresses_by_network = HashMap::with_capacity(2); - - // Mainnet addresses - let mut mainnet_addresses = HashMap::with_capacity(3); - mainnet_addresses.insert(FundingStreamReceiver::Ecc, FUNDING_STREAM_ECC_ADDRESSES_MAINNET.iter().map(|a| a.to_string()).collect()); - mainnet_addresses.insert(FundingStreamReceiver::ZcashFoundation, FUNDING_STREAM_ZF_ADDRESSES_MAINNET.iter().map(|a| a.to_string()).collect()); - mainnet_addresses.insert(FundingStreamReceiver::MajorGrants, FUNDING_STREAM_MG_ADDRESSES_MAINNET.iter().map(|a| a.to_string()).collect()); - addresses_by_network.insert(NetworkKind::Mainnet, mainnet_addresses); - - // Testnet addresses - let mut testnet_addresses = HashMap::with_capacity(3); - testnet_addresses.insert(FundingStreamReceiver::Ecc, FUNDING_STREAM_ECC_ADDRESSES_TESTNET.iter().map(|a| a.to_string()).collect()); - testnet_addresses.insert(FundingStreamReceiver::ZcashFoundation, FUNDING_STREAM_ZF_ADDRESSES_TESTNET.iter().map(|a| a.to_string()).collect()); - testnet_addresses.insert(FundingStreamReceiver::MajorGrants, FUNDING_STREAM_MG_ADDRESSES_TESTNET.iter().map(|a| a.to_string()).collect()); - addresses_by_network.insert(NetworkKind::Testnet, testnet_addresses); - - - // Regtest addresses - // TODO: Move the value here to a field on `testnet::Parameters` (#8367) - // There are no funding stream addresses on Regtest in zcashd, zebrad should do the same for compatibility. - let mut regtest_addresses = HashMap::with_capacity(3); - regtest_addresses.insert(FundingStreamReceiver::Ecc, FUNDING_STREAM_ECC_ADDRESSES_TESTNET.iter().map(|a| a.to_string()).collect()); - regtest_addresses.insert(FundingStreamReceiver::ZcashFoundation, FUNDING_STREAM_ZF_ADDRESSES_TESTNET.iter().map(|a| a.to_string()).collect()); - regtest_addresses.insert(FundingStreamReceiver::MajorGrants, FUNDING_STREAM_MG_ADDRESSES_TESTNET.iter().map(|a| a.to_string()).collect()); - addresses_by_network.insert(NetworkKind::Testnet, regtest_addresses); - - addresses_by_network + /// The post-NU6 funding streams for Testnet + // TODO: Add a reference to lockbox stream ZIP, this is currently based on the number of blocks between the + // start and end heights for Mainnet in https://zips.z.cash/draft-nuttycom-funding-allocation + pub static ref POST_NU6_FUNDING_STREAMS_TESTNET: FundingStreams = FundingStreams { + height_range: Height(2_942_000)..Height(3_362_000), + recipients: HashMap::new() }; } @@ -200,9 +301,6 @@ pub const FUNDING_STREAM_ECC_ADDRESSES_MAINNET: [&str; FUNDING_STREAMS_NUM_ADDRE /// Functionality specific to block subsidy-related consensus rules pub trait ParameterSubsidy { - /// Number of addresses for each funding stream in the Network. - /// [7.10]: - fn num_funding_streams(&self) -> usize; /// Returns the minimum height after the first halving /// as described in [protocol specification §7.10][7.10] /// @@ -212,14 +310,6 @@ pub trait ParameterSubsidy { /// Network methods related to Block Subsidy and Funding Streams impl ParameterSubsidy for Network { - fn num_funding_streams(&self) -> usize { - match self { - Network::Mainnet => FUNDING_STREAMS_NUM_ADDRESSES_MAINNET, - // TODO: Check what zcashd does here, consider adding a field to `NetworkParamters` to make this configurable. - Network::Testnet(_params) => FUNDING_STREAMS_NUM_ADDRESSES_TESTNET, - } - } - fn height_for_first_halving(&self) -> Height { // First halving on Mainnet is at Canopy // while in Testnet is at block constant height of `1_116_000` @@ -233,6 +323,7 @@ impl ParameterSubsidy for Network { } } } + /// List of addresses for the Zcash Foundation funding stream in the Mainnet. pub const FUNDING_STREAM_ZF_ADDRESSES_MAINNET: [&str; FUNDING_STREAMS_NUM_ADDRESSES_MAINNET] = ["t3dvVE3SQEi7kqNzwrfNePxZ1d4hUyztBA1"; FUNDING_STREAMS_NUM_ADDRESSES_MAINNET]; @@ -310,3 +401,27 @@ pub const FUNDING_STREAM_ZF_ADDRESSES_TESTNET: [&str; FUNDING_STREAMS_NUM_ADDRES /// List of addresses for the Major Grants funding stream in the Testnet. pub const FUNDING_STREAM_MG_ADDRESSES_TESTNET: [&str; FUNDING_STREAMS_NUM_ADDRESSES_TESTNET] = ["t2Gvxv2uNM7hbbACjNox4H6DjByoKZ2Fa3P"; FUNDING_STREAMS_NUM_ADDRESSES_TESTNET]; + +/// Returns the address change period +/// as described in [protocol specification §7.10][7.10] +/// +/// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams +pub fn funding_stream_address_period(height: Height, network: &N) -> u32 { + // Spec equation: `address_period = floor((height - (height_for_halving(1) - post_blossom_halving_interval))/funding_stream_address_change_interval)`, + // + // + // Note that the brackets make it so the post blossom halving interval is added to the total. + // + // In Rust, "integer division rounds towards zero": + // + // This is the same as `floor()`, because these numbers are all positive. + + let height_after_first_halving = height - network.height_for_first_halving(); + + let address_period = (height_after_first_halving + POST_BLOSSOM_HALVING_INTERVAL) + / FUNDING_STREAM_ADDRESS_CHANGE_INTERVAL; + + address_period + .try_into() + .expect("all values are positive and smaller than the input height") +} diff --git a/zebra-chain/src/parameters/network/testnet.rs b/zebra-chain/src/parameters/network/testnet.rs index 415b78a114d..97ffb20a9d4 100644 --- a/zebra-chain/src/parameters/network/testnet.rs +++ b/zebra-chain/src/parameters/network/testnet.rs @@ -6,12 +6,20 @@ use crate::{ parameters::{ constants::{magics, SLOW_START_INTERVAL, SLOW_START_SHIFT}, network_upgrade::TESTNET_ACTIVATION_HEIGHTS, - Network, NetworkUpgrade, NETWORK_UPGRADES_IN_ORDER, + subsidy::{funding_stream_address_period, FUNDING_STREAM_RECEIVER_DENOMINATOR}, + Network, NetworkKind, NetworkUpgrade, NETWORK_UPGRADES_IN_ORDER, }, work::difficulty::{ExpandedDifficulty, U256}, }; -use super::magic::Magic; +use super::{ + magic::Magic, + subsidy::{ + FundingStreamReceiver, FundingStreamRecipient, FundingStreams, ParameterSubsidy, + FIRST_HALVING_TESTNET, POST_NU6_FUNDING_STREAMS_MAINNET, POST_NU6_FUNDING_STREAMS_TESTNET, + PRE_NU6_FUNDING_STREAMS_MAINNET, PRE_NU6_FUNDING_STREAMS_TESTNET, + }, +}; /// The Regtest NU5 activation height in tests // TODO: Serialize testnet parameters in Config then remove this and use a configured NU5 activation height. @@ -42,9 +50,127 @@ const REGTEST_GENESIS_HASH: &str = const TESTNET_GENESIS_HASH: &str = "05a60a92d99d85997cce3b87616c089f6124d7342af37106edc76126334a2c38"; +/// 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 + } +} + +/// Configurable funding stream recipient for configured Testnets. +#[derive(Deserialize, Clone, Debug)] +#[serde(deny_unknown_fields)] +pub struct ConfiguredFundingStreamRecipient { + /// Funding stream receiver, see [`FundingStreams::recipients`] for more details. + pub receiver: FundingStreamReceiver, + /// The numerator for each funding stream receiver category, see [`FundingStreamRecipient::numerator`] for more details. + pub numerator: u64, + /// Addresses for the funding stream recipient, see [`FundingStreamRecipient::addresses`] for more details. + pub addresses: Vec, +} + +impl ConfiguredFundingStreamRecipient { + /// Converts a [`ConfiguredFundingStreamRecipient`] to a [`FundingStreamReceiver`] and [`FundingStreamRecipient`]. + pub fn into_recipient(self) -> (FundingStreamReceiver, FundingStreamRecipient) { + ( + self.receiver, + FundingStreamRecipient::new(self.numerator, self.addresses), + ) + } +} + +/// Configurable funding streams for configured Testnets. +#[derive(Deserialize, Clone, Default, Debug)] +#[serde(deny_unknown_fields)] +pub struct ConfiguredFundingStreams { + /// Start and end height for funding streams see [`FundingStreams::height_range`] for more details. + pub height_range: Option>, + /// Funding stream recipients, see [`FundingStreams::recipients`] for more details. + pub recipients: Option>, +} + +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 { + let height_range = self + .height_range + .unwrap_or(default_funding_streams.height_range().clone()); + + let recipients = self + .recipients + .map(|recipients| { + recipients + .into_iter() + .map(ConfiguredFundingStreamRecipient::into_recipient) + .collect() + }) + .unwrap_or(default_funding_streams.recipients().clone()); + + assert!( + height_range.start < height_range.end, + "funding stream end height must be above start height" + ); + + 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 recipient in funding_streams.recipients().values() { + // TODO: Make an exception for the `Deferred` receiver. + 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 that sum of receiver numerators is valid. + + let sum_numerators: u64 = funding_streams + .recipients() + .values() + .map(|r| r.numerator()) + .sum(); + + assert!( + sum_numerators <= FUNDING_STREAM_RECEIVER_DENOMINATOR, + "sum of funding stream numerators must not be \ + greater than denominator of {FUNDING_STREAM_RECEIVER_DENOMINATOR}" + ); + + funding_streams + } +} + /// Configurable activation heights for Regtest and configured Testnets. #[derive(Deserialize, Default, Clone)] -#[serde(rename_all = "PascalCase")] +#[serde(rename_all = "PascalCase", deny_unknown_fields)] pub struct ConfiguredActivationHeights { /// Activation height for `BeforeOverwinter` network upgrade. pub before_overwinter: Option, @@ -79,6 +205,10 @@ pub struct ParametersBuilder { activation_heights: BTreeMap, /// Slow start interval for this network slow_start_interval: Height, + /// Pre-NU6 funding streams for this network + pre_nu6_funding_streams: FundingStreams, + /// Post-NU6 funding streams for this network + post_nu6_funding_streams: FundingStreams, /// Target difficulty limit for this network target_difficulty_limit: ExpandedDifficulty, /// A flag for disabling proof-of-work checks when Zebra is validating blocks @@ -113,6 +243,8 @@ impl Default for ParametersBuilder { .to_expanded() .expect("difficulty limits are valid expanded values"), disable_pow: false, + pre_nu6_funding_streams: PRE_NU6_FUNDING_STREAMS_TESTNET.clone(), + post_nu6_funding_streams: POST_NU6_FUNDING_STREAMS_TESTNET.clone(), } } } @@ -235,6 +367,26 @@ impl ParametersBuilder { self } + /// Sets pre-NU6 funding streams to be used in the [`Parameters`] being built. + pub fn with_pre_nu6_funding_streams( + mut self, + funding_streams: ConfiguredFundingStreams, + ) -> Self { + self.pre_nu6_funding_streams = + funding_streams.convert_with_default(PRE_NU6_FUNDING_STREAMS_TESTNET.clone()); + self + } + + /// Sets post-NU6 funding streams to be used in the [`Parameters`] being built. + pub fn with_post_nu6_funding_streams( + mut self, + funding_streams: ConfiguredFundingStreams, + ) -> Self { + self.post_nu6_funding_streams = + funding_streams.convert_with_default(POST_NU6_FUNDING_STREAMS_TESTNET.clone()); + self + } + /// Sets the target difficulty limit to be used in the [`Parameters`] being built. // TODO: Accept a hex-encoded String instead? pub fn with_target_difficulty_limit( @@ -263,6 +415,8 @@ impl ParametersBuilder { genesis_hash, activation_heights, slow_start_interval, + pre_nu6_funding_streams, + post_nu6_funding_streams, target_difficulty_limit, disable_pow, } = self; @@ -273,6 +427,8 @@ impl ParametersBuilder { activation_heights, slow_start_interval, slow_start_shift: Height(slow_start_interval.0 / 2), + pre_nu6_funding_streams, + post_nu6_funding_streams, target_difficulty_limit, disable_pow, } @@ -291,6 +447,8 @@ impl ParametersBuilder { genesis_hash, activation_heights, slow_start_interval, + pre_nu6_funding_streams, + post_nu6_funding_streams, target_difficulty_limit, disable_pow, } = Self::default(); @@ -299,6 +457,8 @@ impl ParametersBuilder { && self.network_magic == network_magic && self.genesis_hash == genesis_hash && self.slow_start_interval == slow_start_interval + && self.pre_nu6_funding_streams == pre_nu6_funding_streams + && self.post_nu6_funding_streams == post_nu6_funding_streams && self.target_difficulty_limit == target_difficulty_limit && self.disable_pow == disable_pow } @@ -323,6 +483,10 @@ pub struct Parameters { slow_start_interval: Height, /// Slow start shift for this network, always half the slow start interval slow_start_shift: Height, + /// Pre-NU6 funding streams for this network + pre_nu6_funding_streams: FundingStreams, + /// Post-NU6 funding streams for this network + post_nu6_funding_streams: FundingStreams, /// Target difficulty limit for this network target_difficulty_limit: ExpandedDifficulty, /// A flag for disabling proof-of-work checks when Zebra is validating blocks @@ -383,23 +547,31 @@ impl Parameters { /// Returns true if the instance of [`Parameters`] represents Regtest. pub fn is_regtest(&self) -> bool { + if self.network_magic != magics::REGTEST { + return false; + } + let Self { network_name, - network_magic, + // Already checked network magic above + network_magic: _, genesis_hash, // Activation heights are configurable on Regtest activation_heights: _, slow_start_interval, slow_start_shift, + pre_nu6_funding_streams, + post_nu6_funding_streams, target_difficulty_limit, disable_pow, } = Self::new_regtest(None, None); self.network_name == network_name - && self.network_magic == network_magic && self.genesis_hash == genesis_hash && self.slow_start_interval == slow_start_interval && self.slow_start_shift == slow_start_shift + && self.pre_nu6_funding_streams == pre_nu6_funding_streams + && self.post_nu6_funding_streams == post_nu6_funding_streams && self.target_difficulty_limit == target_difficulty_limit && self.disable_pow == disable_pow } @@ -434,6 +606,16 @@ impl Parameters { self.slow_start_shift } + /// Returns pre-NU6 funding streams for this network + pub fn pre_nu6_funding_streams(&self) -> &FundingStreams { + &self.pre_nu6_funding_streams + } + + /// Returns post-NU6 funding streams for this network + pub fn post_nu6_funding_streams(&self) -> &FundingStreams { + &self.post_nu6_funding_streams + } + /// Returns the target difficulty limit for this network pub fn target_difficulty_limit(&self) -> ExpandedDifficulty { self.target_difficulty_limit @@ -472,4 +654,31 @@ impl Network { SLOW_START_SHIFT } } + + /// Returns pre-NU6 funding streams for this network + pub fn pre_nu6_funding_streams(&self) -> &FundingStreams { + if let Self::Testnet(params) = self { + params.pre_nu6_funding_streams() + } else { + &PRE_NU6_FUNDING_STREAMS_MAINNET + } + } + + /// Returns post-NU6 funding streams for this network + pub fn post_nu6_funding_streams(&self) -> &FundingStreams { + if let Self::Testnet(params) = self { + params.post_nu6_funding_streams() + } else { + &POST_NU6_FUNDING_STREAMS_MAINNET + } + } + + /// Returns post-Canopy funding streams for this network at the provided height + pub fn funding_streams(&self, height: Height) -> &FundingStreams { + if NetworkUpgrade::current(self, height) < NetworkUpgrade::Nu6 { + self.pre_nu6_funding_streams() + } else { + self.post_nu6_funding_streams() + } + } } diff --git a/zebra-chain/src/parameters/network/tests/vectors.rs b/zebra-chain/src/parameters/network/tests/vectors.rs index 5df16d7d38d..c6020590ab4 100644 --- a/zebra-chain/src/parameters/network/tests/vectors.rs +++ b/zebra-chain/src/parameters/network/tests/vectors.rs @@ -6,8 +6,14 @@ 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, MAX_NETWORK_NAME_LENGTH, RESERVED_NETWORK_NAMES, + self, ConfiguredActivationHeights, ConfiguredFundingStreamRecipient, + ConfiguredFundingStreams, MAX_NETWORK_NAME_LENGTH, RESERVED_NETWORK_NAMES, }, Network, NetworkUpgrade, MAINNET_ACTIVATION_HEIGHTS, NETWORK_UPGRADES_IN_ORDER, TESTNET_ACTIVATION_HEIGHTS, @@ -295,3 +301,144 @@ fn check_full_activation_list() { ); } } + +/// 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"); +} diff --git a/zebra-consensus/src/block/check.rs b/zebra-consensus/src/block/check.rs index d18893ecb73..0463a9c41a9 100644 --- a/zebra-consensus/src/block/check.rs +++ b/zebra-consensus/src/block/check.rs @@ -198,7 +198,7 @@ pub fn subsidy_is_valid(block: &Block, network: &Network) -> Result<(), BlockErr subsidy::funding_streams::funding_stream_address(height, network, receiver); let has_expected_output = - subsidy::funding_streams::filter_outputs_by_address(coinbase, &address) + subsidy::funding_streams::filter_outputs_by_address(coinbase, address) .iter() .map(zebra_chain::transparent::Output::value) .any(|value| value == expected_amount); diff --git a/zebra-consensus/src/block/subsidy/funding_streams.rs b/zebra-consensus/src/block/subsidy/funding_streams.rs index c3389782b17..886ca77ff24 100644 --- a/zebra-consensus/src/block/subsidy/funding_streams.rs +++ b/zebra-consensus/src/block/subsidy/funding_streams.rs @@ -2,17 +2,17 @@ //! //! [7.8]: https://zips.z.cash/protocol/protocol.pdf#subsidies -use std::{collections::HashMap, str::FromStr}; +use std::collections::HashMap; use zebra_chain::{ amount::{Amount, Error, NonNegative}, block::Height, - parameters::{Network, NetworkUpgrade::*}, + parameters::{subsidy::*, Network, NetworkUpgrade::*}, transaction::Transaction, transparent::{self, Script}, }; -use crate::{block::subsidy::general::block_subsidy, parameters::subsidy::*}; +use crate::block::subsidy::general::block_subsidy; #[cfg(test)] mod tests; @@ -29,17 +29,17 @@ pub fn funding_stream_values( let mut results = HashMap::new(); if height >= canopy_height { - let range = FUNDING_STREAM_HEIGHT_RANGES.get(&network.kind()).unwrap(); - if range.contains(&height) { + let funding_streams = network.funding_streams(height); + if funding_streams.height_range().contains(&height) { let block_subsidy = block_subsidy(height, network)?; - for (&receiver, &numerator) in FUNDING_STREAM_RECEIVER_NUMERATORS.iter() { + for (&receiver, recipient) in funding_streams.recipients() { // - Spec equation: `fs.value = floor(block_subsidy(height)*(fs.numerator/fs.denominator))`: // https://zips.z.cash/protocol/protocol.pdf#subsidies // - In Rust, "integer division rounds towards zero": // https://doc.rust-lang.org/stable/reference/expressions/operator-expr.html#arithmetic-and-logical-binary-operators // This is the same as `floor()`, because these numbers are all positive. - let amount_value = - ((block_subsidy * numerator)? / FUNDING_STREAM_RECEIVER_DENOMINATOR)?; + let amount_value = ((block_subsidy * recipient.numerator())? + / FUNDING_STREAM_RECEIVER_DENOMINATOR)?; results.insert(receiver, amount_value); } @@ -48,49 +48,34 @@ pub fn funding_stream_values( Ok(results) } -/// Returns the address change period -/// as described in [protocol specification §7.10][7.10] -/// -/// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams -fn funding_stream_address_period(height: Height, network: &Network) -> u32 { - // Spec equation: `address_period = floor((height - (height_for_halving(1) - post_blossom_halving_interval))/funding_stream_address_change_interval)`, - // - // - // Note that the brackets make it so the post blossom halving interval is added to the total. - // - // In Rust, "integer division rounds towards zero": - // - // This is the same as `floor()`, because these numbers are all positive. - - let height_after_first_halving = height - network.height_for_first_halving(); - - let address_period = (height_after_first_halving + POST_BLOSSOM_HALVING_INTERVAL) - / FUNDING_STREAM_ADDRESS_CHANGE_INTERVAL; - - address_period - .try_into() - .expect("all values are positive and smaller than the input height") -} - /// Returns the position in the address slice for each funding stream /// as described in [protocol specification §7.10][7.10] /// /// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams fn funding_stream_address_index(height: Height, network: &Network) -> usize { - let num_addresses = network.num_funding_streams(); + let funding_streams = network.funding_streams(height); let index = 1u32 .checked_add(funding_stream_address_period(height, network)) .expect("no overflow should happen in this sum") .checked_sub(funding_stream_address_period( - FUNDING_STREAM_HEIGHT_RANGES - .get(&network.kind()) - .unwrap() - .start, + funding_streams.height_range().start, network, )) .expect("no overflow should happen in this sub") as usize; + // Funding stream recipients may not have the same number of addresses on configured Testnets, + // the number of addresses for each recipient should be validated for a configured height range + // when configured Testnet parameters are built. + let num_addresses = funding_streams + .recipients() + .values() + .next() + // TODO: Return an Option from this function and replace `.unwrap()` with `?` + .unwrap() + .addresses() + .len(); + assert!(index > 0 && index <= num_addresses); // spec formula will output an index starting at 1 but // Zebra indices for addresses start at zero, return converted. @@ -105,25 +90,24 @@ pub fn funding_stream_address( height: Height, network: &Network, receiver: FundingStreamReceiver, -) -> transparent::Address { +) -> &transparent::Address { let index = funding_stream_address_index(height, network); - let address = &FUNDING_STREAM_ADDRESSES - .get(&network.kind()) - .expect("there is always another hash map as value for a given valid network") - .get(&receiver) - .expect("in the inner hash map there is always a vector of strings with addresses")[index]; - transparent::Address::from_str(address).expect("address should deserialize") + let funding_streams = network.funding_streams(height); + funding_streams + .recipient(receiver) + // TODO: Change return type to option and return None here instead of panicking + .unwrap() + .addresses() + .get(index) + // TODO: Change return type to option and return None here instead of panicking + .unwrap() } /// Return a human-readable name and a specification URL for the funding stream `receiver`. pub fn funding_stream_recipient_info( receiver: FundingStreamReceiver, ) -> (&'static str, &'static str) { - let name = FUNDING_STREAM_NAMES - .get(&receiver) - .expect("all funding streams have a name"); - - (name, FUNDING_STREAM_SPECIFICATION) + (receiver.name(), FUNDING_STREAM_SPECIFICATION) } /// Given a funding stream P2SH address, create a script and check if it is the same diff --git a/zebra-consensus/src/block/subsidy/funding_streams/tests.rs b/zebra-consensus/src/block/subsidy/funding_streams/tests.rs index 7f6011a422c..da2dc225179 100644 --- a/zebra-consensus/src/block/subsidy/funding_streams/tests.rs +++ b/zebra-consensus/src/block/subsidy/funding_streams/tests.rs @@ -1,6 +1,7 @@ //! Tests for funding streams. use color_eyre::Report; +use zebra_chain::parameters::{subsidy::FundingStreamReceiver, NetworkKind}; use super::*; @@ -44,7 +45,8 @@ fn test_funding_stream_values() -> Result<(), Report> { ); // funding stream period is ending - let range = FUNDING_STREAM_HEIGHT_RANGES.get(&network.kind()).unwrap(); + // TODO: Check post-NU6 funding streams here as well. + let range = network.pre_nu6_funding_streams().height_range(); let end = range.end; let last = end - 1; @@ -61,20 +63,23 @@ fn test_funding_stream_values() -> Result<(), Report> { #[test] fn test_funding_stream_addresses() -> Result<(), Report> { let _init_guard = zebra_test::init(); + for network in Network::iter() { + for (receiver, recipient) in network.pre_nu6_funding_streams().recipients() { + for address in recipient.addresses() { + let expected_network_kind = match network.kind() { + NetworkKind::Mainnet => NetworkKind::Mainnet, + // `Regtest` uses `Testnet` transparent addresses. + NetworkKind::Testnet | NetworkKind::Regtest => NetworkKind::Testnet, + }; - for (network, receivers) in FUNDING_STREAM_ADDRESSES.iter() { - for (receiver, addresses) in receivers { - for address in addresses { - let address = - transparent::Address::from_str(address).expect("address should deserialize"); assert_eq!( - &address.network_kind(), - network, + address.network_kind(), + expected_network_kind, "incorrect network for {receiver:?} funding stream address constant: {address}", ); // Asserts if address is not a P2SH address. - let _script = new_coinbase_script(&address); + let _script = new_coinbase_script(address); } } } diff --git a/zebra-consensus/src/block/subsidy/general.rs b/zebra-consensus/src/block/subsidy/general.rs index d7013c84ed4..193f32cb2b3 100644 --- a/zebra-consensus/src/block/subsidy/general.rs +++ b/zebra-consensus/src/block/subsidy/general.rs @@ -7,11 +7,11 @@ use std::collections::HashSet; use zebra_chain::{ amount::{Amount, Error, NonNegative}, block::{Height, HeightDiff}, - parameters::{Network, NetworkUpgrade::*}, + parameters::{subsidy::*, Network, NetworkUpgrade::*}, transaction::Transaction, }; -use crate::{funding_stream_values, parameters::subsidy::*}; +use crate::funding_stream_values; /// The divisor used for halvings. /// diff --git a/zebra-consensus/src/lib.rs b/zebra-consensus/src/lib.rs index 8ba8fa3a2be..66323a3b550 100644 --- a/zebra-consensus/src/lib.rs +++ b/zebra-consensus/src/lib.rs @@ -36,7 +36,6 @@ mod block; mod checkpoint; -mod parameters; mod primitives; mod script; @@ -64,7 +63,6 @@ pub use checkpoint::{ }; pub use config::Config; pub use error::BlockError; -pub use parameters::{FundingStreamReceiver, ParameterSubsidy}; pub use primitives::{ed25519, groth16, halo2, redjubjub, redpallas}; pub use router::RouterError; diff --git a/zebra-consensus/src/parameters.rs b/zebra-consensus/src/parameters.rs deleted file mode 100644 index 0b3798c0ba2..00000000000 --- a/zebra-consensus/src/parameters.rs +++ /dev/null @@ -1,17 +0,0 @@ -//! The consensus parameters for each Zcash network. -//! -//! This module contains the consensus parameters which are required for -//! verification. -//! -//! Some consensus parameters change based on network upgrades. Each network -//! upgrade happens at a particular block height. Some parameters have a value -//! (or function) before the upgrade height, at the upgrade height, and after -//! the upgrade height. (For example, the value of the reserved field in the -//! block header during the Heartwood upgrade.) -//! -//! Typically, consensus parameters are accessed via a function that takes a -//! `Network` and `block::Height`. - -pub mod subsidy; - -pub use subsidy::*; diff --git a/zebra-network/src/config.rs b/zebra-network/src/config.rs index 4f965267c65..00f4f8b4460 100644 --- a/zebra-network/src/config.rs +++ b/zebra-network/src/config.rs @@ -16,7 +16,7 @@ use tracing::Span; use zebra_chain::{ parameters::{ - testnet::{self, ConfiguredActivationHeights}, + testnet::{self, ConfiguredActivationHeights, ConfiguredFundingStreams}, Magic, Network, NetworkKind, }, work::difficulty::U256, @@ -641,6 +641,7 @@ impl<'de> Deserialize<'de> for Config { D: Deserializer<'de>, { #[derive(Deserialize)] + #[serde(deny_unknown_fields)] struct DTestnetParameters { network_name: Option, network_magic: Option<[u8; 4]>, @@ -649,6 +650,8 @@ impl<'de> Deserialize<'de> for Config { disable_pow: Option, genesis_hash: Option, activation_heights: Option, + pre_nu6_funding_streams: Option, + post_nu6_funding_streams: Option, } #[derive(Deserialize)] @@ -736,6 +739,8 @@ impl<'de> Deserialize<'de> for Config { disable_pow, genesis_hash, activation_heights, + pre_nu6_funding_streams, + post_nu6_funding_streams, }), ) => { let mut params_builder = testnet::Parameters::build(); @@ -758,6 +763,14 @@ 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 diff --git a/zebra-rpc/src/methods/get_block_template_rpcs.rs b/zebra-rpc/src/methods/get_block_template_rpcs.rs index a1c368ac4e0..7c75a02779c 100644 --- a/zebra-rpc/src/methods/get_block_template_rpcs.rs +++ b/zebra-rpc/src/methods/get_block_template_rpcs.rs @@ -14,7 +14,7 @@ use zebra_chain::{ block::{self, Block, Height, TryIntoHeight}, chain_sync_status::ChainSyncStatus, chain_tip::ChainTip, - parameters::{Network, NetworkKind, POW_AVERAGING_WINDOW}, + parameters::{subsidy::ParameterSubsidy, Network, NetworkKind, POW_AVERAGING_WINDOW}, primitives, serialization::ZcashDeserializeInto, transparent::{ @@ -22,10 +22,7 @@ use zebra_chain::{ }, work::difficulty::{ParameterDifficulty as _, U256}, }; -use zebra_consensus::{ - funding_stream_address, funding_stream_values, miner_subsidy, ParameterSubsidy as _, - RouterError, -}; +use zebra_consensus::{funding_stream_address, funding_stream_values, miner_subsidy, RouterError}; use zebra_network::AddressBookPeers; use zebra_node_services::mempool; use zebra_state::{ReadRequest, ReadResponse}; diff --git a/zebra-rpc/src/methods/get_block_template_rpcs/constants.rs b/zebra-rpc/src/methods/get_block_template_rpcs/constants.rs index 75b26055c8e..3fd4696980d 100644 --- a/zebra-rpc/src/methods/get_block_template_rpcs/constants.rs +++ b/zebra-rpc/src/methods/get_block_template_rpcs/constants.rs @@ -2,8 +2,10 @@ use jsonrpc_core::ErrorCode; -use zebra_chain::block; -use zebra_consensus::FundingStreamReceiver::{self, *}; +use zebra_chain::{ + block, + parameters::subsidy::FundingStreamReceiver::{self, *}, +}; /// When long polling, the amount of time we wait between mempool queries. /// (And sync status queries, which we do right before mempool queries.) diff --git a/zebra-rpc/src/methods/get_block_template_rpcs/get_block_template.rs b/zebra-rpc/src/methods/get_block_template_rpcs/get_block_template.rs index 62026b1bfad..899566ee9a8 100644 --- a/zebra-rpc/src/methods/get_block_template_rpcs/get_block_template.rs +++ b/zebra-rpc/src/methods/get_block_template_rpcs/get_block_template.rs @@ -14,14 +14,12 @@ use zebra_chain::{ }, chain_sync_status::ChainSyncStatus, chain_tip::ChainTip, - parameters::{Network, NetworkUpgrade}, + parameters::{subsidy::FundingStreamReceiver, Network, NetworkUpgrade}, serialization::ZcashDeserializeInto, transaction::{Transaction, UnminedTx, VerifiedUnminedTx}, transparent, }; -use zebra_consensus::{ - funding_stream_address, funding_stream_values, miner_subsidy, FundingStreamReceiver, -}; +use zebra_consensus::{funding_stream_address, funding_stream_values, miner_subsidy}; use zebra_node_services::mempool; use zebra_state::GetBlockTemplateChainInfo; @@ -375,7 +373,7 @@ pub fn standard_coinbase_outputs( // Optional TODO: move this into a zebra_consensus function? let funding_streams: HashMap< FundingStreamReceiver, - (Amount, transparent::Address), + (Amount, &transparent::Address), > = funding_streams .into_iter() .map(|(receiver, amount)| { @@ -400,17 +398,17 @@ pub fn standard_coinbase_outputs( /// If `like_zcashd` is true, try to match the coinbase transactions generated by `zcashd` /// in the `getblocktemplate` RPC. fn combine_coinbase_outputs( - funding_streams: HashMap, transparent::Address)>, + funding_streams: HashMap, &transparent::Address)>, miner_address: &transparent::Address, miner_reward: Amount, like_zcashd: bool, ) -> Vec<(Amount, transparent::Script)> { // Combine all the funding streams with the miner reward. - let mut coinbase_outputs: Vec<(Amount, transparent::Address)> = funding_streams + let mut coinbase_outputs: Vec<(Amount, &transparent::Address)> = funding_streams .into_iter() .map(|(_receiver, (amount, address))| (amount, address)) .collect(); - coinbase_outputs.push((miner_reward, miner_address.clone())); + coinbase_outputs.push((miner_reward, miner_address)); let mut coinbase_outputs: Vec<(Amount, transparent::Script)> = coinbase_outputs .iter() diff --git a/zebra-rpc/src/methods/get_block_template_rpcs/types/subsidy.rs b/zebra-rpc/src/methods/get_block_template_rpcs/types/subsidy.rs index 55572390733..f5ac478bf6b 100644 --- a/zebra-rpc/src/methods/get_block_template_rpcs/types/subsidy.rs +++ b/zebra-rpc/src/methods/get_block_template_rpcs/types/subsidy.rs @@ -2,9 +2,10 @@ use zebra_chain::{ amount::{Amount, NonNegative}, + parameters::subsidy::FundingStreamReceiver, transparent, }; -use zebra_consensus::{funding_stream_recipient_info, FundingStreamReceiver}; +use zebra_consensus::funding_stream_recipient_info; use crate::methods::get_block_template_rpcs::types::zec::Zec; @@ -66,7 +67,7 @@ impl FundingStream { pub fn new( receiver: FundingStreamReceiver, value: Amount, - address: transparent::Address, + address: &transparent::Address, ) -> FundingStream { let (recipient, specification) = funding_stream_recipient_info(receiver); @@ -75,7 +76,7 @@ impl FundingStream { specification: specification.to_string(), value: value.into(), value_zat: value, - address, + address: address.clone(), } } } diff --git a/zebrad/tests/common/configs/v1.9.0.toml b/zebrad/tests/common/configs/v1.9.0.toml index b99a1e38182..11bcf62107a 100644 --- a/zebrad/tests/common/configs/v1.9.0.toml +++ b/zebrad/tests/common/configs/v1.9.0.toml @@ -74,6 +74,125 @@ Canopy = 1_028_500 NU5 = 1_842_420 NU6 = 2_000_000 +[network.testnet_parameters.pre_nu6_funding_streams.height_range] +start = 0 +end = 100 + +[[network.testnet_parameters.post_nu6_funding_streams.recipients]] +receiver = "ECC" +numerator = 7 +addresses = [ + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", +] + +[[network.testnet_parameters.post_nu6_funding_streams.recipients]] +receiver = "ZcashFoundation" +numerator = 5 +addresses = [ + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", + "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz", +] + [rpc] debug_force_finished_sync = false parallel_cpu_threads = 0