use std::{collections::BTreeMap, fmt, sync::Arc};
use crate::{
amount::{Amount, NonNegative},
block::{self, Height, HeightDiff},
parameters::{
checkpoint::list::{CheckpointList, TESTNET_CHECKPOINT_LIST},
constants::{magics, SLOW_START_INTERVAL, SLOW_START_SHIFT},
network::error::ParametersBuilderError,
network_upgrade::TESTNET_ACTIVATION_HEIGHTS,
subsidy::{
constants::mainnet,
constants::testnet,
constants::{
BLOSSOM_POW_TARGET_SPACING_RATIO, FUNDING_STREAM_RECEIVER_DENOMINATOR,
POST_BLOSSOM_HALVING_INTERVAL, PRE_BLOSSOM_HALVING_INTERVAL,
},
funding_stream_address_period, FundingStreamReceiver, FundingStreamRecipient,
FundingStreams,
},
Network, NetworkKind, NetworkUpgrade,
},
transparent,
work::difficulty::{ExpandedDifficulty, U256},
};
use super::magic::Magic;
pub const RESERVED_NETWORK_NAMES: [&str; 6] = [
"Mainnet",
"Testnet",
"Regtest",
"MainnetKind",
"TestnetKind",
"RegtestKind",
];
pub const MAX_NETWORK_NAME_LENGTH: usize = 30;
pub const MAX_HRP_LENGTH: usize = 30;
const REGTEST_GENESIS_HASH: &str =
"029f11d80ef9765602235e1bc9727e3eb6ba20839319f761fee920d63401e327";
const TESTNET_GENESIS_HASH: &str =
"05a60a92d99d85997cce3b87616c089f6124d7342af37106edc76126334a2c38";
const PRE_BLOSSOM_REGTEST_HALVING_INTERVAL: HeightDiff = 144;
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct ConfiguredFundingStreamRecipient {
pub receiver: FundingStreamReceiver,
pub numerator: u64,
pub addresses: Option<Vec<String>>,
}
impl ConfiguredFundingStreamRecipient {
pub fn new_for(receiver: FundingStreamReceiver) -> Self {
use FundingStreamReceiver::*;
match receiver {
Ecc => Self {
receiver: Ecc,
numerator: 7,
addresses: Some(
testnet::FUNDING_STREAM_ECC_ADDRESSES
.map(ToString::to_string)
.to_vec(),
),
},
ZcashFoundation => Self {
receiver: ZcashFoundation,
numerator: 5,
addresses: Some(
testnet::FUNDING_STREAM_ZF_ADDRESSES
.map(ToString::to_string)
.to_vec(),
),
},
MajorGrants => Self {
receiver: MajorGrants,
numerator: 8,
addresses: Some(
testnet::FUNDING_STREAM_MG_ADDRESSES
.map(ToString::to_string)
.to_vec(),
),
},
Deferred => Self {
receiver,
numerator: 0,
addresses: None,
},
}
}
pub fn into_recipient(self) -> (FundingStreamReceiver, FundingStreamRecipient) {
(
self.receiver,
FundingStreamRecipient::new(self.numerator, self.addresses.unwrap_or_default()),
)
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct ConfiguredLockboxDisbursement {
pub address: String,
pub amount: Amount<NonNegative>,
}
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
#[serde(deny_unknown_fields)]
pub struct ConfiguredFundingStreams {
pub height_range: Option<std::ops::Range<Height>>,
pub recipients: Option<Vec<ConfiguredFundingStreamRecipient>>,
}
impl From<&FundingStreams> for ConfiguredFundingStreams {
fn from(value: &FundingStreams) -> Self {
Self {
height_range: Some(value.height_range().clone()),
recipients: Some(
value
.recipients()
.iter()
.map(|(receiver, recipient)| ConfiguredFundingStreamRecipient {
receiver: *receiver,
numerator: recipient.numerator(),
addresses: Some(
recipient
.addresses()
.iter()
.map(ToString::to_string)
.collect(),
),
})
.collect(),
),
}
}
}
impl From<(transparent::Address, Amount<NonNegative>)> for ConfiguredLockboxDisbursement {
fn from((address, amount): (transparent::Address, Amount<NonNegative>)) -> Self {
Self {
address: address.to_string(),
amount,
}
}
}
impl From<&BTreeMap<Height, NetworkUpgrade>> for ConfiguredActivationHeights {
fn from(activation_heights: &BTreeMap<Height, NetworkUpgrade>) -> Self {
let mut configured_activation_heights = ConfiguredActivationHeights::default();
for (height, network_upgrade) in activation_heights {
let field = match network_upgrade {
NetworkUpgrade::BeforeOverwinter => {
&mut configured_activation_heights.before_overwinter
}
NetworkUpgrade::Overwinter => &mut configured_activation_heights.overwinter,
NetworkUpgrade::Sapling => &mut configured_activation_heights.sapling,
NetworkUpgrade::Blossom => &mut configured_activation_heights.blossom,
NetworkUpgrade::Heartwood => &mut configured_activation_heights.heartwood,
NetworkUpgrade::Canopy => &mut configured_activation_heights.canopy,
NetworkUpgrade::Nu5 => &mut configured_activation_heights.nu5,
NetworkUpgrade::Nu6 => &mut configured_activation_heights.nu6,
NetworkUpgrade::Nu6_1 => &mut configured_activation_heights.nu6_1,
NetworkUpgrade::Nu7 => &mut configured_activation_heights.nu7,
#[cfg(zcash_unstable = "zfuture")]
NetworkUpgrade::ZFuture => &mut configured_activation_heights.zfuture,
NetworkUpgrade::Genesis => continue,
};
*field = Some(height.0)
}
configured_activation_heights
}
}
impl From<BTreeMap<Height, NetworkUpgrade>> for ConfiguredActivationHeights {
fn from(value: BTreeMap<Height, NetworkUpgrade>) -> Self {
Self::from(&value)
}
}
impl ConfiguredFundingStreams {
fn convert_with_default(
self,
default_funding_streams: Option<FundingStreams>,
) -> FundingStreams {
let height_range = self.height_range.unwrap_or_else(|| {
default_funding_streams
.as_ref()
.expect("default required")
.height_range()
.clone()
});
let recipients = self
.recipients
.map(|recipients| {
recipients
.into_iter()
.map(ConfiguredFundingStreamRecipient::into_recipient)
.collect()
})
.unwrap_or_else(|| {
default_funding_streams
.as_ref()
.expect("default required")
.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);
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
}
pub fn into_funding_streams_unchecked(self) -> FundingStreams {
let height_range = self.height_range.expect("must have height range");
let recipients = self
.recipients
.into_iter()
.flat_map(|recipients| {
recipients
.into_iter()
.map(ConfiguredFundingStreamRecipient::into_recipient)
})
.collect();
FundingStreams::new(height_range, recipients)
}
}
fn num_funding_stream_addresses_required_for_height_range(
height_range: &std::ops::Range<Height>,
network: &Network,
) -> usize {
1u32.checked_add(funding_stream_address_period(
height_range
.end
.previous()
.expect("end height must be above start height and genesis height"),
network,
))
.expect("no overflow should happen in this sum")
.checked_sub(funding_stream_address_period(height_range.start, network))
.expect("no overflow should happen in this sub") as usize
}
fn check_funding_stream_address_period(funding_streams: &FundingStreams, network: &Network) {
let expected_min_num_addresses = num_funding_stream_addresses_required_for_height_range(
funding_streams.height_range(),
network,
);
for (&receiver, recipient) in funding_streams.recipients() {
if receiver == FundingStreamReceiver::Deferred {
continue;
}
let num_addresses = recipient.addresses().len();
assert!(
num_addresses >= expected_min_num_addresses,
"recipients must have a sufficient number of addresses for height range, \
minimum num addresses required: {expected_min_num_addresses}, only {num_addresses} were provided.\
receiver: {receiver:?}, recipient: {recipient:?}"
);
for address in recipient.addresses() {
assert_eq!(
address.network_kind(),
NetworkKind::Testnet,
"configured funding stream addresses must be for Testnet"
);
}
}
}
#[derive(Serialize, Deserialize, Default, Clone, Copy, Debug, PartialEq)]
#[serde(rename_all = "PascalCase", deny_unknown_fields)]
pub struct ConfiguredActivationHeights {
pub before_overwinter: Option<u32>,
pub overwinter: Option<u32>,
pub sapling: Option<u32>,
pub blossom: Option<u32>,
pub heartwood: Option<u32>,
pub canopy: Option<u32>,
#[serde(rename = "NU5")]
pub nu5: Option<u32>,
#[serde(rename = "NU6")]
pub nu6: Option<u32>,
#[serde(rename = "NU6.1")]
pub nu6_1: Option<u32>,
#[serde(rename = "NU7")]
pub nu7: Option<u32>,
#[serde(rename = "ZFuture")]
#[cfg(zcash_unstable = "zfuture")]
pub zfuture: Option<u32>,
}
impl ConfiguredActivationHeights {
fn for_regtest(self) -> Self {
let Self {
before_overwinter,
overwinter,
sapling,
blossom,
heartwood,
canopy,
nu5,
nu6,
nu6_1,
nu7,
#[cfg(zcash_unstable = "zfuture")]
zfuture,
} = self;
let overwinter = overwinter.or(before_overwinter).or(Some(1));
let sapling = sapling.or(overwinter);
let blossom = blossom.or(sapling);
let heartwood = heartwood.or(blossom);
let canopy = canopy.or(heartwood);
Self {
before_overwinter,
overwinter,
sapling,
blossom,
heartwood,
canopy,
nu5,
nu6,
nu6_1,
nu7,
#[cfg(zcash_unstable = "zfuture")]
zfuture,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
pub enum ConfiguredCheckpoints {
Default(bool),
Path(std::path::PathBuf),
HeightsAndHashes(Vec<(block::Height, block::Hash)>),
}
impl Default for ConfiguredCheckpoints {
fn default() -> Self {
Self::Default(false)
}
}
impl From<Arc<CheckpointList>> for ConfiguredCheckpoints {
fn from(value: Arc<CheckpointList>) -> Self {
Self::HeightsAndHashes(value.iter_cloned().collect())
}
}
impl From<bool> for ConfiguredCheckpoints {
fn from(value: bool) -> Self {
Self::Default(value)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ParametersBuilder {
network_name: String,
network_magic: Magic,
genesis_hash: block::Hash,
activation_heights: BTreeMap<Height, NetworkUpgrade>,
slow_start_interval: Height,
funding_streams: Vec<FundingStreams>,
should_lock_funding_stream_address_period: bool,
target_difficulty_limit: ExpandedDifficulty,
disable_pow: bool,
should_allow_unshielded_coinbase_spends: bool,
pre_blossom_halving_interval: HeightDiff,
post_blossom_halving_interval: HeightDiff,
lockbox_disbursements: Vec<(String, Amount<NonNegative>)>,
checkpoints: Arc<CheckpointList>,
}
impl Default for ParametersBuilder {
fn default() -> Self {
Self {
network_name: "UnknownTestnet".to_string(),
network_magic: magics::TESTNET,
activation_heights: TESTNET_ACTIVATION_HEIGHTS.iter().cloned().collect(),
genesis_hash: TESTNET_GENESIS_HASH
.parse()
.expect("hard-coded hash parses"),
slow_start_interval: SLOW_START_INTERVAL,
target_difficulty_limit: ExpandedDifficulty::from((U256::one() << 251) - 1)
.to_compact()
.to_expanded()
.expect("difficulty limits are valid expanded values"),
disable_pow: false,
funding_streams: testnet::FUNDING_STREAMS.clone(),
should_lock_funding_stream_address_period: false,
pre_blossom_halving_interval: PRE_BLOSSOM_HALVING_INTERVAL,
post_blossom_halving_interval: POST_BLOSSOM_HALVING_INTERVAL,
should_allow_unshielded_coinbase_spends: false,
lockbox_disbursements: testnet::NU6_1_LOCKBOX_DISBURSEMENTS
.iter()
.map(|(addr, amount)| (addr.to_string(), *amount))
.collect(),
checkpoints: TESTNET_CHECKPOINT_LIST.clone(),
}
}
}
impl ParametersBuilder {
pub fn with_network_name(
mut self,
network_name: impl fmt::Display,
) -> Result<Self, ParametersBuilderError> {
let network_name = network_name.to_string();
if RESERVED_NETWORK_NAMES.contains(&network_name.as_str()) {
return Err(ParametersBuilderError::ReservedNetworkName {
network_name,
reserved_names: RESERVED_NETWORK_NAMES.to_vec(),
});
}
if network_name.len() > MAX_NETWORK_NAME_LENGTH {
return Err(ParametersBuilderError::NetworkNameTooLong {
network_name,
max_length: MAX_NETWORK_NAME_LENGTH,
});
}
if !network_name
.chars()
.all(|x| x.is_alphanumeric() || x == '_')
{
return Err(ParametersBuilderError::InvalidCharacter);
}
self.network_name = network_name;
Ok(self)
}
pub fn with_network_magic(
mut self,
network_magic: Magic,
) -> Result<Self, ParametersBuilderError> {
if [magics::MAINNET, magics::REGTEST]
.into_iter()
.any(|reserved_magic| network_magic == reserved_magic)
{
return Err(ParametersBuilderError::ReservedNetworkMagic);
}
self.network_magic = network_magic;
Ok(self)
}
pub fn with_genesis_hash(
mut self,
genesis_hash: impl fmt::Display,
) -> Result<Self, ParametersBuilderError> {
self.genesis_hash = genesis_hash
.to_string()
.parse()
.map_err(|_| ParametersBuilderError::InvalidGenesisHash)?;
Ok(self)
}
pub fn with_activation_heights(
mut self,
ConfiguredActivationHeights {
before_overwinter,
overwinter,
sapling,
blossom,
heartwood,
canopy,
nu5,
nu6,
nu6_1,
nu7,
#[cfg(zcash_unstable = "zfuture")]
zfuture,
}: ConfiguredActivationHeights,
) -> Result<Self, ParametersBuilderError> {
use NetworkUpgrade::*;
if self.should_lock_funding_stream_address_period {
return Err(ParametersBuilderError::LockFundingStreams);
}
let activation_heights: BTreeMap<_, _> = {
let activation_heights = before_overwinter
.into_iter()
.map(|h| (h, BeforeOverwinter))
.chain(overwinter.into_iter().map(|h| (h, Overwinter)))
.chain(sapling.into_iter().map(|h| (h, Sapling)))
.chain(blossom.into_iter().map(|h| (h, Blossom)))
.chain(heartwood.into_iter().map(|h| (h, Heartwood)))
.chain(canopy.into_iter().map(|h| (h, Canopy)))
.chain(nu5.into_iter().map(|h| (h, Nu5)))
.chain(nu6.into_iter().map(|h| (h, Nu6)))
.chain(nu6_1.into_iter().map(|h| (h, Nu6_1)))
.chain(nu7.into_iter().map(|h| (h, Nu7)));
#[cfg(zcash_unstable = "zfuture")]
let activation_heights =
activation_heights.chain(zfuture.into_iter().map(|h| (h, ZFuture)));
activation_heights
.map(|(h, nu)| {
let height = h
.try_into()
.map_err(|_| ParametersBuilderError::InvalidActivationHeight)?;
Ok((height, nu))
})
.collect::<Result<BTreeMap<_, _>, _>>()?
};
let network_upgrades: Vec<_> = activation_heights.iter().map(|(_h, &nu)| nu).collect();
let mut activation_heights_iter = activation_heights.iter();
for expected_network_upgrade in NetworkUpgrade::iter() {
if !network_upgrades.contains(&expected_network_upgrade) {
continue;
} else if let Some((&height, &network_upgrade)) = activation_heights_iter.next() {
if height == Height(0) {
return Err(ParametersBuilderError::InvalidHeightZero);
}
if network_upgrade != expected_network_upgrade {
return Err(ParametersBuilderError::OutOfOrderUpgrades);
}
}
}
self.activation_heights.split_off(&Height(1));
self.activation_heights.extend(activation_heights);
Ok(self)
}
pub fn with_slow_start_interval(mut self, slow_start_interval: Height) -> Self {
self.slow_start_interval = slow_start_interval;
self
}
pub fn with_funding_streams(mut self, funding_streams: Vec<ConfiguredFundingStreams>) -> Self {
self.funding_streams = funding_streams
.into_iter()
.enumerate()
.map(|(idx, streams)| {
let default_streams = testnet::FUNDING_STREAMS.get(idx).cloned();
streams.convert_with_default(default_streams)
})
.collect();
self.should_lock_funding_stream_address_period = true;
self
}
pub fn clear_funding_streams(mut self) -> Self {
self.funding_streams = vec![];
self
}
pub fn extend_funding_streams(mut self) -> Self {
let network = self.to_network_unchecked();
for funding_streams in &mut self.funding_streams {
funding_streams.extend_recipient_addresses(
num_funding_stream_addresses_required_for_height_range(
funding_streams.height_range(),
&network,
),
);
}
self
}
pub fn with_target_difficulty_limit(
mut self,
target_difficulty_limit: impl Into<ExpandedDifficulty>,
) -> Result<Self, ParametersBuilderError> {
self.target_difficulty_limit = target_difficulty_limit
.into()
.to_compact()
.to_expanded()
.ok_or(ParametersBuilderError::InvaildDifficultyLimits)?;
Ok(self)
}
pub fn with_disable_pow(mut self, disable_pow: bool) -> Self {
self.disable_pow = disable_pow;
self
}
pub fn with_unshielded_coinbase_spends(
mut self,
should_allow_unshielded_coinbase_spends: bool,
) -> Self {
self.should_allow_unshielded_coinbase_spends = should_allow_unshielded_coinbase_spends;
self
}
pub fn with_halving_interval(
mut self,
pre_blossom_halving_interval: HeightDiff,
) -> Result<Self, ParametersBuilderError> {
if self.should_lock_funding_stream_address_period {
return Err(ParametersBuilderError::HalvingIntervalAfterFundingStreams);
}
self.pre_blossom_halving_interval = pre_blossom_halving_interval;
self.post_blossom_halving_interval =
self.pre_blossom_halving_interval * (BLOSSOM_POW_TARGET_SPACING_RATIO as HeightDiff);
Ok(self)
}
pub fn with_lockbox_disbursements(
mut self,
lockbox_disbursements: Vec<ConfiguredLockboxDisbursement>,
) -> Self {
self.lockbox_disbursements = lockbox_disbursements
.into_iter()
.map(|ConfiguredLockboxDisbursement { address, amount }| (address, amount))
.collect();
self
}
pub fn with_checkpoints(
mut self,
checkpoints: impl Into<ConfiguredCheckpoints>,
) -> Result<Self, ParametersBuilderError> {
self.checkpoints = match checkpoints.into() {
ConfiguredCheckpoints::Default(true) => TESTNET_CHECKPOINT_LIST.clone(),
ConfiguredCheckpoints::Default(false) => Arc::new(
CheckpointList::from_list([(block::Height(0), self.genesis_hash)])
.map_err(|_| ParametersBuilderError::FailedToParseDefaultCheckpoint)?,
),
ConfiguredCheckpoints::Path(path_buf) => {
let Ok(raw_checkpoints_str) = std::fs::read_to_string(&path_buf) else {
return Err(ParametersBuilderError::FailedToReadCheckpointFile {
path_buf: path_buf.clone(),
});
};
Arc::new(
raw_checkpoints_str
.parse::<CheckpointList>()
.map_err(|err| ParametersBuilderError::FailedToParseCheckpointFile {
path_buf: path_buf.clone(),
err: err.to_string(),
})?,
)
}
ConfiguredCheckpoints::HeightsAndHashes(items) => Arc::new(
CheckpointList::from_list(items)
.map_err(|_| ParametersBuilderError::InvalidCustomCheckpoints)?,
),
};
Ok(self)
}
pub fn clear_checkpoints(self) -> Result<Self, ParametersBuilderError> {
self.with_checkpoints(ConfiguredCheckpoints::Default(false))
}
fn finish(self) -> Parameters {
let Self {
network_name,
network_magic,
genesis_hash,
activation_heights,
slow_start_interval,
funding_streams,
should_lock_funding_stream_address_period: _,
target_difficulty_limit,
disable_pow,
should_allow_unshielded_coinbase_spends,
pre_blossom_halving_interval,
post_blossom_halving_interval,
lockbox_disbursements,
checkpoints,
} = self;
Parameters {
network_name,
network_magic,
genesis_hash,
activation_heights,
slow_start_interval,
slow_start_shift: Height(slow_start_interval.0 / 2),
funding_streams,
target_difficulty_limit,
disable_pow,
should_allow_unshielded_coinbase_spends,
pre_blossom_halving_interval,
post_blossom_halving_interval,
lockbox_disbursements,
checkpoints,
}
}
fn to_network_unchecked(&self) -> Network {
Network::new_configured_testnet(self.clone().finish())
}
pub fn to_network(self) -> Result<Network, ParametersBuilderError> {
let network = self.to_network_unchecked();
for fs in &self.funding_streams {
check_funding_stream_address_period(fs, &network);
}
if network.checkpoint_list().hash(Height(0)) != Some(network.genesis_hash()) {
return Err(ParametersBuilderError::CheckpointGenesisMismatch);
}
if network.checkpoint_list().max_height() < network.mandatory_checkpoint_height() {
return Err(ParametersBuilderError::InsufficientCheckpointCoverage);
}
Ok(network)
}
pub fn is_compatible_with_default_parameters(&self) -> bool {
let Self {
network_name: _,
network_magic,
genesis_hash,
activation_heights,
slow_start_interval,
funding_streams,
should_lock_funding_stream_address_period: _,
target_difficulty_limit,
disable_pow,
should_allow_unshielded_coinbase_spends,
pre_blossom_halving_interval,
post_blossom_halving_interval,
lockbox_disbursements,
checkpoints: _,
} = Self::default();
self.activation_heights == activation_heights
&& self.network_magic == network_magic
&& self.genesis_hash == genesis_hash
&& self.slow_start_interval == slow_start_interval
&& self.funding_streams == funding_streams
&& self.target_difficulty_limit == target_difficulty_limit
&& self.disable_pow == disable_pow
&& self.should_allow_unshielded_coinbase_spends
== should_allow_unshielded_coinbase_spends
&& self.pre_blossom_halving_interval == pre_blossom_halving_interval
&& self.post_blossom_halving_interval == post_blossom_halving_interval
&& self.lockbox_disbursements == lockbox_disbursements
}
}
#[derive(Debug, Default, Clone)]
pub struct RegtestParameters {
pub activation_heights: ConfiguredActivationHeights,
pub funding_streams: Option<Vec<ConfiguredFundingStreams>>,
pub lockbox_disbursements: Option<Vec<ConfiguredLockboxDisbursement>>,
pub checkpoints: Option<ConfiguredCheckpoints>,
pub extend_funding_stream_addresses_as_required: Option<bool>,
}
impl From<ConfiguredActivationHeights> for RegtestParameters {
fn from(value: ConfiguredActivationHeights) -> Self {
Self {
activation_heights: value,
..Default::default()
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Parameters {
network_name: String,
network_magic: Magic,
genesis_hash: block::Hash,
activation_heights: BTreeMap<Height, NetworkUpgrade>,
slow_start_interval: Height,
slow_start_shift: Height,
funding_streams: Vec<FundingStreams>,
target_difficulty_limit: ExpandedDifficulty,
disable_pow: bool,
should_allow_unshielded_coinbase_spends: bool,
pre_blossom_halving_interval: HeightDiff,
post_blossom_halving_interval: HeightDiff,
lockbox_disbursements: Vec<(String, Amount<NonNegative>)>,
checkpoints: Arc<CheckpointList>,
}
impl Default for Parameters {
fn default() -> Self {
Self {
network_name: "Testnet".to_string(),
..Self::build().finish()
}
}
}
impl Parameters {
pub fn build() -> ParametersBuilder {
ParametersBuilder::default()
}
pub fn new_regtest(
RegtestParameters {
activation_heights,
funding_streams,
lockbox_disbursements,
checkpoints,
extend_funding_stream_addresses_as_required,
}: RegtestParameters,
) -> Result<Self, ParametersBuilderError> {
let mut parameters = Self::build()
.with_genesis_hash(REGTEST_GENESIS_HASH)?
.with_target_difficulty_limit(U256::from_big_endian(&[0x0f; 32]))?
.with_disable_pow(true)
.with_unshielded_coinbase_spends(true)
.with_slow_start_interval(Height::MIN)
.with_activation_heights(activation_heights.for_regtest())?
.with_halving_interval(PRE_BLOSSOM_REGTEST_HALVING_INTERVAL)?
.with_funding_streams(funding_streams.unwrap_or_default())
.with_lockbox_disbursements(lockbox_disbursements.unwrap_or_default())
.with_checkpoints(checkpoints.unwrap_or_default())?;
if Some(true) == extend_funding_stream_addresses_as_required {
parameters = parameters.extend_funding_streams();
}
Ok(Self {
network_name: "Regtest".to_string(),
network_magic: magics::REGTEST,
..parameters.finish()
})
}
pub fn is_default_testnet(&self) -> bool {
self == &Self::default()
}
pub fn is_regtest(&self) -> bool {
if self.network_magic != magics::REGTEST {
return false;
}
let Self {
network_name,
network_magic: _,
genesis_hash,
activation_heights: _,
slow_start_interval,
slow_start_shift,
funding_streams: _,
target_difficulty_limit,
disable_pow,
should_allow_unshielded_coinbase_spends,
pre_blossom_halving_interval,
post_blossom_halving_interval,
lockbox_disbursements: _,
checkpoints: _,
} = Self::new_regtest(Default::default()).expect("default regtest parameters are valid");
self.network_name == network_name
&& self.genesis_hash == genesis_hash
&& self.slow_start_interval == slow_start_interval
&& self.slow_start_shift == slow_start_shift
&& self.target_difficulty_limit == target_difficulty_limit
&& self.disable_pow == disable_pow
&& self.should_allow_unshielded_coinbase_spends
== should_allow_unshielded_coinbase_spends
&& self.pre_blossom_halving_interval == pre_blossom_halving_interval
&& self.post_blossom_halving_interval == post_blossom_halving_interval
}
pub fn network_name(&self) -> &str {
&self.network_name
}
pub fn network_magic(&self) -> Magic {
self.network_magic
}
pub fn genesis_hash(&self) -> block::Hash {
self.genesis_hash
}
pub fn activation_heights(&self) -> &BTreeMap<Height, NetworkUpgrade> {
&self.activation_heights
}
pub fn slow_start_interval(&self) -> Height {
self.slow_start_interval
}
pub fn slow_start_shift(&self) -> Height {
self.slow_start_shift
}
pub fn funding_streams(&self) -> &Vec<FundingStreams> {
&self.funding_streams
}
pub fn target_difficulty_limit(&self) -> ExpandedDifficulty {
self.target_difficulty_limit
}
pub fn disable_pow(&self) -> bool {
self.disable_pow
}
pub fn should_allow_unshielded_coinbase_spends(&self) -> bool {
self.should_allow_unshielded_coinbase_spends
}
pub fn pre_blossom_halving_interval(&self) -> HeightDiff {
self.pre_blossom_halving_interval
}
pub fn post_blossom_halving_interval(&self) -> HeightDiff {
self.post_blossom_halving_interval
}
pub fn lockbox_disbursement_total_amount(&self) -> Amount<NonNegative> {
self.lockbox_disbursements()
.into_iter()
.map(|(_addr, amount)| amount)
.reduce(|a, b| (a + b).expect("sum of configured amounts should be valid"))
.unwrap_or_default()
}
pub fn lockbox_disbursements(&self) -> Vec<(transparent::Address, Amount<NonNegative>)> {
self.lockbox_disbursements
.iter()
.map(|(addr, amount)| {
(
addr.parse().expect("hard-coded address must deserialize"),
*amount,
)
})
.collect()
}
pub fn checkpoints(&self) -> Arc<CheckpointList> {
self.checkpoints.clone()
}
}
impl Network {
pub fn parameters(&self) -> Option<Arc<Parameters>> {
if let Self::Testnet(parameters) = self {
Some(parameters.clone())
} else {
None
}
}
pub fn disable_pow(&self) -> bool {
if let Self::Testnet(params) = self {
params.disable_pow()
} else {
false
}
}
pub fn slow_start_interval(&self) -> Height {
if let Self::Testnet(params) = self {
params.slow_start_interval()
} else {
SLOW_START_INTERVAL
}
}
pub fn slow_start_shift(&self) -> Height {
if let Self::Testnet(params) = self {
params.slow_start_shift()
} else {
SLOW_START_SHIFT
}
}
pub fn funding_streams(&self, height: Height) -> Option<&FundingStreams> {
self.all_funding_streams()
.iter()
.find(|&streams| streams.height_range().contains(&height))
}
pub fn all_funding_streams(&self) -> &Vec<FundingStreams> {
if let Self::Testnet(params) = self {
params.funding_streams()
} else {
&mainnet::FUNDING_STREAMS
}
}
pub fn should_allow_unshielded_coinbase_spends(&self) -> bool {
if let Self::Testnet(params) = self {
params.should_allow_unshielded_coinbase_spends()
} else {
false
}
}
pub fn founder_address_list(&self) -> &[&str] {
match self {
Network::Mainnet => &mainnet::FOUNDER_ADDRESS_LIST,
Network::Testnet(_) => &testnet::FOUNDER_ADDRESS_LIST,
}
}
}