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 .field("funding_streams", params.funding_streams())
149 .field("lockbox_disbursements", ¶ms.lockbox_disbursements())
150 .field("checkpoints", ¶ms.checkpoints())
151 .finish(),
152 Self::Testnet(params) if params.is_default_testnet() => {
153 write!(f, "{self}")
154 }
155 Self::Testnet(params) => f.debug_tuple("ConfiguredTestnet").field(params).finish(),
156 }
157 }
158}
159
160impl Network {
161 pub fn new_default_testnet() -> Self {
163 Self::Testnet(Arc::new(testnet::Parameters::default()))
164 }
165
166 pub fn new_configured_testnet(params: testnet::Parameters) -> Self {
168 Self::Testnet(Arc::new(params))
169 }
170
171 pub fn new_regtest(params: testnet::RegtestParameters) -> Self {
173 Self::new_configured_testnet(testnet::Parameters::new_regtest(params))
174 }
175
176 pub fn is_default_testnet(&self) -> bool {
178 if let Self::Testnet(params) = self {
179 params.is_default_testnet()
180 } else {
181 false
182 }
183 }
184
185 pub fn is_regtest(&self) -> bool {
187 if let Self::Testnet(params) = self {
188 params.is_regtest()
189 } else {
190 false
191 }
192 }
193
194 pub fn kind(&self) -> NetworkKind {
196 match self {
197 Network::Mainnet => NetworkKind::Mainnet,
198 Network::Testnet(params) if params.is_regtest() => NetworkKind::Regtest,
199 Network::Testnet(_) => NetworkKind::Testnet,
200 }
201 }
202
203 pub fn t_addr_kind(&self) -> NetworkKind {
207 match self {
208 Network::Mainnet => NetworkKind::Mainnet,
209 Network::Testnet(_) => NetworkKind::Testnet,
210 }
211 }
212
213 pub fn iter() -> impl Iterator<Item = Self> {
215 [Self::Mainnet, Self::new_default_testnet()].into_iter()
216 }
217
218 pub fn is_max_block_time_enforced(&self, height: block::Height) -> bool {
227 match self {
228 Network::Mainnet => true,
229 Network::Testnet(_params) => height >= super::TESTNET_MAX_TIME_START_HEIGHT,
231 }
232 }
233
234 pub fn default_port(&self) -> u16 {
236 match self {
237 Network::Mainnet => 8233,
238 Network::Testnet(_params) => 18233,
240 }
241 }
242
243 pub fn mandatory_checkpoint_height(&self) -> Height {
253 NetworkUpgrade::Canopy
255 .activation_height(self)
256 .expect("Canopy activation height must be present on all networks")
257 .previous()
258 .expect("Canopy activation height must be above min height")
259 }
260
261 pub fn bip70_network_name(&self) -> String {
264 self.kind().bip70_network_name()
265 }
266
267 pub fn lowercase_name(&self) -> String {
269 self.to_string().to_ascii_lowercase()
270 }
271
272 pub fn is_a_test_network(&self) -> bool {
274 *self != Network::Mainnet
275 }
276
277 pub fn sapling_activation_height(&self) -> Height {
280 super::NetworkUpgrade::Sapling
281 .activation_height(self)
282 .expect("Sapling activation height needs to be set")
283 }
284
285 pub fn lockbox_disbursement_total_amount(&self, height: Height) -> Amount<NonNegative> {
288 if Some(height) != NetworkUpgrade::Nu6_1.activation_height(self) {
289 return Amount::zero();
290 };
291
292 match self {
293 Self::Mainnet => subsidy::EXPECTED_NU6_1_LOCKBOX_DISBURSEMENTS_TOTAL_MAINNET,
294 Self::Testnet(params) if params.is_default_testnet() => {
295 subsidy::EXPECTED_NU6_1_LOCKBOX_DISBURSEMENTS_TOTAL_TESTNET
296 }
297 Self::Testnet(params) => params.lockbox_disbursement_total_amount(),
298 }
299 }
300
301 pub fn lockbox_disbursements(
303 &self,
304 height: Height,
305 ) -> Vec<(transparent::Address, Amount<NonNegative>)> {
306 if Some(height) != NetworkUpgrade::Nu6_1.activation_height(self) {
307 return Vec::new();
308 };
309
310 let expected_lockbox_disbursements = match self {
311 Self::Mainnet => subsidy::NU6_1_LOCKBOX_DISBURSEMENTS_MAINNET.to_vec(),
312 Self::Testnet(params) if params.is_default_testnet() => {
313 subsidy::NU6_1_LOCKBOX_DISBURSEMENTS_TESTNET.to_vec()
314 }
315 Self::Testnet(params) => return params.lockbox_disbursements(),
316 };
317
318 expected_lockbox_disbursements
319 .into_iter()
320 .map(|(addr, amount)| {
321 (
322 addr.parse().expect("hard-coded address must deserialize"),
323 amount,
324 )
325 })
326 .collect()
327 }
328}
329
330impl FromStr for Network {
332 type Err = InvalidNetworkError;
333
334 fn from_str(string: &str) -> Result<Self, Self::Err> {
335 match string.to_lowercase().as_str() {
336 "mainnet" => Ok(Network::Mainnet),
337 "testnet" => Ok(Network::new_default_testnet()),
338 _ => Err(InvalidNetworkError(string.to_owned())),
339 }
340 }
341}
342
343#[derive(Clone, Debug, Error)]
344#[error("Invalid network: {0}")]
345pub struct InvalidNetworkError(String);
346
347impl zcash_protocol::consensus::Parameters for Network {
348 fn network_type(&self) -> zcash_protocol::consensus::NetworkType {
349 self.kind().into()
350 }
351
352 fn activation_height(
353 &self,
354 nu: zcash_protocol::consensus::NetworkUpgrade,
355 ) -> Option<zcash_protocol::consensus::BlockHeight> {
356 NetworkUpgrade::from(nu)
357 .activation_height(self)
358 .map(|Height(h)| zcash_protocol::consensus::BlockHeight::from_u32(h))
359 }
360}