1//! Consensus parameters for each Zcash network.
23use std::{fmt, str::FromStr, sync::Arc};
45use thiserror::Error;
67use crate::{
8 block::{self, Height},
9 parameters::NetworkUpgrade,
10};
1112pub mod magic;
13pub mod subsidy;
14pub mod testnet;
1516#[cfg(test)]
17mod tests;
1819/// An enum describing the kind of network, whether it's the production mainnet or a testnet.
20// Note: The order of these variants is important for correct bincode (de)serialization
21// of history trees in the db format.
22// TODO: Replace bincode (de)serialization of `HistoryTreeParts` in a db format upgrade?
23#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
24pub enum NetworkKind {
25/// The production mainnet.
26#[default]
27Mainnet,
2829/// A test network.
30Testnet,
3132/// Regtest mode
33Regtest,
34}
3536impl From<Network> for NetworkKind {
37fn from(network: Network) -> Self {
38 network.kind()
39 }
40}
4142/// An enum describing the possible network choices.
43#[derive(Clone, Default, Eq, PartialEq, Serialize)]
44#[serde(into = "NetworkKind")]
45pub enum Network {
46/// The production mainnet.
47#[default]
48Mainnet,
4950/// A test network such as the default public testnet,
51 /// a configured testnet, or Regtest.
52Testnet(Arc<testnet::Parameters>),
53}
5455impl NetworkKind {
56/// Returns the human-readable prefix for Base58Check-encoded transparent
57 /// pay-to-public-key-hash payment addresses for the network.
58pub fn b58_pubkey_address_prefix(self) -> [u8; 2] {
59match self {
60Self::Mainnet => zcash_primitives::constants::mainnet::B58_PUBKEY_ADDRESS_PREFIX,
61Self::Testnet | Self::Regtest => {
62 zcash_primitives::constants::testnet::B58_PUBKEY_ADDRESS_PREFIX
63 }
64 }
65 }
6667/// Returns the human-readable prefix for Base58Check-encoded transparent pay-to-script-hash
68 /// payment addresses for the network.
69pub fn b58_script_address_prefix(self) -> [u8; 2] {
70match self {
71Self::Mainnet => zcash_primitives::constants::mainnet::B58_SCRIPT_ADDRESS_PREFIX,
72Self::Testnet | Self::Regtest => {
73 zcash_primitives::constants::testnet::B58_SCRIPT_ADDRESS_PREFIX
74 }
75 }
76 }
7778/// Return the network name as defined in
79 /// [BIP70](https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#paymentdetailspaymentrequest)
80pub fn bip70_network_name(&self) -> String {
81if *self == Self::Mainnet {
82"main".to_string()
83 } else {
84"test".to_string()
85 }
86 }
8788/// Returns the 2 bytes prefix for Bech32m-encoded transparent TEX
89 /// payment addresses for the network as defined in [ZIP-320](https://zips.z.cash/zip-0320.html).
90pub fn tex_address_prefix(self) -> [u8; 2] {
91// TODO: Add this bytes to `zcash_primitives::constants`?
92match self {
93Self::Mainnet => [0x1c, 0xb8],
94Self::Testnet | Self::Regtest => [0x1d, 0x25],
95 }
96 }
97}
9899impl From<NetworkKind> for &'static str {
100fn from(network: NetworkKind) -> &'static str {
101// These should be different from the `Display` impl for `Network` so that its lowercase form
102 // can't be parsed as the default Testnet in the `Network` `FromStr` impl, it's easy to
103 // distinguish them in logs, and so it's generally harder to confuse the two.
104match network {
105 NetworkKind::Mainnet => "MainnetKind",
106 NetworkKind::Testnet => "TestnetKind",
107 NetworkKind::Regtest => "RegtestKind",
108 }
109 }
110}
111112impl fmt::Display for NetworkKind {
113fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 f.write_str((*self).into())
115 }
116}
117118impl<'a> From<&'a Network> for &'a str {
119fn from(network: &'a Network) -> &'a str {
120match network {
121 Network::Mainnet => "Mainnet",
122 Network::Testnet(params) => params.network_name(),
123 }
124 }
125}
126127impl fmt::Display for Network {
128fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 f.write_str(self.into())
130 }
131}
132133impl std::fmt::Debug for Network {
134fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135match self {
136Self::Mainnet => write!(f, "{self}"),
137Self::Testnet(params) if params.is_regtest() => f
138 .debug_struct("Regtest")
139 .field("activation_heights", params.activation_heights())
140 .finish(),
141Self::Testnet(params) if params.is_default_testnet() => {
142write!(f, "{self}")
143 }
144Self::Testnet(params) => f.debug_tuple("ConfiguredTestnet").field(params).finish(),
145 }
146 }
147}
148149impl Network {
150/// Creates a new [`Network::Testnet`] with the default Testnet [`testnet::Parameters`].
151pub fn new_default_testnet() -> Self {
152Self::Testnet(Arc::new(testnet::Parameters::default()))
153 }
154155/// Creates a new configured [`Network::Testnet`] with the provided Testnet [`testnet::Parameters`].
156pub fn new_configured_testnet(params: testnet::Parameters) -> Self {
157Self::Testnet(Arc::new(params))
158 }
159160/// Creates a new [`Network::Testnet`] with `Regtest` parameters and the provided network upgrade activation heights.
161pub fn new_regtest(
162 configured_activation_heights: testnet::ConfiguredActivationHeights,
163 ) -> Self {
164Self::new_configured_testnet(testnet::Parameters::new_regtest(
165 configured_activation_heights,
166 ))
167 }
168169/// Returns true if the network is the default Testnet, or false otherwise.
170pub fn is_default_testnet(&self) -> bool {
171if let Self::Testnet(params) = self {
172 params.is_default_testnet()
173 } else {
174false
175}
176 }
177178/// Returns true if the network is Regtest, or false otherwise.
179pub fn is_regtest(&self) -> bool {
180if let Self::Testnet(params) = self {
181 params.is_regtest()
182 } else {
183false
184}
185 }
186187/// Returns the [`NetworkKind`] for this network.
188pub fn kind(&self) -> NetworkKind {
189match self {
190 Network::Mainnet => NetworkKind::Mainnet,
191 Network::Testnet(params) if params.is_regtest() => NetworkKind::Regtest,
192 Network::Testnet(_) => NetworkKind::Testnet,
193 }
194 }
195196/// Returns [`NetworkKind::Testnet`] on Testnet and Regtest, or [`NetworkKind::Mainnet`] on Mainnet.
197 ///
198 /// This is used for transparent addresses, as the address prefix is the same on Regtest as it is on Testnet.
199pub fn t_addr_kind(&self) -> NetworkKind {
200match self {
201 Network::Mainnet => NetworkKind::Mainnet,
202 Network::Testnet(_) => NetworkKind::Testnet,
203 }
204 }
205206/// Returns an iterator over [`Network`] variants.
207pub fn iter() -> impl Iterator<Item = Self> {
208 [Self::Mainnet, Self::new_default_testnet()].into_iter()
209 }
210211/// Returns true if the maximum block time rule is active for `network` and `height`.
212 ///
213 /// Always returns true if `network` is the Mainnet.
214 /// If `network` is the Testnet, the `height` should be at least
215 /// TESTNET_MAX_TIME_START_HEIGHT to return true.
216 /// Returns false otherwise.
217 ///
218 /// Part of the consensus rules at <https://zips.z.cash/protocol/protocol.pdf#blockheader>
219pub fn is_max_block_time_enforced(&self, height: block::Height) -> bool {
220match self {
221 Network::Mainnet => true,
222// TODO: Move `TESTNET_MAX_TIME_START_HEIGHT` to a field on testnet::Parameters (#8364)
223Network::Testnet(_params) => height >= super::TESTNET_MAX_TIME_START_HEIGHT,
224 }
225 }
226227/// Get the default port associated to this network.
228pub fn default_port(&self) -> u16 {
229match self {
230 Network::Mainnet => 8233,
231// TODO: Add a `default_port` field to `testnet::Parameters` to return here. (zcashd uses 18344 for Regtest)
232Network::Testnet(_params) => 18233,
233 }
234 }
235236/// Get the mandatory minimum checkpoint height for this network.
237 ///
238 /// Mandatory checkpoints are a Zebra-specific feature.
239 /// If a Zcash consensus rule only applies before the mandatory checkpoint,
240 /// Zebra can skip validation of that rule.
241 /// This is necessary because Zebra can't fully validate the blocks prior to Canopy.
242// TODO:
243 // - Support constructing pre-Canopy coinbase tx and block templates and return `Height::MAX` instead of panicking
244 // when Canopy activation height is `None` (#8434)
245pub fn mandatory_checkpoint_height(&self) -> Height {
246// Currently this is just before Canopy activation
247NetworkUpgrade::Canopy
248 .activation_height(self)
249 .expect("Canopy activation height must be present on all networks")
250 .previous()
251 .expect("Canopy activation height must be above min height")
252 }
253254/// Return the network name as defined in
255 /// [BIP70](https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#paymentdetailspaymentrequest)
256pub fn bip70_network_name(&self) -> String {
257self.kind().bip70_network_name()
258 }
259260/// Return the lowercase network name.
261pub fn lowercase_name(&self) -> String {
262self.to_string().to_ascii_lowercase()
263 }
264265/// Returns `true` if this network is a testing network.
266pub fn is_a_test_network(&self) -> bool {
267*self != Network::Mainnet
268 }
269270/// Returns the Sapling activation height for this network.
271// TODO: Return an `Option` here now that network upgrade activation heights are configurable on Regtest and custom Testnets
272pub fn sapling_activation_height(&self) -> Height {
273super::NetworkUpgrade::Sapling
274 .activation_height(self)
275 .expect("Sapling activation height needs to be set")
276 }
277}
278279// This is used for parsing a command-line argument for the `TipHeight` command in zebrad.
280impl FromStr for Network {
281type Err = InvalidNetworkError;
282283fn from_str(string: &str) -> Result<Self, Self::Err> {
284match string.to_lowercase().as_str() {
285"mainnet" => Ok(Network::Mainnet),
286"testnet" => Ok(Network::new_default_testnet()),
287_ => Err(InvalidNetworkError(string.to_owned())),
288 }
289 }
290}
291292#[derive(Clone, Debug, Error)]
293#[error("Invalid network: {0}")]
294pub struct InvalidNetworkError(String);
295296impl zcash_protocol::consensus::Parameters for Network {
297fn network_type(&self) -> zcash_protocol::consensus::NetworkType {
298self.kind().into()
299 }
300301fn activation_height(
302&self,
303 nu: zcash_protocol::consensus::NetworkUpgrade,
304 ) -> Option<zcash_protocol::consensus::BlockHeight> {
305 NetworkUpgrade::from(nu)
306 .activation_height(self)
307 .map(|Height(h)| zcash_protocol::consensus::BlockHeight::from_u32(h))
308 }
309}