Applies some suggestions from code review

This commit is contained in:
Arya 2024-04-01 18:12:55 -04:00
parent 4900a20cad
commit cb8726b38a
8 changed files with 94 additions and 59 deletions

View File

@ -1,15 +1,8 @@
//! Error types for zebra-chain parameters //! Error types for zebra-chain parameters
use std::fmt; use thiserror::Error;
/// An error indicating that Zebra network is not supported in type conversions. /// An error indicating that Zebra network is not supported in type conversions.
#[derive(Debug)] #[derive(Clone, Debug, Error)]
pub struct UnsupportedNetwork; #[error("Unsupported Zcash network parameters: {0}")]
pub struct UnsupportedNetwork(pub String);
impl fmt::Display for UnsupportedNetwork {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "unsupported Zcash network parameters")
}
}
impl std::error::Error for UnsupportedNetwork {}

View File

@ -66,8 +66,10 @@ impl NetworkParameters {
} }
#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] #[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
/// An enum describing the kind of network, whether it's the production mainnet or a testnet. /// An enum describing the kind of network, whether it's the production mainnet or a testnet.
// Note: The order of these variants is important for correct bincode (de)serialization
// of history trees in the db format.
// TODO: Replace bincode (de)serialization of `HistoryTreeParts` in a db format upgrade?
pub enum NetworkKind { pub enum NetworkKind {
/// The production mainnet. /// The production mainnet.
#[default] #[default]
@ -75,6 +77,9 @@ pub enum NetworkKind {
/// A test network. /// A test network.
Testnet, Testnet,
/// Regtest mode
Regtest,
} }
impl From<Network> for NetworkKind { impl From<Network> for NetworkKind {
@ -92,7 +97,8 @@ pub enum Network {
#[default] #[default]
Mainnet, Mainnet,
/// The oldest public test network. /// A test network such as the default public testnet,
/// a configured testnet, or Regtest.
Testnet(Arc<NetworkParameters>), Testnet(Arc<NetworkParameters>),
} }
@ -112,26 +118,21 @@ impl NetworkKind {
/// Return the network name as defined in /// Return the network name as defined in
/// [BIP70](https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#paymentdetailspaymentrequest) /// [BIP70](https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#paymentdetailspaymentrequest)
pub fn bip70_network_name(&self) -> String { pub fn bip70_network_name(&self) -> String {
match self { if *self == Self::Mainnet {
Self::Mainnet => "main".to_string(), "main".to_string()
Self::Testnet => "test".to_string(), } else {
} "test".to_string()
}
/// Converts a [`zcash_address::Network`] to a [`NetworkKind`].
pub fn from_zcash_address(network: zcash_address::Network) -> Self {
match network {
zcash_address::Network::Main => NetworkKind::Mainnet,
zcash_address::Network::Test | zcash_address::Network::Regtest => NetworkKind::Testnet,
} }
} }
} }
impl From<NetworkKind> for &'static str { impl From<NetworkKind> for &'static str {
fn from(network: NetworkKind) -> &'static str { fn from(network: NetworkKind) -> &'static str {
// These should be different from the `Display` impl for `Network`
match network { match network {
NetworkKind::Mainnet => "MainnetKind", NetworkKind::Mainnet => "MainnetKind",
NetworkKind::Testnet => "TestnetKind", NetworkKind::Testnet => "TestnetKind",
NetworkKind::Regtest => "RegtestKind",
} }
} }
} }
@ -148,8 +149,7 @@ impl From<&Network> for &'static str {
Network::Mainnet => "Mainnet", Network::Mainnet => "Mainnet",
// TODO: // TODO:
// - Add a `name` field to use here instead of checking `is_default_testnet()` // - Add a `name` field to use here instead of checking `is_default_testnet()`
// - Find out what zcashd calls the regtest cache dir for the `Network::new_regtest()` method, or // - zcashd calls the Regtest cache dir 'regtest' (#8327).
// if it always uses an ephemeral db, and do the same for Regtest in Zebra (#8327).
Network::Testnet(params) if params.is_default_testnet() => "Testnet", Network::Testnet(params) if params.is_default_testnet() => "Testnet",
Network::Testnet(_params) => "UnknownTestnet", Network::Testnet(_params) => "UnknownTestnet",
} }
@ -186,18 +186,11 @@ impl Network {
pub fn kind(&self) -> NetworkKind { pub fn kind(&self) -> NetworkKind {
match self { match self {
Network::Mainnet => NetworkKind::Mainnet, Network::Mainnet => NetworkKind::Mainnet,
// TODO: Return `NetworkKind::Regtest` if the parameters match the default Regtest params
Network::Testnet(_) => NetworkKind::Testnet, Network::Testnet(_) => NetworkKind::Testnet,
} }
} }
/// Returns the default [`Network`] for a [`NetworkKind`].
pub fn from_kind(kind: NetworkKind) -> Self {
match kind {
NetworkKind::Mainnet => Self::Mainnet,
NetworkKind::Testnet => Self::new_default_testnet(),
}
}
/// Returns an iterator over [`Network`] variants. /// Returns an iterator over [`Network`] variants.
pub fn iter() -> impl Iterator<Item = Self> { pub fn iter() -> impl Iterator<Item = Self> {
// TODO: Use default values of `Testnet` variant when adding fields for #7845. // TODO: Use default values of `Testnet` variant when adding fields for #7845.

View File

@ -68,16 +68,9 @@ impl TryFrom<&Network> for zcash_address::Network {
// TODO: If the network parameters match `Regtest` parameters, convert to // TODO: If the network parameters match `Regtest` parameters, convert to
// `zcash_address::Network::Regtest instead of returning `UnsupportedAddress` error. // `zcash_address::Network::Regtest instead of returning `UnsupportedAddress` error.
// (#7119, #7839) // (#7119, #7839)
Network::Testnet(_params) => Err(UnsupportedNetwork), Network::Testnet(_params) => Err(UnsupportedNetwork(
} "could not convert configured testnet to zcash_address::Network".to_string(),
} )),
}
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,
} }
} }
} }
@ -205,10 +198,34 @@ impl Address {
Self::Transparent(address) => Some(address.to_string()), Self::Transparent(address) => Some(address.to_string()),
Self::Sapling { address, network } => { Self::Sapling { address, network } => {
let data = address.to_bytes(); let data = address.to_bytes();
let address = ZcashAddress::from_sapling((*network).into(), data); let address = ZcashAddress::from_sapling(network.to_zcash_address(), data);
Some(address.encode()) Some(address.encode())
} }
Self::Unified { .. } => None, Self::Unified { .. } => None,
} }
} }
} }
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 {
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 {
NetworkKind::Mainnet => zcash_address::Network::Main,
NetworkKind::Testnet => zcash_address::Network::Test,
NetworkKind::Regtest => zcash_address::Network::Regtest,
}
}
}

View File

@ -351,7 +351,10 @@ impl TryFrom<&Network> for zcash_primitives::consensus::Network {
Network::Testnet(_params) if network.is_default_testnet() => { Network::Testnet(_params) if network.is_default_testnet() => {
Ok(zcash_primitives::consensus::Network::TestNetwork) Ok(zcash_primitives::consensus::Network::TestNetwork)
} }
Network::Testnet(_params) => Err(UnsupportedNetwork), Network::Testnet(_params) => Err(UnsupportedNetwork(
"could not convert configured testnet to zcash_primitives::consensus::Network"
.to_string(),
)),
} }
} }
} }
@ -360,7 +363,9 @@ impl From<NetworkKind> for zcash_primitives::consensus::Network {
fn from(network: NetworkKind) -> Self { fn from(network: NetworkKind) -> Self {
match network { match network {
NetworkKind::Mainnet => zcash_primitives::consensus::Network::MainNetwork, NetworkKind::Mainnet => zcash_primitives::consensus::Network::MainNetwork,
NetworkKind::Testnet => zcash_primitives::consensus::Network::TestNetwork, NetworkKind::Testnet | NetworkKind::Regtest => {
zcash_primitives::consensus::Network::TestNetwork
}
} }
} }
} }

View File

@ -29,10 +29,6 @@ use proptest::prelude::*;
#[derive( #[derive(
Clone, Eq, PartialEq, Hash, serde_with::SerializeDisplay, serde_with::DeserializeFromStr, Clone, Eq, PartialEq, Hash, serde_with::SerializeDisplay, serde_with::DeserializeFromStr,
)] )]
#[cfg_attr(
any(test, feature = "proptest-impl"),
derive(proptest_derive::Arbitrary)
)]
pub enum Address { pub enum Address {
/// P2SH (Pay to Script Hash) addresses /// P2SH (Pay to Script Hash) addresses
PayToScriptHash { PayToScriptHash {

View File

@ -1,8 +1,8 @@
use proptest::{arbitrary::any, collection::vec, prelude::*}; use proptest::{arbitrary::any, collection::vec, prelude::*};
use crate::{block, LedgerState}; use crate::{block, parameters::NetworkKind, LedgerState};
use super::{CoinbaseData, Input, OutPoint, Script, GENESIS_COINBASE_DATA}; use super::{Address, CoinbaseData, Input, OutPoint, Script, GENESIS_COINBASE_DATA};
impl Input { impl Input {
/// Construct a strategy for creating valid-ish vecs of Inputs. /// Construct a strategy for creating valid-ish vecs of Inputs.
@ -46,3 +46,27 @@ impl Arbitrary for Input {
type Strategy = BoxedStrategy<Self>; type Strategy = BoxedStrategy<Self>;
} }
impl Arbitrary for Address {
type Parameters = ();
fn arbitrary_with(_args: ()) -> Self::Strategy {
any::<(bool, bool, [u8; 20])>()
.prop_map(|(is_mainnet, is_p2pkh, hash_bytes)| {
let network = if is_mainnet {
NetworkKind::Mainnet
} else {
NetworkKind::Testnet
};
if is_p2pkh {
Address::from_pub_key_hash(network, hash_bytes)
} else {
Address::from_script_hash(network, hash_bytes)
}
})
.boxed()
}
type Strategy = BoxedStrategy<Self>;
}

View File

@ -682,7 +682,12 @@ impl<'de> Deserialize<'de> for Config {
Network::new_configured_testnet(network_params) Network::new_configured_testnet(network_params)
} else { } else {
Network::from_kind(network_kind) // 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"),
}
}; };
let listen_addr = match listen_addr.parse::<SocketAddr>() { let listen_addr = match listen_addr.parse::<SocketAddr>() {

View File

@ -498,14 +498,17 @@ impl AddressTransaction {
/// Returns a byte representing the [`transparent::Address`] variant. /// Returns a byte representing the [`transparent::Address`] variant.
fn address_variant(address: &transparent::Address) -> u8 { fn address_variant(address: &transparent::Address) -> u8 {
use NetworkKind::*;
// Return smaller values for more common variants. // Return smaller values for more common variants.
// //
// (This probably doesn't matter, but it might help slightly with data compression.) // (This probably doesn't matter, but it might help slightly with data compression.)
match (address.network(), address) { match (address.network(), address) {
(NetworkKind::Mainnet, PayToPublicKeyHash { .. }) => 0, (Mainnet, PayToPublicKeyHash { .. }) => 0,
(NetworkKind::Mainnet, PayToScriptHash { .. }) => 1, (Mainnet, PayToScriptHash { .. }) => 1,
(NetworkKind::Testnet, PayToPublicKeyHash { .. }) => 2, // There's no way to distinguish between Regtest and Testnet for encoded transparent addresses,
(NetworkKind::Testnet, PayToScriptHash { .. }) => 3, // so the network kind should always be `Mainnet` or `Testnet`.
(Testnet | Regtest, PayToPublicKeyHash { .. }) => 2,
(Testnet | Regtest, PayToScriptHash { .. }) => 3,
} }
} }
@ -531,7 +534,6 @@ impl FromDisk for transparent::Address {
let network = if address_variant < 2 { let network = if address_variant < 2 {
NetworkKind::Mainnet NetworkKind::Mainnet
} else { } else {
// TODO: Replace `network` field on `Address` with the prefix and a method for checking if it's the mainnet prefix.
NetworkKind::Testnet NetworkKind::Testnet
}; };