zebra_chain/parameters/
network.rs

1//! Consensus parameters for each Zcash network.
2
3use std::{fmt, str::FromStr, sync::Arc};
4
5use thiserror::Error;
6
7use crate::{
8    block::{self, Height},
9    parameters::NetworkUpgrade,
10};
11
12pub mod magic;
13pub mod subsidy;
14pub mod testnet;
15
16#[cfg(test)]
17mod tests;
18
19/// 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]
27    Mainnet,
28
29    /// A test network.
30    Testnet,
31
32    /// Regtest mode
33    Regtest,
34}
35
36impl From<Network> for NetworkKind {
37    fn from(net: Network) -> Self {
38        NetworkKind::from(&net)
39    }
40}
41
42impl From<&Network> for NetworkKind {
43    fn from(net: &Network) -> Self {
44        net.kind()
45    }
46}
47
48/// 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]
54    Mainnet,
55
56    /// A test network such as the default public testnet,
57    /// a configured testnet, or Regtest.
58    Testnet(Arc<testnet::Parameters>),
59}
60
61impl NetworkKind {
62    /// Returns the human-readable prefix for Base58Check-encoded transparent
63    /// pay-to-public-key-hash payment addresses for the network.
64    pub fn b58_pubkey_address_prefix(self) -> [u8; 2] {
65        match self {
66            Self::Mainnet => zcash_primitives::constants::mainnet::B58_PUBKEY_ADDRESS_PREFIX,
67            Self::Testnet | Self::Regtest => {
68                zcash_primitives::constants::testnet::B58_PUBKEY_ADDRESS_PREFIX
69            }
70        }
71    }
72
73    /// Returns the human-readable prefix for Base58Check-encoded transparent pay-to-script-hash
74    /// payment addresses for the network.
75    pub fn b58_script_address_prefix(self) -> [u8; 2] {
76        match self {
77            Self::Mainnet => zcash_primitives::constants::mainnet::B58_SCRIPT_ADDRESS_PREFIX,
78            Self::Testnet | Self::Regtest => {
79                zcash_primitives::constants::testnet::B58_SCRIPT_ADDRESS_PREFIX
80            }
81        }
82    }
83
84    /// Return the network name as defined in
85    /// [BIP70](https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#paymentdetailspaymentrequest)
86    pub fn bip70_network_name(&self) -> String {
87        if *self == Self::Mainnet {
88            "main".to_string()
89        } else {
90            "test".to_string()
91        }
92    }
93
94    /// 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).
96    pub fn tex_address_prefix(self) -> [u8; 2] {
97        // TODO: Add this bytes to `zcash_primitives::constants`?
98        match self {
99            Self::Mainnet => [0x1c, 0xb8],
100            Self::Testnet | Self::Regtest => [0x1d, 0x25],
101        }
102    }
103}
104
105impl From<NetworkKind> for &'static str {
106    fn 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.
110        match network {
111            NetworkKind::Mainnet => "MainnetKind",
112            NetworkKind::Testnet => "TestnetKind",
113            NetworkKind::Regtest => "RegtestKind",
114        }
115    }
116}
117
118impl fmt::Display for NetworkKind {
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        f.write_str((*self).into())
121    }
122}
123
124impl<'a> From<&'a Network> for &'a str {
125    fn from(network: &'a Network) -> &'a str {
126        match network {
127            Network::Mainnet => "Mainnet",
128            Network::Testnet(params) => params.network_name(),
129        }
130    }
131}
132
133impl fmt::Display for Network {
134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135        f.write_str(self.into())
136    }
137}
138
139impl std::fmt::Debug for Network {
140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141        match self {
142            Self::Mainnet => write!(f, "{self}"),
143            Self::Testnet(params) if params.is_regtest() => f
144                .debug_struct("Regtest")
145                .field("activation_heights", params.activation_heights())
146                .finish(),
147            Self::Testnet(params) if params.is_default_testnet() => {
148                write!(f, "{self}")
149            }
150            Self::Testnet(params) => f.debug_tuple("ConfiguredTestnet").field(params).finish(),
151        }
152    }
153}
154
155impl Network {
156    /// Creates a new [`Network::Testnet`] with the default Testnet [`testnet::Parameters`].
157    pub fn new_default_testnet() -> Self {
158        Self::Testnet(Arc::new(testnet::Parameters::default()))
159    }
160
161    /// Creates a new configured [`Network::Testnet`] with the provided Testnet [`testnet::Parameters`].
162    pub fn new_configured_testnet(params: testnet::Parameters) -> Self {
163        Self::Testnet(Arc::new(params))
164    }
165
166    /// Creates a new [`Network::Testnet`] with `Regtest` parameters and the provided network upgrade activation heights.
167    pub fn new_regtest(
168        configured_activation_heights: testnet::ConfiguredActivationHeights,
169    ) -> Self {
170        Self::new_configured_testnet(testnet::Parameters::new_regtest(
171            configured_activation_heights,
172        ))
173    }
174
175    /// Returns true if the network is the default Testnet, or false otherwise.
176    pub fn is_default_testnet(&self) -> bool {
177        if let Self::Testnet(params) = self {
178            params.is_default_testnet()
179        } else {
180            false
181        }
182    }
183
184    /// Returns true if the network is Regtest, or false otherwise.
185    pub fn is_regtest(&self) -> bool {
186        if let Self::Testnet(params) = self {
187            params.is_regtest()
188        } else {
189            false
190        }
191    }
192
193    /// Returns the [`NetworkKind`] for this network.
194    pub fn kind(&self) -> NetworkKind {
195        match self {
196            Network::Mainnet => NetworkKind::Mainnet,
197            Network::Testnet(params) if params.is_regtest() => NetworkKind::Regtest,
198            Network::Testnet(_) => NetworkKind::Testnet,
199        }
200    }
201
202    /// 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.
205    pub fn t_addr_kind(&self) -> NetworkKind {
206        match self {
207            Network::Mainnet => NetworkKind::Mainnet,
208            Network::Testnet(_) => NetworkKind::Testnet,
209        }
210    }
211
212    /// Returns an iterator over [`Network`] variants.
213    pub fn iter() -> impl Iterator<Item = Self> {
214        [Self::Mainnet, Self::new_default_testnet()].into_iter()
215    }
216
217    /// 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>
225    pub fn is_max_block_time_enforced(&self, height: block::Height) -> bool {
226        match self {
227            Network::Mainnet => true,
228            // TODO: Move `TESTNET_MAX_TIME_START_HEIGHT` to a field on testnet::Parameters (#8364)
229            Network::Testnet(_params) => height >= super::TESTNET_MAX_TIME_START_HEIGHT,
230        }
231    }
232
233    /// Get the default port associated to this network.
234    pub fn default_port(&self) -> u16 {
235        match self {
236            Network::Mainnet => 8233,
237            // TODO: Add a `default_port` field to `testnet::Parameters` to return here. (zcashd uses 18344 for Regtest)
238            Network::Testnet(_params) => 18233,
239        }
240    }
241
242    /// 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)
251    pub fn mandatory_checkpoint_height(&self) -> Height {
252        // Currently this is just before Canopy activation
253        NetworkUpgrade::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    }
259
260    /// Return the network name as defined in
261    /// [BIP70](https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#paymentdetailspaymentrequest)
262    pub fn bip70_network_name(&self) -> String {
263        self.kind().bip70_network_name()
264    }
265
266    /// Return the lowercase network name.
267    pub fn lowercase_name(&self) -> String {
268        self.to_string().to_ascii_lowercase()
269    }
270
271    /// Returns `true` if this network is a testing network.
272    pub fn is_a_test_network(&self) -> bool {
273        *self != Network::Mainnet
274    }
275
276    /// 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
278    pub fn sapling_activation_height(&self) -> Height {
279        super::NetworkUpgrade::Sapling
280            .activation_height(self)
281            .expect("Sapling activation height needs to be set")
282    }
283}
284
285// This is used for parsing a command-line argument for the `TipHeight` command in zebrad.
286impl FromStr for Network {
287    type Err = InvalidNetworkError;
288
289    fn from_str(string: &str) -> Result<Self, Self::Err> {
290        match 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}
297
298#[derive(Clone, Debug, Error)]
299#[error("Invalid network: {0}")]
300pub struct InvalidNetworkError(String);
301
302impl zcash_protocol::consensus::Parameters for Network {
303    fn network_type(&self) -> zcash_protocol::consensus::NetworkType {
304        self.kind().into()
305    }
306
307    fn 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}