zebra_chain/parameters/
network_upgrade.rs

1//! Network upgrade consensus parameters for Zcash.
2
3use NetworkUpgrade::*;
4
5use crate::block;
6use crate::parameters::{Network, Network::*};
7
8use std::collections::{BTreeMap, HashMap};
9use std::fmt;
10
11use chrono::{DateTime, Duration, Utc};
12use hex::{FromHex, ToHex};
13
14#[cfg(any(test, feature = "proptest-impl"))]
15use proptest_derive::Arbitrary;
16
17/// A list of network upgrades in the order that they must be activated.
18const NETWORK_UPGRADES_IN_ORDER: &[NetworkUpgrade] = &[
19    Genesis,
20    BeforeOverwinter,
21    Overwinter,
22    Sapling,
23    Blossom,
24    Heartwood,
25    Canopy,
26    Nu5,
27    Nu6,
28    Nu6_1,
29    #[cfg(any(test, feature = "zebra-test"))]
30    Nu7,
31];
32
33/// A Zcash network upgrade.
34///
35/// Network upgrades change the Zcash network protocol or consensus rules. Note that they have no
36/// designated codenames from NU5 onwards.
37#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, Ord, PartialOrd)]
38#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
39pub enum NetworkUpgrade {
40    /// The Zcash protocol for a Genesis block.
41    ///
42    /// Zcash genesis blocks use a different set of consensus rules from
43    /// other BeforeOverwinter blocks, so we treat them like a separate network
44    /// upgrade.
45    Genesis,
46    /// The Zcash protocol before the Overwinter upgrade.
47    ///
48    /// We avoid using `Sprout`, because the specification says that Sprout
49    /// is the name of the pre-Sapling protocol, before and after Overwinter.
50    BeforeOverwinter,
51    /// The Zcash protocol after the Overwinter upgrade.
52    Overwinter,
53    /// The Zcash protocol after the Sapling upgrade.
54    Sapling,
55    /// The Zcash protocol after the Blossom upgrade.
56    Blossom,
57    /// The Zcash protocol after the Heartwood upgrade.
58    Heartwood,
59    /// The Zcash protocol after the Canopy upgrade.
60    Canopy,
61    /// The Zcash protocol after the NU5 upgrade.
62    #[serde(rename = "NU5")]
63    Nu5,
64    /// The Zcash protocol after the NU6 upgrade.
65    #[serde(rename = "NU6")]
66    Nu6,
67    /// The Zcash protocol after the NU6.1 upgrade.
68    #[serde(rename = "NU6.1")]
69    Nu6_1,
70    /// The Zcash protocol after the NU7 upgrade.
71    #[serde(rename = "NU7")]
72    Nu7,
73}
74
75impl TryFrom<u32> for NetworkUpgrade {
76    type Error = crate::Error;
77
78    fn try_from(branch_id: u32) -> Result<Self, Self::Error> {
79        CONSENSUS_BRANCH_IDS
80            .iter()
81            .find(|id| id.1 == ConsensusBranchId(branch_id))
82            .map(|nu| nu.0)
83            .ok_or(Self::Error::InvalidConsensusBranchId)
84    }
85}
86
87impl fmt::Display for NetworkUpgrade {
88    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
89        // Same as the debug representation for now
90        fmt::Debug::fmt(self, f)
91    }
92}
93
94/// Mainnet network upgrade activation heights.
95///
96/// This is actually a bijective map, but it is const, so we use a vector, and
97/// do the uniqueness check in the unit tests.
98///
99/// # Correctness
100///
101/// Don't use this directly; use NetworkUpgrade::activation_list() so that
102/// we can switch to fake activation heights for some tests.
103#[allow(unused)]
104pub(super) const MAINNET_ACTIVATION_HEIGHTS: &[(block::Height, NetworkUpgrade)] = &[
105    (block::Height(0), Genesis),
106    (block::Height(1), BeforeOverwinter),
107    (block::Height(347_500), Overwinter),
108    (block::Height(419_200), Sapling),
109    (block::Height(653_600), Blossom),
110    (block::Height(903_000), Heartwood),
111    (block::Height(1_046_400), Canopy),
112    (block::Height(1_687_104), Nu5),
113    (block::Height(2_726_400), Nu6),
114];
115
116/// Fake mainnet network upgrade activation heights, used in tests.
117#[allow(unused)]
118const FAKE_MAINNET_ACTIVATION_HEIGHTS: &[(block::Height, NetworkUpgrade)] = &[
119    (block::Height(0), Genesis),
120    (block::Height(5), BeforeOverwinter),
121    (block::Height(10), Overwinter),
122    (block::Height(15), Sapling),
123    (block::Height(20), Blossom),
124    (block::Height(25), Heartwood),
125    (block::Height(30), Canopy),
126    (block::Height(35), Nu5),
127    (block::Height(40), Nu6),
128    (block::Height(45), Nu6_1),
129    (block::Height(50), Nu7),
130];
131
132/// The block height at which NU6.1 activates on the default Testnet.
133// See NU6.1 Testnet activation height in zcashd:
134// <https://github.com/zcash/zcash/blob/b65b008a7b334a2f7c2eaae1b028e011f2e21dd1/src/chainparams.cpp#L472>
135pub const NU6_1_ACTIVATION_HEIGHT_TESTNET: block::Height = block::Height(3_536_500);
136
137/// Testnet network upgrade activation heights.
138///
139/// This is actually a bijective map, but it is const, so we use a vector, and
140/// do the uniqueness check in the unit tests.
141///
142/// # Correctness
143///
144/// Don't use this directly; use NetworkUpgrade::activation_list() so that
145/// we can switch to fake activation heights for some tests.
146#[allow(unused)]
147pub(super) const TESTNET_ACTIVATION_HEIGHTS: &[(block::Height, NetworkUpgrade)] = &[
148    (block::Height(0), Genesis),
149    (block::Height(1), BeforeOverwinter),
150    (block::Height(207_500), Overwinter),
151    (block::Height(280_000), Sapling),
152    (block::Height(584_000), Blossom),
153    (block::Height(903_800), Heartwood),
154    (block::Height(1_028_500), Canopy),
155    (block::Height(1_842_420), Nu5),
156    (block::Height(2_976_000), Nu6),
157    (NU6_1_ACTIVATION_HEIGHT_TESTNET, Nu6_1),
158];
159
160/// Fake testnet network upgrade activation heights, used in tests.
161#[allow(unused)]
162const FAKE_TESTNET_ACTIVATION_HEIGHTS: &[(block::Height, NetworkUpgrade)] = &[
163    (block::Height(0), Genesis),
164    (block::Height(5), BeforeOverwinter),
165    (block::Height(10), Overwinter),
166    (block::Height(15), Sapling),
167    (block::Height(20), Blossom),
168    (block::Height(25), Heartwood),
169    (block::Height(30), Canopy),
170    (block::Height(35), Nu5),
171    (block::Height(40), Nu6),
172    (block::Height(45), Nu6_1),
173    (block::Height(50), Nu7),
174];
175
176/// The Consensus Branch Id, used to bind transactions and blocks to a
177/// particular network upgrade.
178#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)]
179pub struct ConsensusBranchId(pub(crate) u32);
180
181impl ConsensusBranchId {
182    /// Return the hash bytes in big-endian byte-order suitable for printing out byte by byte.
183    ///
184    /// Zebra displays consensus branch IDs in big-endian byte-order,
185    /// following the convention set by zcashd.
186    fn bytes_in_display_order(&self) -> [u8; 4] {
187        self.0.to_be_bytes()
188    }
189}
190
191impl From<ConsensusBranchId> for u32 {
192    fn from(branch: ConsensusBranchId) -> u32 {
193        branch.0
194    }
195}
196
197impl From<u32> for ConsensusBranchId {
198    fn from(branch: u32) -> Self {
199        ConsensusBranchId(branch)
200    }
201}
202
203impl ToHex for &ConsensusBranchId {
204    fn encode_hex<T: FromIterator<char>>(&self) -> T {
205        self.bytes_in_display_order().encode_hex()
206    }
207
208    fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
209        self.bytes_in_display_order().encode_hex_upper()
210    }
211}
212
213impl ToHex for ConsensusBranchId {
214    fn encode_hex<T: FromIterator<char>>(&self) -> T {
215        self.bytes_in_display_order().encode_hex()
216    }
217
218    fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
219        self.bytes_in_display_order().encode_hex_upper()
220    }
221}
222
223impl FromHex for ConsensusBranchId {
224    type Error = <[u8; 4] as FromHex>::Error;
225
226    fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
227        let branch = <[u8; 4]>::from_hex(hex)?;
228        Ok(ConsensusBranchId(u32::from_be_bytes(branch)))
229    }
230}
231
232impl fmt::Display for ConsensusBranchId {
233    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
234        f.write_str(&self.encode_hex::<String>())
235    }
236}
237
238impl TryFrom<ConsensusBranchId> for zcash_primitives::consensus::BranchId {
239    type Error = crate::Error;
240
241    fn try_from(id: ConsensusBranchId) -> Result<Self, Self::Error> {
242        zcash_primitives::consensus::BranchId::try_from(u32::from(id))
243            .map_err(|_| Self::Error::InvalidConsensusBranchId)
244    }
245}
246
247/// Network Upgrade Consensus Branch Ids.
248///
249/// Branch ids are the same for mainnet and testnet. If there is a testnet
250/// rollback after a bug, the branch id changes.
251///
252/// Branch ids were introduced in the Overwinter upgrade, so there are no
253/// Genesis or BeforeOverwinter branch ids.
254///
255/// This is actually a bijective map, but it is const, so we use a vector, and
256/// do the uniqueness check in the unit tests.
257pub(crate) const CONSENSUS_BRANCH_IDS: &[(NetworkUpgrade, ConsensusBranchId)] = &[
258    (Overwinter, ConsensusBranchId(0x5ba81b19)),
259    (Sapling, ConsensusBranchId(0x76b809bb)),
260    (Blossom, ConsensusBranchId(0x2bb40e60)),
261    (Heartwood, ConsensusBranchId(0xf5b9230b)),
262    (Canopy, ConsensusBranchId(0xe9ff75a6)),
263    (Nu5, ConsensusBranchId(0xc2d6d0b4)),
264    (Nu6, ConsensusBranchId(0xc8e71055)),
265    (Nu6_1, ConsensusBranchId(0x4dec4df0)),
266    #[cfg(any(test, feature = "zebra-test"))]
267    (Nu7, ConsensusBranchId(0x77190ad8)),
268];
269
270/// The target block spacing before Blossom.
271const PRE_BLOSSOM_POW_TARGET_SPACING: i64 = 150;
272
273/// The target block spacing after Blossom activation.
274pub const POST_BLOSSOM_POW_TARGET_SPACING: u32 = 75;
275
276/// The averaging window for difficulty threshold arithmetic mean calculations.
277///
278/// `PoWAveragingWindow` in the Zcash specification.
279pub const POW_AVERAGING_WINDOW: usize = 17;
280
281/// The multiplier used to derive the testnet minimum difficulty block time gap
282/// threshold.
283///
284/// Based on <https://zips.z.cash/zip-0208#minimum-difficulty-blocks-on-the-test-network>
285const TESTNET_MINIMUM_DIFFICULTY_GAP_MULTIPLIER: i32 = 6;
286
287/// The start height for the testnet minimum difficulty consensus rule.
288///
289/// Based on <https://zips.z.cash/zip-0208#minimum-difficulty-blocks-on-the-test-network>
290const TESTNET_MINIMUM_DIFFICULTY_START_HEIGHT: block::Height = block::Height(299_188);
291
292/// The activation height for the block maximum time rule on Testnet.
293///
294/// Part of the block header consensus rules in the Zcash specification at
295/// <https://zips.z.cash/protocol/protocol.pdf#blockheader>
296pub const TESTNET_MAX_TIME_START_HEIGHT: block::Height = block::Height(653_606);
297
298impl Network {
299    /// Returns a map between activation heights and network upgrades for `network`,
300    /// in ascending height order.
301    ///
302    /// If the activation height of a future upgrade is not known, that
303    /// network upgrade does not appear in the list.
304    ///
305    /// This is actually a bijective map.
306    ///
307    /// When the environment variable TEST_FAKE_ACTIVATION_HEIGHTS is set
308    /// and it's a test build, this returns a list of fake activation heights
309    /// used by some tests.
310    ///
311    /// Note: This skips implicit network upgrade activations, use [`Network::full_activation_list`]
312    ///       to get an explicit list of all network upgrade activations.
313    pub fn activation_list(&self) -> BTreeMap<block::Height, NetworkUpgrade> {
314        match self {
315            // To prevent accidentally setting this somehow, only check the env var
316            // when being compiled for tests. We can't use cfg(test) since the
317            // test that uses this is in zebra-state, and cfg(test) is not
318            // set for dependencies. However, zebra-state does set the
319            // zebra-test feature of zebra-chain if it's a dev dependency.
320            //
321            // Cargo features are additive, so all test binaries built along with
322            // zebra-state will have this feature enabled. But we are using
323            // Rust Edition 2021 and Cargo resolver version 2, so the "zebra-test"
324            // feature should only be enabled for tests:
325            // https://doc.rust-lang.org/cargo/reference/features.html#resolver-version-2-command-line-flags
326            #[cfg(feature = "zebra-test")]
327            Mainnet if std::env::var_os("TEST_FAKE_ACTIVATION_HEIGHTS").is_some() => {
328                FAKE_MAINNET_ACTIVATION_HEIGHTS.iter().cloned().collect()
329            }
330            #[cfg(feature = "zebra-test")]
331            Testnet(_) if std::env::var_os("TEST_FAKE_ACTIVATION_HEIGHTS").is_some() => {
332                FAKE_TESTNET_ACTIVATION_HEIGHTS.iter().cloned().collect()
333            }
334            Mainnet => MAINNET_ACTIVATION_HEIGHTS.iter().cloned().collect(),
335            Testnet(params) => params.activation_heights().clone(),
336        }
337    }
338
339    /// Returns a vector of all implicit and explicit network upgrades for `network`,
340    /// in ascending height order.
341    pub fn full_activation_list(&self) -> Vec<(block::Height, NetworkUpgrade)> {
342        NETWORK_UPGRADES_IN_ORDER
343            .iter()
344            .map_while(|&nu| Some((NetworkUpgrade::activation_height(&nu, self)?, nu)))
345            .collect()
346    }
347}
348
349impl NetworkUpgrade {
350    /// Returns the current network upgrade and its activation height for `network` and `height`.
351    pub fn current_with_activation_height(
352        network: &Network,
353        height: block::Height,
354    ) -> (NetworkUpgrade, block::Height) {
355        network
356            .activation_list()
357            .range(..=height)
358            .map(|(&h, &nu)| (nu, h))
359            .next_back()
360            .expect("every height has a current network upgrade")
361    }
362
363    /// Returns the current network upgrade for `network` and `height`.
364    pub fn current(network: &Network, height: block::Height) -> NetworkUpgrade {
365        network
366            .activation_list()
367            .range(..=height)
368            .map(|(_, nu)| *nu)
369            .next_back()
370            .expect("every height has a current network upgrade")
371    }
372
373    /// Returns the next expected network upgrade after this network upgrade.
374    pub fn next_upgrade(self) -> Option<Self> {
375        Self::iter().skip_while(|&nu| self != nu).nth(1)
376    }
377
378    /// Returns the previous network upgrade before this network upgrade.
379    pub fn previous_upgrade(self) -> Option<Self> {
380        Self::iter().rev().skip_while(|&nu| self != nu).nth(1)
381    }
382
383    /// Returns the next network upgrade for `network` and `height`.
384    ///
385    /// Returns None if the next upgrade has not been implemented in Zebra
386    /// yet.
387    #[cfg(test)]
388    pub fn next(network: &Network, height: block::Height) -> Option<NetworkUpgrade> {
389        use std::ops::Bound::*;
390
391        network
392            .activation_list()
393            .range((Excluded(height), Unbounded))
394            .map(|(_, nu)| *nu)
395            .next()
396    }
397
398    /// Returns the activation height for this network upgrade on `network`, or
399    ///
400    /// Returns the activation height of the first network upgrade that follows
401    /// this network upgrade if there is no activation height for this network upgrade
402    /// such as on Regtest or a configured Testnet where multiple network upgrades have the
403    /// same activation height, or if one is omitted when others that follow it are included.
404    ///
405    /// Returns None if this network upgrade is a future upgrade, and its
406    /// activation height has not been set yet.
407    ///
408    /// Returns None if this network upgrade has not been configured on a Testnet or Regtest.
409    pub fn activation_height(&self, network: &Network) -> Option<block::Height> {
410        network
411            .activation_list()
412            .iter()
413            .find(|(_, nu)| nu == &self)
414            .map(|(height, _)| *height)
415            .or_else(|| {
416                self.next_upgrade()
417                    .and_then(|next_nu| next_nu.activation_height(network))
418            })
419    }
420
421    /// Returns `true` if `height` is the activation height of any network upgrade
422    /// on `network`.
423    ///
424    /// Use [`NetworkUpgrade::activation_height`] to get the specific network
425    /// upgrade.
426    pub fn is_activation_height(network: &Network, height: block::Height) -> bool {
427        network.activation_list().contains_key(&height)
428    }
429
430    /// Returns an unordered mapping between NetworkUpgrades and their ConsensusBranchIds.
431    ///
432    /// Branch ids are the same for mainnet and testnet.
433    ///
434    /// If network upgrade does not have a branch id, that network upgrade does
435    /// not appear in the list.
436    ///
437    /// This is actually a bijective map.
438    pub(crate) fn branch_id_list() -> HashMap<NetworkUpgrade, ConsensusBranchId> {
439        CONSENSUS_BRANCH_IDS.iter().cloned().collect()
440    }
441
442    /// Returns the consensus branch id for this network upgrade.
443    ///
444    /// Returns None if this network upgrade has no consensus branch id.
445    pub fn branch_id(&self) -> Option<ConsensusBranchId> {
446        NetworkUpgrade::branch_id_list().get(self).cloned()
447    }
448
449    /// Returns the target block spacing for the network upgrade.
450    ///
451    /// Based on [`PRE_BLOSSOM_POW_TARGET_SPACING`] and
452    /// [`POST_BLOSSOM_POW_TARGET_SPACING`] from the Zcash specification.
453    pub fn target_spacing(&self) -> Duration {
454        let spacing_seconds = match self {
455            Genesis | BeforeOverwinter | Overwinter | Sapling => PRE_BLOSSOM_POW_TARGET_SPACING,
456            Blossom | Heartwood | Canopy | Nu5 | Nu6 | Nu6_1 | Nu7 => {
457                POST_BLOSSOM_POW_TARGET_SPACING.into()
458            }
459        };
460
461        Duration::seconds(spacing_seconds)
462    }
463
464    /// Returns the target block spacing for `network` and `height`.
465    ///
466    /// See [`NetworkUpgrade::target_spacing`] for details.
467    pub fn target_spacing_for_height(network: &Network, height: block::Height) -> Duration {
468        NetworkUpgrade::current(network, height).target_spacing()
469    }
470
471    /// Returns all the target block spacings for `network` and the heights where they start.
472    pub fn target_spacings(
473        network: &Network,
474    ) -> impl Iterator<Item = (block::Height, Duration)> + '_ {
475        [
476            (NetworkUpgrade::Genesis, PRE_BLOSSOM_POW_TARGET_SPACING),
477            (
478                NetworkUpgrade::Blossom,
479                POST_BLOSSOM_POW_TARGET_SPACING.into(),
480            ),
481        ]
482        .into_iter()
483        .filter_map(move |(upgrade, spacing_seconds)| {
484            let activation_height = upgrade.activation_height(network)?;
485            let target_spacing = Duration::seconds(spacing_seconds);
486            Some((activation_height, target_spacing))
487        })
488    }
489
490    /// Returns the minimum difficulty block spacing for `network` and `height`.
491    /// Returns `None` if the testnet minimum difficulty consensus rule is not active.
492    ///
493    /// Based on <https://zips.z.cash/zip-0208#minimum-difficulty-blocks-on-the-test-network>
494    pub fn minimum_difficulty_spacing_for_height(
495        network: &Network,
496        height: block::Height,
497    ) -> Option<Duration> {
498        match (network, height) {
499            // TODO: Move `TESTNET_MINIMUM_DIFFICULTY_START_HEIGHT` to a field on testnet::Parameters (#8364)
500            (Network::Testnet(_params), height)
501                if height < TESTNET_MINIMUM_DIFFICULTY_START_HEIGHT =>
502            {
503                None
504            }
505            (Network::Mainnet, _) => None,
506            (Network::Testnet(_params), _) => {
507                let network_upgrade = NetworkUpgrade::current(network, height);
508                Some(network_upgrade.target_spacing() * TESTNET_MINIMUM_DIFFICULTY_GAP_MULTIPLIER)
509            }
510        }
511    }
512
513    /// Returns true if the gap between `block_time` and `previous_block_time` is
514    /// greater than the Testnet minimum difficulty time gap. This time gap
515    /// depends on the `network` and `block_height`.
516    ///
517    /// Returns false on Mainnet, when `block_height` is less than the minimum
518    /// difficulty start height, and when the time gap is too small.
519    ///
520    /// `block_time` can be less than, equal to, or greater than
521    /// `previous_block_time`, because block times are provided by miners.
522    ///
523    /// Implements the Testnet minimum difficulty adjustment from ZIPs 205 and 208.
524    ///
525    /// Spec Note: Some parts of ZIPs 205 and 208 previously specified an incorrect
526    /// check for the time gap. This function implements the correct "greater than"
527    /// check.
528    pub fn is_testnet_min_difficulty_block(
529        network: &Network,
530        block_height: block::Height,
531        block_time: DateTime<Utc>,
532        previous_block_time: DateTime<Utc>,
533    ) -> bool {
534        let block_time_gap = block_time - previous_block_time;
535        if let Some(min_difficulty_gap) =
536            NetworkUpgrade::minimum_difficulty_spacing_for_height(network, block_height)
537        {
538            block_time_gap > min_difficulty_gap
539        } else {
540            false
541        }
542    }
543
544    /// Returns the averaging window timespan for the network upgrade.
545    ///
546    /// `AveragingWindowTimespan` from the Zcash specification.
547    pub fn averaging_window_timespan(&self) -> Duration {
548        self.target_spacing() * POW_AVERAGING_WINDOW.try_into().expect("fits in i32")
549    }
550
551    /// Returns the averaging window timespan for `network` and `height`.
552    ///
553    /// See [`NetworkUpgrade::averaging_window_timespan`] for details.
554    pub fn averaging_window_timespan_for_height(
555        network: &Network,
556        height: block::Height,
557    ) -> Duration {
558        NetworkUpgrade::current(network, height).averaging_window_timespan()
559    }
560
561    /// Returns an iterator over [`NetworkUpgrade`] variants.
562    pub fn iter() -> impl DoubleEndedIterator<Item = NetworkUpgrade> {
563        NETWORK_UPGRADES_IN_ORDER.iter().copied()
564    }
565}
566
567impl From<zcash_protocol::consensus::NetworkUpgrade> for NetworkUpgrade {
568    fn from(nu: zcash_protocol::consensus::NetworkUpgrade) -> Self {
569        match nu {
570            zcash_protocol::consensus::NetworkUpgrade::Overwinter => Self::Overwinter,
571            zcash_protocol::consensus::NetworkUpgrade::Sapling => Self::Sapling,
572            zcash_protocol::consensus::NetworkUpgrade::Blossom => Self::Blossom,
573            zcash_protocol::consensus::NetworkUpgrade::Heartwood => Self::Heartwood,
574            zcash_protocol::consensus::NetworkUpgrade::Canopy => Self::Canopy,
575            zcash_protocol::consensus::NetworkUpgrade::Nu5 => Self::Nu5,
576            zcash_protocol::consensus::NetworkUpgrade::Nu6 => Self::Nu6,
577            zcash_protocol::consensus::NetworkUpgrade::Nu6_1 => Self::Nu6_1,
578            // zcash_protocol::consensus::NetworkUpgrade::Nu7 => Self::Nu7,
579        }
580    }
581}
582
583impl ConsensusBranchId {
584    /// The value used by `zcashd` RPCs for missing consensus branch IDs.
585    ///
586    /// # Consensus
587    ///
588    /// This value must only be used in RPCs.
589    ///
590    /// The consensus rules handle missing branch IDs by rejecting blocks and transactions,
591    /// so this substitute value must not be used in consensus-critical code.
592    pub const RPC_MISSING_ID: ConsensusBranchId = ConsensusBranchId(0);
593
594    /// Returns the current consensus branch id for `network` and `height`.
595    ///
596    /// Returns None if the network has no branch id at this height.
597    pub fn current(network: &Network, height: block::Height) -> Option<ConsensusBranchId> {
598        NetworkUpgrade::current(network, height).branch_id()
599    }
600}