zebra_chain/parameters/
network.rs1use std::{fmt, str::FromStr, sync::Arc};
4
5use thiserror::Error;
6
7use crate::{
8 amount::{Amount, NonNegative},
9 block::{self, Height},
10 parameters::NetworkUpgrade,
11 transparent,
12};
13
14mod error;
15pub mod magic;
16pub mod subsidy;
17pub mod testnet;
18
19#[cfg(test)]
20mod tests;
21
22const MAINNET_TEMPORARY_ORCHARD_DISABLING_SOFT_FORK_HEIGHT: Height = Height(3_363_426);
27
28const TESTNET_TEMPORARY_ORCHARD_DISABLING_SOFT_FORK_HEIGHT: Height = Height(4_048_500);
32
33#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
38pub enum NetworkKind {
39 #[default]
41 Mainnet,
42
43 Testnet,
45
46 Regtest,
48}
49
50impl From<Network> for NetworkKind {
51 fn from(net: Network) -> Self {
52 NetworkKind::from(&net)
53 }
54}
55
56impl From<&Network> for NetworkKind {
57 fn from(net: &Network) -> Self {
58 net.kind()
59 }
60}
61
62#[derive(Clone, Default, Eq, PartialEq, Serialize)]
64#[serde(into = "NetworkKind")]
65pub enum Network {
66 #[default]
68 Mainnet,
69
70 Testnet(Arc<testnet::Parameters>),
73}
74
75impl NetworkKind {
76 pub fn b58_pubkey_address_prefix(self) -> [u8; 2] {
79 match self {
80 Self::Mainnet => zcash_protocol::constants::mainnet::B58_PUBKEY_ADDRESS_PREFIX,
81 Self::Testnet | Self::Regtest => {
82 zcash_protocol::constants::testnet::B58_PUBKEY_ADDRESS_PREFIX
83 }
84 }
85 }
86
87 pub fn b58_script_address_prefix(self) -> [u8; 2] {
90 match self {
91 Self::Mainnet => zcash_protocol::constants::mainnet::B58_SCRIPT_ADDRESS_PREFIX,
92 Self::Testnet | Self::Regtest => {
93 zcash_protocol::constants::testnet::B58_SCRIPT_ADDRESS_PREFIX
94 }
95 }
96 }
97
98 pub fn bip70_network_name(&self) -> String {
101 if *self == Self::Mainnet {
102 "main".to_string()
103 } else {
104 "test".to_string()
105 }
106 }
107
108 pub fn tex_address_prefix(self) -> [u8; 2] {
111 match self {
113 Self::Mainnet => [0x1c, 0xb8],
114 Self::Testnet | Self::Regtest => [0x1d, 0x25],
115 }
116 }
117}
118
119impl From<NetworkKind> for &'static str {
120 fn from(network: NetworkKind) -> &'static str {
121 match network {
125 NetworkKind::Mainnet => "MainnetKind",
126 NetworkKind::Testnet => "TestnetKind",
127 NetworkKind::Regtest => "RegtestKind",
128 }
129 }
130}
131
132impl fmt::Display for NetworkKind {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 f.write_str((*self).into())
135 }
136}
137
138impl<'a> From<&'a Network> for &'a str {
139 fn from(network: &'a Network) -> &'a str {
140 match network {
141 Network::Mainnet => "Mainnet",
142 Network::Testnet(params) => params.network_name(),
143 }
144 }
145}
146
147impl fmt::Display for Network {
148 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149 f.write_str(self.into())
150 }
151}
152
153impl std::fmt::Debug for Network {
154 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155 match self {
156 Self::Mainnet => write!(f, "{self}"),
157 Self::Testnet(params) if params.is_regtest() => f
158 .debug_struct("Regtest")
159 .field("activation_heights", params.activation_heights())
160 .field("funding_streams", params.funding_streams())
161 .field("lockbox_disbursements", ¶ms.lockbox_disbursements())
162 .field("checkpoints", ¶ms.checkpoints())
163 .finish(),
164 Self::Testnet(params) if params.is_default_testnet() => {
165 write!(f, "{self}")
166 }
167 Self::Testnet(params) => f.debug_tuple("ConfiguredTestnet").field(params).finish(),
168 }
169 }
170}
171
172impl Network {
173 pub fn new_default_testnet() -> Self {
175 Self::Testnet(Arc::new(testnet::Parameters::default()))
176 }
177
178 pub fn new_configured_testnet(params: testnet::Parameters) -> Self {
180 Self::Testnet(Arc::new(params))
181 }
182
183 pub fn new_regtest(params: testnet::RegtestParameters) -> Self {
185 Self::new_configured_testnet(
186 testnet::Parameters::new_regtest(params)
187 .expect("regtest parameters should always be valid"),
188 )
189 }
190
191 pub fn is_default_testnet(&self) -> bool {
193 if let Self::Testnet(params) = self {
194 params.is_default_testnet()
195 } else {
196 false
197 }
198 }
199
200 pub fn is_regtest(&self) -> bool {
202 if let Self::Testnet(params) = self {
203 params.is_regtest()
204 } else {
205 false
206 }
207 }
208
209 pub fn kind(&self) -> NetworkKind {
211 match self {
212 Network::Mainnet => NetworkKind::Mainnet,
213 Network::Testnet(params) if params.is_regtest() => NetworkKind::Regtest,
214 Network::Testnet(_) => NetworkKind::Testnet,
215 }
216 }
217
218 pub fn t_addr_kind(&self) -> NetworkKind {
222 match self {
223 Network::Mainnet => NetworkKind::Mainnet,
224 Network::Testnet(_) => NetworkKind::Testnet,
225 }
226 }
227
228 pub fn iter() -> impl Iterator<Item = Self> {
230 [Self::Mainnet, Self::new_default_testnet()].into_iter()
231 }
232
233 pub fn is_max_block_time_enforced(&self, height: block::Height) -> bool {
242 match self {
243 Network::Mainnet => true,
244 Network::Testnet(_params) => height >= super::TESTNET_MAX_TIME_START_HEIGHT,
246 }
247 }
248
249 pub fn default_port(&self) -> u16 {
251 match self {
252 Network::Mainnet => 8233,
253 Network::Testnet(_params) => 18233,
255 }
256 }
257
258 pub fn mandatory_checkpoint_height(&self) -> Height {
268 NetworkUpgrade::Canopy
270 .activation_height(self)
271 .expect("Canopy activation height must be present on all networks")
272 .previous()
273 .expect("Canopy activation height must be above min height")
274 }
275
276 pub fn bip70_network_name(&self) -> String {
279 self.kind().bip70_network_name()
280 }
281
282 pub fn lowercase_name(&self) -> String {
284 self.to_string().to_ascii_lowercase()
285 }
286
287 pub fn is_a_test_network(&self) -> bool {
289 *self != Network::Mainnet
290 }
291
292 pub fn sapling_activation_height(&self) -> Height {
295 super::NetworkUpgrade::Sapling
296 .activation_height(self)
297 .expect("Sapling activation height needs to be set")
298 }
299
300 pub fn lockbox_disbursement_total_amount(&self, height: Height) -> Amount<NonNegative> {
303 if Some(height) != NetworkUpgrade::Nu6_1.activation_height(self) {
304 return Amount::zero();
305 };
306
307 match self {
308 Self::Mainnet => {
309 subsidy::constants::mainnet::EXPECTED_NU6_1_LOCKBOX_DISBURSEMENTS_TOTAL
310 }
311 Self::Testnet(params) if params.is_default_testnet() => {
312 subsidy::constants::testnet::EXPECTED_NU6_1_LOCKBOX_DISBURSEMENTS_TOTAL
313 }
314 Self::Testnet(params) => params.lockbox_disbursement_total_amount(),
315 }
316 }
317
318 pub fn lockbox_disbursements(
320 &self,
321 height: Height,
322 ) -> Vec<(transparent::Address, Amount<NonNegative>)> {
323 if Some(height) != NetworkUpgrade::Nu6_1.activation_height(self) {
324 return Vec::new();
325 };
326
327 let expected_lockbox_disbursements = match self {
328 Self::Mainnet => subsidy::constants::mainnet::NU6_1_LOCKBOX_DISBURSEMENTS.to_vec(),
329 Self::Testnet(params) if params.is_default_testnet() => {
330 subsidy::constants::testnet::NU6_1_LOCKBOX_DISBURSEMENTS.to_vec()
331 }
332 Self::Testnet(params) => return params.lockbox_disbursements(),
333 };
334
335 expected_lockbox_disbursements
336 .into_iter()
337 .map(|(addr, amount)| {
338 (
339 addr.parse().expect("hard-coded address must deserialize"),
340 amount,
341 )
342 })
343 .collect()
344 }
345
346 pub fn temporary_orchard_disabling_soft_fork_height(&self) -> Option<Height> {
349 match self {
350 Network::Mainnet => Some(MAINNET_TEMPORARY_ORCHARD_DISABLING_SOFT_FORK_HEIGHT),
351 Network::Testnet(parameters) => {
352 parameters.temporary_orchard_disabling_soft_fork_height()
353 }
354 }
355 }
356
357 pub fn temporary_orchard_disabling_soft_fork_active(&self, height: Height) -> bool {
359 self.temporary_orchard_disabling_soft_fork_height()
360 .is_some_and(|h| height >= h)
361 }
362
363 pub fn is_orchard_temporarily_disabled(&self, height: Height) -> bool {
370 self.temporary_orchard_disabling_soft_fork_active(height)
371 && NetworkUpgrade::Nu6_2
372 .activation_height(self)
373 .is_none_or(|nu6_2| height < nu6_2)
374 }
375
376 pub fn is_temporary_orchard_disabling_soft_fork_activation_height(
383 &self,
384 height: Height,
385 ) -> bool {
386 self.temporary_orchard_disabling_soft_fork_height() == Some(height)
387 }
388
389 pub fn orchard_canonical_proof_size_rule_active(&self, height: Height) -> bool {
398 NetworkUpgrade::Nu6_2
399 .activation_height(self)
400 .is_some_and(|h| height >= h)
401 }
402}
403
404impl FromStr for Network {
406 type Err = InvalidNetworkError;
407
408 fn from_str(string: &str) -> Result<Self, Self::Err> {
409 match string.to_lowercase().as_str() {
410 "mainnet" => Ok(Network::Mainnet),
411 "testnet" => Ok(Network::new_default_testnet()),
412 _ => Err(InvalidNetworkError(string.to_owned())),
413 }
414 }
415}
416
417#[derive(Clone, Debug, Error)]
418#[error("Invalid network: {0}")]
419pub struct InvalidNetworkError(String);
420
421impl zcash_protocol::consensus::Parameters for Network {
422 fn network_type(&self) -> zcash_protocol::consensus::NetworkType {
423 self.kind().into()
424 }
425
426 fn activation_height(
427 &self,
428 nu: zcash_protocol::consensus::NetworkUpgrade,
429 ) -> Option<zcash_protocol::consensus::BlockHeight> {
430 NetworkUpgrade::from(nu)
431 .activation_height(self)
432 .map(|Height(h)| zcash_protocol::consensus::BlockHeight::from_u32(h))
433 }
434}