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(net: Network) -> Self {
38 NetworkKind::from(&net)
39 }
40}
4142impl From<&Network> for NetworkKind {
43fn from(net: &Network) -> Self {
44 net.kind()
45 }
46}
4748/// An enum describing the possible network choices.
49#[derive(Clone, Default, Eq, PartialEq, Serialize)]
50#[serde(into = "NetworkKind")]
51pub enum Network {
52/// The production mainnet.
53#[default]
54Mainnet,
5556/// A test network such as the default public testnet,
57 /// a configured testnet, or Regtest.
58Testnet(Arc<testnet::Parameters>),
59}
6061impl NetworkKind {
62/// Returns the human-readable prefix for Base58Check-encoded transparent
63 /// pay-to-public-key-hash payment addresses for the network.
64pub fn b58_pubkey_address_prefix(self) -> [u8; 2] {
65match self {
66Self::Mainnet => zcash_primitives::constants::mainnet::B58_PUBKEY_ADDRESS_PREFIX,
67Self::Testnet | Self::Regtest => {
68 zcash_primitives::constants::testnet::B58_PUBKEY_ADDRESS_PREFIX
69 }
70 }
71 }
7273/// Returns the human-readable prefix for Base58Check-encoded transparent pay-to-script-hash
74 /// payment addresses for the network.
75pub fn b58_script_address_prefix(self) -> [u8; 2] {
76match self {
77Self::Mainnet => zcash_primitives::constants::mainnet::B58_SCRIPT_ADDRESS_PREFIX,
78Self::Testnet | Self::Regtest => {
79 zcash_primitives::constants::testnet::B58_SCRIPT_ADDRESS_PREFIX
80 }
81 }
82 }
8384/// Return the network name as defined in
85 /// [BIP70](https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#paymentdetailspaymentrequest)
86pub fn bip70_network_name(&self) -> String {
87if *self == Self::Mainnet {
88"main".to_string()
89 } else {
90"test".to_string()
91 }
92 }
9394/// Returns the 2 bytes prefix for Bech32m-encoded transparent TEX
95 /// payment addresses for the network as defined in [ZIP-320](https://zips.z.cash/zip-0320.html).
96pub fn tex_address_prefix(self) -> [u8; 2] {
97// TODO: Add this bytes to `zcash_primitives::constants`?
98match self {
99Self::Mainnet => [0x1c, 0xb8],
100Self::Testnet | Self::Regtest => [0x1d, 0x25],
101 }
102 }
103}
104105impl From<NetworkKind> for &'static str {
106fn from(network: NetworkKind) -> &'static str {
107// These should be different from the `Display` impl for `Network` so that its lowercase form
108 // can't be parsed as the default Testnet in the `Network` `FromStr` impl, it's easy to
109 // distinguish them in logs, and so it's generally harder to confuse the two.
110match network {
111 NetworkKind::Mainnet => "MainnetKind",
112 NetworkKind::Testnet => "TestnetKind",
113 NetworkKind::Regtest => "RegtestKind",
114 }
115 }
116}
117118impl fmt::Display for NetworkKind {
119fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 f.write_str((*self).into())
121 }
122}
123124impl<'a> From<&'a Network> for &'a str {
125fn from(network: &'a Network) -> &'a str {
126match network {
127 Network::Mainnet => "Mainnet",
128 Network::Testnet(params) => params.network_name(),
129 }
130 }
131}
132133impl fmt::Display for Network {
134fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 f.write_str(self.into())
136 }
137}
138139impl std::fmt::Debug for Network {
140fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141match self {
142Self::Mainnet => write!(f, "{self}"),
143Self::Testnet(params) if params.is_regtest() => f
144 .debug_struct("Regtest")
145 .field("activation_heights", params.activation_heights())
146 .finish(),
147Self::Testnet(params) if params.is_default_testnet() => {
148write!(f, "{self}")
149 }
150Self::Testnet(params) => f.debug_tuple("ConfiguredTestnet").field(params).finish(),
151 }
152 }
153}
154155impl Network {
156/// Creates a new [`Network::Testnet`] with the default Testnet [`testnet::Parameters`].
157pub fn new_default_testnet() -> Self {
158Self::Testnet(Arc::new(testnet::Parameters::default()))
159 }
160161/// Creates a new configured [`Network::Testnet`] with the provided Testnet [`testnet::Parameters`].
162pub fn new_configured_testnet(params: testnet::Parameters) -> Self {
163Self::Testnet(Arc::new(params))
164 }
165166/// Creates a new [`Network::Testnet`] with `Regtest` parameters and the provided network upgrade activation heights.
167pub fn new_regtest(
168 configured_activation_heights: testnet::ConfiguredActivationHeights,
169 ) -> Self {
170Self::new_configured_testnet(testnet::Parameters::new_regtest(
171 configured_activation_heights,
172 ))
173 }
174175/// Returns true if the network is the default Testnet, or false otherwise.
176pub fn is_default_testnet(&self) -> bool {
177if let Self::Testnet(params) = self {
178 params.is_default_testnet()
179 } else {
180false
181}
182 }
183184/// Returns true if the network is Regtest, or false otherwise.
185pub fn is_regtest(&self) -> bool {
186if let Self::Testnet(params) = self {
187 params.is_regtest()
188 } else {
189false
190}
191 }
192193/// Returns the [`NetworkKind`] for this network.
194pub fn kind(&self) -> NetworkKind {
195match self {
196 Network::Mainnet => NetworkKind::Mainnet,
197 Network::Testnet(params) if params.is_regtest() => NetworkKind::Regtest,
198 Network::Testnet(_) => NetworkKind::Testnet,
199 }
200 }
201202/// Returns [`NetworkKind::Testnet`] on Testnet and Regtest, or [`NetworkKind::Mainnet`] on Mainnet.
203 ///
204 /// This is used for transparent addresses, as the address prefix is the same on Regtest as it is on Testnet.
205pub fn t_addr_kind(&self) -> NetworkKind {
206match self {
207 Network::Mainnet => NetworkKind::Mainnet,
208 Network::Testnet(_) => NetworkKind::Testnet,
209 }
210 }
211212/// Returns an iterator over [`Network`] variants.
213pub fn iter() -> impl Iterator<Item = Self> {
214 [Self::Mainnet, Self::new_default_testnet()].into_iter()
215 }
216217/// Returns true if the maximum block time rule is active for `network` and `height`.
218 ///
219 /// Always returns true if `network` is the Mainnet.
220 /// If `network` is the Testnet, the `height` should be at least
221 /// TESTNET_MAX_TIME_START_HEIGHT to return true.
222 /// Returns false otherwise.
223 ///
224 /// Part of the consensus rules at <https://zips.z.cash/protocol/protocol.pdf#blockheader>
225pub fn is_max_block_time_enforced(&self, height: block::Height) -> bool {
226match self {
227 Network::Mainnet => true,
228// TODO: Move `TESTNET_MAX_TIME_START_HEIGHT` to a field on testnet::Parameters (#8364)
229Network::Testnet(_params) => height >= super::TESTNET_MAX_TIME_START_HEIGHT,
230 }
231 }
232233/// Get the default port associated to this network.
234pub fn default_port(&self) -> u16 {
235match self {
236 Network::Mainnet => 8233,
237// TODO: Add a `default_port` field to `testnet::Parameters` to return here. (zcashd uses 18344 for Regtest)
238Network::Testnet(_params) => 18233,
239 }
240 }
241242/// Get the mandatory minimum checkpoint height for this network.
243 ///
244 /// Mandatory checkpoints are a Zebra-specific feature.
245 /// If a Zcash consensus rule only applies before the mandatory checkpoint,
246 /// Zebra can skip validation of that rule.
247 /// This is necessary because Zebra can't fully validate the blocks prior to Canopy.
248// TODO:
249 // - Support constructing pre-Canopy coinbase tx and block templates and return `Height::MAX` instead of panicking
250 // when Canopy activation height is `None` (#8434)
251pub fn mandatory_checkpoint_height(&self) -> Height {
252// Currently this is just before Canopy activation
253NetworkUpgrade::Canopy
254 .activation_height(self)
255 .expect("Canopy activation height must be present on all networks")
256 .previous()
257 .expect("Canopy activation height must be above min height")
258 }
259260/// Return the network name as defined in
261 /// [BIP70](https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#paymentdetailspaymentrequest)
262pub fn bip70_network_name(&self) -> String {
263self.kind().bip70_network_name()
264 }
265266/// Return the lowercase network name.
267pub fn lowercase_name(&self) -> String {
268self.to_string().to_ascii_lowercase()
269 }
270271/// Returns `true` if this network is a testing network.
272pub fn is_a_test_network(&self) -> bool {
273*self != Network::Mainnet
274 }
275276/// Returns the Sapling activation height for this network.
277// TODO: Return an `Option` here now that network upgrade activation heights are configurable on Regtest and custom Testnets
278pub fn sapling_activation_height(&self) -> Height {
279super::NetworkUpgrade::Sapling
280 .activation_height(self)
281 .expect("Sapling activation height needs to be set")
282 }
283}
284285// This is used for parsing a command-line argument for the `TipHeight` command in zebrad.
286impl FromStr for Network {
287type Err = InvalidNetworkError;
288289fn from_str(string: &str) -> Result<Self, Self::Err> {
290match string.to_lowercase().as_str() {
291"mainnet" => Ok(Network::Mainnet),
292"testnet" => Ok(Network::new_default_testnet()),
293_ => Err(InvalidNetworkError(string.to_owned())),
294 }
295 }
296}
297298#[derive(Clone, Debug, Error)]
299#[error("Invalid network: {0}")]
300pub struct InvalidNetworkError(String);
301302impl zcash_protocol::consensus::Parameters for Network {
303fn network_type(&self) -> zcash_protocol::consensus::NetworkType {
304self.kind().into()
305 }
306307fn activation_height(
308&self,
309 nu: zcash_protocol::consensus::NetworkUpgrade,
310 ) -> Option<zcash_protocol::consensus::BlockHeight> {
311 NetworkUpgrade::from(nu)
312 .activation_height(self)
313 .map(|Height(h)| zcash_protocol::consensus::BlockHeight::from_u32(h))
314 }
315}