Replaces Vec with HashMap, adds `ConfiguredFundingStreams` type and conversion logic with constraints.
Minor refactors
This commit is contained in:
parent
930c488bf7
commit
9a9464c9d1
|
@ -1,4 +1,6 @@
|
|||
//! Constants for Block Subsidy and Funding Streams
|
||||
//! Constants and calculations for Block Subsidy and Funding Streams
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
|
@ -6,6 +8,7 @@ use crate::{
|
|||
amount::COIN,
|
||||
block::{Height, HeightDiff},
|
||||
parameters::{Network, NetworkUpgrade},
|
||||
transparent,
|
||||
};
|
||||
|
||||
/// The largest block subsidy, used before the first halving.
|
||||
|
@ -36,9 +39,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.
|
||||
|
@ -48,6 +52,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
|
||||
|
@ -59,78 +77,86 @@ pub const FUNDING_STREAM_RECEIVER_DENOMINATOR: u64 = 100;
|
|||
pub const FUNDING_STREAM_SPECIFICATION: &str = "https://zips.z.cash/zip-0214";
|
||||
|
||||
/// Funding stream recipients and height ranges.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[derive(Deserialize, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct FundingStreams {
|
||||
recipients: Vec<FundingStreamRecipient>,
|
||||
/// Start and end Heights for funding streams
|
||||
/// as described in [protocol specification §7.10.1][7.10.1].
|
||||
///
|
||||
/// [7.10.1]: https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams
|
||||
height_range: std::ops::Range<Height>,
|
||||
/// Funding stream recipients by [`FundingStreamReceiver`].
|
||||
recipients: HashMap<FundingStreamReceiver, FundingStreamRecipient>,
|
||||
}
|
||||
|
||||
impl FundingStreams {
|
||||
/// Creates a new [`FundingStreams`].
|
||||
pub fn new(
|
||||
height_range: std::ops::Range<Height>,
|
||||
recipients: HashMap<FundingStreamReceiver, FundingStreamRecipient>,
|
||||
) -> Self {
|
||||
Self {
|
||||
height_range,
|
||||
recipients,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns height range where these [`FundingStreams`] should apply.
|
||||
pub fn height_range(&self) -> &std::ops::Range<Height> {
|
||||
&self.height_range
|
||||
}
|
||||
|
||||
/// Returns recipients of these [`FundingStreams`].
|
||||
pub fn recipients(&self) -> &[FundingStreamRecipient] {
|
||||
pub fn recipients(&self) -> &HashMap<FundingStreamReceiver, FundingStreamRecipient> {
|
||||
&self.recipients
|
||||
}
|
||||
|
||||
/// Returns a recipient with the provided receiver.
|
||||
pub fn recipient_by_receiver(
|
||||
&self,
|
||||
receiver: FundingStreamReceiver,
|
||||
) -> Option<&FundingStreamRecipient> {
|
||||
self.recipients
|
||||
.iter()
|
||||
.find(|recipient| recipient.receiver() == 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(Clone, Debug, Eq, PartialEq)]
|
||||
#[derive(Deserialize, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct FundingStreamRecipient {
|
||||
receiver: FundingStreamReceiver,
|
||||
name: String,
|
||||
/// 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
|
||||
numerator: u64,
|
||||
addresses: Vec<String>,
|
||||
/// Addresses for the funding stream recipient
|
||||
addresses: Vec<transparent::Address>,
|
||||
}
|
||||
|
||||
impl FundingStreamRecipient {
|
||||
/// Creates a new [`FundingStreamRecipient`].
|
||||
pub fn new(
|
||||
receiver: FundingStreamReceiver,
|
||||
name: impl Into<String>,
|
||||
numerator: u64,
|
||||
addresses: Vec<String>,
|
||||
) -> Self {
|
||||
pub fn new<I, T>(numerator: u64, addresses: I) -> Self
|
||||
where
|
||||
T: ToString,
|
||||
I: IntoIterator<Item = T>,
|
||||
{
|
||||
Self {
|
||||
receiver,
|
||||
name: name.into(),
|
||||
numerator,
|
||||
addresses,
|
||||
addresses: addresses
|
||||
.into_iter()
|
||||
.map(|addr| {
|
||||
let addr = addr.to_string();
|
||||
addr.parse()
|
||||
.expect("funding stream address must deserialize")
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the receiver of this funding stream.
|
||||
pub fn receiver(&self) -> FundingStreamReceiver {
|
||||
self.receiver
|
||||
}
|
||||
|
||||
/// Returns the name of this funding stream.
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// 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) -> &[String] {
|
||||
pub fn addresses(&self) -> &[transparent::Address] {
|
||||
&self.addresses
|
||||
}
|
||||
}
|
||||
|
@ -138,102 +164,66 @@ impl FundingStreamRecipient {
|
|||
lazy_static! {
|
||||
/// The pre-NU6 funding streams for Mainnet
|
||||
pub static ref PRE_NU6_FUNDING_STREAMS_MAINNET: FundingStreams = FundingStreams {
|
||||
recipients: vec![
|
||||
FundingStreamRecipient::new(
|
||||
FundingStreamReceiver::Ecc,
|
||||
"Electric Coin Company",
|
||||
7,
|
||||
FUNDING_STREAM_ECC_ADDRESSES_MAINNET
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect(),
|
||||
),
|
||||
FundingStreamRecipient::new(
|
||||
FundingStreamReceiver::ZcashFoundation,
|
||||
"Zcash Foundation",
|
||||
5,
|
||||
FUNDING_STREAM_ZF_ADDRESSES_MAINNET
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect(),
|
||||
),
|
||||
FundingStreamRecipient::new(
|
||||
FundingStreamReceiver::MajorGrants,
|
||||
"Major Grants",
|
||||
8,
|
||||
FUNDING_STREAM_MG_ADDRESSES_MAINNET
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect(),
|
||||
),
|
||||
],
|
||||
height_range: Height(1_046_400)..Height(2_726_400),
|
||||
};
|
||||
|
||||
/// The pre-NU6 funding streams for Testnet
|
||||
pub static ref PRE_NU6_FUNDING_STREAMS_TESTNET: FundingStreams = FundingStreams {
|
||||
recipients: vec![
|
||||
FundingStreamRecipient::new(
|
||||
recipients: [
|
||||
(
|
||||
FundingStreamReceiver::Ecc,
|
||||
"Electric Coin Company",
|
||||
7,
|
||||
FUNDING_STREAM_ECC_ADDRESSES_TESTNET
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect(),
|
||||
FundingStreamRecipient::new(7, FUNDING_STREAM_ECC_ADDRESSES_MAINNET.iter()),
|
||||
),
|
||||
FundingStreamRecipient::new(
|
||||
(
|
||||
FundingStreamReceiver::ZcashFoundation,
|
||||
"Zcash Foundation",
|
||||
5,
|
||||
FUNDING_STREAM_ZF_ADDRESSES_TESTNET
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect(),
|
||||
FundingStreamRecipient::new(5, FUNDING_STREAM_ZF_ADDRESSES_MAINNET),
|
||||
),
|
||||
FundingStreamRecipient::new(
|
||||
(
|
||||
FundingStreamReceiver::MajorGrants,
|
||||
"Major Grants",
|
||||
8,
|
||||
FUNDING_STREAM_MG_ADDRESSES_TESTNET
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect(),
|
||||
FundingStreamRecipient::new(8, FUNDING_STREAM_MG_ADDRESSES_MAINNET),
|
||||
),
|
||||
],
|
||||
height_range: Height(1_028_500)..Height(2_796_000),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
};
|
||||
|
||||
/// The post-NU6 funding streams for Mainnet
|
||||
pub static ref POST_NU6_FUNDING_STREAMS_MAINNET: FundingStreams = FundingStreams {
|
||||
recipients: vec![
|
||||
FundingStreamRecipient::new(
|
||||
FundingStreamReceiver::MajorGrants,
|
||||
"Major Grants",
|
||||
8,
|
||||
FUNDING_STREAM_MG_ADDRESSES_MAINNET
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect(),
|
||||
),
|
||||
],
|
||||
height_range: Height(2_726_400)..Height(3_146_400),
|
||||
recipients: [(
|
||||
FundingStreamReceiver::MajorGrants,
|
||||
FundingStreamRecipient::new(8, FUNDING_STREAM_MG_ADDRESSES_MAINNET)
|
||||
)]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
};
|
||||
|
||||
/// The pre-NU6 funding streams for Testnet
|
||||
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(),
|
||||
};
|
||||
|
||||
/// The post-NU6 funding streams for Testnet
|
||||
pub static ref POST_NU6_FUNDING_STREAMS_TESTNET: FundingStreams = FundingStreams {
|
||||
recipients: vec![
|
||||
FundingStreamRecipient::new(
|
||||
FundingStreamReceiver::MajorGrants,
|
||||
"Major Grants",
|
||||
8,
|
||||
FUNDING_STREAM_MG_ADDRESSES_TESTNET
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect(),
|
||||
),
|
||||
],
|
||||
height_range: Height(2_942_000)..Height(3_362_000),
|
||||
recipients: [(
|
||||
FundingStreamReceiver::MajorGrants,
|
||||
FundingStreamRecipient::new(8, FUNDING_STREAM_MG_ADDRESSES_TESTNET),
|
||||
)]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -312,7 +302,7 @@ pub trait ParameterSubsidy {
|
|||
}
|
||||
|
||||
/// Network methods related to Block Subsidy and Funding Streams
|
||||
impl ParameterSubsidy for Network {
|
||||
impl ParameterSubsidy for &Network {
|
||||
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`
|
||||
|
@ -326,6 +316,13 @@ impl ParameterSubsidy for Network {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ParameterSubsidy for Network {
|
||||
fn height_for_first_halving(&self) -> Height {
|
||||
(&self).height_for_first_halving()
|
||||
}
|
||||
}
|
||||
|
||||
/// 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];
|
||||
|
@ -403,3 +400,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: impl ParameterSubsidy) -> u32 {
|
||||
// Spec equation: `address_period = floor((height - (height_for_halving(1) - post_blossom_halving_interval))/funding_stream_address_change_interval)`,
|
||||
// <https://zips.z.cash/protocol/protocol.pdf#fundingstreams>
|
||||
//
|
||||
// Note that the brackets make it so the post blossom halving interval is added to the total.
|
||||
//
|
||||
// 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 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")
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use crate::{
|
|||
parameters::{
|
||||
constants::{magics, SLOW_START_INTERVAL, SLOW_START_SHIFT},
|
||||
network_upgrade::TESTNET_ACTIVATION_HEIGHTS,
|
||||
subsidy::{funding_stream_address_period, FUNDING_STREAM_RECEIVER_DENOMINATOR},
|
||||
Network, NetworkUpgrade, NETWORK_UPGRADES_IN_ORDER,
|
||||
},
|
||||
work::difficulty::{ExpandedDifficulty, U256},
|
||||
|
@ -14,7 +15,8 @@ use crate::{
|
|||
use super::{
|
||||
magic::Magic,
|
||||
subsidy::{
|
||||
FundingStreams, POST_NU6_FUNDING_STREAMS_MAINNET, POST_NU6_FUNDING_STREAMS_TESTNET,
|
||||
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,
|
||||
},
|
||||
};
|
||||
|
@ -48,9 +50,116 @@ const REGTEST_GENESIS_HASH: &str =
|
|||
const TESTNET_GENESIS_HASH: &str =
|
||||
"05a60a92d99d85997cce3b87616c089f6124d7342af37106edc76126334a2c38";
|
||||
|
||||
struct TestnetParameterSubsidyImpl;
|
||||
|
||||
impl ParameterSubsidy for TestnetParameterSubsidyImpl {
|
||||
fn height_for_first_halving(&self) -> Height {
|
||||
FIRST_HALVING_TESTNET
|
||||
}
|
||||
}
|
||||
|
||||
/// Configurable funding streams for Regtest and 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<String>,
|
||||
}
|
||||
|
||||
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 Regtest and 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<std::ops::Range<Height>>,
|
||||
/// Funding stream recipients, see [`FundingStreams::recipients`] for more details.
|
||||
pub recipients: Option<Vec<ConfiguredFundingStreamRecipient>>,
|
||||
}
|
||||
|
||||
impl ConfiguredFundingStreams {
|
||||
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}"
|
||||
);
|
||||
}
|
||||
|
||||
// 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<u32>,
|
||||
|
@ -247,6 +356,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(
|
||||
|
@ -527,7 +656,7 @@ impl Network {
|
|||
/// Returns post-NU6 funding streams for this network
|
||||
pub fn post_nu6_funding_streams(&self) -> &FundingStreams {
|
||||
if let Self::Testnet(params) = self {
|
||||
params.pre_nu6_funding_streams()
|
||||
params.post_nu6_funding_streams()
|
||||
} else {
|
||||
&POST_NU6_FUNDING_STREAMS_MAINNET
|
||||
}
|
||||
|
|
|
@ -6,8 +6,13 @@ use zcash_protocol::consensus::NetworkConstants as _;
|
|||
use crate::{
|
||||
block::Height,
|
||||
parameters::{
|
||||
subsidy::{
|
||||
FundingStreamReceiver, 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 +300,126 @@ fn check_full_activation_list() {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_funding_streams() {
|
||||
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()
|
||||
});
|
||||
});
|
||||
|
||||
// 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",
|
||||
);
|
||||
}
|
||||
|
|
|
@ -197,7 +197,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);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
//!
|
||||
//! [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},
|
||||
|
@ -32,7 +32,7 @@ pub fn funding_stream_values(
|
|||
let funding_streams = network.funding_streams(height);
|
||||
if funding_streams.height_range().contains(&height) {
|
||||
let block_subsidy = block_subsidy(height, network)?;
|
||||
for recipient in funding_streams.recipients() {
|
||||
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":
|
||||
|
@ -41,37 +41,13 @@ pub fn funding_stream_values(
|
|||
let amount_value = ((block_subsidy * recipient.numerator())?
|
||||
/ FUNDING_STREAM_RECEIVER_DENOMINATOR)?;
|
||||
|
||||
results.insert(recipient.receiver(), amount_value);
|
||||
results.insert(receiver, amount_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
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)`,
|
||||
// <https://zips.z.cash/protocol/protocol.pdf#fundingstreams>
|
||||
//
|
||||
// Note that the brackets make it so the post blossom halving interval is added to the total.
|
||||
//
|
||||
// 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 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]
|
||||
///
|
||||
|
@ -90,8 +66,9 @@ fn funding_stream_address_index(height: Height, network: &Network) -> usize {
|
|||
|
||||
let num_addresses = funding_streams
|
||||
.recipients()
|
||||
.first()
|
||||
.map(|recipient| recipient.addresses().len())
|
||||
.iter()
|
||||
.next()
|
||||
.map(|(_, recipient)| recipient.addresses().len())
|
||||
.unwrap_or_default();
|
||||
|
||||
assert!(index > 0 && index <= num_addresses);
|
||||
|
@ -108,35 +85,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 funding_streams = network.funding_streams(height);
|
||||
let address = &funding_streams
|
||||
.recipient_by_receiver(receiver)
|
||||
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();
|
||||
transparent::Address::from_str(address).expect("address should deserialize")
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Return a human-readable name and a specification URL for the funding stream `receiver`.
|
||||
pub fn funding_stream_recipient_info(
|
||||
network: &Network,
|
||||
height: Height,
|
||||
receiver: FundingStreamReceiver,
|
||||
) -> (String, &'static str) {
|
||||
let name = network
|
||||
.funding_streams(height)
|
||||
.recipient_by_receiver(receiver)
|
||||
// TODO: Replace with optional return type
|
||||
.unwrap()
|
||||
.name()
|
||||
.to_string();
|
||||
|
||||
(name, FUNDING_STREAM_SPECIFICATION)
|
||||
) -> (&'static str, &'static str) {
|
||||
(receiver.name(), FUNDING_STREAM_SPECIFICATION)
|
||||
}
|
||||
|
||||
/// Given a funding stream P2SH address, create a script and check if it is the same
|
||||
|
|
|
@ -64,12 +64,8 @@ fn test_funding_stream_values() -> Result<(), Report> {
|
|||
fn test_funding_stream_addresses() -> Result<(), Report> {
|
||||
let _init_guard = zebra_test::init();
|
||||
for network in Network::iter() {
|
||||
for funding_stream_recipient in network.pre_nu6_funding_streams().recipients() {
|
||||
let receiver = funding_stream_recipient.receiver();
|
||||
for address in funding_stream_recipient.addresses() {
|
||||
let address =
|
||||
transparent::Address::from_str(address).expect("address should deserialize");
|
||||
|
||||
for (receiver, recipient) in network.pre_nu6_funding_streams().recipients() {
|
||||
for address in recipient.addresses() {
|
||||
assert_eq!(
|
||||
address.network_kind(),
|
||||
network.kind(),
|
||||
|
@ -77,7 +73,7 @@ fn test_funding_stream_addresses() -> Result<(), Report> {
|
|||
);
|
||||
|
||||
// Asserts if address is not a P2SH address.
|
||||
let _script = new_coinbase_script(&address);
|
||||
let _script = new_coinbase_script(address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String>,
|
||||
network_magic: Option<[u8; 4]>,
|
||||
|
@ -649,6 +650,8 @@ impl<'de> Deserialize<'de> for Config {
|
|||
disable_pow: Option<bool>,
|
||||
genesis_hash: Option<String>,
|
||||
activation_heights: Option<ConfiguredActivationHeights>,
|
||||
pre_nu6_funding_streams: Option<ConfiguredFundingStreams>,
|
||||
post_nu6_funding_streams: Option<ConfiguredFundingStreams>,
|
||||
}
|
||||
|
||||
#[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
|
||||
|
|
|
@ -1199,10 +1199,7 @@ where
|
|||
.iter()
|
||||
.map(|(receiver, value)| {
|
||||
let address = funding_stream_address(height, &network, *receiver);
|
||||
(
|
||||
*receiver,
|
||||
FundingStream::new(&network, height, *receiver, *value, address),
|
||||
)
|
||||
(*receiver, FundingStream::new(*receiver, *value, address))
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
|
@ -373,7 +373,7 @@ pub fn standard_coinbase_outputs(
|
|||
// Optional TODO: move this into a zebra_consensus function?
|
||||
let funding_streams: HashMap<
|
||||
FundingStreamReceiver,
|
||||
(Amount<NonNegative>, transparent::Address),
|
||||
(Amount<NonNegative>, &transparent::Address),
|
||||
> = funding_streams
|
||||
.into_iter()
|
||||
.map(|(receiver, amount)| {
|
||||
|
@ -398,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<FundingStreamReceiver, (Amount<NonNegative>, transparent::Address)>,
|
||||
funding_streams: HashMap<FundingStreamReceiver, (Amount<NonNegative>, &transparent::Address)>,
|
||||
miner_address: &transparent::Address,
|
||||
miner_reward: Amount<NonNegative>,
|
||||
like_zcashd: bool,
|
||||
) -> Vec<(Amount<NonNegative>, transparent::Script)> {
|
||||
// Combine all the funding streams with the miner reward.
|
||||
let mut coinbase_outputs: Vec<(Amount<NonNegative>, transparent::Address)> = funding_streams
|
||||
let mut coinbase_outputs: Vec<(Amount<NonNegative>, &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<NonNegative>, transparent::Script)> = coinbase_outputs
|
||||
.iter()
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
use zebra_chain::{
|
||||
amount::{Amount, NonNegative},
|
||||
block::Height,
|
||||
parameters::{subsidy::FundingStreamReceiver, Network},
|
||||
parameters::subsidy::FundingStreamReceiver,
|
||||
transparent,
|
||||
};
|
||||
use zebra_consensus::funding_stream_recipient_info;
|
||||
|
@ -66,20 +65,18 @@ pub struct FundingStream {
|
|||
impl FundingStream {
|
||||
/// Convert a `receiver`, `value`, and `address` into a `FundingStream` response.
|
||||
pub fn new(
|
||||
network: &Network,
|
||||
height: Height,
|
||||
receiver: FundingStreamReceiver,
|
||||
value: Amount<NonNegative>,
|
||||
address: transparent::Address,
|
||||
address: &transparent::Address,
|
||||
) -> FundingStream {
|
||||
let (recipient, specification) = funding_stream_recipient_info(network, height, receiver);
|
||||
let (recipient, specification) = funding_stream_recipient_info(receiver);
|
||||
|
||||
FundingStream {
|
||||
recipient: recipient.to_string(),
|
||||
specification: specification.to_string(),
|
||||
value: value.into(),
|
||||
value_zat: value,
|
||||
address,
|
||||
address: address.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,6 +74,126 @@ 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
|
||||
|
|
Loading…
Reference in New Issue