zebra_chain/parameters/network/
testnet.rs

1//! Types and implementation for Testnet consensus parameters
2use std::{collections::BTreeMap, fmt, sync::Arc};
3
4use crate::{
5    block::{self, Height, HeightDiff},
6    parameters::{
7        constants::{magics, SLOW_START_INTERVAL, SLOW_START_SHIFT},
8        network_upgrade::TESTNET_ACTIVATION_HEIGHTS,
9        subsidy::{funding_stream_address_period, FUNDING_STREAM_RECEIVER_DENOMINATOR},
10        Network, NetworkKind, NetworkUpgrade,
11    },
12    work::difficulty::{ExpandedDifficulty, U256},
13};
14
15use super::{
16    magic::Magic,
17    subsidy::{
18        FundingStreamReceiver, FundingStreamRecipient, FundingStreams,
19        BLOSSOM_POW_TARGET_SPACING_RATIO, POST_BLOSSOM_HALVING_INTERVAL,
20        POST_NU6_FUNDING_STREAMS_MAINNET, POST_NU6_FUNDING_STREAMS_TESTNET,
21        PRE_BLOSSOM_HALVING_INTERVAL, PRE_NU6_FUNDING_STREAMS_MAINNET,
22        PRE_NU6_FUNDING_STREAMS_TESTNET,
23    },
24};
25
26/// Reserved network names that should not be allowed for configured Testnets.
27pub const RESERVED_NETWORK_NAMES: [&str; 6] = [
28    "Mainnet",
29    "Testnet",
30    "Regtest",
31    "MainnetKind",
32    "TestnetKind",
33    "RegtestKind",
34];
35
36/// Maximum length for a configured network name.
37pub const MAX_NETWORK_NAME_LENGTH: usize = 30;
38
39/// Maximum length for a configured human-readable prefix.
40pub const MAX_HRP_LENGTH: usize = 30;
41
42/// The block hash of the Regtest genesis block, `zcash-cli -regtest getblockhash 0`
43const REGTEST_GENESIS_HASH: &str =
44    "029f11d80ef9765602235e1bc9727e3eb6ba20839319f761fee920d63401e327";
45
46/// The block hash of the Testnet genesis block, `zcash-cli -testnet getblockhash 0`
47const TESTNET_GENESIS_HASH: &str =
48    "05a60a92d99d85997cce3b87616c089f6124d7342af37106edc76126334a2c38";
49
50/// The halving height interval in the regtest is 6 hours.
51/// [zcashd regtest halving interval](https://github.com/zcash/zcash/blob/v5.10.0/src/consensus/params.h#L252)
52const PRE_BLOSSOM_REGTEST_HALVING_INTERVAL: HeightDiff = 144;
53
54/// Configurable funding stream recipient for configured Testnets.
55#[derive(Serialize, Deserialize, Clone, Debug)]
56#[serde(deny_unknown_fields)]
57pub struct ConfiguredFundingStreamRecipient {
58    /// Funding stream receiver, see [`FundingStreams::recipients`] for more details.
59    pub receiver: FundingStreamReceiver,
60    /// The numerator for each funding stream receiver category, see [`FundingStreamRecipient::numerator`] for more details.
61    pub numerator: u64,
62    /// Addresses for the funding stream recipient, see [`FundingStreamRecipient::addresses`] for more details.
63    pub addresses: Option<Vec<String>>,
64}
65
66impl ConfiguredFundingStreamRecipient {
67    /// Converts a [`ConfiguredFundingStreamRecipient`] to a [`FundingStreamReceiver`] and [`FundingStreamRecipient`].
68    pub fn into_recipient(self) -> (FundingStreamReceiver, FundingStreamRecipient) {
69        (
70            self.receiver,
71            FundingStreamRecipient::new(self.numerator, self.addresses.unwrap_or_default()),
72        )
73    }
74}
75
76/// Configurable funding streams for configured Testnets.
77#[derive(Serialize, Deserialize, Clone, Default, Debug)]
78#[serde(deny_unknown_fields)]
79pub struct ConfiguredFundingStreams {
80    /// Start and end height for funding streams see [`FundingStreams::height_range`] for more details.
81    pub height_range: Option<std::ops::Range<Height>>,
82    /// Funding stream recipients, see [`FundingStreams::recipients`] for more details.
83    pub recipients: Option<Vec<ConfiguredFundingStreamRecipient>>,
84}
85
86impl From<&FundingStreams> for ConfiguredFundingStreams {
87    fn from(value: &FundingStreams) -> Self {
88        Self {
89            height_range: Some(value.height_range().clone()),
90            recipients: Some(
91                value
92                    .recipients()
93                    .iter()
94                    .map(|(receiver, recipient)| ConfiguredFundingStreamRecipient {
95                        receiver: *receiver,
96                        numerator: recipient.numerator(),
97                        addresses: Some(
98                            recipient
99                                .addresses()
100                                .iter()
101                                .map(ToString::to_string)
102                                .collect(),
103                        ),
104                    })
105                    .collect(),
106            ),
107        }
108    }
109}
110
111impl From<&BTreeMap<Height, NetworkUpgrade>> for ConfiguredActivationHeights {
112    fn from(activation_heights: &BTreeMap<Height, NetworkUpgrade>) -> Self {
113        let mut configured_activation_heights = ConfiguredActivationHeights::default();
114
115        for (height, network_upgrade) in activation_heights.iter() {
116            let field = match network_upgrade {
117                NetworkUpgrade::BeforeOverwinter => {
118                    &mut configured_activation_heights.before_overwinter
119                }
120                NetworkUpgrade::Overwinter => &mut configured_activation_heights.overwinter,
121                NetworkUpgrade::Sapling => &mut configured_activation_heights.sapling,
122                NetworkUpgrade::Blossom => &mut configured_activation_heights.blossom,
123                NetworkUpgrade::Heartwood => &mut configured_activation_heights.heartwood,
124                NetworkUpgrade::Canopy => &mut configured_activation_heights.canopy,
125                NetworkUpgrade::Nu5 => &mut configured_activation_heights.nu5,
126                NetworkUpgrade::Nu6 => &mut configured_activation_heights.nu6,
127                NetworkUpgrade::Nu6_1 => &mut configured_activation_heights.nu6_1,
128                NetworkUpgrade::Nu7 => &mut configured_activation_heights.nu7,
129                NetworkUpgrade::Genesis => {
130                    continue;
131                }
132            };
133
134            *field = Some(height.0)
135        }
136
137        configured_activation_heights
138    }
139}
140
141impl ConfiguredFundingStreams {
142    /// Returns an empty [`ConfiguredFundingStreams`].
143    fn empty() -> Self {
144        Self {
145            height_range: None,
146            recipients: Some(Vec::new()),
147        }
148    }
149
150    /// Converts a [`ConfiguredFundingStreams`] to a [`FundingStreams`], using the provided default values
151    /// if `height_range` or `recipients` are None.
152    fn convert_with_default(
153        self,
154        default_funding_streams: FundingStreams,
155        parameters_builder: &ParametersBuilder,
156    ) -> FundingStreams {
157        let network = parameters_builder.to_network_unchecked();
158        let height_range = self
159            .height_range
160            .unwrap_or(default_funding_streams.height_range().clone());
161
162        let recipients = self
163            .recipients
164            .map(|recipients| {
165                recipients
166                    .into_iter()
167                    .map(ConfiguredFundingStreamRecipient::into_recipient)
168                    .collect()
169            })
170            .unwrap_or(default_funding_streams.recipients().clone());
171
172        assert!(
173            height_range.start < height_range.end,
174            "funding stream end height must be above start height"
175        );
176
177        let funding_streams = FundingStreams::new(height_range.clone(), recipients);
178
179        check_funding_stream_address_period(&funding_streams, &network);
180
181        // check that sum of receiver numerators is valid.
182
183        let sum_numerators: u64 = funding_streams
184            .recipients()
185            .values()
186            .map(|r| r.numerator())
187            .sum();
188
189        assert!(
190            sum_numerators <= FUNDING_STREAM_RECEIVER_DENOMINATOR,
191            "sum of funding stream numerators must not be \
192         greater than denominator of {FUNDING_STREAM_RECEIVER_DENOMINATOR}"
193        );
194
195        funding_streams
196    }
197}
198
199/// Checks that the provided [`FundingStreams`] has sufficient recipient addresses for the
200/// funding stream address period of the provided [`Network`].
201fn check_funding_stream_address_period(funding_streams: &FundingStreams, network: &Network) {
202    let height_range = funding_streams.height_range();
203    let expected_min_num_addresses =
204        1u32.checked_add(funding_stream_address_period(
205            height_range
206                .end
207                .previous()
208                .expect("end height must be above start height and genesis height"),
209            network,
210        ))
211        .expect("no overflow should happen in this sum")
212        .checked_sub(funding_stream_address_period(height_range.start, network))
213        .expect("no overflow should happen in this sub") as usize;
214
215    for (&receiver, recipient) in funding_streams.recipients() {
216        if receiver == FundingStreamReceiver::Deferred {
217            // The `Deferred` receiver doesn't need any addresses.
218            continue;
219        }
220
221        assert!(
222            recipient.addresses().len() >= expected_min_num_addresses,
223            "recipients must have a sufficient number of addresses for height range, \
224         minimum num addresses required: {expected_min_num_addresses}"
225        );
226
227        for address in recipient.addresses() {
228            assert_eq!(
229                address.network_kind(),
230                NetworkKind::Testnet,
231                "configured funding stream addresses must be for Testnet"
232            );
233        }
234    }
235}
236
237/// Configurable activation heights for Regtest and configured Testnets.
238#[derive(Serialize, Deserialize, Default, Clone)]
239#[serde(rename_all = "PascalCase", deny_unknown_fields)]
240pub struct ConfiguredActivationHeights {
241    /// Activation height for `BeforeOverwinter` network upgrade.
242    pub before_overwinter: Option<u32>,
243    /// Activation height for `Overwinter` network upgrade.
244    pub overwinter: Option<u32>,
245    /// Activation height for `Sapling` network upgrade.
246    pub sapling: Option<u32>,
247    /// Activation height for `Blossom` network upgrade.
248    pub blossom: Option<u32>,
249    /// Activation height for `Heartwood` network upgrade.
250    pub heartwood: Option<u32>,
251    /// Activation height for `Canopy` network upgrade.
252    pub canopy: Option<u32>,
253    /// Activation height for `NU5` network upgrade.
254    #[serde(rename = "NU5")]
255    pub nu5: Option<u32>,
256    /// Activation height for `NU6` network upgrade.
257    #[serde(rename = "NU6")]
258    pub nu6: Option<u32>,
259    /// Activation height for `NU6.1` network upgrade.
260    #[serde(rename = "NU6.1")]
261    pub nu6_1: Option<u32>,
262    /// Activation height for `NU7` network upgrade.
263    #[serde(rename = "NU7")]
264    pub nu7: Option<u32>,
265}
266
267/// Builder for the [`Parameters`] struct.
268#[derive(Clone, Debug, Eq, PartialEq)]
269pub struct ParametersBuilder {
270    /// The name of this network to be used by the `Display` trait impl.
271    network_name: String,
272    /// The network magic, acts as an identifier for the network.
273    network_magic: Magic,
274    /// The genesis block hash
275    genesis_hash: block::Hash,
276    /// The network upgrade activation heights for this network, see [`Parameters::activation_heights`] for more details.
277    activation_heights: BTreeMap<Height, NetworkUpgrade>,
278    /// Slow start interval for this network
279    slow_start_interval: Height,
280    /// Pre-NU6 funding streams for this network
281    pre_nu6_funding_streams: FundingStreams,
282    /// Post-NU6 funding streams for this network
283    post_nu6_funding_streams: FundingStreams,
284    /// A flag indicating whether to allow changes to fields that affect
285    /// the funding stream address period.
286    should_lock_funding_stream_address_period: bool,
287    /// Target difficulty limit for this network
288    target_difficulty_limit: ExpandedDifficulty,
289    /// A flag for disabling proof-of-work checks when Zebra is validating blocks
290    disable_pow: bool,
291    /// Whether to allow transactions with transparent outputs to spend coinbase outputs,
292    /// similar to `fCoinbaseMustBeShielded` in zcashd.
293    should_allow_unshielded_coinbase_spends: bool,
294    /// The pre-Blossom halving interval for this network
295    pre_blossom_halving_interval: HeightDiff,
296    /// The post-Blossom halving interval for this network
297    post_blossom_halving_interval: HeightDiff,
298}
299
300impl Default for ParametersBuilder {
301    /// Creates a [`ParametersBuilder`] with all of the default Testnet parameters except `network_name`.
302    fn default() -> Self {
303        Self {
304            network_name: "UnknownTestnet".to_string(),
305            network_magic: magics::TESTNET,
306            // # Correctness
307            //
308            // `Genesis` network upgrade activation height must always be 0
309            activation_heights: TESTNET_ACTIVATION_HEIGHTS.iter().cloned().collect(),
310            genesis_hash: TESTNET_GENESIS_HASH
311                .parse()
312                .expect("hard-coded hash parses"),
313            slow_start_interval: SLOW_START_INTERVAL,
314            // Testnet PoWLimit is defined as `2^251 - 1` on page 73 of the protocol specification:
315            // <https://zips.z.cash/protocol/protocol.pdf>
316            //
317            // `zcashd` converts the PoWLimit into a compact representation before
318            // using it to perform difficulty filter checks.
319            //
320            // The Zcash specification converts to compact for the default difficulty
321            // filter, but not for testnet minimum difficulty blocks. (ZIP 205 and
322            // ZIP 208 don't specify this conversion either.) See #1277 for details.
323            target_difficulty_limit: ExpandedDifficulty::from((U256::one() << 251) - 1)
324                .to_compact()
325                .to_expanded()
326                .expect("difficulty limits are valid expanded values"),
327            disable_pow: false,
328            pre_nu6_funding_streams: PRE_NU6_FUNDING_STREAMS_TESTNET.clone(),
329            post_nu6_funding_streams: POST_NU6_FUNDING_STREAMS_TESTNET.clone(),
330            should_lock_funding_stream_address_period: false,
331            pre_blossom_halving_interval: PRE_BLOSSOM_HALVING_INTERVAL,
332            post_blossom_halving_interval: POST_BLOSSOM_HALVING_INTERVAL,
333            should_allow_unshielded_coinbase_spends: false,
334        }
335    }
336}
337
338impl ParametersBuilder {
339    /// Sets the network name to be used in the [`Parameters`] being built.
340    pub fn with_network_name(mut self, network_name: impl fmt::Display) -> Self {
341        self.network_name = network_name.to_string();
342
343        assert!(
344            !RESERVED_NETWORK_NAMES.contains(&self.network_name.as_str()),
345            "cannot use reserved network name '{network_name}' as configured Testnet name, reserved names: {RESERVED_NETWORK_NAMES:?}"
346        );
347
348        assert!(
349            self.network_name.len() <= MAX_NETWORK_NAME_LENGTH,
350            "network name {network_name} is too long, must be {MAX_NETWORK_NAME_LENGTH} characters or less"
351        );
352
353        assert!(
354            self.network_name
355                .chars()
356                .all(|x| x.is_alphanumeric() || x == '_'),
357            "network name must include only alphanumeric characters or '_'"
358        );
359
360        self
361    }
362
363    /// Sets the network name to be used in the [`Parameters`] being built.
364    pub fn with_network_magic(mut self, network_magic: Magic) -> Self {
365        assert!(
366            [magics::MAINNET, magics::REGTEST]
367                .into_iter()
368                .all(|reserved_magic| network_magic != reserved_magic),
369            "network magic should be distinct from reserved network magics"
370        );
371
372        self.network_magic = network_magic;
373
374        self
375    }
376
377    /// Parses the hex-encoded block hash and sets it as the genesis hash in the [`Parameters`] being built.
378    pub fn with_genesis_hash(mut self, genesis_hash: impl fmt::Display) -> Self {
379        self.genesis_hash = genesis_hash
380            .to_string()
381            .parse()
382            .expect("configured genesis hash must parse");
383        self
384    }
385
386    /// Checks that the provided network upgrade activation heights are in the correct order, then
387    /// sets them as the new network upgrade activation heights.
388    pub fn with_activation_heights(
389        mut self,
390        ConfiguredActivationHeights {
391            before_overwinter,
392            overwinter,
393            sapling,
394            blossom,
395            heartwood,
396            canopy,
397            nu5,
398            nu6,
399            nu6_1,
400            nu7,
401        }: ConfiguredActivationHeights,
402    ) -> Self {
403        use NetworkUpgrade::*;
404
405        if self.should_lock_funding_stream_address_period {
406            panic!("activation heights on ParametersBuilder must not be set after setting funding streams");
407        }
408
409        // # Correctness
410        //
411        // These must be in order so that later network upgrades overwrite prior ones
412        // if multiple network upgrades are configured with the same activation height.
413        let activation_heights: BTreeMap<_, _> = before_overwinter
414            .into_iter()
415            .map(|h| (h, BeforeOverwinter))
416            .chain(overwinter.into_iter().map(|h| (h, Overwinter)))
417            .chain(sapling.into_iter().map(|h| (h, Sapling)))
418            .chain(blossom.into_iter().map(|h| (h, Blossom)))
419            .chain(heartwood.into_iter().map(|h| (h, Heartwood)))
420            .chain(canopy.into_iter().map(|h| (h, Canopy)))
421            .chain(nu5.into_iter().map(|h| (h, Nu5)))
422            .chain(nu6.into_iter().map(|h| (h, Nu6)))
423            .chain(nu6_1.into_iter().map(|h| (h, Nu6_1)))
424            .chain(nu7.into_iter().map(|h| (h, Nu7)))
425            .map(|(h, nu)| (h.try_into().expect("activation height must be valid"), nu))
426            .collect();
427
428        let network_upgrades: Vec<_> = activation_heights.iter().map(|(_h, &nu)| nu).collect();
429
430        // Check that the provided network upgrade activation heights are in the same order by height as the default testnet activation heights
431        let mut activation_heights_iter = activation_heights.iter();
432        for expected_network_upgrade in NetworkUpgrade::iter() {
433            if !network_upgrades.contains(&expected_network_upgrade) {
434                continue;
435            } else if let Some((&height, &network_upgrade)) = activation_heights_iter.next() {
436                assert_ne!(
437                    height,
438                    Height(0),
439                    "Height(0) is reserved for the `Genesis` upgrade"
440                );
441
442                assert!(
443                    network_upgrade == expected_network_upgrade,
444                    "network upgrades must be activated in order specified by the protocol"
445                );
446            }
447        }
448
449        // # Correctness
450        //
451        // Height(0) must be reserved for the `NetworkUpgrade::Genesis`.
452        self.activation_heights.split_off(&Height(1));
453        self.activation_heights.extend(activation_heights);
454
455        self
456    }
457
458    /// Sets the slow start interval to be used in the [`Parameters`] being built.
459    pub fn with_slow_start_interval(mut self, slow_start_interval: Height) -> Self {
460        self.slow_start_interval = slow_start_interval;
461        self
462    }
463
464    /// Sets pre-NU6 funding streams to be used in the [`Parameters`] being built.
465    pub fn with_pre_nu6_funding_streams(
466        mut self,
467        funding_streams: ConfiguredFundingStreams,
468    ) -> Self {
469        self.pre_nu6_funding_streams =
470            funding_streams.convert_with_default(PRE_NU6_FUNDING_STREAMS_TESTNET.clone(), &self);
471        self.should_lock_funding_stream_address_period = true;
472        self
473    }
474
475    /// Sets post-NU6 funding streams to be used in the [`Parameters`] being built.
476    pub fn with_post_nu6_funding_streams(
477        mut self,
478        funding_streams: ConfiguredFundingStreams,
479    ) -> Self {
480        self.post_nu6_funding_streams =
481            funding_streams.convert_with_default(POST_NU6_FUNDING_STREAMS_TESTNET.clone(), &self);
482        self.should_lock_funding_stream_address_period = true;
483        self
484    }
485
486    /// Sets the target difficulty limit to be used in the [`Parameters`] being built.
487    // TODO: Accept a hex-encoded String instead?
488    pub fn with_target_difficulty_limit(
489        mut self,
490        target_difficulty_limit: impl Into<ExpandedDifficulty>,
491    ) -> Self {
492        self.target_difficulty_limit = target_difficulty_limit
493            .into()
494            .to_compact()
495            .to_expanded()
496            .expect("difficulty limits are valid expanded values");
497        self
498    }
499
500    /// Sets the `disable_pow` flag to be used in the [`Parameters`] being built.
501    pub fn with_disable_pow(mut self, disable_pow: bool) -> Self {
502        self.disable_pow = disable_pow;
503        self
504    }
505
506    /// Sets the `disable_pow` flag to be used in the [`Parameters`] being built.
507    pub fn with_unshielded_coinbase_spends(
508        mut self,
509        should_allow_unshielded_coinbase_spends: bool,
510    ) -> Self {
511        self.should_allow_unshielded_coinbase_spends = should_allow_unshielded_coinbase_spends;
512        self
513    }
514
515    /// Sets the pre and post Blosssom halving intervals to be used in the [`Parameters`] being built.
516    pub fn with_halving_interval(mut self, pre_blossom_halving_interval: HeightDiff) -> Self {
517        if self.should_lock_funding_stream_address_period {
518            panic!("halving interval on ParametersBuilder must not be set after setting funding streams");
519        }
520
521        self.pre_blossom_halving_interval = pre_blossom_halving_interval;
522        self.post_blossom_halving_interval =
523            self.pre_blossom_halving_interval * (BLOSSOM_POW_TARGET_SPACING_RATIO as HeightDiff);
524        self
525    }
526
527    /// Converts the builder to a [`Parameters`] struct
528    fn finish(self) -> Parameters {
529        let Self {
530            network_name,
531            network_magic,
532            genesis_hash,
533            activation_heights,
534            slow_start_interval,
535            pre_nu6_funding_streams,
536            post_nu6_funding_streams,
537            should_lock_funding_stream_address_period: _,
538            target_difficulty_limit,
539            disable_pow,
540            should_allow_unshielded_coinbase_spends,
541            pre_blossom_halving_interval,
542            post_blossom_halving_interval,
543        } = self;
544        Parameters {
545            network_name,
546            network_magic,
547            genesis_hash,
548            activation_heights,
549            slow_start_interval,
550            slow_start_shift: Height(slow_start_interval.0 / 2),
551            pre_nu6_funding_streams,
552            post_nu6_funding_streams,
553            target_difficulty_limit,
554            disable_pow,
555            should_allow_unshielded_coinbase_spends,
556            pre_blossom_halving_interval,
557            post_blossom_halving_interval,
558        }
559    }
560
561    /// Converts the builder to a configured [`Network::Testnet`]
562    fn to_network_unchecked(&self) -> Network {
563        Network::new_configured_testnet(self.clone().finish())
564    }
565
566    /// Checks funding streams and converts the builder to a configured [`Network::Testnet`]
567    pub fn to_network(self) -> Network {
568        let network = self.to_network_unchecked();
569
570        // Final check that the configured funding streams will be valid for these Testnet parameters.
571        // TODO: Always check funding stream address period once the testnet parameters are being serialized (#8920).
572        #[cfg(not(any(test, feature = "proptest-impl")))]
573        {
574            check_funding_stream_address_period(&self.pre_nu6_funding_streams, &network);
575            check_funding_stream_address_period(&self.post_nu6_funding_streams, &network);
576        }
577
578        network
579    }
580
581    /// Returns true if these [`Parameters`] should be compatible with the default Testnet parameters.
582    pub fn is_compatible_with_default_parameters(&self) -> bool {
583        let Self {
584            network_name: _,
585            network_magic,
586            genesis_hash,
587            activation_heights,
588            slow_start_interval,
589            pre_nu6_funding_streams,
590            post_nu6_funding_streams,
591            should_lock_funding_stream_address_period: _,
592            target_difficulty_limit,
593            disable_pow,
594            should_allow_unshielded_coinbase_spends,
595            pre_blossom_halving_interval,
596            post_blossom_halving_interval,
597        } = Self::default();
598
599        self.activation_heights == activation_heights
600            && self.network_magic == network_magic
601            && self.genesis_hash == genesis_hash
602            && self.slow_start_interval == slow_start_interval
603            && self.pre_nu6_funding_streams == pre_nu6_funding_streams
604            && self.post_nu6_funding_streams == post_nu6_funding_streams
605            && self.target_difficulty_limit == target_difficulty_limit
606            && self.disable_pow == disable_pow
607            && self.should_allow_unshielded_coinbase_spends
608                == should_allow_unshielded_coinbase_spends
609            && self.pre_blossom_halving_interval == pre_blossom_halving_interval
610            && self.post_blossom_halving_interval == post_blossom_halving_interval
611    }
612}
613
614/// Network consensus parameters for test networks such as Regtest and the default Testnet.
615#[derive(Clone, Debug, Eq, PartialEq)]
616pub struct Parameters {
617    /// The name of this network to be used by the `Display` trait impl.
618    network_name: String,
619    /// The network magic, acts as an identifier for the network.
620    network_magic: Magic,
621    /// The genesis block hash
622    genesis_hash: block::Hash,
623    /// The network upgrade activation heights for this network.
624    ///
625    /// Note: This value is ignored by `Network::activation_list()` when `zebra-chain` is
626    ///       compiled with the `zebra-test` feature flag AND the `TEST_FAKE_ACTIVATION_HEIGHTS`
627    ///       environment variable is set.
628    activation_heights: BTreeMap<Height, NetworkUpgrade>,
629    /// Slow start interval for this network
630    slow_start_interval: Height,
631    /// Slow start shift for this network, always half the slow start interval
632    slow_start_shift: Height,
633    /// Pre-NU6 funding streams for this network
634    pre_nu6_funding_streams: FundingStreams,
635    /// Post-NU6 funding streams for this network
636    post_nu6_funding_streams: FundingStreams,
637    /// Target difficulty limit for this network
638    target_difficulty_limit: ExpandedDifficulty,
639    /// A flag for disabling proof-of-work checks when Zebra is validating blocks
640    disable_pow: bool,
641    /// Whether to allow transactions with transparent outputs to spend coinbase outputs,
642    /// similar to `fCoinbaseMustBeShielded` in zcashd.
643    should_allow_unshielded_coinbase_spends: bool,
644    /// Pre-Blossom halving interval for this network
645    pre_blossom_halving_interval: HeightDiff,
646    /// Post-Blossom halving interval for this network
647    post_blossom_halving_interval: HeightDiff,
648}
649
650impl Default for Parameters {
651    /// Returns an instance of the default public testnet [`Parameters`].
652    fn default() -> Self {
653        Self {
654            network_name: "Testnet".to_string(),
655            ..Self::build().finish()
656        }
657    }
658}
659
660impl Parameters {
661    /// Creates a new [`ParametersBuilder`].
662    pub fn build() -> ParametersBuilder {
663        ParametersBuilder::default()
664    }
665
666    /// Accepts a [`ConfiguredActivationHeights`].
667    ///
668    /// Creates an instance of [`Parameters`] with `Regtest` values.
669    pub fn new_regtest(
670        ConfiguredActivationHeights { nu5, nu6, nu7, .. }: ConfiguredActivationHeights,
671    ) -> Self {
672        #[cfg(any(test, feature = "proptest-impl"))]
673        let nu5 = nu5.or(Some(100));
674
675        let parameters = Self::build()
676            .with_genesis_hash(REGTEST_GENESIS_HASH)
677            // This value is chosen to match zcashd, see: <https://github.com/zcash/zcash/blob/master/src/chainparams.cpp#L654>
678            .with_target_difficulty_limit(U256::from_big_endian(&[0x0f; 32]))
679            .with_disable_pow(true)
680            .with_unshielded_coinbase_spends(true)
681            .with_slow_start_interval(Height::MIN)
682            // Removes default Testnet activation heights if not configured,
683            // most network upgrades are disabled by default for Regtest in zcashd
684            .with_activation_heights(ConfiguredActivationHeights {
685                canopy: Some(1),
686                nu5,
687                nu6,
688                nu7,
689                ..Default::default()
690            })
691            .with_halving_interval(PRE_BLOSSOM_REGTEST_HALVING_INTERVAL);
692
693        // TODO: Always clear funding streams on Regtest once the testnet parameters are being serialized (#8920).
694        // #[cfg(not(any(test, feature = "proptest-impl")))]
695        let parameters = parameters
696            .with_pre_nu6_funding_streams(ConfiguredFundingStreams::empty())
697            .with_post_nu6_funding_streams(ConfiguredFundingStreams::empty());
698
699        Self {
700            network_name: "Regtest".to_string(),
701            network_magic: magics::REGTEST,
702            ..parameters.finish()
703        }
704    }
705
706    /// Returns true if the instance of [`Parameters`] represents the default public Testnet.
707    pub fn is_default_testnet(&self) -> bool {
708        self == &Self::default()
709    }
710
711    /// Returns true if the instance of [`Parameters`] represents Regtest.
712    pub fn is_regtest(&self) -> bool {
713        if self.network_magic != magics::REGTEST {
714            return false;
715        }
716
717        let Self {
718            network_name,
719            // Already checked network magic above
720            network_magic: _,
721            genesis_hash,
722            // Activation heights are configurable on Regtest
723            activation_heights: _,
724            slow_start_interval,
725            slow_start_shift,
726            pre_nu6_funding_streams,
727            post_nu6_funding_streams,
728            target_difficulty_limit,
729            disable_pow,
730            should_allow_unshielded_coinbase_spends,
731            pre_blossom_halving_interval,
732            post_blossom_halving_interval,
733        } = Self::new_regtest(Default::default());
734
735        self.network_name == network_name
736            && self.genesis_hash == genesis_hash
737            && self.slow_start_interval == slow_start_interval
738            && self.slow_start_shift == slow_start_shift
739            && self.pre_nu6_funding_streams == pre_nu6_funding_streams
740            && self.post_nu6_funding_streams == post_nu6_funding_streams
741            && self.target_difficulty_limit == target_difficulty_limit
742            && self.disable_pow == disable_pow
743            && self.should_allow_unshielded_coinbase_spends
744                == should_allow_unshielded_coinbase_spends
745            && self.pre_blossom_halving_interval == pre_blossom_halving_interval
746            && self.post_blossom_halving_interval == post_blossom_halving_interval
747    }
748
749    /// Returns the network name
750    pub fn network_name(&self) -> &str {
751        &self.network_name
752    }
753
754    /// Returns the network magic
755    pub fn network_magic(&self) -> Magic {
756        self.network_magic
757    }
758
759    /// Returns the genesis hash
760    pub fn genesis_hash(&self) -> block::Hash {
761        self.genesis_hash
762    }
763
764    /// Returns the network upgrade activation heights
765    pub fn activation_heights(&self) -> &BTreeMap<Height, NetworkUpgrade> {
766        &self.activation_heights
767    }
768
769    /// Returns slow start interval for this network
770    pub fn slow_start_interval(&self) -> Height {
771        self.slow_start_interval
772    }
773
774    /// Returns slow start shift for this network
775    pub fn slow_start_shift(&self) -> Height {
776        self.slow_start_shift
777    }
778
779    /// Returns pre-NU6 funding streams for this network
780    pub fn pre_nu6_funding_streams(&self) -> &FundingStreams {
781        &self.pre_nu6_funding_streams
782    }
783
784    /// Returns post-NU6 funding streams for this network
785    pub fn post_nu6_funding_streams(&self) -> &FundingStreams {
786        &self.post_nu6_funding_streams
787    }
788
789    /// Returns the target difficulty limit for this network
790    pub fn target_difficulty_limit(&self) -> ExpandedDifficulty {
791        self.target_difficulty_limit
792    }
793
794    /// Returns true if proof-of-work validation should be disabled for this network
795    pub fn disable_pow(&self) -> bool {
796        self.disable_pow
797    }
798
799    /// Returns true if this network should allow transactions with transparent outputs
800    /// that spend coinbase outputs.
801    pub fn should_allow_unshielded_coinbase_spends(&self) -> bool {
802        self.should_allow_unshielded_coinbase_spends
803    }
804
805    /// Returns the pre-Blossom halving interval for this network
806    pub fn pre_blossom_halving_interval(&self) -> HeightDiff {
807        self.pre_blossom_halving_interval
808    }
809
810    /// Returns the post-Blossom halving interval for this network
811    pub fn post_blossom_halving_interval(&self) -> HeightDiff {
812        self.post_blossom_halving_interval
813    }
814}
815
816impl Network {
817    /// Returns the parameters of this network if it is a Testnet.
818    pub fn parameters(&self) -> Option<Arc<Parameters>> {
819        if let Self::Testnet(parameters) = self {
820            Some(parameters.clone())
821        } else {
822            None
823        }
824    }
825
826    /// Returns true if proof-of-work validation should be disabled for this network
827    pub fn disable_pow(&self) -> bool {
828        if let Self::Testnet(params) = self {
829            params.disable_pow()
830        } else {
831            false
832        }
833    }
834
835    /// Returns slow start interval for this network
836    pub fn slow_start_interval(&self) -> Height {
837        if let Self::Testnet(params) = self {
838            params.slow_start_interval()
839        } else {
840            SLOW_START_INTERVAL
841        }
842    }
843
844    /// Returns slow start shift for this network
845    pub fn slow_start_shift(&self) -> Height {
846        if let Self::Testnet(params) = self {
847            params.slow_start_shift()
848        } else {
849            SLOW_START_SHIFT
850        }
851    }
852
853    /// Returns pre-NU6 funding streams for this network
854    ///
855    /// Commonly referred to as the "Dev Fund".
856    ///
857    /// Defined in [Zcash Protocol Specification §7.10.1][7.10.1]
858    ///
859    /// [7.10.1]: https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams
860    pub fn pre_nu6_funding_streams(&self) -> &FundingStreams {
861        if let Self::Testnet(params) = self {
862            params.pre_nu6_funding_streams()
863        } else {
864            &PRE_NU6_FUNDING_STREAMS_MAINNET
865        }
866    }
867
868    /// Returns post-NU6 funding streams for this network
869    ///
870    /// Defined in [Zcash Protocol Specification §7.10.1][7.10.1]
871    ///
872    /// [7.10.1]: https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams
873    pub fn post_nu6_funding_streams(&self) -> &FundingStreams {
874        if let Self::Testnet(params) = self {
875            params.post_nu6_funding_streams()
876        } else {
877            &POST_NU6_FUNDING_STREAMS_MAINNET
878        }
879    }
880
881    /// Returns post-Canopy funding streams for this network at the provided height
882    pub fn funding_streams(&self, height: Height) -> &FundingStreams {
883        if NetworkUpgrade::current(self, height) < NetworkUpgrade::Nu6 {
884            self.pre_nu6_funding_streams()
885        } else {
886            self.post_nu6_funding_streams()
887        }
888    }
889
890    /// Returns true if this network should allow transactions with transparent outputs
891    /// that spend coinbase outputs.
892    pub fn should_allow_unshielded_coinbase_spends(&self) -> bool {
893        if let Self::Testnet(params) = self {
894            params.should_allow_unshielded_coinbase_spends()
895        } else {
896            false
897        }
898    }
899}