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
14pub mod magic;
15pub mod subsidy;
16pub mod testnet;
17
18#[cfg(test)]
19mod tests;
20
21#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
26pub enum NetworkKind {
27 #[default]
29 Mainnet,
30
31 Testnet,
33
34 Regtest,
36}
37
38impl From<Network> for NetworkKind {
39 fn from(net: Network) -> Self {
40 NetworkKind::from(&net)
41 }
42}
43
44impl From<&Network> for NetworkKind {
45 fn from(net: &Network) -> Self {
46 net.kind()
47 }
48}
49
50#[derive(Clone, Default, Eq, PartialEq, Serialize)]
52#[serde(into = "NetworkKind")]
53pub enum Network {
54 #[default]
56 Mainnet,
57
58 Testnet(Arc<testnet::Parameters>),
61}
62
63impl NetworkKind {
64 pub fn b58_pubkey_address_prefix(self) -> [u8; 2] {
67 match self {
68 Self::Mainnet => zcash_primitives::constants::mainnet::B58_PUBKEY_ADDRESS_PREFIX,
69 Self::Testnet | Self::Regtest => {
70 zcash_primitives::constants::testnet::B58_PUBKEY_ADDRESS_PREFIX
71 }
72 }
73 }
74
75 pub fn b58_script_address_prefix(self) -> [u8; 2] {
78 match self {
79 Self::Mainnet => zcash_primitives::constants::mainnet::B58_SCRIPT_ADDRESS_PREFIX,
80 Self::Testnet | Self::Regtest => {
81 zcash_primitives::constants::testnet::B58_SCRIPT_ADDRESS_PREFIX
82 }
83 }
84 }
85
86 pub fn bip70_network_name(&self) -> String {
89 if *self == Self::Mainnet {
90 "main".to_string()
91 } else {
92 "test".to_string()
93 }
94 }
95
96 pub fn tex_address_prefix(self) -> [u8; 2] {
99 match self {
101 Self::Mainnet => [0x1c, 0xb8],
102 Self::Testnet | Self::Regtest => [0x1d, 0x25],
103 }
104 }
105}
106
107impl From<NetworkKind> for &'static str {
108 fn from(network: NetworkKind) -> &'static str {
109 match network {
113 NetworkKind::Mainnet => "MainnetKind",
114 NetworkKind::Testnet => "TestnetKind",
115 NetworkKind::Regtest => "RegtestKind",
116 }
117 }
118}
119
120impl fmt::Display for NetworkKind {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 f.write_str((*self).into())
123 }
124}
125
126impl<'a> From<&'a Network> for &'a str {
127 fn from(network: &'a Network) -> &'a str {
128 match network {
129 Network::Mainnet => "Mainnet",
130 Network::Testnet(params) => params.network_name(),
131 }
132 }
133}
134
135impl fmt::Display for Network {
136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137 f.write_str(self.into())
138 }
139}
140
141impl std::fmt::Debug for Network {
142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143 match self {
144 Self::Mainnet => write!(f, "{self}"),
145 Self::Testnet(params) if params.is_regtest() => f
146 .debug_struct("Regtest")
147 .field("activation_heights", params.activation_heights())
148 .finish(),
149 Self::Testnet(params) if params.is_default_testnet() => {
150 write!(f, "{self}")
151 }
152 Self::Testnet(params) => f.debug_tuple("ConfiguredTestnet").field(params).finish(),
153 }
154 }
155}
156
157impl Network {
158 pub fn new_default_testnet() -> Self {
160 Self::Testnet(Arc::new(testnet::Parameters::default()))
161 }
162
163 pub fn new_configured_testnet(params: testnet::Parameters) -> Self {
165 Self::Testnet(Arc::new(params))
166 }
167
168 pub fn new_regtest(
170 configured_activation_heights: testnet::ConfiguredActivationHeights,
171 ) -> Self {
172 Self::new_configured_testnet(testnet::Parameters::new_regtest(
173 configured_activation_heights,
174 ))
175 }
176
177 pub fn is_default_testnet(&self) -> bool {
179 if let Self::Testnet(params) = self {
180 params.is_default_testnet()
181 } else {
182 false
183 }
184 }
185
186 pub fn is_regtest(&self) -> bool {
188 if let Self::Testnet(params) = self {
189 params.is_regtest()
190 } else {
191 false
192 }
193 }
194
195 pub fn kind(&self) -> NetworkKind {
197 match self {
198 Network::Mainnet => NetworkKind::Mainnet,
199 Network::Testnet(params) if params.is_regtest() => NetworkKind::Regtest,
200 Network::Testnet(_) => NetworkKind::Testnet,
201 }
202 }
203
204 pub fn t_addr_kind(&self) -> NetworkKind {
208 match self {
209 Network::Mainnet => NetworkKind::Mainnet,
210 Network::Testnet(_) => NetworkKind::Testnet,
211 }
212 }
213
214 pub fn iter() -> impl Iterator<Item = Self> {
216 [Self::Mainnet, Self::new_default_testnet()].into_iter()
217 }
218
219 pub fn is_max_block_time_enforced(&self, height: block::Height) -> bool {
228 match self {
229 Network::Mainnet => true,
230 Network::Testnet(_params) => height >= super::TESTNET_MAX_TIME_START_HEIGHT,
232 }
233 }
234
235 pub fn default_port(&self) -> u16 {
237 match self {
238 Network::Mainnet => 8233,
239 Network::Testnet(_params) => 18233,
241 }
242 }
243
244 pub fn mandatory_checkpoint_height(&self) -> Height {
254 NetworkUpgrade::Canopy
256 .activation_height(self)
257 .expect("Canopy activation height must be present on all networks")
258 .previous()
259 .expect("Canopy activation height must be above min height")
260 }
261
262 pub fn bip70_network_name(&self) -> String {
265 self.kind().bip70_network_name()
266 }
267
268 pub fn lowercase_name(&self) -> String {
270 self.to_string().to_ascii_lowercase()
271 }
272
273 pub fn is_a_test_network(&self) -> bool {
275 *self != Network::Mainnet
276 }
277
278 pub fn sapling_activation_height(&self) -> Height {
281 super::NetworkUpgrade::Sapling
282 .activation_height(self)
283 .expect("Sapling activation height needs to be set")
284 }
285
286 pub fn lockbox_disbursement_total_amount(&self, height: Height) -> Amount<NonNegative> {
289 if Some(height) != NetworkUpgrade::Nu6_1.activation_height(self) {
290 return Amount::zero();
291 };
292
293 match self {
294 Self::Mainnet => subsidy::EXPECTED_NU6_1_LOCKBOX_DISBURSEMENTS_TOTAL_MAINNET,
295 Self::Testnet(params) if params.is_default_testnet() => {
296 subsidy::EXPECTED_NU6_1_LOCKBOX_DISBURSEMENTS_TOTAL_TESTNET
297 }
298 Self::Testnet(params) => params.lockbox_disbursement_total_amount(),
299 }
300 }
301
302 pub fn lockbox_disbursements(
304 &self,
305 height: Height,
306 ) -> Vec<(transparent::Address, Amount<NonNegative>)> {
307 if Some(height) != NetworkUpgrade::Nu6_1.activation_height(self) {
308 return Vec::new();
309 };
310
311 let expected_lockbox_disbursements = match self {
312 Self::Mainnet => subsidy::NU6_1_LOCKBOX_DISBURSEMENTS_MAINNET.to_vec(),
313 Self::Testnet(params) if params.is_default_testnet() => {
314 subsidy::NU6_1_LOCKBOX_DISBURSEMENTS_TESTNET.to_vec()
315 }
316 Self::Testnet(params) => return params.lockbox_disbursements(),
317 };
318
319 expected_lockbox_disbursements
320 .into_iter()
321 .map(|(addr, amount)| {
322 (
323 addr.parse().expect("hard-coded address must deserialize"),
324 amount,
325 )
326 })
327 .collect()
328 }
329}
330
331impl FromStr for Network {
333 type Err = InvalidNetworkError;
334
335 fn from_str(string: &str) -> Result<Self, Self::Err> {
336 match string.to_lowercase().as_str() {
337 "mainnet" => Ok(Network::Mainnet),
338 "testnet" => Ok(Network::new_default_testnet()),
339 _ => Err(InvalidNetworkError(string.to_owned())),
340 }
341 }
342}
343
344#[derive(Clone, Debug, Error)]
345#[error("Invalid network: {0}")]
346pub struct InvalidNetworkError(String);
347
348impl zcash_protocol::consensus::Parameters for Network {
349 fn network_type(&self) -> zcash_protocol::consensus::NetworkType {
350 self.kind().into()
351 }
352
353 fn activation_height(
354 &self,
355 nu: zcash_protocol::consensus::NetworkUpgrade,
356 ) -> Option<zcash_protocol::consensus::BlockHeight> {
357 NetworkUpgrade::from(nu)
358 .activation_height(self)
359 .map(|Height(h)| zcash_protocol::consensus::BlockHeight::from_u32(h))
360 }
361}