Add Regtest variant of `Network` and Parameter

closes #1051

Also:
Adds encoding tests + cargo fmt
Adds tests on ZIP-321
Adds coverage on new code
Supports nu6 activation
`local-consensus` feature is disabled by default
This commit is contained in:
Francisco Gindre 2023-11-29 17:59:49 -03:00
parent efc0d7d697
commit 529fd3d562
No known key found for this signature in database
GPG Key ID: 6B61CD8DAA2862B4
7 changed files with 363 additions and 7 deletions

View File

@ -841,6 +841,9 @@ mod tests {
transaction::components::{amount::NonNegativeAmount, Amount},
};
#[cfg(feature = "local-consensus")]
use zcash_primitives::{local_consensus::LocalNetwork, BlockHeight};
use crate::address::Address;
use super::{
@ -1010,6 +1013,27 @@ mod tests {
);
}
#[cfg(feature = "local-consensus")]
#[test]
fn test_zip321_spec_regtest_valid_examples() {
let params = LocalNetwork {
overwinter: Some(BlockHeight::from_u32(1)),
sapling: Some(BlockHeight::from_u32(1)),
blossom: Some(BlockHeight::from_u32(1)),
heartwood: Some(BlockHeight::from_u32(1)),
canopy: Some(BlockHeight::from_u32(1)),
nu5: Some(BlockHeight::from_u32(1)),
nu6: Some(BlockHeight::from_u32(1)),
z_future: Some(BlockHeight::from_u32(1)),
};
let valid_1 = "zcash:zregtestsapling1qqqqqqqqqqqqqqqqqqcguyvaw2vjk4sdyeg0lc970u659lvhqq7t0np6hlup5lusxle7505hlz3?amount=1&memo=VGhpcyBpcyBhIHNpbXBsZSBtZW1vLg&message=Thank%20you%20for%20your%20purchase";
let v1r = TransactionRequest::from_uri(&params, valid_1).unwrap();
assert_eq!(
v1r.payments.get(0).map(|p| p.amount),
Some(NonNegativeAmount::const_from_u64(100000000))
);
}
#[test]
fn test_zip321_spec_invalid_examples() {
// invalid; missing `address=`

View File

@ -502,7 +502,7 @@ mod tests {
let encoded_main = "zxviews1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qy3zw4wc246aw9rlfyg5ndlwvne7mwdq0qe6vxl42pqmcf8pvmmd5slmjxduqa9evgej6wa3th2505xq4nggrxdm93rxk4rpdjt5nmq2vn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjsxmansf";
let encoded_test = "zxviewtestsapling1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qy3zw4wc246aw9rlfyg5ndlwvne7mwdq0qe6vxl42pqmcf8pvmmd5slmjxduqa9evgej6wa3th2505xq4nggrxdm93rxk4rpdjt5nmq2vn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjs8evfkz";
let encoded_regtest = "zxviewregtestsapling1qqqqqqqqqqqqqq8n3zjjmvhhr854uy3qhpda3ml34haf0x388z5r7h4st4kpsf6qy3zw4wc246aw9rlfyg5ndlwvne7mwdq0qe6vxl42pqmcf8pvmmd5slmjxduqa9evgej6wa3th2505xq4nggrxdm93rxk4rpdjt5nmq2vn44e2uhm7h0hsagfvkk4n7n6nfer6u57v9cac84t7nl2zth0xpyfeg0w2p2wv2yn6jn923aaz0vdaml07l60ahapk6efchyxwysrvjskjkzax";
assert_eq!(
encode_extended_full_viewing_key(
constants::mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
@ -526,10 +526,19 @@ mod tests {
),
encoded_test
);
assert_eq!(
encode_extended_full_viewing_key(
constants::regtest::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
&extfvk
),
encoded_regtest
);
assert_eq!(
decode_extended_full_viewing_key(
constants::testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
encoded_test
constants::regtest::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
encoded_regtest
)
.unwrap(),
extfvk
@ -550,11 +559,14 @@ mod tests {
"zs1qqqqqqqqqqqqqqqqqqcguyvaw2vjk4sdyeg0lc970u659lvhqq7t0np6hlup5lusxle75c8v35z";
let encoded_test =
"ztestsapling1qqqqqqqqqqqqqqqqqqcguyvaw2vjk4sdyeg0lc970u659lvhqq7t0np6hlup5lusxle75ss7jnk";
let encoded_regtest =
"zregtestsapling1qqqqqqqqqqqqqqqqqqcguyvaw2vjk4sdyeg0lc970u659lvhqq7t0np6hlup5lusxle7505hlz3";
assert_eq!(
encode_payment_address(constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS, &addr),
encoded_main
);
assert_eq!(
decode_payment_address(
constants::mainnet::HRP_SAPLING_PAYMENT_ADDRESS,
@ -568,6 +580,12 @@ mod tests {
encode_payment_address(constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS, &addr),
encoded_test
);
assert_eq!(
encode_payment_address(constants::regtest::HRP_SAPLING_PAYMENT_ADDRESS, &addr),
encoded_regtest
);
assert_eq!(
decode_payment_address(
constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS,
@ -576,6 +594,15 @@ mod tests {
.unwrap(),
addr
);
assert_eq!(
decode_payment_address(
constants::regtest::HRP_SAPLING_PAYMENT_ADDRESS,
encoded_regtest
)
.unwrap(),
addr
);
}
#[test]

View File

@ -54,6 +54,16 @@ and this library adheres to Rust's notion of
- `impl {Clone, PartialEq, Eq} for zcash_primitives::memo::Error`
- `impl {PartialEq, Eq} for zcash_primitives::sapling::note::Rseed`
- `impl From<TxId> for [u8; 32]`
- Added feature `local-consensus` to crate `zcash_primitives`
- Adds `pub mod local_consensus`
- `local_consensus::FullNodeParameters` adds functionality to refer to Full Node
parameters that can be either (public) Consensus nodes like `Network::MainNetwork`
or `Network::TestNetwork`, or a `Local(P)` with a set of Parameters `P`. `Regtest`
is considered a `LocalConsensus`.
- `zcash_primitives::local_consensus::LocalNetwork` provides a type for specifying
network upgrade activation heights for a given `FullNodeParameters::Local(P)`
- `impl Parameters for LocalNetwork` that use provided activation heights and
defaults to `constants::regtest::` for everything else.
### Changed
- `zcash_primitives::transaction`:
@ -112,6 +122,9 @@ and this library adheres to Rust's notion of
- `zcash_primitives::zip32`:
- `ChildIndex` has been changed from an enum to an opaque struct, and no
longer supports non-hardened indices.
- `zcash_client_backend` changes related to `local-consensus` feature:
- added tests that verify `zip321` supports Payment URIs with `Local(P)`
network parameters.
### Removed
- `zcash_primitives::constants`:

View File

@ -132,6 +132,8 @@ unstable-nu6 = []
## Exposes early in-development features that are not yet planned for any network upgrade.
zfuture = []
## Exposes support for working with a local consensus (e.g. regtest
local-consensus = []
[lib]
bench = false

View File

@ -688,11 +688,10 @@ pub mod testing {
#[cfg(test)]
mod tests {
use std::convert::TryFrom;
use super::{
BlockHeight, BranchId, NetworkUpgrade, Parameters, MAIN_NETWORK, UPGRADES_IN_ORDER,
};
use std::convert::TryFrom;
#[test]
fn nu_ordering() {

View File

@ -26,7 +26,8 @@ pub mod merkle_tree;
use sapling;
pub mod transaction;
pub use zip32;
pub mod zip339;
#[cfg(feature = "zfuture")]
pub mod extensions;
#[cfg(feature = "local-consensus")]
pub mod local_consensus;
pub mod zip339;

View File

@ -0,0 +1,290 @@
use crate::{
consensus::{BlockHeight, Network, NetworkUpgrade, Parameters},
constants,
};
/// Represents the Network Parameters of the Full Node the caller connects to.
pub enum FullNodeParameters<P: Parameters> {
/// Variant indicating a Zcash Full Node running on public Mainnet or Testnet
Consensus(Network),
/// The consensus parameters of a local or non-public network like one or more nodes
/// in `regtest` mode.
Local(P),
}
/// a `LocalNetwork` setup should define the activation heights
/// of network upgrades. `None` is considered as "not activated"
/// These heights are not validated. Callers shall initialized
/// them according to the settings used on the Full Nodes they
/// are connecting to.
///
/// Example:
/// Regtest Zcashd using the following `zcash.conf`
/// ```
/// ## NUPARAMS
/// nuparams=5ba81b19:1 # Overwinter
/// nuparams=76b809bb:1 # Sapling
/// nuparams=2bb40e60:1 # Blossom
/// nuparams=f5b9230b:1 # Heartwood
/// nuparams=e9ff75a6:1 # Canopy
/// nuparams=c2d6d0b4:1 # NU5
/// ```
/// would use the following `LocalNetwork` struct
/// ```
/// let regtest = LocalNetwork {
/// overwinter: Some(BlockHeight::from_u32(1)),
/// sapling: Some(BlockHeight::from_u32(1)),
/// blossom: Some(BlockHeight::from_u32(1)),
/// heartwood: Some(BlockHeight::from_u32(1)),
/// canopy: Some(BlockHeight::from_u32(1)),
/// nu5: Some(BlockHeight::from_u32(1)),
/// #[cfg(feature = "unstable-nu6")]
/// nu6: Some(BlockHeight::from_u32(1)),
/// #[cfg(feature = "zfuture")]
/// z_future: Some(BlockHeight::from_u32(1)),
/// };
/// ```
///
#[derive(Clone, PartialEq, Eq, Copy, Debug)]
pub struct LocalNetwork {
pub overwinter: Option<BlockHeight>,
pub sapling: Option<BlockHeight>,
pub blossom: Option<BlockHeight>,
pub heartwood: Option<BlockHeight>,
pub canopy: Option<BlockHeight>,
pub nu5: Option<BlockHeight>,
pub nu6: Option<BlockHeight>,
#[cfg(feature = "zfuture")]
pub z_future: Option<BlockHeight>,
}
/// Parameters default implementation for `LocalNetwork`
/// Important note:
/// The following functions return `constants::regtest` values
/// ```
/// fn coin_type()
/// fn address_network()
/// fn hrp_sapling_extended_spending_key()
/// fn hrp_sapling_extended_full_viewing_key()
/// fn hrp_sapling_payment_address()
/// fn b58_script_address_prefix()
/// ```
impl Parameters for LocalNetwork {
fn activation_height(&self, nu: NetworkUpgrade) -> Option<BlockHeight> {
match nu {
NetworkUpgrade::Overwinter => self.overwinter,
NetworkUpgrade::Sapling => self.sapling,
NetworkUpgrade::Blossom => self.blossom,
NetworkUpgrade::Heartwood => self.heartwood,
NetworkUpgrade::Canopy => self.canopy,
NetworkUpgrade::Nu5 => self.nu5,
#[cfg(feature = "unstable-nu6")]
NetworkUpgrade::Nu6 => self.nu6,
#[cfg(feature = "zfuture")]
NetworkUpgrade::ZFuture => self.z_future,
}
}
fn coin_type(&self) -> u32 {
constants::regtest::COIN_TYPE
}
fn address_network(&self) -> Option<zcash_address::Network> {
Some(zcash_address::Network::Regtest)
}
fn hrp_sapling_extended_spending_key(&self) -> &str {
constants::regtest::HRP_SAPLING_EXTENDED_SPENDING_KEY
}
fn hrp_sapling_extended_full_viewing_key(&self) -> &str {
constants::regtest::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY
}
fn hrp_sapling_payment_address(&self) -> &str {
constants::regtest::HRP_SAPLING_PAYMENT_ADDRESS
}
fn b58_pubkey_address_prefix(&self) -> [u8; 2] {
constants::regtest::B58_PUBKEY_ADDRESS_PREFIX
}
fn b58_script_address_prefix(&self) -> [u8; 2] {
constants::regtest::B58_SCRIPT_ADDRESS_PREFIX
}
fn is_nu_active(&self, nu: NetworkUpgrade, height: BlockHeight) -> bool {
self.activation_height(nu).map_or(false, |h| h <= height)
}
}
#[cfg(test)]
mod tests {
use crate::{
consensus::{BlockHeight, NetworkUpgrade, Parameters},
constants,
local_consensus::LocalNetwork,
};
#[test]
fn regtest_nu_activation() {
let expected_overwinter = BlockHeight::from_u32(1);
let expected_sapling = BlockHeight::from_u32(2);
let expected_blossom = BlockHeight::from_u32(3);
let expected_heartwood = BlockHeight::from_u32(4);
let expected_canopy = BlockHeight::from_u32(5);
let expected_nu5 = BlockHeight::from_u32(6);
#[cfg(feature = "unstable-nu6")]
let expected_nu6 = BlockHeight::from_u32(7);
#[cfg(feature = "zfuture")]
let expected_z_future = BlockHeight::from_u32(7);
let regtest = LocalNetwork {
overwinter: Some(expected_overwinter),
sapling: Some(expected_sapling),
blossom: Some(expected_blossom),
heartwood: Some(expected_heartwood),
canopy: Some(expected_canopy),
nu5: Some(expected_nu5),
#[cfg(feature = "unstable-nu6")]
nu6: Some(expected_nu6),
#[cfg(feature = "zfuture")]
z_future: Some(expected_z_future),
};
assert!(regtest.is_nu_active(NetworkUpgrade::Overwinter, expected_overwinter));
assert!(regtest.is_nu_active(NetworkUpgrade::Sapling, expected_sapling));
assert!(regtest.is_nu_active(NetworkUpgrade::Blossom, expected_blossom));
assert!(regtest.is_nu_active(NetworkUpgrade::Heartwood, expected_heartwood));
assert!(regtest.is_nu_active(NetworkUpgrade::Canopy, expected_canopy));
assert!(regtest.is_nu_active(NetworkUpgrade::Nu5, expected_nu5));
#[cfg(feature = "unstable-nu6")]
assert!(regtest.is_nu_active(NetworkUpgrade::Nu6, expected_nu6));
#[cfg(feature = "zfuture")]
assert!(!regtest.is_nu_active(NetworkUpgrade::ZFuture, expected_nu5));
assert_eq!(regtest.coin_type(), constants::regtest::COIN_TYPE);
assert_eq!(
regtest.hrp_sapling_extended_spending_key(),
constants::regtest::HRP_SAPLING_EXTENDED_SPENDING_KEY
);
assert_eq!(
regtest.hrp_sapling_extended_full_viewing_key(),
constants::regtest::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY
);
assert_eq!(
regtest.hrp_sapling_payment_address(),
constants::regtest::HRP_SAPLING_PAYMENT_ADDRESS
);
assert_eq!(
regtest.b58_pubkey_address_prefix(),
constants::regtest::B58_PUBKEY_ADDRESS_PREFIX
);
}
#[test]
fn regtest_activation_heights() {
let expected_overwinter = BlockHeight::from_u32(1);
let expected_sapling = BlockHeight::from_u32(2);
let expected_blossom = BlockHeight::from_u32(3);
let expected_heartwood = BlockHeight::from_u32(4);
let expected_canopy = BlockHeight::from_u32(5);
let expected_nu5 = BlockHeight::from_u32(6);
#[cfg(feature = "unstable-nu6")]
let expected_nu6 = BlockHeight::from_u32(7);
#[cfg(feature = "zfuture")]
let expected_z_future = BlockHeight::from_u32(7);
let regtest = LocalNetwork {
overwinter: Some(expected_overwinter),
sapling: Some(expected_sapling),
blossom: Some(expected_blossom),
heartwood: Some(expected_heartwood),
canopy: Some(expected_canopy),
nu5: Some(expected_nu5),
#[cfg(feature = "unstable-nu6")]
nu6: Some(expected_nu6),
#[cfg(feature = "zfuture")]
z_future: Some(expected_z_future),
};
assert_eq!(
regtest.activation_height(NetworkUpgrade::Overwinter),
Some(expected_overwinter)
);
assert_eq!(
regtest.activation_height(NetworkUpgrade::Sapling),
Some(expected_sapling)
);
assert_eq!(
regtest.activation_height(NetworkUpgrade::Blossom),
Some(expected_blossom)
);
assert_eq!(
regtest.activation_height(NetworkUpgrade::Heartwood),
Some(expected_heartwood)
);
assert_eq!(
regtest.activation_height(NetworkUpgrade::Canopy),
Some(expected_canopy)
);
assert_eq!(
regtest.activation_height(NetworkUpgrade::Nu5),
Some(expected_nu5)
);
#[cfg(feature = "zfuture")]
assert_eq!(
regtest.activation_height(NetworkUpgrade::ZFuture),
Some(expected_z_future)
);
}
#[test]
fn regtests_constants() {
let expected_overwinter = BlockHeight::from_u32(1);
let expected_sapling = BlockHeight::from_u32(2);
let expected_blossom = BlockHeight::from_u32(3);
let expected_heartwood = BlockHeight::from_u32(4);
let expected_canopy = BlockHeight::from_u32(5);
let expected_nu5 = BlockHeight::from_u32(6);
#[cfg(feature = "unstable-nu6")]
let expected_nu6 = BlockHeight::from_u32(7);
#[cfg(feature = "zfuture")]
let expected_z_future = BlockHeight::from_u32(7);
let regtest = LocalNetwork {
overwinter: Some(expected_overwinter),
sapling: Some(expected_sapling),
blossom: Some(expected_blossom),
heartwood: Some(expected_heartwood),
canopy: Some(expected_canopy),
nu5: Some(expected_nu5),
#[cfg(feature = "unstable-nu6")]
nu6: Some(expected_nu6),
#[cfg(feature = "zfuture")]
z_future: Some(expected_z_future),
};
assert_eq!(regtest.coin_type(), constants::regtest::COIN_TYPE);
assert_eq!(
regtest.hrp_sapling_extended_spending_key(),
constants::regtest::HRP_SAPLING_EXTENDED_SPENDING_KEY
);
assert_eq!(
regtest.hrp_sapling_extended_full_viewing_key(),
constants::regtest::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY
);
assert_eq!(
regtest.hrp_sapling_payment_address(),
constants::regtest::HRP_SAPLING_PAYMENT_ADDRESS
);
assert_eq!(
regtest.b58_pubkey_address_prefix(),
constants::regtest::B58_PUBKEY_ADDRESS_PREFIX
);
assert_eq!(
regtest.b58_script_address_prefix(),
constants::regtest::B58_SCRIPT_ADDRESS_PREFIX
);
}
}