change(consensus): Refactor production code for network consensus rules to `Network` methods (#8340)

* begin refactor suggested as "step 2": https://github.com/ZcashFoundation/zebra/issues/7968#issue-2003245309
Squashed from multiple commits to enable partial rebase

* break out more little traits

* add activation implementation leveraging From<Network> for lrz::cons::

* for transfer of ownership I cannot return a type that's owned by the method

* hrp_sapling_extended_full_viewing_key

* complete implementation of interface of Parameters on Network reuse Parameters on zcash Network where possible

* move doc-comments to trait declarations (from impls)

* Simplify/complete Parameters impl for Network

* Add checkpoint_list method, move documentation, etc

* move last match network to inside network method

* add back comment

* use zcash_address for parameter types in zebra-chain

* use inherent methods instead of big parameters passthrough

* revert to implementation of From on zcash_primitives::..::Network vs &zcash_prim...

* move match

* add test to block maximum time rule

* update changelog

* finish porting target_difficutly_limit

* remove obscelete code comment

* revert non-functional change

* finish migrating target_difficulty_limit, checkpoint_list

* update changelog

---------

Co-authored-by: Hazel OHearn <gygaxis@zingolabs.org>
This commit is contained in:
Za Wilcox 2024-03-12 15:41:44 -06:00 committed by GitHub
parent 9b91d4bc0e
commit 3bef54b764
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 284 additions and 243 deletions

View File

@ -5,6 +5,20 @@ All notable changes to Zebra are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org). and this project adheres to [Semantic Versioning](https://semver.org).
## Unreleased
### Added
- `zebra_chain::Network` methods:
- `b58_pubkey_address_prefix`, `b58_script_address_prefix`, `num_funding_streams`
### Changed
- Functions that take a `zebra_chain::Network` as an argument have been moved to be methods of `Network`, including
- `zebra_chain::parameters`:
- `genesis::genesis_hash`, `NetworkUpgrade::activation_list`, `NetworkUpgrade::is_max_block_time_enforced`,
- `zebra_chain::work::difficulty::ExpandedDifficulty::target_difficutly_limit`
- `zebra_consensus::height_for_first_halving`
- `zebra_consensus::checkpoint::CheckpointList::new` (now `Network::checkpoint_list`)
## [Zebra 1.6.0](https://github.com/ZcashFoundation/zebra/releases/tag/v1.6.0) - 2024-02-23 ## [Zebra 1.6.0](https://github.com/ZcashFoundation/zebra/releases/tag/v1.6.0) - 2024-02-23
This release exposes the shielded scanning functionality through an initial This release exposes the shielded scanning functionality through an initial

View File

@ -31,7 +31,6 @@ async-error = [
# Mining RPC support # Mining RPC support
getblocktemplate-rpcs = [ getblocktemplate-rpcs = [
"zcash_address",
] ]
# Experimental shielded scanning support # Experimental shielded scanning support
@ -132,8 +131,7 @@ serde_json = { version = "1.0.113", optional = true }
# Production feature async-error and testing feature proptest-impl # Production feature async-error and testing feature proptest-impl
tokio = { version = "1.36.0", optional = true } tokio = { version = "1.36.0", optional = true }
# Production feature getblocktemplate-rpcs zcash_address = { version = "0.3.1" }
zcash_address = { version = "0.3.1", optional = true }
# Experimental feature shielded-scan # Experimental feature shielded-scan
zcash_client_backend = { version = "0.10.0-rc.1", optional = true } zcash_client_backend = { version = "0.10.0-rc.1", optional = true }

View File

@ -1,21 +1,7 @@
//! Genesis consensus parameters for each Zcash network. //! Genesis consensus parameters for each Zcash network.
use crate::{block, parameters::Network};
/// The previous block hash for the genesis block. /// The previous block hash for the genesis block.
/// ///
/// All known networks use the Bitcoin `null` value for the parent of the /// All known networks use the Bitcoin `null` value for the parent of the
/// genesis block. (In Bitcoin, `null` is `[0; 32]`.) /// genesis block. (In Bitcoin, `null` is `[0; 32]`.)
pub const GENESIS_PREVIOUS_BLOCK_HASH: block::Hash = block::Hash([0; 32]); pub const GENESIS_PREVIOUS_BLOCK_HASH: crate::block::Hash = crate::block::Hash([0; 32]);
/// Returns the hash for the genesis block in `network`.
pub fn genesis_hash(network: Network) -> block::Hash {
match network {
// zcash-cli getblockhash 0
Network::Mainnet => "00040fe8ec8471911baa1db1266ea15dd06b4a8a5c453883c000b031973dce08",
// zcash-cli -testnet getblockhash 0
Network::Testnet => "05a60a92d99d85997cce3b87616c089f6124d7342af37106edc76126334a2c38",
}
.parse()
.expect("hard-coded hash parses")
}

View File

@ -5,7 +5,7 @@ use std::{fmt, str::FromStr};
use thiserror::Error; use thiserror::Error;
use crate::{ use crate::{
block::{Height, HeightDiff}, block::{self, Height, HeightDiff},
parameters::NetworkUpgrade::Canopy, parameters::NetworkUpgrade::Canopy,
}; };
@ -63,6 +63,35 @@ pub enum Network {
Testnet, Testnet,
} }
use zcash_primitives::consensus::{Network as ZcashPrimitivesNetwork, Parameters as _};
impl Network {
/// Returns the human-readable prefix for Base58Check-encoded transparent
/// pay-to-public-key-hash payment addresses for the network.
pub fn b58_pubkey_address_prefix(&self) -> [u8; 2] {
<ZcashPrimitivesNetwork>::from(*self).b58_pubkey_address_prefix()
}
/// Returns the human-readable prefix for Base58Check-encoded transparent pay-to-script-hash
/// payment addresses for the network.
pub fn b58_script_address_prefix(&self) -> [u8; 2] {
<ZcashPrimitivesNetwork>::from(*self).b58_script_address_prefix()
}
/// Returns true if the maximum block time rule is active for `network` and `height`.
///
/// Always returns true if `network` is the Mainnet.
/// If `network` is the Testnet, the `height` should be at least
/// TESTNET_MAX_TIME_START_HEIGHT to return true.
/// Returns false otherwise.
///
/// Part of the consensus rules at <https://zips.z.cash/protocol/protocol.pdf#blockheader>
pub fn is_max_block_time_enforced(self, height: block::Height) -> bool {
match self {
Network::Mainnet => true,
Network::Testnet => height >= super::TESTNET_MAX_TIME_START_HEIGHT,
}
}
}
impl From<Network> for &'static str { impl From<Network> for &'static str {
fn from(network: Network) -> &'static str { fn from(network: Network) -> &'static str {
match network { match network {

View File

@ -1,7 +1,10 @@
use proptest::prelude::*; use proptest::prelude::*;
use super::super::{Network, ZIP_212_GRACE_PERIOD_DURATION}; use super::super::{Network, ZIP_212_GRACE_PERIOD_DURATION};
use crate::parameters::NetworkUpgrade; use crate::{
block::Height,
parameters::{NetworkUpgrade, TESTNET_MAX_TIME_START_HEIGHT},
};
proptest! { proptest! {
/// Check that the mandatory checkpoint is after the ZIP-212 grace period. /// Check that the mandatory checkpoint is after the ZIP-212 grace period.
@ -23,4 +26,16 @@ proptest! {
assert!(network.mandatory_checkpoint_height() >= grace_period_end_height); assert!(network.mandatory_checkpoint_height() >= grace_period_end_height);
} }
#[test]
/// Asserts that the activation height is correct for the block
/// maximum time rule on Testnet is correct.
fn max_block_times_correct_enforcement(height in any::<Height>()) {
let _init_guard = zebra_test::init();
assert!(Network::Mainnet.is_max_block_time_enforced(height));
assert_eq!(Network::Testnet.is_max_block_time_enforced(height), TESTNET_MAX_TIME_START_HEIGHT <= height);
}
} }

View File

@ -229,7 +229,7 @@ const TESTNET_MINIMUM_DIFFICULTY_START_HEIGHT: block::Height = block::Height(299
/// <https://zips.z.cash/protocol/protocol.pdf#blockheader> /// <https://zips.z.cash/protocol/protocol.pdf#blockheader>
pub const TESTNET_MAX_TIME_START_HEIGHT: block::Height = block::Height(653_606); pub const TESTNET_MAX_TIME_START_HEIGHT: block::Height = block::Height(653_606);
impl NetworkUpgrade { impl Network {
/// Returns a map between activation heights and network upgrades for `network`, /// Returns a map between activation heights and network upgrades for `network`,
/// in ascending height order. /// in ascending height order.
/// ///
@ -241,7 +241,7 @@ impl NetworkUpgrade {
/// When the environment variable TEST_FAKE_ACTIVATION_HEIGHTS is set /// When the environment variable TEST_FAKE_ACTIVATION_HEIGHTS is set
/// and it's a test build, this returns a list of fake activation heights /// and it's a test build, this returns a list of fake activation heights
/// used by some tests. /// used by some tests.
pub fn activation_list(network: Network) -> BTreeMap<block::Height, NetworkUpgrade> { pub fn activation_list(&self) -> BTreeMap<block::Height, NetworkUpgrade> {
let (mainnet_heights, testnet_heights) = { let (mainnet_heights, testnet_heights) = {
#[cfg(not(feature = "zebra-test"))] #[cfg(not(feature = "zebra-test"))]
{ {
@ -269,7 +269,7 @@ impl NetworkUpgrade {
(MAINNET_ACTIVATION_HEIGHTS, TESTNET_ACTIVATION_HEIGHTS) (MAINNET_ACTIVATION_HEIGHTS, TESTNET_ACTIVATION_HEIGHTS)
} }
}; };
match network { match self {
Mainnet => mainnet_heights, Mainnet => mainnet_heights,
Testnet => testnet_heights, Testnet => testnet_heights,
} }
@ -277,10 +277,12 @@ impl NetworkUpgrade {
.cloned() .cloned()
.collect() .collect()
} }
}
impl NetworkUpgrade {
/// Returns the current network upgrade for `network` and `height`. /// Returns the current network upgrade for `network` and `height`.
pub fn current(network: Network, height: block::Height) -> NetworkUpgrade { pub fn current(network: Network, height: block::Height) -> NetworkUpgrade {
NetworkUpgrade::activation_list(network) network
.activation_list()
.range(..=height) .range(..=height)
.map(|(_, nu)| *nu) .map(|(_, nu)| *nu)
.next_back() .next_back()
@ -292,7 +294,8 @@ impl NetworkUpgrade {
/// Returns None if the next upgrade has not been implemented in Zebra /// Returns None if the next upgrade has not been implemented in Zebra
/// yet. /// yet.
pub fn next(network: Network, height: block::Height) -> Option<NetworkUpgrade> { pub fn next(network: Network, height: block::Height) -> Option<NetworkUpgrade> {
NetworkUpgrade::activation_list(network) network
.activation_list()
.range((Excluded(height), Unbounded)) .range((Excluded(height), Unbounded))
.map(|(_, nu)| *nu) .map(|(_, nu)| *nu)
.next() .next()
@ -303,7 +306,8 @@ impl NetworkUpgrade {
/// Returns None if this network upgrade is a future upgrade, and its /// Returns None if this network upgrade is a future upgrade, and its
/// activation height has not been set yet. /// activation height has not been set yet.
pub fn activation_height(&self, network: Network) -> Option<block::Height> { pub fn activation_height(&self, network: Network) -> Option<block::Height> {
NetworkUpgrade::activation_list(network) network
.activation_list()
.iter() .iter()
.filter(|(_, nu)| nu == &self) .filter(|(_, nu)| nu == &self)
.map(|(height, _)| *height) .map(|(height, _)| *height)
@ -316,7 +320,7 @@ impl NetworkUpgrade {
/// Use [`NetworkUpgrade::activation_height`] to get the specific network /// Use [`NetworkUpgrade::activation_height`] to get the specific network
/// upgrade. /// upgrade.
pub fn is_activation_height(network: Network, height: block::Height) -> bool { pub fn is_activation_height(network: Network, height: block::Height) -> bool {
NetworkUpgrade::activation_list(network).contains_key(&height) network.activation_list().contains_key(&height)
} }
/// Returns an unordered mapping between NetworkUpgrades and their ConsensusBranchIds. /// Returns an unordered mapping between NetworkUpgrades and their ConsensusBranchIds.
@ -445,20 +449,6 @@ impl NetworkUpgrade {
NetworkUpgrade::current(network, height).averaging_window_timespan() NetworkUpgrade::current(network, height).averaging_window_timespan()
} }
/// Returns true if the maximum block time rule is active for `network` and `height`.
///
/// Always returns true if `network` is the Mainnet.
/// If `network` is the Testnet, the `height` should be at least
/// TESTNET_MAX_TIME_START_HEIGHT to return true.
/// Returns false otherwise.
///
/// Part of the consensus rules at <https://zips.z.cash/protocol/protocol.pdf#blockheader>
pub fn is_max_block_time_enforced(network: Network, height: block::Height) -> bool {
match network {
Network::Mainnet => true,
Network::Testnet => height >= TESTNET_MAX_TIME_START_HEIGHT,
}
}
/// Returns the NetworkUpgrade given an u32 as ConsensusBranchId /// Returns the NetworkUpgrade given an u32 as ConsensusBranchId
pub fn from_branch_id(branch_id: u32) -> Option<NetworkUpgrade> { pub fn from_branch_id(branch_id: u32) -> Option<NetworkUpgrade> {
CONSENSUS_BRANCH_IDS CONSENSUS_BRANCH_IDS

View File

@ -14,14 +14,14 @@ use NetworkUpgrade::*;
fn activation_bijective() { fn activation_bijective() {
let _init_guard = zebra_test::init(); let _init_guard = zebra_test::init();
let mainnet_activations = NetworkUpgrade::activation_list(Mainnet); let mainnet_activations = Mainnet.activation_list();
let mainnet_heights: HashSet<&block::Height> = mainnet_activations.keys().collect(); let mainnet_heights: HashSet<&block::Height> = mainnet_activations.keys().collect();
assert_eq!(MAINNET_ACTIVATION_HEIGHTS.len(), mainnet_heights.len()); assert_eq!(MAINNET_ACTIVATION_HEIGHTS.len(), mainnet_heights.len());
let mainnet_nus: HashSet<&NetworkUpgrade> = mainnet_activations.values().collect(); let mainnet_nus: HashSet<&NetworkUpgrade> = mainnet_activations.values().collect();
assert_eq!(MAINNET_ACTIVATION_HEIGHTS.len(), mainnet_nus.len()); assert_eq!(MAINNET_ACTIVATION_HEIGHTS.len(), mainnet_nus.len());
let testnet_activations = NetworkUpgrade::activation_list(Testnet); let testnet_activations = Testnet.activation_list();
let testnet_heights: HashSet<&block::Height> = testnet_activations.keys().collect(); let testnet_heights: HashSet<&block::Height> = testnet_activations.keys().collect();
assert_eq!(TESTNET_ACTIVATION_HEIGHTS.len(), testnet_heights.len()); assert_eq!(TESTNET_ACTIVATION_HEIGHTS.len(), testnet_heights.len());
@ -46,7 +46,7 @@ fn activation_extremes_testnet() {
fn activation_extremes(network: Network) { fn activation_extremes(network: Network) {
// The first three upgrades are Genesis, BeforeOverwinter, and Overwinter // The first three upgrades are Genesis, BeforeOverwinter, and Overwinter
assert_eq!( assert_eq!(
NetworkUpgrade::activation_list(network).get(&block::Height(0)), network.activation_list().get(&block::Height(0)),
Some(&Genesis) Some(&Genesis)
); );
assert_eq!(Genesis.activation_height(network), Some(block::Height(0))); assert_eq!(Genesis.activation_height(network), Some(block::Height(0)));
@ -62,7 +62,7 @@ fn activation_extremes(network: Network) {
); );
assert_eq!( assert_eq!(
NetworkUpgrade::activation_list(network).get(&block::Height(1)), network.activation_list().get(&block::Height(1)),
Some(&BeforeOverwinter) Some(&BeforeOverwinter)
); );
assert_eq!( assert_eq!(
@ -91,7 +91,7 @@ fn activation_extremes(network: Network) {
// We assume that the last upgrade we know about continues forever // We assume that the last upgrade we know about continues forever
// (even if we suspect that won't be true) // (even if we suspect that won't be true)
assert_ne!( assert_ne!(
NetworkUpgrade::activation_list(network).get(&block::Height::MAX), network.activation_list().get(&block::Height::MAX),
Some(&Genesis) Some(&Genesis)
); );
assert!(!NetworkUpgrade::is_activation_height( assert!(!NetworkUpgrade::is_activation_height(
@ -121,7 +121,7 @@ fn activation_consistent_testnet() {
/// Check that the `activation_height`, `is_activation_height`, /// Check that the `activation_height`, `is_activation_height`,
/// `current`, and `next` functions are consistent for `network`. /// `current`, and `next` functions are consistent for `network`.
fn activation_consistent(network: Network) { fn activation_consistent(network: Network) {
let activation_list = NetworkUpgrade::activation_list(network); let activation_list = network.activation_list();
let network_upgrades: HashSet<&NetworkUpgrade> = activation_list.values().collect(); let network_upgrades: HashSet<&NetworkUpgrade> = activation_list.values().collect();
for &network_upgrade in network_upgrades { for &network_upgrade in network_upgrades {

View File

@ -25,24 +25,12 @@ pub fn decrypts_successfully(transaction: &Transaction, network: Network, height
if let Some(bundle) = alt_tx.sapling_bundle() { if let Some(bundle) = alt_tx.sapling_bundle() {
for output in bundle.shielded_outputs().iter() { for output in bundle.shielded_outputs().iter() {
let recovery = match network { let recovery = zcash_primitives::sapling::note_encryption::try_sapling_output_recovery(
Network::Mainnet => { &<zcash_primitives::consensus::Network>::from(network),
zcash_primitives::sapling::note_encryption::try_sapling_output_recovery( alt_height,
&zcash_primitives::consensus::MAIN_NETWORK, &null_sapling_ovk,
alt_height, output,
&null_sapling_ovk, );
output,
)
}
Network::Testnet => {
zcash_primitives::sapling::note_encryption::try_sapling_output_recovery(
&zcash_primitives::consensus::TEST_NETWORK,
alt_height,
&null_sapling_ovk,
output,
)
}
};
if recovery.is_none() { if recovery.is_none() {
return false; return false;
} }

View File

@ -15,20 +15,6 @@ use crate::{
#[cfg(test)] #[cfg(test)]
use proptest::prelude::*; use proptest::prelude::*;
/// Magic numbers used to identify what networks Transparent Addresses
/// are associated with.
mod magics {
pub mod p2sh {
pub const MAINNET: [u8; 2] = [0x1C, 0xBD];
pub const TESTNET: [u8; 2] = [0x1C, 0xBA];
}
pub mod p2pkh {
pub const MAINNET: [u8; 2] = [0x1C, 0xB8];
pub const TESTNET: [u8; 2] = [0x1D, 0x25];
}
}
/// Transparent Zcash Addresses /// Transparent Zcash Addresses
/// ///
/// In Bitcoin a single byte is used for the version field identifying /// In Bitcoin a single byte is used for the version field identifying
@ -118,24 +104,14 @@ impl ZcashSerialize for Address {
network, network,
script_hash, script_hash,
} => { } => {
// Dev network doesn't have a recommendation so we writer.write_all(&network.b58_script_address_prefix())?;
// default to testnet bytes if it's not mainnet.
match *network {
Network::Mainnet => writer.write_all(&magics::p2sh::MAINNET[..])?,
_ => writer.write_all(&magics::p2sh::TESTNET[..])?,
}
writer.write_all(script_hash)? writer.write_all(script_hash)?
} }
Address::PayToPublicKeyHash { Address::PayToPublicKeyHash {
network, network,
pub_key_hash, pub_key_hash,
} => { } => {
// Dev network doesn't have a recommendation so we writer.write_all(&network.b58_pubkey_address_prefix())?;
// default to testnet bytes if it's not mainnet.
match *network {
Network::Mainnet => writer.write_all(&magics::p2pkh::MAINNET[..])?,
_ => writer.write_all(&magics::p2pkh::TESTNET[..])?,
}
writer.write_all(pub_key_hash)? writer.write_all(pub_key_hash)?
} }
} }
@ -153,22 +129,30 @@ impl ZcashDeserialize for Address {
reader.read_exact(&mut hash_bytes)?; reader.read_exact(&mut hash_bytes)?;
match version_bytes { match version_bytes {
magics::p2sh::MAINNET => Ok(Address::PayToScriptHash { zcash_primitives::constants::mainnet::B58_SCRIPT_ADDRESS_PREFIX => {
network: Network::Mainnet, Ok(Address::PayToScriptHash {
script_hash: hash_bytes, network: Network::Mainnet,
}), script_hash: hash_bytes,
magics::p2sh::TESTNET => Ok(Address::PayToScriptHash { })
network: Network::Testnet, }
script_hash: hash_bytes, zcash_primitives::constants::testnet::B58_SCRIPT_ADDRESS_PREFIX => {
}), Ok(Address::PayToScriptHash {
magics::p2pkh::MAINNET => Ok(Address::PayToPublicKeyHash { network: Network::Testnet,
network: Network::Mainnet, script_hash: hash_bytes,
pub_key_hash: hash_bytes, })
}), }
magics::p2pkh::TESTNET => Ok(Address::PayToPublicKeyHash { zcash_primitives::constants::mainnet::B58_PUBKEY_ADDRESS_PREFIX => {
network: Network::Testnet, Ok(Address::PayToPublicKeyHash {
pub_key_hash: hash_bytes, network: Network::Mainnet,
}), pub_key_hash: hash_bytes,
})
}
zcash_primitives::constants::testnet::B58_PUBKEY_ADDRESS_PREFIX => {
Ok(Address::PayToPublicKeyHash {
network: Network::Testnet,
pub_key_hash: hash_bytes,
})
}
_ => Err(SerializationError::Parse("bad t-addr version/type")), _ => Err(SerializationError::Parse("bad t-addr version/type")),
} }
} }

View File

@ -384,32 +384,6 @@ impl ExpandedDifficulty {
U256::from_little_endian(&hash.0).into() U256::from_little_endian(&hash.0).into()
} }
/// Returns the easiest target difficulty allowed on `network`.
///
/// # Consensus
///
/// See `PoWLimit` in the Zcash specification:
/// <https://zips.z.cash/protocol/protocol.pdf#constants>
pub fn target_difficulty_limit(network: Network) -> ExpandedDifficulty {
let limit: U256 = match network {
/* 2^243 - 1 */
Network::Mainnet => (U256::one() << 243) - 1,
/* 2^251 - 1 */
Network::Testnet => (U256::one() << 251) - 1,
};
// `zcashd` converts the PoWLimit into a compact representation before
// using it to perform difficulty filter checks.
//
// The Zcash specification converts to compact for the default difficulty
// filter, but not for testnet minimum difficulty blocks. (ZIP 205 and
// ZIP 208 don't specify this conversion either.) See #1277 for details.
ExpandedDifficulty(limit)
.to_compact()
.to_expanded()
.expect("difficulty limits are valid expanded values")
}
/// Calculate the CompactDifficulty for an expanded difficulty. /// Calculate the CompactDifficulty for an expanded difficulty.
/// ///
/// # Consensus /// # Consensus
@ -681,7 +655,8 @@ impl PartialCumulativeWork {
pub fn difficulty_multiplier_for_display(&self, network: Network) -> f64 { pub fn difficulty_multiplier_for_display(&self, network: Network) -> f64 {
// This calculation is similar to the `getdifficulty` RPC, see that code for details. // This calculation is similar to the `getdifficulty` RPC, see that code for details.
let pow_limit = ExpandedDifficulty::target_difficulty_limit(network) let pow_limit = network
.target_difficulty_limit()
.to_compact() .to_compact()
.to_work() .to_work()
.expect("target difficult limit is valid work"); .expect("target difficult limit is valid work");
@ -705,6 +680,41 @@ impl PartialCumulativeWork {
} }
} }
/// Network methods related to Difficulty
pub trait ParameterDifficulty {
/// Returns the easiest target difficulty allowed on `network`.
///
/// # Consensus
///
/// See `PoWLimit` in the Zcash specification:
/// <https://zips.z.cash/protocol/protocol.pdf#constants>
fn target_difficulty_limit(&self) -> ExpandedDifficulty;
}
impl ParameterDifficulty for Network {
/// Returns the easiest target difficulty allowed on `network`.
/// See [`ParameterDifficulty::target_difficulty_limit`]
fn target_difficulty_limit(&self) -> ExpandedDifficulty {
let limit: U256 = match self {
/* 2^243 - 1 */
Network::Mainnet => (U256::one() << 243) - 1,
/* 2^251 - 1 */
Network::Testnet => (U256::one() << 251) - 1,
};
// `zcashd` converts the PoWLimit into a compact representation before
// using it to perform difficulty filter checks.
//
// The Zcash specification converts to compact for the default difficulty
// filter, but not for testnet minimum difficulty blocks. (ZIP 205 and
// ZIP 208 don't specify this conversion either.) See #1277 for details.
ExpandedDifficulty(limit)
.to_compact()
.to_expanded()
.expect("difficulty limits are valid expanded values")
}
}
impl From<Work> for PartialCumulativeWork { impl From<Work> for PartialCumulativeWork {
fn from(work: Work) -> Self { fn from(work: Work) -> Self {
PartialCumulativeWork(work.0) PartialCumulativeWork(work.0)

View File

@ -309,9 +309,9 @@ fn block_difficulty_for_network(network: Network) -> Result<(), Report> {
/// SPANDOC: Check the PoWLimit for block {?height, ?network, ?threshold, ?hash} /// SPANDOC: Check the PoWLimit for block {?height, ?network, ?threshold, ?hash}
{ {
// the consensus rule // the consensus rule
assert!(threshold <= ExpandedDifficulty::target_difficulty_limit(network)); assert!(threshold <= network.target_difficulty_limit());
// check that ordering is transitive, we checked `hash <= threshold` above // check that ordering is transitive, we checked `hash <= threshold` above
assert!(hash <= ExpandedDifficulty::target_difficulty_limit(network)); assert!(hash <= network.target_difficulty_limit());
} }
/// SPANDOC: Check compact round-trip for block {?height, ?network} /// SPANDOC: Check compact round-trip for block {?height, ?network}
@ -376,7 +376,7 @@ fn genesis_block_difficulty_for_network(network: Network) -> Result<(), Report>
{ {
assert_eq!( assert_eq!(
threshold, threshold,
ExpandedDifficulty::target_difficulty_limit(network), network.target_difficulty_limit(),
"genesis block difficulty thresholds must be equal to the PoWLimit" "genesis block difficulty thresholds must be equal to the PoWLimit"
); );
} }
@ -477,12 +477,12 @@ fn check_testnet_minimum_difficulty_block(height: block::Height) -> Result<(), R
/// SPANDOC: Check that the testnet minimum difficulty is the PoWLimit {?height, ?threshold, ?hash} /// SPANDOC: Check that the testnet minimum difficulty is the PoWLimit {?height, ?threshold, ?hash}
{ {
assert_eq!(threshold, ExpandedDifficulty::target_difficulty_limit(Network::Testnet), assert_eq!(threshold, Network::Testnet.target_difficulty_limit(),
"testnet minimum difficulty thresholds should be equal to the PoWLimit. Hint: Blocks with large gaps are allowed to have the minimum difficulty, but it's not required."); "testnet minimum difficulty thresholds should be equal to the PoWLimit. Hint: Blocks with large gaps are allowed to have the minimum difficulty, but it's not required.");
// all blocks pass the minimum difficulty threshold, even if they aren't minimum // all blocks pass the minimum difficulty threshold, even if they aren't minimum
// difficulty blocks, because it's the lowest permitted difficulty // difficulty blocks, because it's the lowest permitted difficulty
assert!( assert!(
hash <= ExpandedDifficulty::target_difficulty_limit(Network::Testnet), hash <= Network::Testnet.target_difficulty_limit(),
"testnet minimum difficulty hashes must be less than the PoWLimit" "testnet minimum difficulty hashes must be less than the PoWLimit"
); );
} }

View File

@ -9,7 +9,10 @@ use zebra_chain::{
block::{Block, Hash, Header, Height}, block::{Block, Hash, Header, Height},
parameters::{Network, NetworkUpgrade}, parameters::{Network, NetworkUpgrade},
transaction, transaction,
work::{difficulty::ExpandedDifficulty, equihash}, work::{
difficulty::{ExpandedDifficulty, ParameterDifficulty as _},
equihash,
},
}; };
use crate::{error::*, parameters::SLOW_START_INTERVAL}; use crate::{error::*, parameters::SLOW_START_INTERVAL};
@ -78,13 +81,13 @@ pub fn difficulty_threshold_is_valid(
// The PowLimit check is part of `Threshold()` in the spec, but it doesn't // The PowLimit check is part of `Threshold()` in the spec, but it doesn't
// actually depend on any previous blocks. // actually depend on any previous blocks.
if difficulty_threshold > ExpandedDifficulty::target_difficulty_limit(network) { if difficulty_threshold > network.target_difficulty_limit() {
Err(BlockError::TargetDifficultyLimit( Err(BlockError::TargetDifficultyLimit(
*height, *height,
*hash, *hash,
difficulty_threshold, difficulty_threshold,
network, network,
ExpandedDifficulty::target_difficulty_limit(network), network.target_difficulty_limit(),
))?; ))?;
} }

View File

@ -48,22 +48,6 @@ pub fn funding_stream_values(
Ok(results) Ok(results)
} }
/// Returns the minimum height after the first halving
/// as described in [protocol specification §7.10][7.10]
///
/// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams
pub fn height_for_first_halving(network: Network) -> Height {
// First halving on Mainnet is at Canopy
// while in Testnet is at block constant height of `1_116_000`
// https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams
match network {
Network::Mainnet => Canopy
.activation_height(network)
.expect("canopy activation height should be available"),
Network::Testnet => FIRST_HALVING_TESTNET,
}
}
/// Returns the address change period /// Returns the address change period
/// as described in [protocol specification §7.10][7.10] /// as described in [protocol specification §7.10][7.10]
/// ///
@ -78,7 +62,7 @@ fn funding_stream_address_period(height: Height, network: Network) -> u32 {
// <https://doc.rust-lang.org/stable/reference/expressions/operator-expr.html#arithmetic-and-logical-binary-operators> // <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. // This is the same as `floor()`, because these numbers are all positive.
let height_after_first_halving = height - height_for_first_halving(network); let height_after_first_halving = height - network.height_for_first_halving();
let address_period = (height_after_first_halving + POST_BLOSSOM_HALVING_INTERVAL) let address_period = (height_after_first_halving + POST_BLOSSOM_HALVING_INTERVAL)
/ FUNDING_STREAM_ADDRESS_CHANGE_INTERVAL; / FUNDING_STREAM_ADDRESS_CHANGE_INTERVAL;
@ -93,10 +77,7 @@ fn funding_stream_address_period(height: Height, network: Network) -> u32 {
/// ///
/// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams /// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams
fn funding_stream_address_index(height: Height, network: Network) -> usize { fn funding_stream_address_index(height: Height, network: Network) -> usize {
let num_addresses = match network { let num_addresses = network.num_funding_streams();
Network::Mainnet => FUNDING_STREAMS_NUM_ADDRESSES_MAINNET,
Network::Testnet => FUNDING_STREAMS_NUM_ADDRESSES_TESTNET,
};
let index = 1u32 let index = 1u32
.checked_add(funding_stream_address_period(height, network)) .checked_add(funding_stream_address_period(height, network))

View File

@ -131,11 +131,7 @@ mod test {
fn halving_for_network(network: Network) -> Result<(), Report> { fn halving_for_network(network: Network) -> Result<(), Report> {
let blossom_height = Blossom.activation_height(network).unwrap(); let blossom_height = Blossom.activation_height(network).unwrap();
let first_halving_height = match network { let first_halving_height = network.height_for_first_halving();
Network::Mainnet => Canopy.activation_height(network).unwrap(),
// Based on "7.8 Calculation of Block Subsidy and Founders' Reward"
Network::Testnet => Height(1_116_000),
};
assert_eq!( assert_eq!(
1, 1,
@ -261,11 +257,7 @@ mod test {
fn block_subsidy_for_network(network: Network) -> Result<(), Report> { fn block_subsidy_for_network(network: Network) -> Result<(), Report> {
let blossom_height = Blossom.activation_height(network).unwrap(); let blossom_height = Blossom.activation_height(network).unwrap();
let first_halving_height = match network { let first_halving_height = network.height_for_first_halving();
Network::Mainnet => Canopy.activation_height(network).unwrap(),
// Based on "7.8 Calculation of Block Subsidy and Founders' Reward"
Network::Testnet => Height(1_116_000),
};
// After slow-start mining and before Blossom the block subsidy is 12.5 ZEC // After slow-start mining and before Blossom the block subsidy is 12.5 ZEC
// https://z.cash/support/faq/#what-is-slow-start-mining // https://z.cash/support/faq/#what-is-slow-start-mining

View File

@ -19,7 +19,7 @@ use zebra_chain::{
parameters::{Network, NetworkUpgrade}, parameters::{Network, NetworkUpgrade},
serialization::{ZcashDeserialize, ZcashDeserializeInto}, serialization::{ZcashDeserialize, ZcashDeserializeInto},
transaction::{arbitrary::transaction_to_fake_v5, LockTime, Transaction}, transaction::{arbitrary::transaction_to_fake_v5, LockTime, Transaction},
work::difficulty::{ExpandedDifficulty, INVALID_COMPACT_DIFFICULTY}, work::difficulty::{ParameterDifficulty as _, INVALID_COMPACT_DIFFICULTY},
}; };
use zebra_script::CachedFfiTransaction; use zebra_script::CachedFfiTransaction;
use zebra_test::transcript::{ExpectedTranscriptError, Transcript}; use zebra_test::transcript::{ExpectedTranscriptError, Transcript};
@ -241,7 +241,7 @@ fn difficulty_validation_failure() -> Result<(), Report> {
hash, hash,
difficulty_threshold, difficulty_threshold,
Network::Mainnet, Network::Mainnet,
ExpandedDifficulty::target_difficulty_limit(Network::Mainnet), Network::Mainnet.target_difficulty_limit(),
); );
assert_eq!(expected, result); assert_eq!(expected, result);

View File

@ -42,7 +42,7 @@ use crate::{
TargetHeight::{self, *}, TargetHeight::{self, *},
}, },
error::BlockError, error::BlockError,
BoxError, BoxError, ParameterCheckpoint as _,
}; };
pub(crate) mod list; pub(crate) mod list;
@ -207,7 +207,7 @@ where
initial_tip: Option<(block::Height, block::Hash)>, initial_tip: Option<(block::Height, block::Hash)>,
state_service: S, state_service: S,
) -> Self { ) -> Self {
let checkpoint_list = CheckpointList::new(network); let checkpoint_list = network.checkpoint_list();
let max_height = checkpoint_list.max_height(); let max_height = checkpoint_list.max_height();
tracing::info!( tracing::info!(
?max_height, ?max_height,

View File

@ -17,7 +17,7 @@ use std::{
}; };
use zebra_chain::block; use zebra_chain::block;
use zebra_chain::parameters::{genesis_hash, Network}; use zebra_chain::parameters::Network;
/// The hard-coded checkpoints for mainnet, generated using the /// The hard-coded checkpoints for mainnet, generated using the
/// `zebra-checkpoints` tool. /// `zebra-checkpoints` tool.
@ -43,6 +43,47 @@ const MAINNET_CHECKPOINTS: &str = include_str!("main-checkpoints.txt");
/// information. /// information.
const TESTNET_CHECKPOINTS: &str = include_str!("test-checkpoints.txt"); const TESTNET_CHECKPOINTS: &str = include_str!("test-checkpoints.txt");
/// Network methods related to checkpoints
pub trait ParameterCheckpoint {
/// Returns the hash for the genesis block in `network`.
fn genesis_hash(&self) -> zebra_chain::block::Hash;
/// Returns the hard-coded checkpoint list for `network`.
fn checkpoint_list(&self) -> CheckpointList;
}
impl ParameterCheckpoint for Network {
fn genesis_hash(&self) -> zebra_chain::block::Hash {
match self {
// zcash-cli getblockhash 0
Network::Mainnet => "00040fe8ec8471911baa1db1266ea15dd06b4a8a5c453883c000b031973dce08",
// zcash-cli -testnet getblockhash 0
Network::Testnet => "05a60a92d99d85997cce3b87616c089f6124d7342af37106edc76126334a2c38",
}
.parse()
.expect("hard-coded hash parses")
}
fn checkpoint_list(&self) -> CheckpointList {
// parse calls CheckpointList::from_list
let checkpoint_list: CheckpointList = match self {
Network::Mainnet => MAINNET_CHECKPOINTS
.parse()
.expect("Hard-coded Mainnet checkpoint list parses and validates"),
Network::Testnet => TESTNET_CHECKPOINTS
.parse()
.expect("Hard-coded Testnet checkpoint list parses and validates"),
};
match checkpoint_list.hash(block::Height(0)) {
Some(hash) if hash == self.genesis_hash() => checkpoint_list,
Some(_) => {
panic!("The hard-coded genesis checkpoint does not match the network genesis hash")
}
None => unreachable!("Parser should have checked for a missing genesis checkpoint"),
}
}
}
/// A list of block height and hash checkpoints. /// A list of block height and hash checkpoints.
/// ///
/// Checkpoints should be chosen to avoid forks or chain reorganizations, /// Checkpoints should be chosen to avoid forks or chain reorganizations,
@ -81,27 +122,6 @@ impl FromStr for CheckpointList {
} }
impl CheckpointList { impl CheckpointList {
/// Returns the hard-coded checkpoint list for `network`.
pub fn new(network: Network) -> Self {
// parse calls CheckpointList::from_list
let checkpoint_list: CheckpointList = match network {
Network::Mainnet => MAINNET_CHECKPOINTS
.parse()
.expect("Hard-coded Mainnet checkpoint list parses and validates"),
Network::Testnet => TESTNET_CHECKPOINTS
.parse()
.expect("Hard-coded Testnet checkpoint list parses and validates"),
};
match checkpoint_list.hash(block::Height(0)) {
Some(hash) if hash == genesis_hash(network) => checkpoint_list,
Some(_) => {
panic!("The hard-coded genesis checkpoint does not match the network genesis hash")
}
None => unreachable!("Parser should have checked for a missing genesis checkpoint"),
}
}
/// Create a new checkpoint list for `network` from `checkpoint_list`. /// Create a new checkpoint list for `network` from `checkpoint_list`.
/// ///
/// Assumes that the provided genesis checkpoint is correct. /// Assumes that the provided genesis checkpoint is correct.
@ -123,8 +143,8 @@ impl CheckpointList {
// Check that the list starts with the correct genesis block // Check that the list starts with the correct genesis block
match checkpoints.iter().next() { match checkpoints.iter().next() {
Some((block::Height(0), hash)) Some((block::Height(0), hash))
if (hash == &genesis_hash(Network::Mainnet) if (hash == &Network::Mainnet.genesis_hash()
|| hash == &genesis_hash(Network::Testnet)) => {} || hash == &Network::Testnet.genesis_hash()) => {}
Some((block::Height(0), _)) => { Some((block::Height(0), _)) => {
Err("the genesis checkpoint does not match the Mainnet or Testnet genesis hash")? Err("the genesis checkpoint does not match the Mainnet or Testnet genesis hash")?
} }

View File

@ -235,8 +235,8 @@ fn checkpoint_list_load_hard_coded() -> Result<(), BoxError> {
.parse() .parse()
.expect("hard-coded Testnet checkpoint list should parse"); .expect("hard-coded Testnet checkpoint list should parse");
let _ = CheckpointList::new(Mainnet); let _ = Mainnet.checkpoint_list();
let _ = CheckpointList::new(Testnet); let _ = Testnet.checkpoint_list();
Ok(()) Ok(())
} }
@ -257,7 +257,7 @@ fn checkpoint_list_hard_coded_mandatory(network: Network) -> Result<(), BoxError
let mandatory_checkpoint = network.mandatory_checkpoint_height(); let mandatory_checkpoint = network.mandatory_checkpoint_height();
let list = CheckpointList::new(network); let list = network.checkpoint_list();
assert!( assert!(
list.max_height() >= mandatory_checkpoint, list.max_height() >= mandatory_checkpoint,
@ -292,7 +292,7 @@ fn checkpoint_list_hard_coded_max_gap(network: Network) -> Result<(), BoxError>
HeightDiff::try_from(div_ceil(MAX_CHECKPOINT_BYTE_COUNT, MAX_BLOCK_BYTES)) HeightDiff::try_from(div_ceil(MAX_CHECKPOINT_BYTE_COUNT, MAX_BLOCK_BYTES))
.expect("constant fits in HeightDiff"); .expect("constant fits in HeightDiff");
let list = CheckpointList::new(network); let list = network.checkpoint_list();
let mut heights = list.0.keys(); let mut heights = list.0.keys();
// Check that we start at the genesis height // Check that we start at the genesis height

View File

@ -49,18 +49,19 @@ pub use block::{
subsidy::{ subsidy::{
funding_streams::{ funding_streams::{
funding_stream_address, funding_stream_recipient_info, funding_stream_values, funding_stream_address, funding_stream_recipient_info, funding_stream_values,
height_for_first_halving, new_coinbase_script, new_coinbase_script,
}, },
general::miner_subsidy, general::miner_subsidy,
}, },
Request, VerifyBlockError, MAX_BLOCK_SIGOPS, Request, VerifyBlockError, MAX_BLOCK_SIGOPS,
}; };
pub use checkpoint::{ pub use checkpoint::{
CheckpointList, VerifyCheckpointError, MAX_CHECKPOINT_BYTE_COUNT, MAX_CHECKPOINT_HEIGHT_GAP, list::ParameterCheckpoint, CheckpointList, VerifyCheckpointError, MAX_CHECKPOINT_BYTE_COUNT,
MAX_CHECKPOINT_HEIGHT_GAP,
}; };
pub use config::Config; pub use config::Config;
pub use error::BlockError; pub use error::BlockError;
pub use parameters::FundingStreamReceiver; pub use parameters::{FundingStreamReceiver, ParameterSubsidy};
pub use primitives::{ed25519, groth16, halo2, redjubjub, redpallas}; pub use primitives::{ed25519, groth16, halo2, redjubjub, redpallas};
pub use router::RouterError; pub use router::RouterError;

View File

@ -7,7 +7,7 @@ use lazy_static::lazy_static;
use zebra_chain::{ use zebra_chain::{
amount::COIN, amount::COIN,
block::{Height, HeightDiff}, block::{Height, HeightDiff},
parameters::Network, parameters::{Network, NetworkUpgrade},
}; };
/// An initial period from Genesis to this Height where the block subsidy is gradually incremented. [What is slow-start mining][slow-mining] /// An initial period from Genesis to this Height where the block subsidy is gradually incremented. [What is slow-start mining][slow-mining]
@ -198,6 +198,38 @@ pub const FUNDING_STREAM_ECC_ADDRESSES_MAINNET: [&str; FUNDING_STREAMS_NUM_ADDRE
"t3XHAGxRP2FNfhAjxGjxbrQPYtQQjc3RCQD", "t3XHAGxRP2FNfhAjxGjxbrQPYtQQjc3RCQD",
]; ];
/// Functionality specific to block subsidy-related consensus rules
pub trait ParameterSubsidy {
/// Number of addresses for each funding stream in the Network.
/// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams
fn num_funding_streams(&self) -> usize;
/// Returns the minimum height after the first halving
/// as described in [protocol specification §7.10][7.10]
///
/// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams
fn height_for_first_halving(&self) -> Height;
}
/// 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,
Network::Testnet => 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`
// https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams
match self {
Network::Mainnet => NetworkUpgrade::Canopy
.activation_height(*self)
.expect("canopy activation height should be available"),
Network::Testnet => FIRST_HALVING_TESTNET,
}
}
}
/// List of addresses for the Zcash Foundation funding stream in the Mainnet. /// 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] = pub const FUNDING_STREAM_ZF_ADDRESSES_MAINNET: [&str; FUNDING_STREAMS_NUM_ADDRESSES_MAINNET] =
["t3dvVE3SQEi7kqNzwrfNePxZ1d4hUyztBA1"; FUNDING_STREAMS_NUM_ADDRESSES_MAINNET]; ["t3dvVE3SQEi7kqNzwrfNePxZ1d4hUyztBA1"; FUNDING_STREAMS_NUM_ADDRESSES_MAINNET];

View File

@ -36,7 +36,7 @@ use crate::{
block::{Request, SemanticBlockVerifier, VerifyBlockError}, block::{Request, SemanticBlockVerifier, VerifyBlockError},
checkpoint::{CheckpointList, CheckpointVerifier, VerifyCheckpointError}, checkpoint::{CheckpointList, CheckpointVerifier, VerifyCheckpointError},
error::TransactionError, error::TransactionError,
transaction, BoxError, Config, transaction, BoxError, Config, ParameterCheckpoint as _,
}; };
#[cfg(test)] #[cfg(test)]
@ -263,7 +263,7 @@ where
// > activation block hashes given in § 3.12 Mainnet and Testnet on p. 20. // > activation block hashes given in § 3.12 Mainnet and Testnet on p. 20.
// //
// <https://zips.z.cash/protocol/protocol.pdf#blockchain> // <https://zips.z.cash/protocol/protocol.pdf#blockchain>
let full_checkpoints = CheckpointList::new(network); let full_checkpoints = network.checkpoint_list();
let mut already_warned = false; let mut already_warned = false;
for (height, checkpoint_hash) in full_checkpoints.iter() { for (height, checkpoint_hash) in full_checkpoints.iter() {
@ -363,7 +363,7 @@ where
pub fn init_checkpoint_list(config: Config, network: Network) -> (CheckpointList, Height) { pub fn init_checkpoint_list(config: Config, network: Network) -> (CheckpointList, Height) {
// TODO: Zebra parses the checkpoint list three times at startup. // TODO: Zebra parses the checkpoint list three times at startup.
// Instead, cache the checkpoint list for each `network`. // Instead, cache the checkpoint list for each `network`.
let list = CheckpointList::new(network); let list = network.checkpoint_list();
let max_checkpoint_height = if config.checkpoint_sync { let max_checkpoint_height = if config.checkpoint_sync {
list.max_height() list.max_height()

View File

@ -163,7 +163,7 @@ impl Encoder<Message> for Codec {
let start_len = dst.len(); let start_len = dst.len();
{ {
let dst = &mut dst.writer(); let dst = &mut dst.writer();
dst.write_all(&Magic::from(self.builder.network).0[..])?; dst.write_all(&self.builder.network.magic_value().0[..])?;
dst.write_all(command)?; dst.write_all(command)?;
dst.write_u32::<LittleEndian>(body_length as u32)?; dst.write_u32::<LittleEndian>(body_length as u32)?;
@ -389,7 +389,7 @@ impl Decoder for Codec {
"read header from src buffer" "read header from src buffer"
); );
if magic != Magic::from(self.builder.network) { if magic != self.builder.network.magic_value() {
return Err(Parse("supplied magic did not meet expectations")); return Err(Parse("supplied magic did not meet expectations"));
} }
if body_len > self.builder.max_len { if body_len > self.builder.max_len {

View File

@ -23,11 +23,13 @@ impl fmt::Debug for Magic {
f.debug_tuple("Magic").field(&hex::encode(self.0)).finish() f.debug_tuple("Magic").field(&hex::encode(self.0)).finish()
} }
} }
pub(crate) trait ParameterMagic {
impl From<Network> for Magic { fn magic_value(&self) -> Magic;
}
impl ParameterMagic for Network {
/// Get the magic value associated to this `Network`. /// Get the magic value associated to this `Network`.
fn from(network: Network) -> Self { fn magic_value(&self) -> Magic {
match network { match self {
Network::Mainnet => magics::MAINNET, Network::Mainnet => magics::MAINNET,
Network::Testnet => magics::TESTNET, Network::Testnet => magics::TESTNET,
} }

View File

@ -519,7 +519,7 @@ where
// //
// Get the network upgrades in height order, like `zcashd`. // Get the network upgrades in height order, like `zcashd`.
let mut upgrades = IndexMap::new(); let mut upgrades = IndexMap::new();
for (activation_height, network_upgrade) in NetworkUpgrade::activation_list(network) { for (activation_height, network_upgrade) in network.activation_list() {
// Zebra defines network upgrades based on incompatible consensus rule changes, // Zebra defines network upgrades based on incompatible consensus rule changes,
// but zcashd defines them based on ZIPs. // but zcashd defines them based on ZIPs.
// //

View File

@ -20,10 +20,10 @@ use zebra_chain::{
transparent::{ transparent::{
self, EXTRA_ZEBRA_COINBASE_DATA, MAX_COINBASE_DATA_LEN, MAX_COINBASE_HEIGHT_DATA_LEN, self, EXTRA_ZEBRA_COINBASE_DATA, MAX_COINBASE_DATA_LEN, MAX_COINBASE_HEIGHT_DATA_LEN,
}, },
work::difficulty::{ExpandedDifficulty, U256}, work::difficulty::{ParameterDifficulty as _, U256},
}; };
use zebra_consensus::{ use zebra_consensus::{
funding_stream_address, funding_stream_values, height_for_first_halving, miner_subsidy, funding_stream_address, funding_stream_values, miner_subsidy, ParameterSubsidy as _,
RouterError, RouterError,
}; };
use zebra_network::AddressBookPeers; use zebra_network::AddressBookPeers;
@ -1098,7 +1098,7 @@ where
best_chain_tip_height(&latest_chain_tip)? best_chain_tip_height(&latest_chain_tip)?
}; };
if height < height_for_first_halving(network) { if height < network.height_for_first_halving() {
return Err(Error { return Err(Error {
code: ErrorCode::ServerError(0), code: ErrorCode::ServerError(0),
message: "Zebra does not support founders' reward subsidies, \ message: "Zebra does not support founders' reward subsidies, \
@ -1197,7 +1197,7 @@ where
// using this calculation.) // using this calculation.)
// Get expanded difficulties (256 bits), these are the inverse of the work // Get expanded difficulties (256 bits), these are the inverse of the work
let pow_limit: U256 = ExpandedDifficulty::target_difficulty_limit(network).into(); let pow_limit: U256 = network.target_difficulty_limit().into();
let difficulty: U256 = chain_info let difficulty: U256 = chain_info
.expected_difficulty .expected_difficulty
.to_expanded() .to_expanded()

View File

@ -23,7 +23,7 @@ use zebra_chain::{
serialization::{DateTime32, ZcashDeserializeInto}, serialization::{DateTime32, ZcashDeserializeInto},
transaction::Transaction, transaction::Transaction,
transparent, transparent,
work::difficulty::{CompactDifficulty, ExpandedDifficulty}, work::difficulty::{CompactDifficulty, ParameterDifficulty as _},
}; };
use zebra_network::{address_book_peers::MockAddressBookPeers, types::MetaAddr}; use zebra_network::{address_book_peers::MockAddressBookPeers, types::MetaAddr};
use zebra_node_services::mempool; use zebra_node_services::mempool;
@ -115,7 +115,7 @@ pub async fn test_responses<State, ReadState>(
let fake_max_time = DateTime32::from(1654008728); let fake_max_time = DateTime32::from(1654008728);
// Use a valid fractional difficulty for snapshots // Use a valid fractional difficulty for snapshots
let pow_limit = ExpandedDifficulty::target_difficulty_limit(network); let pow_limit = network.target_difficulty_limit();
let fake_difficulty = pow_limit * 2 / 3; let fake_difficulty = pow_limit * 2 / 3;
let fake_difficulty = CompactDifficulty::from(fake_difficulty); let fake_difficulty = CompactDifficulty::from(fake_difficulty);

View File

@ -1658,7 +1658,7 @@ async fn rpc_getdifficulty() {
chain_sync_status::MockSyncStatus, chain_sync_status::MockSyncStatus,
chain_tip::mock::MockChainTip, chain_tip::mock::MockChainTip,
serialization::DateTime32, serialization::DateTime32,
work::difficulty::{CompactDifficulty, ExpandedDifficulty, U256}, work::difficulty::{CompactDifficulty, ExpandedDifficulty, ParameterDifficulty as _, U256},
}; };
use zebra_network::address_book_peers::MockAddressBookPeers; use zebra_network::address_book_peers::MockAddressBookPeers;
@ -1742,7 +1742,7 @@ async fn rpc_getdifficulty() {
assert_eq!(format!("{:.9}", get_difficulty.unwrap()), "0.000122072"); assert_eq!(format!("{:.9}", get_difficulty.unwrap()), "0.000122072");
// Fake the ChainInfo response: difficulty limit - smallest valid difficulty // Fake the ChainInfo response: difficulty limit - smallest valid difficulty
let pow_limit = ExpandedDifficulty::target_difficulty_limit(Mainnet); let pow_limit = Mainnet.target_difficulty_limit();
let fake_difficulty = pow_limit.into(); let fake_difficulty = pow_limit.into();
let mut read_state2 = read_state.clone(); let mut read_state2 = read_state.clone();
let mock_read_state_request_handler = async move { let mock_read_state_request_handler = async move {

View File

@ -277,9 +277,7 @@ fn difficulty_threshold_and_time_are_valid(
// of that block plus 90*60 seconds. // of that block plus 90*60 seconds.
// //
// https://zips.z.cash/protocol/protocol.pdf#blockheader // https://zips.z.cash/protocol/protocol.pdf#blockheader
if NetworkUpgrade::is_max_block_time_enforced(network, candidate_height) if network.is_max_block_time_enforced(candidate_height) && candidate_time > block_time_max {
&& candidate_time > block_time_max
{
Err(ValidateContextError::TimeTooLate { Err(ValidateContextError::TimeTooLate {
candidate_time, candidate_time,
block_time_max, block_time_max,

View File

@ -15,8 +15,8 @@ use zebra_chain::{
parameters::Network, parameters::Network,
parameters::NetworkUpgrade, parameters::NetworkUpgrade,
parameters::POW_AVERAGING_WINDOW, parameters::POW_AVERAGING_WINDOW,
work::difficulty::ExpandedDifficulty,
work::difficulty::{CompactDifficulty, U256}, work::difficulty::{CompactDifficulty, U256},
work::difficulty::{ExpandedDifficulty, ParameterDifficulty as _},
}; };
/// The median block span for time median calculations. /// The median block span for time median calculations.
@ -188,7 +188,7 @@ impl AdjustedDifficulty {
Network::Testnet, Network::Testnet,
"invalid network: the minimum difficulty rule only applies on testnet" "invalid network: the minimum difficulty rule only applies on testnet"
); );
ExpandedDifficulty::target_difficulty_limit(self.network).to_compact() self.network.target_difficulty_limit().to_compact()
} else { } else {
self.threshold_bits() self.threshold_bits()
} }
@ -210,10 +210,7 @@ impl AdjustedDifficulty {
let threshold = (self.mean_target_difficulty() / averaging_window_timespan.num_seconds()) let threshold = (self.mean_target_difficulty() / averaging_window_timespan.num_seconds())
* self.median_timespan_bounded().num_seconds(); * self.median_timespan_bounded().num_seconds();
let threshold = min( let threshold = min(self.network.target_difficulty_limit(), threshold);
ExpandedDifficulty::target_difficulty_limit(self.network),
threshold,
);
threshold.to_compact() threshold.to_compact()
} }

View File

@ -21,8 +21,8 @@ use tower::{
use zebra_chain::{ use zebra_chain::{
block::{self, Height, HeightDiff}, block::{self, Height, HeightDiff},
chain_tip::ChainTip, chain_tip::ChainTip,
parameters::genesis_hash,
}; };
use zebra_consensus::ParameterCheckpoint as _;
use zebra_network as zn; use zebra_network as zn;
use zebra_state as zs; use zebra_state as zs;
@ -500,7 +500,7 @@ where
)); ));
let new_syncer = Self { let new_syncer = Self {
genesis_hash: genesis_hash(config.network.network), genesis_hash: config.network.network.genesis_hash(),
max_checkpoint_height, max_checkpoint_height,
checkpoint_verify_concurrency_limit, checkpoint_verify_concurrency_limit,
full_verify_concurrency_limit, full_verify_concurrency_limit,

View File

@ -16,7 +16,7 @@ use zebra_chain::{
fmt::humantime_seconds, fmt::humantime_seconds,
parameters::{Network, NetworkUpgrade, POST_BLOSSOM_POW_TARGET_SPACING}, parameters::{Network, NetworkUpgrade, POST_BLOSSOM_POW_TARGET_SPACING},
}; };
use zebra_consensus::CheckpointList; use zebra_consensus::ParameterCheckpoint as _;
use zebra_state::MAX_BLOCK_REORG_HEIGHT; use zebra_state::MAX_BLOCK_REORG_HEIGHT;
use crate::components::sync::SyncStatus; use crate::components::sync::SyncStatus;
@ -82,7 +82,8 @@ pub async fn show_block_chain_progress(
// The minimum height of the valid best chain, based on: // The minimum height of the valid best chain, based on:
// - the hard-coded checkpoint height, // - the hard-coded checkpoint height,
// - the minimum number of blocks after the highest checkpoint. // - the minimum number of blocks after the highest checkpoint.
let after_checkpoint_height = CheckpointList::new(network) let after_checkpoint_height = network
.checkpoint_list()
.max_height() .max_height()
.add(min_after_checkpoint_blocks) .add(min_after_checkpoint_blocks)
.expect("hard-coded checkpoint height is far below Height::MAX"); .expect("hard-coded checkpoint height is far below Height::MAX");

View File

@ -5,7 +5,7 @@ use std::time::Duration;
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use zebra_chain::{block::Height, chain_tip::mock::MockChainTip, parameters::Network}; use zebra_chain::{block::Height, chain_tip::mock::MockChainTip, parameters::Network};
use zebra_consensus::CheckpointList; use zebra_consensus::ParameterCheckpoint as _;
use zebrad::components::sync::end_of_support::{self, EOS_PANIC_AFTER, ESTIMATED_RELEASE_HEIGHT}; use zebrad::components::sync::end_of_support::{self, EOS_PANIC_AFTER, ESTIMATED_RELEASE_HEIGHT};
// Estimated blocks per day with the current 75 seconds block spacing. // Estimated blocks per day with the current 75 seconds block spacing.
@ -54,7 +54,7 @@ fn end_of_support_function() {
#[tracing_test::traced_test] #[tracing_test::traced_test]
fn end_of_support_date() { fn end_of_support_date() {
// Get the list of checkpoints. // Get the list of checkpoints.
let list = CheckpointList::new(Network::Mainnet); let list = Network::Mainnet.checkpoint_list();
// Get the last one we have and use it as tip. // Get the last one we have and use it as tip.
let higher_checkpoint = list.max_height(); let higher_checkpoint = list.max_height();