zebra_chain/parameters/network/
testnet.rs

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