1use NetworkUpgrade::*;
4
5use crate::block;
6use crate::parameters::{Network, Network::*};
7use crate::serialization::BytesInDisplayOrder;
8
9use std::collections::{BTreeMap, HashMap};
10use std::fmt;
11
12use chrono::{DateTime, Duration, Utc};
13use hex::{FromHex, ToHex};
14
15#[cfg(any(test, feature = "proptest-impl"))]
16use proptest_derive::Arbitrary;
17
18const NETWORK_UPGRADES_IN_ORDER: &[NetworkUpgrade] = &[
20 Genesis,
21 BeforeOverwinter,
22 Overwinter,
23 Sapling,
24 Blossom,
25 Heartwood,
26 Canopy,
27 Nu5,
28 Nu6,
29 Nu6_1,
30 #[cfg(any(test, feature = "zebra-test"))]
31 Nu7,
32];
33
34#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, Ord, PartialOrd)]
39#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
40pub enum NetworkUpgrade {
41 Genesis,
47 BeforeOverwinter,
52 Overwinter,
54 Sapling,
56 Blossom,
58 Heartwood,
60 Canopy,
62 #[serde(rename = "NU5")]
64 Nu5,
65 #[serde(rename = "NU6")]
67 Nu6,
68 #[serde(rename = "NU6.1")]
70 Nu6_1,
71 #[serde(rename = "NU7")]
73 Nu7,
74
75 #[cfg(zcash_unstable = "zfuture")]
76 ZFuture,
77}
78
79impl TryFrom<u32> for NetworkUpgrade {
80 type Error = crate::Error;
81
82 fn try_from(branch_id: u32) -> Result<Self, Self::Error> {
83 CONSENSUS_BRANCH_IDS
84 .iter()
85 .find(|id| id.1 == ConsensusBranchId(branch_id))
86 .map(|nu| nu.0)
87 .ok_or(Self::Error::InvalidConsensusBranchId)
88 }
89}
90
91impl fmt::Display for NetworkUpgrade {
92 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93 fmt::Debug::fmt(self, f)
95 }
96}
97
98#[allow(unused)]
108pub(super) const MAINNET_ACTIVATION_HEIGHTS: &[(block::Height, NetworkUpgrade)] = {
109 use super::constants::activation_heights::mainnet::*;
110 &[
111 (block::Height(0), Genesis),
112 (BEFORE_OVERWINTER, BeforeOverwinter),
113 (OVERWINTER, Overwinter),
114 (SAPLING, Sapling),
115 (BLOSSOM, Blossom),
116 (HEARTWOOD, Heartwood),
117 (CANOPY, Canopy),
118 (NU5, Nu5),
119 (NU6, Nu6),
120 (NU6_1, Nu6_1),
121 ]
122};
123#[allow(unused)]
133pub(super) const TESTNET_ACTIVATION_HEIGHTS: &[(block::Height, NetworkUpgrade)] = {
134 use super::constants::activation_heights::testnet::*;
135 &[
136 (block::Height(0), Genesis),
137 (BEFORE_OVERWINTER, BeforeOverwinter),
138 (OVERWINTER, Overwinter),
139 (SAPLING, Sapling),
140 (BLOSSOM, Blossom),
141 (HEARTWOOD, Heartwood),
142 (CANOPY, Canopy),
143 (NU5, Nu5),
144 (NU6, Nu6),
145 (NU6_1, Nu6_1),
146 ]
147};
148
149#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
152pub struct ConsensusBranchId(pub(crate) u32);
153
154impl BytesInDisplayOrder<false, 4> for ConsensusBranchId {
155 fn bytes_in_serialized_order(&self) -> [u8; 4] {
156 self.0.to_be_bytes()
157 }
158
159 fn from_bytes_in_serialized_order(bytes: [u8; 4]) -> Self {
160 ConsensusBranchId(u32::from_be_bytes(bytes))
161 }
162}
163
164impl From<ConsensusBranchId> for u32 {
165 fn from(branch: ConsensusBranchId) -> u32 {
166 branch.0
167 }
168}
169
170impl From<u32> for ConsensusBranchId {
171 fn from(branch: u32) -> Self {
172 ConsensusBranchId(branch)
173 }
174}
175
176impl ToHex for &ConsensusBranchId {
177 fn encode_hex<T: FromIterator<char>>(&self) -> T {
178 self.bytes_in_display_order().encode_hex()
179 }
180
181 fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
182 self.bytes_in_display_order().encode_hex_upper()
183 }
184}
185
186impl ToHex for ConsensusBranchId {
187 fn encode_hex<T: FromIterator<char>>(&self) -> T {
188 self.bytes_in_display_order().encode_hex()
189 }
190
191 fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
192 self.bytes_in_display_order().encode_hex_upper()
193 }
194}
195
196impl FromHex for ConsensusBranchId {
197 type Error = <[u8; 4] as FromHex>::Error;
198
199 fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
200 let branch = <[u8; 4]>::from_hex(hex)?;
201 Ok(ConsensusBranchId(u32::from_be_bytes(branch)))
202 }
203}
204
205impl fmt::Display for ConsensusBranchId {
206 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
207 f.write_str(&self.encode_hex::<String>())
208 }
209}
210
211impl TryFrom<ConsensusBranchId> for zcash_primitives::consensus::BranchId {
212 type Error = crate::Error;
213
214 fn try_from(id: ConsensusBranchId) -> Result<Self, Self::Error> {
215 zcash_primitives::consensus::BranchId::try_from(u32::from(id))
216 .map_err(|_| Self::Error::InvalidConsensusBranchId)
217 }
218}
219
220pub(crate) const CONSENSUS_BRANCH_IDS: &[(NetworkUpgrade, ConsensusBranchId)] = &[
231 (Overwinter, ConsensusBranchId(0x5ba81b19)),
232 (Sapling, ConsensusBranchId(0x76b809bb)),
233 (Blossom, ConsensusBranchId(0x2bb40e60)),
234 (Heartwood, ConsensusBranchId(0xf5b9230b)),
235 (Canopy, ConsensusBranchId(0xe9ff75a6)),
236 (Nu5, ConsensusBranchId(0xc2d6d0b4)),
237 (Nu6, ConsensusBranchId(0xc8e71055)),
238 (Nu6_1, ConsensusBranchId(0x4dec4df0)),
239 #[cfg(any(test, feature = "zebra-test"))]
240 (Nu7, ConsensusBranchId(0x77190ad8)),
241 #[cfg(zcash_unstable = "zfuture")]
242 (ZFuture, ConsensusBranchId(0xffffffff)),
243];
244
245const PRE_BLOSSOM_POW_TARGET_SPACING: i64 = 150;
247
248pub const POST_BLOSSOM_POW_TARGET_SPACING: u32 = 75;
250
251pub const POW_AVERAGING_WINDOW: usize = 17;
255
256const TESTNET_MINIMUM_DIFFICULTY_GAP_MULTIPLIER: i32 = 6;
261
262const TESTNET_MINIMUM_DIFFICULTY_START_HEIGHT: block::Height = block::Height(299_188);
266
267pub const TESTNET_MAX_TIME_START_HEIGHT: block::Height = block::Height(653_606);
272
273impl Network {
274 pub fn activation_list(&self) -> BTreeMap<block::Height, NetworkUpgrade> {
285 match self {
286 Mainnet => MAINNET_ACTIVATION_HEIGHTS.iter().cloned().collect(),
287 Testnet(params) => params.activation_heights().clone(),
288 }
289 }
290
291 pub fn full_activation_list(&self) -> Vec<(block::Height, NetworkUpgrade)> {
294 NETWORK_UPGRADES_IN_ORDER
295 .iter()
296 .map_while(|&nu| Some((NetworkUpgrade::activation_height(&nu, self)?, nu)))
297 .collect()
298 }
299}
300
301impl NetworkUpgrade {
302 pub fn current_with_activation_height(
304 network: &Network,
305 height: block::Height,
306 ) -> (NetworkUpgrade, block::Height) {
307 network
308 .activation_list()
309 .range(..=height)
310 .map(|(&h, &nu)| (nu, h))
311 .next_back()
312 .expect("every height has a current network upgrade")
313 }
314
315 pub fn current(network: &Network, height: block::Height) -> NetworkUpgrade {
317 network
318 .activation_list()
319 .range(..=height)
320 .map(|(_, nu)| *nu)
321 .next_back()
322 .expect("every height has a current network upgrade")
323 }
324
325 pub fn next_upgrade(self) -> Option<Self> {
327 Self::iter().skip_while(|&nu| self != nu).nth(1)
328 }
329
330 pub fn previous_upgrade(self) -> Option<Self> {
332 Self::iter().rev().skip_while(|&nu| self != nu).nth(1)
333 }
334
335 #[cfg(test)]
340 pub fn next(network: &Network, height: block::Height) -> Option<NetworkUpgrade> {
341 use std::ops::Bound::*;
342
343 network
344 .activation_list()
345 .range((Excluded(height), Unbounded))
346 .map(|(_, nu)| *nu)
347 .next()
348 }
349
350 pub fn activation_height(&self, network: &Network) -> Option<block::Height> {
362 network
363 .activation_list()
364 .iter()
365 .find(|(_, nu)| nu == &self)
366 .map(|(height, _)| *height)
367 .or_else(|| {
368 self.next_upgrade()
369 .and_then(|next_nu| next_nu.activation_height(network))
370 })
371 }
372
373 pub fn is_activation_height(network: &Network, height: block::Height) -> bool {
379 network.activation_list().contains_key(&height)
380 }
381
382 pub(crate) fn branch_id_list() -> HashMap<NetworkUpgrade, ConsensusBranchId> {
391 CONSENSUS_BRANCH_IDS.iter().cloned().collect()
392 }
393
394 pub fn branch_id(&self) -> Option<ConsensusBranchId> {
398 NetworkUpgrade::branch_id_list().get(self).cloned()
399 }
400
401 pub fn target_spacing(&self) -> Duration {
406 let spacing_seconds = match self {
407 Genesis | BeforeOverwinter | Overwinter | Sapling => PRE_BLOSSOM_POW_TARGET_SPACING,
408 Blossom | Heartwood | Canopy | Nu5 | Nu6 | Nu6_1 | Nu7 => {
409 POST_BLOSSOM_POW_TARGET_SPACING.into()
410 }
411
412 #[cfg(zcash_unstable = "zfuture")]
413 ZFuture => POST_BLOSSOM_POW_TARGET_SPACING.into(),
414 };
415
416 Duration::seconds(spacing_seconds)
417 }
418
419 pub fn target_spacing_for_height(network: &Network, height: block::Height) -> Duration {
423 NetworkUpgrade::current(network, height).target_spacing()
424 }
425
426 pub fn target_spacings(
428 network: &Network,
429 ) -> impl Iterator<Item = (block::Height, Duration)> + '_ {
430 [
431 (NetworkUpgrade::Genesis, PRE_BLOSSOM_POW_TARGET_SPACING),
432 (
433 NetworkUpgrade::Blossom,
434 POST_BLOSSOM_POW_TARGET_SPACING.into(),
435 ),
436 ]
437 .into_iter()
438 .filter_map(move |(upgrade, spacing_seconds)| {
439 let activation_height = upgrade.activation_height(network)?;
440 let target_spacing = Duration::seconds(spacing_seconds);
441 Some((activation_height, target_spacing))
442 })
443 }
444
445 pub fn minimum_difficulty_spacing_for_height(
450 network: &Network,
451 height: block::Height,
452 ) -> Option<Duration> {
453 match (network, height) {
454 (Network::Testnet(_params), height)
456 if height < TESTNET_MINIMUM_DIFFICULTY_START_HEIGHT =>
457 {
458 None
459 }
460 (Network::Mainnet, _) => None,
461 (Network::Testnet(_params), _) => {
462 let network_upgrade = NetworkUpgrade::current(network, height);
463 Some(network_upgrade.target_spacing() * TESTNET_MINIMUM_DIFFICULTY_GAP_MULTIPLIER)
464 }
465 }
466 }
467
468 pub fn is_testnet_min_difficulty_block(
484 network: &Network,
485 block_height: block::Height,
486 block_time: DateTime<Utc>,
487 previous_block_time: DateTime<Utc>,
488 ) -> bool {
489 let block_time_gap = block_time - previous_block_time;
490 if let Some(min_difficulty_gap) =
491 NetworkUpgrade::minimum_difficulty_spacing_for_height(network, block_height)
492 {
493 block_time_gap > min_difficulty_gap
494 } else {
495 false
496 }
497 }
498
499 pub fn averaging_window_timespan(&self) -> Duration {
503 self.target_spacing() * POW_AVERAGING_WINDOW.try_into().expect("fits in i32")
504 }
505
506 pub fn averaging_window_timespan_for_height(
510 network: &Network,
511 height: block::Height,
512 ) -> Duration {
513 NetworkUpgrade::current(network, height).averaging_window_timespan()
514 }
515
516 pub fn iter() -> impl DoubleEndedIterator<Item = NetworkUpgrade> {
518 NETWORK_UPGRADES_IN_ORDER.iter().copied()
519 }
520}
521
522impl From<zcash_protocol::consensus::NetworkUpgrade> for NetworkUpgrade {
523 fn from(nu: zcash_protocol::consensus::NetworkUpgrade) -> Self {
524 match nu {
525 zcash_protocol::consensus::NetworkUpgrade::Overwinter => Self::Overwinter,
526 zcash_protocol::consensus::NetworkUpgrade::Sapling => Self::Sapling,
527 zcash_protocol::consensus::NetworkUpgrade::Blossom => Self::Blossom,
528 zcash_protocol::consensus::NetworkUpgrade::Heartwood => Self::Heartwood,
529 zcash_protocol::consensus::NetworkUpgrade::Canopy => Self::Canopy,
530 zcash_protocol::consensus::NetworkUpgrade::Nu5 => Self::Nu5,
531 zcash_protocol::consensus::NetworkUpgrade::Nu6 => Self::Nu6,
532 zcash_protocol::consensus::NetworkUpgrade::Nu6_1 => Self::Nu6_1,
533 #[cfg(zcash_unstable = "nu7")]
534 zcash_protocol::consensus::NetworkUpgrade::Nu7 => Self::Nu7,
535 #[cfg(zcash_unstable = "zfuture")]
536 zcash_protocol::consensus::NetworkUpgrade::ZFuture => Self::ZFuture,
537 }
538 }
539}
540
541impl ConsensusBranchId {
542 pub const RPC_MISSING_ID: ConsensusBranchId = ConsensusBranchId(0);
551
552 pub fn current(network: &Network, height: block::Height) -> Option<ConsensusBranchId> {
556 NetworkUpgrade::current(network, height).branch_id()
557 }
558}