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