diff --git a/zebra-chain/src/parameters/network.rs b/zebra-chain/src/parameters/network.rs index 15a6d392476..4d78d55be09 100644 --- a/zebra-chain/src/parameters/network.rs +++ b/zebra-chain/src/parameters/network.rs @@ -63,6 +63,100 @@ pub enum Network { Testnet, } +/// A magic number identifying the network. +#[derive(Copy, Clone, Eq, PartialEq)] +#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))] +pub struct Magic(pub [u8; 4]); + +impl fmt::Debug for Magic { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("Magic").field(&hex::encode(self.0)).finish() + } +} + +/// Magic numbers used to identify different Zcash networks. +pub mod magics { + use super::*; + /// The production mainnet. + pub const MAINNET: Magic = Magic([0x24, 0xe9, 0x27, 0x64]); + /// The testnet. + pub const TESTNET: Magic = Magic([0xfa, 0x1a, 0xf9, 0xbf]); +} +pub trait AllParameters: zcash_primitives::consensus::Parameters { + fn height_for_first_halving(&self) -> Height; + fn genesis_hash(&self) -> crate::block::Hash; + fn magic_value(&self) -> Magic; +} +impl AllParameters for Network { + fn height_for_first_halving(&self) -> Height { + match self { + Network::Mainnet => Canopy + .activation_height(*self) + .expect("canopy activation height should be available"), + Network::Testnet => constants::FIRST_HALVING_TESTNET, + } + } + + fn genesis_hash(&self) -> crate::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 magic_value(&self) -> Magic { + match self { + Network::Mainnet => magics::MAINNET, + Network::Testnet => magics::TESTNET, + } + } +} +impl zcash_primitives::consensus::Parameters for Network { + fn activation_height( + &self, + nu: zcash_primitives::consensus::NetworkUpgrade, + ) -> Option { + todo!() + } + + fn coin_type(&self) -> u32 { + match self { + Network::Mainnet => zcash_primitives::constants::mainnet::COIN_TYPE, + Network::Testnet => zcash_primitives::constants::testnet::COIN_TYPE, + } + } + + fn address_network(&self) -> Option { + todo!() + } + + fn hrp_sapling_extended_spending_key(&self) -> &str { + todo!() + } + + fn hrp_sapling_extended_full_viewing_key(&self) -> &str { + todo!() + } + + fn hrp_sapling_payment_address(&self) -> &str { + todo!() + } + + fn b58_pubkey_address_prefix(&self) -> [u8; 2] { + match self { + Network::Mainnet => zcash_primitives::constants::mainnet::B58_PUBKEY_ADDRESS_PREFIX, + Network::Testnet => zcash_primitives::constants::testnet::B58_PUBKEY_ADDRESS_PREFIX, + } + } + + fn b58_script_address_prefix(&self) -> [u8; 2] { + todo!() + } +} impl From for &'static str { fn from(network: Network) -> &'static str { match network { diff --git a/zebra-chain/src/primitives/zcash_note_encryption.rs b/zebra-chain/src/primitives/zcash_note_encryption.rs index 6f46af82d27..81cc4a557d8 100644 --- a/zebra-chain/src/primitives/zcash_note_encryption.rs +++ b/zebra-chain/src/primitives/zcash_note_encryption.rs @@ -25,24 +25,12 @@ pub fn decrypts_successfully(transaction: &Transaction, network: Network, height if let Some(bundle) = alt_tx.sapling_bundle() { for output in bundle.shielded_outputs().iter() { - let recovery = match network { - Network::Mainnet => { - zcash_primitives::sapling::note_encryption::try_sapling_output_recovery( - &zcash_primitives::consensus::MAIN_NETWORK, - alt_height, - &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, - ) - } - }; + let recovery = zcash_primitives::sapling::note_encryption::try_sapling_output_recovery( + &network, + alt_height, + &null_sapling_ovk, + output, + ); if recovery.is_none() { return false; } diff --git a/zebra-chain/src/transparent/address.rs b/zebra-chain/src/transparent/address.rs index 0faeb9216fb..48c392cd7bc 100644 --- a/zebra-chain/src/transparent/address.rs +++ b/zebra-chain/src/transparent/address.rs @@ -5,6 +5,7 @@ use std::{fmt, io}; use ripemd::{Digest, Ripemd160}; use secp256k1::PublicKey; use sha2::Sha256; +use zcash_primitives::consensus::Parameters as _; use crate::{ parameters::Network, @@ -118,24 +119,14 @@ impl ZcashSerialize for Address { network, script_hash, } => { - // Dev network doesn't have a recommendation so we - // 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(&network.b58_script_address_prefix())?; writer.write_all(script_hash)? } Address::PayToPublicKeyHash { network, pub_key_hash, } => { - // Dev network doesn't have a recommendation so we - // 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(&network.b58_pubkey_address_prefix())?; writer.write_all(pub_key_hash)? } } @@ -153,22 +144,30 @@ impl ZcashDeserialize for Address { reader.read_exact(&mut hash_bytes)?; match version_bytes { - magics::p2sh::MAINNET => Ok(Address::PayToScriptHash { - network: Network::Mainnet, - script_hash: hash_bytes, - }), - magics::p2sh::TESTNET => Ok(Address::PayToScriptHash { - network: Network::Testnet, - script_hash: hash_bytes, - }), - magics::p2pkh::MAINNET => Ok(Address::PayToPublicKeyHash { - network: Network::Mainnet, - pub_key_hash: hash_bytes, - }), - magics::p2pkh::TESTNET => Ok(Address::PayToPublicKeyHash { - network: Network::Testnet, - pub_key_hash: hash_bytes, - }), + zcash_primitives::constants::mainnet::B58_SCRIPT_ADDRESS_PREFIX => { + Ok(Address::PayToScriptHash { + network: Network::Mainnet, + script_hash: hash_bytes, + }) + } + zcash_primitives::constants::testnet::B58_SCRIPT_ADDRESS_PREFIX => { + Ok(Address::PayToScriptHash { + network: Network::Testnet, + script_hash: hash_bytes, + }) + } + zcash_primitives::constants::mainnet::B58_PUBKEY_ADDRESS_PREFIX => { + Ok(Address::PayToPublicKeyHash { + 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")), } } diff --git a/zebra-consensus/src/block/subsidy/funding_streams.rs b/zebra-consensus/src/block/subsidy/funding_streams.rs index 977b14a56d8..0820040ef58 100644 --- a/zebra-consensus/src/block/subsidy/funding_streams.rs +++ b/zebra-consensus/src/block/subsidy/funding_streams.rs @@ -56,12 +56,7 @@ 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, - } + network.height_for_first_halving() } /// Returns the address change period @@ -93,10 +88,7 @@ fn funding_stream_address_period(height: Height, network: Network) -> u32 { /// /// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams fn funding_stream_address_index(height: Height, network: Network) -> usize { - let num_addresses = match network { - Network::Mainnet => FUNDING_STREAMS_NUM_ADDRESSES_MAINNET, - Network::Testnet => FUNDING_STREAMS_NUM_ADDRESSES_TESTNET, - }; + let num_addresses = network.num_funding_streams(); let index = 1u32 .checked_add(funding_stream_address_period(height, network)) diff --git a/zebra-consensus/src/block/subsidy/general.rs b/zebra-consensus/src/block/subsidy/general.rs index 5be97f3a990..57f33ee1237 100644 --- a/zebra-consensus/src/block/subsidy/general.rs +++ b/zebra-consensus/src/block/subsidy/general.rs @@ -131,11 +131,7 @@ mod test { fn halving_for_network(network: Network) -> Result<(), Report> { let blossom_height = Blossom.activation_height(network).unwrap(); - let first_halving_height = match network { - Network::Mainnet => Canopy.activation_height(network).unwrap(), - // Based on "7.8 Calculation of Block Subsidy and Founders' Reward" - Network::Testnet => Height(1_116_000), - }; + let first_halving_height = network.height_for_first_halving(); assert_eq!( 1, diff --git a/zebra-consensus/src/checkpoint/list.rs b/zebra-consensus/src/checkpoint/list.rs index a22ce888c4b..98a64667064 100644 --- a/zebra-consensus/src/checkpoint/list.rs +++ b/zebra-consensus/src/checkpoint/list.rs @@ -94,7 +94,7 @@ impl CheckpointList { }; match checkpoint_list.hash(block::Height(0)) { - Some(hash) if hash == genesis_hash(network) => checkpoint_list, + Some(hash) if hash == network.genesis_hash() => checkpoint_list, Some(_) => { panic!("The hard-coded genesis checkpoint does not match the network genesis hash") } @@ -123,8 +123,8 @@ impl CheckpointList { // Check that the list starts with the correct genesis block match checkpoints.iter().next() { Some((block::Height(0), hash)) - if (hash == &genesis_hash(Network::Mainnet) - || hash == &genesis_hash(Network::Testnet)) => {} + if (hash == &Network::Mainnet.genesis_hash() + || hash == &Network::Testnet.genesis_hash()) => {} Some((block::Height(0), _)) => { Err("the genesis checkpoint does not match the Mainnet or Testnet genesis hash")? } diff --git a/zebra-consensus/src/parameters/subsidy.rs b/zebra-consensus/src/parameters/subsidy.rs index ba8f910d8e3..80df5b97c0c 100644 --- a/zebra-consensus/src/parameters/subsidy.rs +++ b/zebra-consensus/src/parameters/subsidy.rs @@ -198,6 +198,18 @@ pub const FUNDING_STREAM_ECC_ADDRESSES_MAINNET: [&str; FUNDING_STREAMS_NUM_ADDRE "t3XHAGxRP2FNfhAjxGjxbrQPYtQQjc3RCQD", ]; +pub trait ParameterSubsidy { + fn num_funding_streams(&self) -> usize; +} + +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, + } + } +} /// 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] = ["t3dvVE3SQEi7kqNzwrfNePxZ1d4hUyztBA1"; FUNDING_STREAMS_NUM_ADDRESSES_MAINNET]; diff --git a/zebra-network/src/protocol/external/codec.rs b/zebra-network/src/protocol/external/codec.rs index 88f54c6b242..79b65b141b5 100644 --- a/zebra-network/src/protocol/external/codec.rs +++ b/zebra-network/src/protocol/external/codec.rs @@ -13,7 +13,10 @@ use tokio_util::codec::{Decoder, Encoder}; use zebra_chain::{ block::{self, Block}, - parameters::Network, + parameters::{ + network::{AllParameters as _, Magic}, + Network, + }, serialization::{ sha256d, zcash_deserialize_bytes_external_count, zcash_deserialize_string_external_count, CompactSizeMessage, FakeWriter, ReadZcashExt, SerializationError as Error, @@ -163,7 +166,7 @@ impl Encoder for Codec { let start_len = dst.len(); { 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_u32::(body_length as u32)?; @@ -389,7 +392,7 @@ impl Decoder for Codec { "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")); } if body_len > self.builder.max_len {