Compare commits

...

11 Commits

Author SHA1 Message Date
Marek 3e90c4fa72
Merge 8fca642d39 into e5d4d739b7 2024-04-25 14:46:19 +02:00
Arya e5d4d739b7
add(consensus/network): Adds `new_regtest()` constructors to `testnet::Parameters` and `Network` (#8413)
* minor cleanup and rename

* Adds an empty NetworkParameters struct to Network::Testnet variant, updates production code.

* Updates tests

* Adds `NetworkKind` and uses it instead of `Network` in `HistoryTreeParts` and `transparent::Address`

* Adds a [network.testnet_parameters] section to the config, uses `NetworkKind` as zebra_network::Config::network field type, and converts 'Network' to `NetworkKind` before serializing

* Applies some suggestions from code review

* Applies suggestions from code review

* returns b58 prefix constants directly to remove From<NetworkKind> impl for zcash_primitives::consensus::Network

* Applies more suggestions from code review.

* moves conversions to zcash_primitives::consensus::Network to where they're used.

* Apply suggestions from code review

Co-authored-by: Marek <mail@marek.onl>

* rename `network` variables and method names typed as NetworkKind to `network_kind`

* use only test block heights for the network associated with them

* Applies more suggestions from code review.

* Rename `NetworkParameters` to `Parameters` and move it a new `testnet` module

* adds activation heights field

* updates stored test config, adds a quicker test for checking that stored configs can be parsed, adds an intermediate representation of activation heights

* implement Parameters for Network

* Passes &Network directly instead of converting to zp_consensus::Network where there were conversions

* fixes a bad merge (removes a network conversion in zcash_note_encryption)

* Adds a test for the Parameters impl for zebra_chain::Network

* fixes doc links

* - Makes the `activation_heights` config field optional by adding a #[serde(default)]
- Panics if a non-zero activation height is provided for the `Genesis` network upgrade
- Always sets the `Genesis` and `BeforeOverwinter` network upgrade activation heights to 0 and 1, `BeforeOverwinter` could be overwritten by a later network upgrade
- Makes the `activation_heights` field on `Parameters` private, adds/uses an accessor method instead, and adds a builder struct and `build()` method

* small refactor of activation_heights() method

* check that activation heights are in the right order

* Updates `NetworkUpgrade::activation_height()` to return the height of the next NetworkUpgrade if it doesn't find the activation height of `&self`

* checks that the miner address is of TestnetKind on Regtest and update assertion message

* Adds a DNetworkUpgradeActivationHeights struct for better control over how activation heights can be configured

* moves all ordered network upgrades to a constant, adds a no_duplicates test, moves struct with activation heights outside deserialization impl and accepts it in `ParametersBuilder::activation_heights()` instead of a Vec

* panics if any network upgrades are configured to activate at Height(0) because it's reserved for Genesis

* Simplifies the `ParametersBuilder::activation_heights()` method and removes an unnecessary test.

* Adds Sapling HRPs as fields on testnet params. (#8398)

* Applies suggestions from code review.

* Update zebra-chain/src/parameters/network_upgrade.rs

Co-authored-by: Marek <mail@marek.onl>

* Adds `network_name` field and accessor method on `Parameters`, uses it in the `Display` impl for `Network`

* Removes unnecessary `.filter()`

* adds constraints on valid network names and a test

* adds config field for setting network name, adds "with_" prefix to ParameterBuilder setter methods

* Adds `MainnetKind`, `TestnetKind`, and `RegtestKind` to reserved network names

* updates stored test configs and fixes `network_name()` docs

* Adds a `new_regtest()` method on `Network` and `testnet::Parameters`, updates config deserialization to return an error if the initial_testnet_peers include any default initial peers AND contain configured activation heights

* Updates `activates_network_upgrades_correctly` test to check Regtest activation heights (and default Mainnet/Testnet)

* Refactors if-let & match statement into general match statement, removes constraint against including `testnet_parameters` field/section in the config when using `Mainnet` or `Regtest`

* Removes outdated TODO

* Restores `testnet_parameters` section of the latest stored config.

* Adds `with_sapling_hrps()` method and uses it to set the Regtest HRPs in `new_regtest()`.

Adds a test for Sapling HRP validation

* Checks that default Mainnet/Testnet/Regtest Sapling HRPs pass validation in `with_sapling_hrps()`

* Uses the correct constant in test

* Adds `is_regtest()` methods

* Updates test docs

* drop custom panic hooks after expected panics

---------

Co-authored-by: Marek <mail@marek.onl>
2024-04-25 04:04:05 +00:00
Marek 8fca642d39
Merge branch 'main' into impl-Params-from-zcash-protocol 2024-04-24 19:33:44 +00:00
Marek 8a7dcedca7 Merge branch 'main' into impl-Params-from-zcash-protocol 2024-04-24 17:30:23 +02:00
Marek 0aa9c69298 Remove a redundant `impl From` 2024-04-24 16:45:12 +02:00
Marek 78ed227a2a Merge branch 'main' into impl-Params-from-zcash-protocol 2024-04-22 21:49:40 +02:00
Marek 98a136af98 Move `mod address` from under the gbt feature 2024-04-22 21:47:41 +02:00
Marek ba6c0d7110 Impl `zcash_protocol::consensus::Parameters` 2024-04-22 20:22:07 +02:00
Marek 3084f3bb69 Impl conversions between network upgrades 2024-04-22 20:20:58 +02:00
Marek 9a45ada2a8 Impl and use conversion between network kinds
These conversions will make implementing
`zcash_protocol::consensus::Parameters` for `Network` easier.
2024-04-22 20:17:31 +02:00
Marek 09920d045e Use full paths for types from `zcash_primitives`
Upcoming commits will use `zcash_protocol::consensus::Parameters`, which
would make names such as `zp_consensus::Parameters` ambiguous since such
a name could stand for both `zcash_primitives...` and
`zcash_protocol...`.
2024-04-22 17:58:19 +02:00
10 changed files with 405 additions and 83 deletions

View File

@ -5898,6 +5898,7 @@ dependencies = [
"zcash_history",
"zcash_note_encryption",
"zcash_primitives",
"zcash_protocol",
"zebra-test",
]

View File

@ -98,6 +98,8 @@ zcash_encoding = "0.2.0"
zcash_history = "0.4.0"
zcash_note_encryption = "0.4.0"
zcash_primitives = { version = "0.13.0", features = ["transparent-inputs"] }
zcash_protocol = { version = "0.1.1" }
zcash_address = { version = "0.3.2" }
# Time
chrono = { version = "0.4.38", default-features = false, features = ["clock", "std", "serde"] }
@ -131,8 +133,6 @@ serde_json = { version = "1.0.116", optional = true }
# Production feature async-error and testing feature proptest-impl
tokio = { version = "1.37.0", optional = true }
zcash_address = { version = "0.3.2" }
# Experimental feature shielded-scan
zcash_client_backend = { version = "0.10.0-rc.1", optional = true }

View File

@ -4,13 +4,13 @@ use std::{fmt, str::FromStr, sync::Arc};
use thiserror::Error;
use zcash_primitives::{consensus as zp_consensus, constants as zp_constants};
use crate::{
block::{self, Height, HeightDiff},
parameters::NetworkUpgrade,
};
use self::testnet::ConfiguredActivationHeights;
pub mod testnet;
#[cfg(test)]
@ -94,8 +94,10 @@ impl NetworkKind {
/// pay-to-public-key-hash payment addresses for the network.
pub fn b58_pubkey_address_prefix(self) -> [u8; 2] {
match self {
Self::Mainnet => zp_constants::mainnet::B58_PUBKEY_ADDRESS_PREFIX,
Self::Testnet | Self::Regtest => zp_constants::testnet::B58_PUBKEY_ADDRESS_PREFIX,
Self::Mainnet => zcash_primitives::constants::mainnet::B58_PUBKEY_ADDRESS_PREFIX,
Self::Testnet | Self::Regtest => {
zcash_primitives::constants::testnet::B58_PUBKEY_ADDRESS_PREFIX
}
}
}
@ -103,8 +105,10 @@ impl NetworkKind {
/// payment addresses for the network.
pub fn b58_script_address_prefix(self) -> [u8; 2] {
match self {
Self::Mainnet => zp_constants::mainnet::B58_SCRIPT_ADDRESS_PREFIX,
Self::Testnet | Self::Regtest => zp_constants::testnet::B58_SCRIPT_ADDRESS_PREFIX,
Self::Mainnet => zcash_primitives::constants::mainnet::B58_SCRIPT_ADDRESS_PREFIX,
Self::Testnet | Self::Regtest => {
zcash_primitives::constants::testnet::B58_SCRIPT_ADDRESS_PREFIX
}
}
}
@ -142,8 +146,6 @@ impl<'a> From<&'a Network> for &'a str {
fn from(network: &'a Network) -> &'a str {
match network {
Network::Mainnet => "Mainnet",
// TODO:
// - zcashd calls the Regtest cache dir 'regtest' (#8327).
Network::Testnet(params) => params.network_name(),
}
}
@ -166,6 +168,11 @@ impl Network {
Self::Testnet(Arc::new(params))
}
/// Creates a new [`Network::Testnet`] with `Regtest` parameters and the provided network upgrade activation heights.
pub fn new_regtest(activation_heights: ConfiguredActivationHeights) -> Self {
Self::new_configured_testnet(testnet::Parameters::new_regtest(activation_heights))
}
/// Returns true if the network is the default Testnet, or false otherwise.
pub fn is_default_testnet(&self) -> bool {
if let Self::Testnet(params) = self {
@ -175,6 +182,15 @@ impl Network {
}
}
/// Returns true if the network is Regtest, or false otherwise.
pub fn is_regtest(&self) -> bool {
if let Self::Testnet(params) = self {
params.is_regtest()
} else {
false
}
}
/// Returns the [`NetworkKind`] for this network.
pub fn kind(&self) -> NetworkKind {
match self {
@ -274,32 +290,23 @@ impl FromStr for Network {
#[error("Invalid network: {0}")]
pub struct InvalidNetworkError(String);
impl zp_consensus::Parameters for Network {
impl zcash_primitives::consensus::Parameters for Network {
fn activation_height(
&self,
nu: zcash_primitives::consensus::NetworkUpgrade,
) -> Option<zcash_primitives::consensus::BlockHeight> {
let target_nu = match nu {
zp_consensus::NetworkUpgrade::Overwinter => NetworkUpgrade::Overwinter,
zp_consensus::NetworkUpgrade::Sapling => NetworkUpgrade::Sapling,
zp_consensus::NetworkUpgrade::Blossom => NetworkUpgrade::Blossom,
zp_consensus::NetworkUpgrade::Heartwood => NetworkUpgrade::Heartwood,
zp_consensus::NetworkUpgrade::Canopy => NetworkUpgrade::Canopy,
zp_consensus::NetworkUpgrade::Nu5 => NetworkUpgrade::Nu5,
};
// Heights are hard-coded below Height::MAX or checked when the config is parsed.
target_nu
NetworkUpgrade::from(nu)
.activation_height(self)
.map(|Height(h)| zp_consensus::BlockHeight::from_u32(h))
.map(|Height(h)| zcash_primitives::consensus::BlockHeight::from_u32(h))
}
fn coin_type(&self) -> u32 {
match self {
Network::Mainnet => zp_constants::mainnet::COIN_TYPE,
Network::Mainnet => zcash_primitives::constants::mainnet::COIN_TYPE,
// The regtest cointype reuses the testnet cointype,
// See <https://github.com/satoshilabs/slips/blob/master/slip-0044.md>
Network::Testnet(_) => zp_constants::testnet::COIN_TYPE,
Network::Testnet(_) => zcash_primitives::constants::testnet::COIN_TYPE,
}
}
@ -313,21 +320,25 @@ impl zp_consensus::Parameters for Network {
fn hrp_sapling_extended_spending_key(&self) -> &str {
match self {
Network::Mainnet => zp_constants::mainnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
Network::Mainnet => {
zcash_primitives::constants::mainnet::HRP_SAPLING_EXTENDED_SPENDING_KEY
}
Network::Testnet(params) => params.hrp_sapling_extended_spending_key(),
}
}
fn hrp_sapling_extended_full_viewing_key(&self) -> &str {
match self {
Network::Mainnet => zp_constants::mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
Network::Mainnet => {
zcash_primitives::constants::mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY
}
Network::Testnet(params) => params.hrp_sapling_extended_full_viewing_key(),
}
}
fn hrp_sapling_payment_address(&self) -> &str {
match self {
Network::Mainnet => zp_constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS,
Network::Mainnet => zcash_primitives::constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS,
Network::Testnet(params) => params.hrp_sapling_payment_address(),
}
}
@ -340,3 +351,18 @@ impl zp_consensus::Parameters for Network {
self.kind().b58_script_address_prefix()
}
}
impl zcash_protocol::consensus::Parameters for Network {
fn network_type(&self) -> zcash_address::Network {
self.kind().into()
}
fn activation_height(
&self,
nu: zcash_protocol::consensus::NetworkUpgrade,
) -> Option<zcash_protocol::consensus::BlockHeight> {
NetworkUpgrade::from(nu)
.activation_height(self)
.map(|Height(h)| zcash_protocol::consensus::BlockHeight::from_u32(h))
}
}

View File

@ -24,6 +24,9 @@ pub const RESERVED_NETWORK_NAMES: [&str; 6] = [
/// Maximum length for a configured network name.
pub const MAX_NETWORK_NAME_LENGTH: usize = 30;
/// Maximum length for a configured human-readable prefix.
pub const MAX_HRP_LENGTH: usize = 30;
/// Configurable activation heights for Regtest and configured Testnets.
#[derive(Deserialize, Default)]
#[serde(rename_all = "PascalCase")]
@ -67,13 +70,7 @@ impl Default for ParametersBuilder {
// # Correctness
//
// `Genesis` network upgrade activation height must always be 0
activation_heights: [
(Height(0), NetworkUpgrade::Genesis),
// TODO: Find out if `BeforeOverwinter` must always be active at Height(1), remove it here if it's not required.
(Height(1), NetworkUpgrade::BeforeOverwinter),
]
.into_iter()
.collect(),
activation_heights: TESTNET_ACTIVATION_HEIGHTS.iter().cloned().collect(),
hrp_sapling_extended_spending_key:
zp_constants::testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY.to_string(),
hrp_sapling_extended_full_viewing_key:
@ -109,6 +106,44 @@ impl ParametersBuilder {
self
}
/// Checks that the provided Sapling human-readable prefixes (HRPs) are valid and unique, then
/// sets the Sapling HRPs to be used in the [`Parameters`] being built.
pub fn with_sapling_hrps(
mut self,
hrp_sapling_extended_spending_key: impl fmt::Display,
hrp_sapling_extended_full_viewing_key: impl fmt::Display,
hrp_sapling_payment_address: impl fmt::Display,
) -> Self {
self.hrp_sapling_extended_spending_key = hrp_sapling_extended_spending_key.to_string();
self.hrp_sapling_extended_full_viewing_key =
hrp_sapling_extended_full_viewing_key.to_string();
self.hrp_sapling_payment_address = hrp_sapling_payment_address.to_string();
let sapling_hrps = [
&self.hrp_sapling_extended_spending_key,
&self.hrp_sapling_extended_full_viewing_key,
&self.hrp_sapling_payment_address,
];
for sapling_hrp in sapling_hrps {
assert!(sapling_hrp.len() <= MAX_HRP_LENGTH, "Sapling human-readable prefix {sapling_hrp} is too long, must be {MAX_HRP_LENGTH} characters or less");
assert!(
sapling_hrp.chars().all(|c| c.is_ascii_lowercase() || c == '-'),
"human-readable prefixes should contain only lowercase ASCII characters and dashes, hrp: {sapling_hrp}"
);
assert_eq!(
sapling_hrps
.iter()
.filter(|&&hrp| hrp == sapling_hrp)
.count(),
1,
"Sapling human-readable prefixes must be unique, repeated Sapling HRP: {sapling_hrp}"
);
}
self
}
/// Checks that the provided network upgrade activation heights are in the correct order, then
/// sets them as the new network upgrade activation heights.
pub fn with_activation_heights(
@ -166,6 +201,7 @@ impl ParametersBuilder {
// # Correctness
//
// Height(0) must be reserved for the `NetworkUpgrade::Genesis`.
// TODO: Find out if `BeforeOverwinter` must always be active at Height(1), remove it here if it's not required.
self.activation_heights.split_off(&Height(2));
self.activation_heights.extend(activation_heights);
@ -220,7 +256,6 @@ impl Default for Parameters {
fn default() -> Self {
Self {
network_name: "Testnet".to_string(),
activation_heights: TESTNET_ACTIVATION_HEIGHTS.iter().cloned().collect(),
..Self::build().finish()
}
}
@ -232,11 +267,46 @@ impl Parameters {
ParametersBuilder::default()
}
/// Accepts a [`ConfiguredActivationHeights`].
///
/// Creates an instance of [`Parameters`] with `Regtest` values.
pub fn new_regtest(activation_heights: ConfiguredActivationHeights) -> Self {
Self {
network_name: "Regtest".to_string(),
..Self::build()
.with_sapling_hrps(
zp_constants::regtest::HRP_SAPLING_EXTENDED_SPENDING_KEY,
zp_constants::regtest::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
zp_constants::regtest::HRP_SAPLING_PAYMENT_ADDRESS,
)
// Removes default Testnet activation heights if not configured,
// most network upgrades are disabled by default for Regtest in zcashd
.with_activation_heights(activation_heights)
.finish()
}
}
/// Returns true if the instance of [`Parameters`] represents the default public Testnet.
pub fn is_default_testnet(&self) -> bool {
self == &Self::default()
}
/// Returns true if the instance of [`Parameters`] represents Regtest.
pub fn is_regtest(&self) -> bool {
let Self {
network_name,
hrp_sapling_extended_spending_key,
hrp_sapling_extended_full_viewing_key,
hrp_sapling_payment_address,
..
} = Self::new_regtest(ConfiguredActivationHeights::default());
self.network_name == network_name
&& self.hrp_sapling_extended_spending_key == hrp_sapling_extended_spending_key
&& self.hrp_sapling_extended_full_viewing_key == hrp_sapling_extended_full_viewing_key
&& self.hrp_sapling_payment_address == hrp_sapling_payment_address
}
/// Returns the network name
pub fn network_name(&self) -> &str {
&self.network_name

View File

@ -1,14 +1,19 @@
//! Fixed test vectors for the network consensus parameters.
use zcash_primitives::consensus::{self as zp_consensus, Parameters};
use zcash_primitives::{
consensus::{self as zp_consensus, Parameters},
constants as zp_constants,
};
use crate::{
block::Height,
parameters::{
testnet::{
self, ConfiguredActivationHeights, MAX_NETWORK_NAME_LENGTH, RESERVED_NETWORK_NAMES,
self, ConfiguredActivationHeights, MAX_HRP_LENGTH, MAX_NETWORK_NAME_LENGTH,
RESERVED_NETWORK_NAMES,
},
Network, NetworkUpgrade, NETWORK_UPGRADES_IN_ORDER,
Network, NetworkUpgrade, MAINNET_ACTIVATION_HEIGHTS, NETWORK_UPGRADES_IN_ORDER,
TESTNET_ACTIVATION_HEIGHTS,
},
};
@ -93,8 +98,9 @@ fn check_parameters_impl() {
}
/// Checks that `NetworkUpgrade::activation_height()` returns the activation height of the next
/// network upgrade if it doesn't find an activation height for a prior network upgrade, and that the
/// `Genesis` upgrade is always at `Height(0)`.
/// network upgrade if it doesn't find an activation height for a prior network upgrade, that the
/// `Genesis` upgrade is always at `Height(0)`, and that the default Mainnet/Testnet/Regtest activation
/// heights are what's expected.
#[test]
fn activates_network_upgrades_correctly() {
let expected_activation_height = 1;
@ -126,6 +132,164 @@ fn activates_network_upgrades_correctly() {
should match NU5 activation height, network_upgrade: {nu}, activation_height: {activation_height:?}"
);
}
let expected_default_regtest_activation_heights = &[
(Height(0), NetworkUpgrade::Genesis),
(Height(1), NetworkUpgrade::BeforeOverwinter),
];
for (network, expected_activation_heights) in [
(Network::Mainnet, MAINNET_ACTIVATION_HEIGHTS),
(Network::new_default_testnet(), TESTNET_ACTIVATION_HEIGHTS),
(
Network::new_regtest(Default::default()),
expected_default_regtest_activation_heights,
),
] {
assert_eq!(
network.activation_list(),
expected_activation_heights.iter().cloned().collect(),
"network activation list should match expected activation heights"
);
}
}
/// Checks that configured testnet names are validated and used correctly.
#[test]
fn check_configured_network_name() {
// Sets a no-op panic hook to avoid long output.
std::panic::set_hook(Box::new(|_| {}));
// Checks that reserved network names cannot be used for configured testnets.
for reserved_network_name in RESERVED_NETWORK_NAMES {
std::panic::catch_unwind(|| {
testnet::Parameters::build().with_network_name(reserved_network_name)
})
.expect_err("should panic when attempting to set network name as a reserved name");
}
// Check that max length is enforced, and that network names may only contain alphanumeric characters and '_'.
for invalid_network_name in [
"a".repeat(MAX_NETWORK_NAME_LENGTH + 1),
"!!!!non-alphanumeric-name".to_string(),
] {
std::panic::catch_unwind(|| {
testnet::Parameters::build().with_network_name(invalid_network_name)
})
.expect_err("should panic when setting network name that's too long or contains non-alphanumeric characters (except '_')");
}
drop(std::panic::take_hook());
// Checks that network names are displayed correctly
assert_eq!(
Network::new_default_testnet().to_string(),
"Testnet",
"default testnet should be displayed as 'Testnet'"
);
assert_eq!(
Network::Mainnet.to_string(),
"Mainnet",
"Mainnet should be displayed as 'Mainnet'"
);
assert_eq!(
Network::new_regtest(Default::default()).to_string(),
"Regtest",
"Regtest should be displayed as 'Regtest'"
);
// Check that network name can contain alphanumeric characters and '_'.
let expected_name = "ConfiguredTestnet_1";
let network = testnet::Parameters::build()
// Check that network name can contain `MAX_NETWORK_NAME_LENGTH` characters
.with_network_name("a".repeat(MAX_NETWORK_NAME_LENGTH))
.with_network_name(expected_name)
.to_network();
// Check that configured network name is displayed
assert_eq!(
network.to_string(),
expected_name,
"network must be displayed as configured network name"
);
}
/// Checks that configured Sapling human-readable prefixes (HRPs) are validated and used correctly.
#[test]
fn check_configured_sapling_hrps() {
// Sets a no-op panic hook to avoid long output.
std::panic::set_hook(Box::new(|_| {}));
// Check that configured Sapling HRPs must be unique.
std::panic::catch_unwind(|| {
testnet::Parameters::build().with_sapling_hrps("", "", "");
})
.expect_err("should panic when setting non-unique Sapling HRPs");
// Check that max length is enforced, and that network names may only contain lowecase ASCII characters and dashes.
for invalid_hrp in [
"a".repeat(MAX_NETWORK_NAME_LENGTH + 1),
"!!!!non-alphabetical-name".to_string(),
"A".to_string(),
] {
std::panic::catch_unwind(|| {
testnet::Parameters::build().with_sapling_hrps(invalid_hrp, "dummy-hrp-a", "dummy-hrp-b");
})
.expect_err("should panic when setting Sapling HRPs that are too long or contain non-alphanumeric characters (except '-')");
}
drop(std::panic::take_hook());
// Check that Sapling HRPs can contain lowercase ascii characters and dashes.
let expected_hrp_sapling_extended_spending_key = "sapling-hrp-a";
let expected_hrp_sapling_extended_full_viewing_key = "sapling-hrp-b";
let expected_hrp_sapling_payment_address = "sapling-hrp-c";
let network = testnet::Parameters::build()
// Check that Sapling HRPs can contain `MAX_HRP_LENGTH` characters
.with_sapling_hrps("a".repeat(MAX_HRP_LENGTH), "dummy-hrp-a", "dummy-hrp-b")
.with_sapling_hrps(
expected_hrp_sapling_extended_spending_key,
expected_hrp_sapling_extended_full_viewing_key,
expected_hrp_sapling_payment_address,
)
.to_network();
// Check that configured Sapling HRPs are returned by `Parameters` trait methods
assert_eq!(
network.hrp_sapling_extended_spending_key(),
expected_hrp_sapling_extended_spending_key,
"should return expected Sapling extended spending key HRP"
);
assert_eq!(
network.hrp_sapling_extended_full_viewing_key(),
expected_hrp_sapling_extended_full_viewing_key,
"should return expected Sapling EFVK HRP"
);
assert_eq!(
network.hrp_sapling_payment_address(),
expected_hrp_sapling_payment_address,
"should return expected Sapling payment address HRP"
);
// Check that default Mainnet, Testnet, and Regtest HRPs are valid, these calls will panic
// if any of the values fail validation.
testnet::Parameters::build()
.with_sapling_hrps(
zp_constants::mainnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
zp_constants::mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
zp_constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS,
)
.with_sapling_hrps(
zp_constants::testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
zp_constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
zp_constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS,
)
.with_sapling_hrps(
zp_constants::regtest::HRP_SAPLING_EXTENDED_SPENDING_KEY,
zp_constants::regtest::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
zp_constants::regtest::HRP_SAPLING_PAYMENT_ADDRESS,
);
}
/// Checks that configured testnet names are validated and used correctly.
@ -153,6 +317,8 @@ fn check_network_name() {
.expect_err("should panic when setting network name that's too long or contains non-alphanumeric characters (except '_')");
}
drop(std::panic::take_hook());
// Checks that network names are displayed correctly
assert_eq!(
Network::new_default_testnet().to_string(),

View File

@ -493,6 +493,32 @@ impl NetworkUpgrade {
}
}
impl From<zcash_protocol::consensus::NetworkUpgrade> for NetworkUpgrade {
fn from(nu: zcash_protocol::consensus::NetworkUpgrade) -> Self {
match nu {
zcash_protocol::consensus::NetworkUpgrade::Overwinter => Self::Overwinter,
zcash_protocol::consensus::NetworkUpgrade::Sapling => Self::Sapling,
zcash_protocol::consensus::NetworkUpgrade::Blossom => Self::Blossom,
zcash_protocol::consensus::NetworkUpgrade::Heartwood => Self::Heartwood,
zcash_protocol::consensus::NetworkUpgrade::Canopy => Self::Canopy,
zcash_protocol::consensus::NetworkUpgrade::Nu5 => Self::Nu5,
}
}
}
impl From<zcash_primitives::consensus::NetworkUpgrade> for NetworkUpgrade {
fn from(value: zcash_primitives::consensus::NetworkUpgrade) -> Self {
match value {
zcash_primitives::consensus::NetworkUpgrade::Overwinter => NetworkUpgrade::Overwinter,
zcash_primitives::consensus::NetworkUpgrade::Sapling => NetworkUpgrade::Sapling,
zcash_primitives::consensus::NetworkUpgrade::Blossom => NetworkUpgrade::Blossom,
zcash_primitives::consensus::NetworkUpgrade::Heartwood => NetworkUpgrade::Heartwood,
zcash_primitives::consensus::NetworkUpgrade::Canopy => NetworkUpgrade::Canopy,
zcash_primitives::consensus::NetworkUpgrade::Nu5 => NetworkUpgrade::Nu5,
}
}
}
impl ConsensusBranchId {
/// The value used by `zcashd` RPCs for missing consensus branch IDs.
///

View File

@ -6,10 +6,8 @@
mod proofs;
#[cfg(feature = "getblocktemplate-rpcs")]
mod address;
#[cfg(feature = "getblocktemplate-rpcs")]
pub use address::Address;
#[cfg(feature = "shielded-scan")]

View File

@ -49,7 +49,7 @@ impl zcash_address::TryFromAddress for Address {
data: [u8; 20],
) -> Result<Self, zcash_address::ConversionError<Self::Error>> {
Ok(Self::Transparent(transparent::Address::from_pub_key_hash(
NetworkKind::from_zcash_address(network),
network.into(),
data,
)))
}
@ -59,7 +59,7 @@ impl zcash_address::TryFromAddress for Address {
data: [u8; 20],
) -> Result<Self, zcash_address::ConversionError<Self::Error>> {
Ok(Self::Transparent(transparent::Address::from_script_hash(
NetworkKind::from_zcash_address(network),
network.into(),
data,
)))
}
@ -68,7 +68,7 @@ impl zcash_address::TryFromAddress for Address {
network: zcash_address::Network,
data: [u8; 43],
) -> Result<Self, zcash_address::ConversionError<Self::Error>> {
let network = NetworkKind::from_zcash_address(network);
let network = network.into();
sapling::PaymentAddress::from_bytes(&data)
.map(|address| Self::Sapling { address, network })
.ok_or_else(|| BoxError::from("not a valid sapling address").into())
@ -78,7 +78,7 @@ impl zcash_address::TryFromAddress for Address {
network: zcash_address::Network,
unified_address: zcash_address::unified::Address,
) -> Result<Self, zcash_address::ConversionError<Self::Error>> {
let network = NetworkKind::from_zcash_address(network);
let network = network.into();
let mut orchard = None;
let mut sapling = None;
let mut transparent = None;
@ -163,7 +163,7 @@ impl Address {
Self::Transparent(address) => Some(address.to_string()),
Self::Sapling { address, network } => {
let data = address.to_bytes();
let address = ZcashAddress::from_sapling(network.to_zcash_address(), data);
let address = ZcashAddress::from_sapling(network.into(), data);
Some(address.encode())
}
Self::Unified { .. } => None,
@ -171,26 +171,28 @@ impl Address {
}
}
impl NetworkKind {
/// Converts a [`zcash_address::Network`] to a [`NetworkKind`].
///
/// This method is meant to be used for decoding Zcash addresses in zebra-rpc methods.
fn from_zcash_address(network: zcash_address::Network) -> Self {
impl From<zcash_address::Network> for NetworkKind {
fn from(network: zcash_address::Network) -> Self {
match network {
zcash_address::Network::Main => NetworkKind::Mainnet,
zcash_address::Network::Test => NetworkKind::Testnet,
zcash_address::Network::Regtest => NetworkKind::Regtest,
}
}
}
/// Converts a [`zcash_address::Network`] to a [`NetworkKind`].
///
/// This method is meant to be used for encoding Zcash addresses in zebra-rpc methods.
fn to_zcash_address(self) -> zcash_address::Network {
match self {
impl From<NetworkKind> for zcash_address::Network {
fn from(network: NetworkKind) -> Self {
match network {
NetworkKind::Mainnet => zcash_address::Network::Main,
NetworkKind::Testnet => zcash_address::Network::Test,
NetworkKind::Regtest => zcash_address::Network::Regtest,
}
}
}
impl From<&NetworkKind> for zcash_address::Network {
fn from(network: &NetworkKind) -> Self {
network.into()
}
}

View File

@ -630,8 +630,7 @@ impl<'de> Deserialize<'de> for Config {
#[derive(Deserialize)]
struct DTestnetParameters {
network_name: Option<String>,
#[serde(default)]
activation_heights: ConfiguredActivationHeights,
activation_heights: Option<ConfiguredActivationHeights>,
}
#[derive(Deserialize)]
@ -640,6 +639,7 @@ impl<'de> Deserialize<'de> for Config {
listen_addr: String,
network: NetworkKind,
testnet_parameters: Option<DTestnetParameters>,
regtest_activation_heights: ConfiguredActivationHeights,
initial_mainnet_peers: IndexSet<String>,
initial_testnet_peers: IndexSet<String>,
cache_dir: CacheDir,
@ -656,6 +656,7 @@ impl<'de> Deserialize<'de> for Config {
listen_addr: "0.0.0.0".to_string(),
network: Default::default(),
testnet_parameters: None,
regtest_activation_heights: ConfiguredActivationHeights::default(),
initial_mainnet_peers: config.initial_mainnet_peers,
initial_testnet_peers: config.initial_testnet_peers,
cache_dir: config.cache_dir,
@ -670,6 +671,7 @@ impl<'de> Deserialize<'de> for Config {
listen_addr,
network: network_kind,
testnet_parameters,
regtest_activation_heights,
initial_mainnet_peers,
initial_testnet_peers,
cache_dir,
@ -678,33 +680,55 @@ impl<'de> Deserialize<'de> for Config {
max_connections_per_ip,
} = DConfig::deserialize(deserializer)?;
// TODO: Panic here if the initial testnet peers are the default initial testnet peers.
let network = if let Some(DTestnetParameters {
network_name,
activation_heights,
}) = testnet_parameters
{
assert_eq!(
network_kind,
/// Accepts an [`IndexSet`] of initial peers,
///
/// Returns true if any of them are the default Testnet or Mainnet initial peers.
fn contains_default_initial_peers(initial_peers: &IndexSet<String>) -> bool {
let Config {
initial_mainnet_peers: mut default_initial_peers,
initial_testnet_peers: default_initial_testnet_peers,
..
} = Config::default();
default_initial_peers.extend(default_initial_testnet_peers);
initial_peers
.intersection(&default_initial_peers)
.next()
.is_some()
}
let network = match (network_kind, testnet_parameters) {
(NetworkKind::Mainnet, _) => Network::Mainnet,
(NetworkKind::Testnet, None) => Network::new_default_testnet(),
(NetworkKind::Regtest, _) => Network::new_regtest(regtest_activation_heights),
(
NetworkKind::Testnet,
"set network to 'Testnet' to use configured testnet parameters"
);
Some(DTestnetParameters {
network_name,
activation_heights,
}),
) => {
let mut params_builder = testnet::Parameters::build();
let mut params_builder = testnet::Parameters::build();
if let Some(network_name) = network_name {
params_builder = params_builder.with_network_name(network_name)
}
if let Some(network_name) = network_name {
params_builder = params_builder.with_network_name(network_name)
}
// Retain default Testnet activation heights unless there's an empty [testnet_parameters.activation_heights] section.
if let Some(activation_heights) = activation_heights {
// Return an error if the initial testnet peers includes any of the default initial Mainnet or Testnet
// peers while activation heights are configured.
// TODO: Check that the network magic is different from the default Mainnet/Testnet network magic too?
if contains_default_initial_peers(&initial_testnet_peers) {
return Err(de::Error::custom(
"cannot use default initial testnet peers with configured activation heights",
));
}
params_builder
.with_activation_heights(activation_heights)
.to_network()
} else {
// Convert to default `Network` for a `NetworkKind` if there are no testnet parameters.
match network_kind {
NetworkKind::Mainnet => Network::Mainnet,
NetworkKind::Testnet => Network::new_default_testnet(),
NetworkKind::Regtest => unimplemented!("Regtest is not yet implemented in Zebra"),
params_builder = params_builder.with_activation_heights(activation_heights)
}
params_builder.to_network()
}
};

View File

@ -57,7 +57,7 @@ initial_testnet_peers = [
]
listen_addr = "0.0.0.0:8233"
max_connections_per_ip = 1
network = "Testnet"
network = "Regtest"
peerset_initial_target_size = 25
[network.testnet_parameters]
@ -72,6 +72,15 @@ Heartwood = 903_800
Canopy = 1_028_500
NU5 = 1_842_420
[network.regtest_activation_heights]
BeforeOverwinter = 1
Overwinter = 207_500
Sapling = 280_000
Blossom = 584_000
Heartwood = 903_800
Canopy = 1_028_500
NU5 = 1_842_420
[rpc]
debug_force_finished_sync = false
parallel_cpu_threads = 0