zebra_chain/parameters/network/
subsidy.rs

1//! Constants and calculations for Block Subsidy and Funding Streams
2//!
3//! This module contains the consensus parameters which are required for
4//! verification.
5//!
6//! Some consensus parameters change based on network upgrades. Each network
7//! upgrade happens at a particular block height. Some parameters have a value
8//! (or function) before the upgrade height, at the upgrade height, and after
9//! the upgrade height. (For example, the value of the reserved field in the
10//! block header during the Heartwood upgrade.)
11//!
12//! Typically, consensus parameters are accessed via a function that takes a
13//! `Network` and `block::Height`.
14
15use std::collections::HashMap;
16
17use lazy_static::lazy_static;
18
19use crate::{
20    amount::COIN,
21    block::{Height, HeightDiff},
22    parameters::{Network, NetworkUpgrade},
23    transparent,
24};
25
26/// The largest block subsidy, used before the first halving.
27///
28/// We use `25 / 2` instead of `12.5`, so that we can calculate the correct value without using floating-point.
29/// This calculation is exact, because COIN is divisible by 2, and the division is done last.
30pub const MAX_BLOCK_SUBSIDY: u64 = ((25 * COIN) / 2) as u64;
31
32/// Used as a multiplier to get the new halving interval after Blossom.
33///
34/// Calculated as `PRE_BLOSSOM_POW_TARGET_SPACING / POST_BLOSSOM_POW_TARGET_SPACING`
35/// in the Zcash specification.
36pub const BLOSSOM_POW_TARGET_SPACING_RATIO: u32 = 2;
37
38/// Halving is at about every 4 years, before Blossom block time is 150 seconds.
39///
40/// `(60 * 60 * 24 * 365 * 4) / 150 = 840960`
41pub const PRE_BLOSSOM_HALVING_INTERVAL: HeightDiff = 840_000;
42
43/// After Blossom the block time is reduced to 75 seconds but halving period should remain around 4 years.
44pub const POST_BLOSSOM_HALVING_INTERVAL: HeightDiff =
45    PRE_BLOSSOM_HALVING_INTERVAL * (BLOSSOM_POW_TARGET_SPACING_RATIO as HeightDiff);
46
47/// The first halving height in the testnet is at block height `1_116_000`
48/// as specified in [protocol specification §7.10.1][7.10.1]
49///
50/// [7.10.1]: https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams
51pub(crate) const FIRST_HALVING_TESTNET: Height = Height(1_116_000);
52
53/// The first halving height in the regtest is at block height `287`.
54const FIRST_HALVING_REGTEST: Height = Height(287);
55
56/// The funding stream receiver categories.
57#[derive(Deserialize, Clone, Copy, Debug, Eq, Hash, PartialEq)]
58pub enum FundingStreamReceiver {
59    /// The Electric Coin Company (Bootstrap Foundation) funding stream.
60    #[serde(rename = "ECC")]
61    Ecc,
62
63    /// The Zcash Foundation funding stream.
64    ZcashFoundation,
65
66    /// The Major Grants (Zcash Community Grants) funding stream.
67    MajorGrants,
68
69    /// The deferred pool contribution, see [ZIP-1015](https://zips.z.cash/zip-1015) for more details.
70    Deferred,
71}
72
73impl FundingStreamReceiver {
74    /// Returns a human-readable name and a specification URL for the receiver, as described in
75    /// [ZIP-1014] and [`zcashd`] before NU6. After NU6, the specification is in the [ZIP-1015].
76    ///
77    /// [ZIP-1014]: https://zips.z.cash/zip-1014#abstract
78    /// [`zcashd`]: https://github.com/zcash/zcash/blob/3f09cfa00a3c90336580a127e0096d99e25a38d6/src/consensus/funding.cpp#L13-L32
79    /// [ZIP-1015]: https://zips.z.cash/zip-1015
80    pub fn info(&self, is_nu6: bool) -> (&'static str, &'static str) {
81        if is_nu6 {
82            (
83                match self {
84                    FundingStreamReceiver::Ecc => "Electric Coin Company",
85                    FundingStreamReceiver::ZcashFoundation => "Zcash Foundation",
86                    FundingStreamReceiver::MajorGrants => "Zcash Community Grants NU6",
87                    FundingStreamReceiver::Deferred => "Lockbox NU6",
88                },
89                LOCKBOX_SPECIFICATION,
90            )
91        } else {
92            (
93                match self {
94                    FundingStreamReceiver::Ecc => "Electric Coin Company",
95                    FundingStreamReceiver::ZcashFoundation => "Zcash Foundation",
96                    FundingStreamReceiver::MajorGrants => "Major Grants",
97                    FundingStreamReceiver::Deferred => "Lockbox NU6",
98                },
99                FUNDING_STREAM_SPECIFICATION,
100            )
101        }
102    }
103}
104
105/// Denominator as described in [protocol specification §7.10.1][7.10.1].
106///
107/// [7.10.1]: https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams
108pub const FUNDING_STREAM_RECEIVER_DENOMINATOR: u64 = 100;
109
110/// The specification for pre-NU6 funding stream receivers, a URL that links to [ZIP-214].
111///
112/// [ZIP-214]: https://zips.z.cash/zip-0214
113pub const FUNDING_STREAM_SPECIFICATION: &str = "https://zips.z.cash/zip-0214";
114
115/// The specification for post-NU6 funding stream and lockbox receivers, a URL that links to [ZIP-1015].
116///
117/// [ZIP-1015]: https://zips.z.cash/zip-1015
118pub const LOCKBOX_SPECIFICATION: &str = "https://zips.z.cash/zip-1015";
119
120/// Funding stream recipients and height ranges.
121#[derive(Deserialize, Clone, Debug, Eq, PartialEq)]
122pub struct FundingStreams {
123    /// Start and end Heights for funding streams
124    /// as described in [protocol specification §7.10.1][7.10.1].
125    ///
126    /// [7.10.1]: https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams
127    height_range: std::ops::Range<Height>,
128    /// Funding stream recipients by [`FundingStreamReceiver`].
129    recipients: HashMap<FundingStreamReceiver, FundingStreamRecipient>,
130}
131
132impl FundingStreams {
133    /// Creates a new [`FundingStreams`].
134    pub fn new(
135        height_range: std::ops::Range<Height>,
136        recipients: HashMap<FundingStreamReceiver, FundingStreamRecipient>,
137    ) -> Self {
138        Self {
139            height_range,
140            recipients,
141        }
142    }
143
144    /// Returns height range where these [`FundingStreams`] should apply.
145    pub fn height_range(&self) -> &std::ops::Range<Height> {
146        &self.height_range
147    }
148
149    /// Returns recipients of these [`FundingStreams`].
150    pub fn recipients(&self) -> &HashMap<FundingStreamReceiver, FundingStreamRecipient> {
151        &self.recipients
152    }
153
154    /// Returns a recipient with the provided receiver.
155    pub fn recipient(&self, receiver: FundingStreamReceiver) -> Option<&FundingStreamRecipient> {
156        self.recipients.get(&receiver)
157    }
158}
159
160/// A funding stream recipient as specified in [protocol specification §7.10.1][7.10.1]
161///
162/// [7.10.1]: https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams
163#[derive(Deserialize, Clone, Debug, Eq, PartialEq)]
164pub struct FundingStreamRecipient {
165    /// The numerator for each funding stream receiver category
166    /// as described in [protocol specification §7.10.1][7.10.1].
167    ///
168    /// [7.10.1]: https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams
169    numerator: u64,
170    /// Addresses for the funding stream recipient
171    addresses: Vec<transparent::Address>,
172}
173
174impl FundingStreamRecipient {
175    /// Creates a new [`FundingStreamRecipient`].
176    pub fn new<I, T>(numerator: u64, addresses: I) -> Self
177    where
178        T: ToString,
179        I: IntoIterator<Item = T>,
180    {
181        Self {
182            numerator,
183            addresses: addresses
184                .into_iter()
185                .map(|addr| {
186                    let addr = addr.to_string();
187                    addr.parse()
188                        .expect("funding stream address must deserialize")
189                })
190                .collect(),
191        }
192    }
193
194    /// Returns the numerator for this funding stream.
195    pub fn numerator(&self) -> u64 {
196        self.numerator
197    }
198
199    /// Returns the receiver of this funding stream.
200    pub fn addresses(&self) -> &[transparent::Address] {
201        &self.addresses
202    }
203}
204
205lazy_static! {
206    /// The pre-NU6 funding streams for Mainnet as described in [protocol specification §7.10.1][7.10.1]
207    /// [7.10.1]: https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams
208    pub static ref PRE_NU6_FUNDING_STREAMS_MAINNET: FundingStreams = FundingStreams {
209        height_range: Height(1_046_400)..Height(2_726_400),
210        recipients: [
211            (
212                FundingStreamReceiver::Ecc,
213                FundingStreamRecipient::new(7, FUNDING_STREAM_ECC_ADDRESSES_MAINNET),
214            ),
215            (
216                FundingStreamReceiver::ZcashFoundation,
217                FundingStreamRecipient::new(5, FUNDING_STREAM_ZF_ADDRESSES_MAINNET),
218            ),
219            (
220                FundingStreamReceiver::MajorGrants,
221                FundingStreamRecipient::new(8, FUNDING_STREAM_MG_ADDRESSES_MAINNET),
222            ),
223        ]
224        .into_iter()
225        .collect(),
226    };
227
228    /// The post-NU6 funding streams for Mainnet as described in [ZIP-1015](https://zips.z.cash/zip-1015).
229    pub static ref POST_NU6_FUNDING_STREAMS_MAINNET: FundingStreams = FundingStreams {
230        height_range: POST_NU6_FUNDING_STREAM_START_RANGE_MAINNET,
231        recipients: [
232            (
233                FundingStreamReceiver::Deferred,
234                FundingStreamRecipient::new::<[&str; 0], &str>(12, []),
235            ),
236            (
237                FundingStreamReceiver::MajorGrants,
238                FundingStreamRecipient::new(8, POST_NU6_FUNDING_STREAM_FPF_ADDRESSES_MAINNET),
239            ),
240        ]
241        .into_iter()
242        .collect()
243    };
244
245    /// The pre-NU6 funding streams for Testnet as described in [protocol specification §7.10.1][7.10.1]
246    /// [7.10.1]: https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams
247    pub static ref PRE_NU6_FUNDING_STREAMS_TESTNET: FundingStreams = FundingStreams {
248        height_range: Height(1_028_500)..Height(2_796_000),
249        recipients: [
250            (
251                FundingStreamReceiver::Ecc,
252                FundingStreamRecipient::new(7, FUNDING_STREAM_ECC_ADDRESSES_TESTNET),
253            ),
254            (
255                FundingStreamReceiver::ZcashFoundation,
256                FundingStreamRecipient::new(5, FUNDING_STREAM_ZF_ADDRESSES_TESTNET),
257            ),
258            (
259                FundingStreamReceiver::MajorGrants,
260                FundingStreamRecipient::new(8, FUNDING_STREAM_MG_ADDRESSES_TESTNET),
261            ),
262        ]
263        .into_iter()
264        .collect(),
265    };
266
267    /// The post-NU6 funding streams for Testnet as described in [ZIP-1015](https://zips.z.cash/zip-1015).
268    pub static ref POST_NU6_FUNDING_STREAMS_TESTNET: FundingStreams = FundingStreams {
269        height_range: POST_NU6_FUNDING_STREAM_START_RANGE_TESTNET,
270        recipients: [
271            (
272                FundingStreamReceiver::Deferred,
273                FundingStreamRecipient::new::<[&str; 0], &str>(12, []),
274            ),
275            (
276                FundingStreamReceiver::MajorGrants,
277                FundingStreamRecipient::new(8, POST_NU6_FUNDING_STREAM_FPF_ADDRESSES_TESTNET),
278            ),
279        ]
280        .into_iter()
281        .collect()
282    };
283}
284
285/// The start height of post-NU6 funding streams on Mainnet as described in [ZIP-1015](https://zips.z.cash/zip-1015).
286const POST_NU6_FUNDING_STREAM_START_HEIGHT_MAINNET: u32 = 2_726_400;
287
288/// The start height of post-NU6 funding streams on Testnet as described in [ZIP-1015](https://zips.z.cash/zip-1015).
289const POST_NU6_FUNDING_STREAM_START_HEIGHT_TESTNET: u32 = 2_976_000;
290
291/// The number of blocks contained in the post-NU6 funding streams height ranges on Mainnet or Testnet, as specified
292/// in [ZIP-1015](https://zips.z.cash/zip-1015).
293const POST_NU6_FUNDING_STREAM_NUM_BLOCKS: u32 = 420_000;
294
295/// The post-NU6 funding stream height range on Mainnet
296const POST_NU6_FUNDING_STREAM_START_RANGE_MAINNET: std::ops::Range<Height> =
297    Height(POST_NU6_FUNDING_STREAM_START_HEIGHT_MAINNET)
298        ..Height(POST_NU6_FUNDING_STREAM_START_HEIGHT_MAINNET + POST_NU6_FUNDING_STREAM_NUM_BLOCKS);
299
300/// The post-NU6 funding stream height range on Testnet
301const POST_NU6_FUNDING_STREAM_START_RANGE_TESTNET: std::ops::Range<Height> =
302    Height(POST_NU6_FUNDING_STREAM_START_HEIGHT_TESTNET)
303        ..Height(POST_NU6_FUNDING_STREAM_START_HEIGHT_TESTNET + POST_NU6_FUNDING_STREAM_NUM_BLOCKS);
304
305/// Address change interval function here as a constant
306/// as described in [protocol specification §7.10.1][7.10.1].
307///
308/// [7.10.1]: https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams
309pub const FUNDING_STREAM_ADDRESS_CHANGE_INTERVAL: HeightDiff = POST_BLOSSOM_HALVING_INTERVAL / 48;
310
311/// Number of addresses for each funding stream in the Mainnet.
312/// In the spec ([protocol specification §7.10][7.10]) this is defined as: `fs.addressindex(fs.endheight - 1)`
313/// however we know this value beforehand so we prefer to make it a constant instead.
314///
315/// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams
316pub const FUNDING_STREAMS_NUM_ADDRESSES_MAINNET: usize = 48;
317
318/// List of addresses for the ECC funding stream in the Mainnet.
319pub const FUNDING_STREAM_ECC_ADDRESSES_MAINNET: [&str; FUNDING_STREAMS_NUM_ADDRESSES_MAINNET] = [
320    "t3LmX1cxWPPPqL4TZHx42HU3U5ghbFjRiif",
321    "t3Toxk1vJQ6UjWQ42tUJz2rV2feUWkpbTDs",
322    "t3ZBdBe4iokmsjdhMuwkxEdqMCFN16YxKe6",
323    "t3ZuaJziLM8xZ32rjDUzVjVtyYdDSz8GLWB",
324    "t3bAtYWa4bi8VrtvqySxnbr5uqcG9czQGTZ",
325    "t3dktADfb5Rmxncpe1HS5BRS5Gcj7MZWYBi",
326    "t3hgskquvKKoCtvxw86yN7q8bzwRxNgUZmc",
327    "t3R1VrLzwcxAZzkX4mX3KGbWpNsgtYtMntj",
328    "t3ff6fhemqPMVujD3AQurxRxTdvS1pPSaa2",
329    "t3cEUQFG3KYnFG6qYhPxSNgGi3HDjUPwC3J",
330    "t3WR9F5U4QvUFqqx9zFmwT6xFqduqRRXnaa",
331    "t3PYc1LWngrdUrJJbHkYPCKvJuvJjcm85Ch",
332    "t3bgkjiUeatWNkhxY3cWyLbTxKksAfk561R",
333    "t3Z5rrR8zahxUpZ8itmCKhMSfxiKjUp5Dk5",
334    "t3PU1j7YW3fJ67jUbkGhSRto8qK2qXCUiW3",
335    "t3S3yaT7EwNLaFZCamfsxxKwamQW2aRGEkh",
336    "t3eutXKJ9tEaPSxZpmowhzKhPfJvmtwTEZK",
337    "t3gbTb7brxLdVVghSPSd3ycGxzHbUpukeDm",
338    "t3UCKW2LrHFqPMQFEbZn6FpjqnhAAbfpMYR",
339    "t3NyHsrnYbqaySoQqEQRyTWkjvM2PLkU7Uu",
340    "t3QEFL6acxuZwiXtW3YvV6njDVGjJ1qeaRo",
341    "t3PdBRr2S1XTDzrV8bnZkXF3SJcrzHWe1wj",
342    "t3ZWyRPpWRo23pKxTLtWsnfEKeq9T4XPxKM",
343    "t3he6QytKCTydhpztykFsSsb9PmBT5JBZLi",
344    "t3VWxWDsLb2TURNEP6tA1ZSeQzUmPKFNxRY",
345    "t3NmWLvZkbciNAipauzsFRMxoZGqmtJksbz",
346    "t3cKr4YxVPvPBG1mCvzaoTTdBNokohsRJ8n",
347    "t3T3smGZn6BoSFXWWXa1RaoQdcyaFjMfuYK",
348    "t3gkDUe9Gm4GGpjMk86TiJZqhztBVMiUSSA",
349    "t3eretuBeBXFHe5jAqeSpUS1cpxVh51fAeb",
350    "t3dN8g9zi2UGJdixGe9txeSxeofLS9t3yFQ",
351    "t3S799pq9sYBFwccRecoTJ3SvQXRHPrHqvx",
352    "t3fhYnv1S5dXwau7GED3c1XErzt4n4vDxmf",
353    "t3cmE3vsBc5xfDJKXXZdpydCPSdZqt6AcNi",
354    "t3h5fPdjJVHaH4HwynYDM5BB3J7uQaoUwKi",
355    "t3Ma35c68BgRX8sdLDJ6WR1PCrKiWHG4Da9",
356    "t3LokMKPL1J8rkJZvVpfuH7dLu6oUWqZKQK",
357    "t3WFFGbEbhJWnASZxVLw2iTJBZfJGGX73mM",
358    "t3L8GLEsUn4QHNaRYcX3EGyXmQ8kjpT1zTa",
359    "t3PgfByBhaBSkH8uq4nYJ9ZBX4NhGCJBVYm",
360    "t3WecsqKDhWXD4JAgBVcnaCC2itzyNZhJrv",
361    "t3ZG9cSfopnsMQupKW5v9sTotjcP5P6RTbn",
362    "t3hC1Ywb5zDwUYYV8LwhvF5rZ6m49jxXSG5",
363    "t3VgMqDL15ZcyQDeqBsBW3W6rzfftrWP2yB",
364    "t3LC94Y6BwLoDtBoK2NuewaEbnko1zvR9rm",
365    "t3cWCUZJR3GtALaTcatrrpNJ3MGbMFVLRwQ",
366    "t3YYF4rPLVxDcF9hHFsXyc5Yq1TFfbojCY6",
367    "t3XHAGxRP2FNfhAjxGjxbrQPYtQQjc3RCQD",
368];
369
370/// Functionality specific to block subsidy-related consensus rules
371pub trait ParameterSubsidy {
372    /// Returns the minimum height after the first halving
373    /// as described in [protocol specification §7.10][7.10]
374    ///
375    /// [7.10]: <https://zips.z.cash/protocol/protocol.pdf#fundingstreams>
376    fn height_for_first_halving(&self) -> Height;
377
378    /// Returns the halving interval after Blossom
379    fn post_blossom_halving_interval(&self) -> HeightDiff;
380
381    /// Returns the halving interval before Blossom
382    fn pre_blossom_halving_interval(&self) -> HeightDiff;
383
384    /// Returns the address change interval for funding streams
385    /// as described in [protocol specification §7.10][7.10].
386    ///
387    /// > FSRecipientChangeInterval := PostBlossomHalvingInterval / 48
388    ///
389    /// [7.10]: https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams
390    fn funding_stream_address_change_interval(&self) -> HeightDiff;
391}
392
393/// Network methods related to Block Subsidy and Funding Streams
394impl ParameterSubsidy for Network {
395    fn height_for_first_halving(&self) -> Height {
396        // First halving on Mainnet is at Canopy
397        // while in Testnet is at block constant height of `1_116_000`
398        // <https://zips.z.cash/protocol/protocol.pdf#zip214fundingstreams>
399        match self {
400            Network::Mainnet => NetworkUpgrade::Canopy
401                .activation_height(self)
402                .expect("canopy activation height should be available"),
403            Network::Testnet(params) => {
404                if params.is_regtest() {
405                    FIRST_HALVING_REGTEST
406                } else if params.is_default_testnet() {
407                    FIRST_HALVING_TESTNET
408                } else {
409                    height_for_halving(1, self).expect("first halving height should be available")
410                }
411            }
412        }
413    }
414
415    fn post_blossom_halving_interval(&self) -> HeightDiff {
416        match self {
417            Network::Mainnet => POST_BLOSSOM_HALVING_INTERVAL,
418            Network::Testnet(params) => params.post_blossom_halving_interval(),
419        }
420    }
421
422    fn pre_blossom_halving_interval(&self) -> HeightDiff {
423        match self {
424            Network::Mainnet => PRE_BLOSSOM_HALVING_INTERVAL,
425            Network::Testnet(params) => params.pre_blossom_halving_interval(),
426        }
427    }
428
429    fn funding_stream_address_change_interval(&self) -> HeightDiff {
430        self.post_blossom_halving_interval() / 48
431    }
432}
433
434/// List of addresses for the Zcash Foundation funding stream in the Mainnet.
435pub const FUNDING_STREAM_ZF_ADDRESSES_MAINNET: [&str; FUNDING_STREAMS_NUM_ADDRESSES_MAINNET] =
436    ["t3dvVE3SQEi7kqNzwrfNePxZ1d4hUyztBA1"; FUNDING_STREAMS_NUM_ADDRESSES_MAINNET];
437
438/// List of addresses for the Major Grants funding stream in the Mainnet.
439pub const FUNDING_STREAM_MG_ADDRESSES_MAINNET: [&str; FUNDING_STREAMS_NUM_ADDRESSES_MAINNET] =
440    ["t3XyYW8yBFRuMnfvm5KLGFbEVz25kckZXym"; FUNDING_STREAMS_NUM_ADDRESSES_MAINNET];
441
442/// Number of addresses for each post-NU6 funding stream on Mainnet.
443/// In the spec ([protocol specification §7.10][7.10]) this is defined as: `fs.addressindex(fs.endheight - 1)`
444/// however we know this value beforehand so we prefer to make it a constant instead.
445///
446/// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams
447pub const POST_NU6_FUNDING_STREAMS_NUM_ADDRESSES_MAINNET: usize = 12;
448
449/// List of addresses for the Major Grants post-NU6 funding stream on Mainnet administered by the Financial Privacy Fund (FPF).
450pub const POST_NU6_FUNDING_STREAM_FPF_ADDRESSES_MAINNET: [&str;
451    POST_NU6_FUNDING_STREAMS_NUM_ADDRESSES_MAINNET] =
452    ["t3cFfPt1Bcvgez9ZbMBFWeZsskxTkPzGCow"; POST_NU6_FUNDING_STREAMS_NUM_ADDRESSES_MAINNET];
453
454/// Number of addresses for each funding stream in the Testnet.
455/// In the spec ([protocol specification §7.10][7.10]) this is defined as: `fs.addressindex(fs.endheight - 1)`
456/// however we know this value beforehand so we prefer to make it a constant instead.
457///
458/// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams
459pub const FUNDING_STREAMS_NUM_ADDRESSES_TESTNET: usize = 51;
460
461/// List of addresses for the ECC funding stream in the Testnet.
462pub const FUNDING_STREAM_ECC_ADDRESSES_TESTNET: [&str; FUNDING_STREAMS_NUM_ADDRESSES_TESTNET] = [
463    "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz",
464    "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz",
465    "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz",
466    "t26ovBdKAJLtrvBsE2QGF4nqBkEuptuPFZz",
467    "t2NNHrgPpE388atmWSF4DxAb3xAoW5Yp45M",
468    "t2VMN28itPyMeMHBEd9Z1hm6YLkQcGA1Wwe",
469    "t2CHa1TtdfUV8UYhNm7oxbzRyfr8616BYh2",
470    "t2F77xtr28U96Z2bC53ZEdTnQSUAyDuoa67",
471    "t2ARrzhbgcpoVBDPivUuj6PzXzDkTBPqfcT",
472    "t278aQ8XbvFR15mecRguiJDQQVRNnkU8kJw",
473    "t2Dp1BGnZsrTXZoEWLyjHmg3EPvmwBnPDGB",
474    "t2KzeqXgf4ju33hiSqCuKDb8iHjPCjMq9iL",
475    "t2Nyxqv1BiWY1eUSiuxVw36oveawYuo18tr",
476    "t2DKFk5JRsVoiuinK8Ti6eM4Yp7v8BbfTyH",
477    "t2CUaBca4k1x36SC4q8Nc8eBoqkMpF3CaLg",
478    "t296SiKL7L5wvFmEdMxVLz1oYgd6fTfcbZj",
479    "t29fBCFbhgsjL3XYEZ1yk1TUh7eTusB6dPg",
480    "t2FGofLJXa419A76Gpf5ncxQB4gQXiQMXjK",
481    "t2ExfrnRVnRiXDvxerQ8nZbcUQvNvAJA6Qu",
482    "t28JUffLp47eKPRHKvwSPzX27i9ow8LSXHx",
483    "t2JXWPtrtyL861rFWMZVtm3yfgxAf4H7uPA",
484    "t2QdgbJoWfYHgyvEDEZBjHmgkr9yNJff3Hi",
485    "t2QW43nkco8r32ZGRN6iw6eSzyDjkMwCV3n",
486    "t2DgYDXMJTYLwNcxighQ9RCgPxMVATRcUdC",
487    "t2Bop7dg33HGZx3wunnQzi2R2ntfpjuti3M",
488    "t2HVeEwovcLq9RstAbYkqngXNEsCe2vjJh9",
489    "t2HxbP5keQSx7p592zWQ5bJ5GrMmGDsV2Xa",
490    "t2TJzUg2matao3mztBRJoWnJY6ekUau6tPD",
491    "t29pMzxmo6wod25YhswcjKv3AFRNiBZHuhj",
492    "t2QBQMRiJKYjshJpE6RhbF7GLo51yE6d4wZ",
493    "t2F5RqnqguzZeiLtYHFx4yYfy6pDnut7tw5",
494    "t2CHvyZANE7XCtg8AhZnrcHCC7Ys1jJhK13",
495    "t2BRzpMdrGWZJ2upsaNQv6fSbkbTy7EitLo",
496    "t2BFixHGQMAWDY67LyTN514xRAB94iEjXp3",
497    "t2Uvz1iVPzBEWfQBH1p7NZJsFhD74tKaG8V",
498    "t2CmFDj5q6rJSRZeHf1SdrowinyMNcj438n",
499    "t2ErNvWEReTfPDBaNizjMPVssz66aVZh1hZ",
500    "t2GeJQ8wBUiHKDVzVM5ZtKfY5reCg7CnASs",
501    "t2L2eFtkKv1G6j55kLytKXTGuir4raAy3yr",
502    "t2EK2b87dpPazb7VvmEGc8iR6SJ289RywGL",
503    "t2DJ7RKeZJxdA4nZn8hRGXE8NUyTzjujph9",
504    "t2K1pXo4eByuWpKLkssyMLe8QKUbxnfFC3H",
505    "t2TB4mbSpuAcCWkH94Leb27FnRxo16AEHDg",
506    "t2Phx4gVL4YRnNsH3jM1M7jE4Fo329E66Na",
507    "t2VQZGmeNomN8c3USefeLL9nmU6M8x8CVzC",
508    "t2RicCvTVTY5y9JkreSRv3Xs8q2K67YxHLi",
509    "t2JrSLxTGc8wtPDe9hwbaeUjCrCfc4iZnDD",
510    "t2Uh9Au1PDDSw117sAbGivKREkmMxVC5tZo",
511    "t2FDwoJKLeEBMTy3oP7RLQ1Fihhvz49a3Bv",
512    "t2FY18mrgtb7QLeHA8ShnxLXuW8cNQ2n1v8",
513    "t2L15TkDYum7dnQRBqfvWdRe8Yw3jVy9z7g",
514];
515
516/// List of addresses for the Zcash Foundation funding stream in the Testnet.
517pub const FUNDING_STREAM_ZF_ADDRESSES_TESTNET: [&str; FUNDING_STREAMS_NUM_ADDRESSES_TESTNET] =
518    ["t27eWDgjFYJGVXmzrXeVjnb5J3uXDM9xH9v"; FUNDING_STREAMS_NUM_ADDRESSES_TESTNET];
519
520/// List of addresses for the Major Grants funding stream in the Testnet.
521pub const FUNDING_STREAM_MG_ADDRESSES_TESTNET: [&str; FUNDING_STREAMS_NUM_ADDRESSES_TESTNET] =
522    ["t2Gvxv2uNM7hbbACjNox4H6DjByoKZ2Fa3P"; FUNDING_STREAMS_NUM_ADDRESSES_TESTNET];
523
524/// Number of addresses for each post-NU6 funding stream in the Testnet.
525/// In the spec ([protocol specification §7.10][7.10]) this is defined as: `fs.addressindex(fs.endheight - 1)`
526/// however we know this value beforehand so we prefer to make it a constant instead.
527///
528/// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams
529pub const POST_NU6_FUNDING_STREAMS_NUM_ADDRESSES_TESTNET: usize = 13;
530
531/// List of addresses for the Major Grants post-NU6 funding stream on Testnet administered by the Financial Privacy Fund (FPF).
532pub const POST_NU6_FUNDING_STREAM_FPF_ADDRESSES_TESTNET: [&str;
533    POST_NU6_FUNDING_STREAMS_NUM_ADDRESSES_TESTNET] =
534    ["t2HifwjUj9uyxr9bknR8LFuQbc98c3vkXtu"; POST_NU6_FUNDING_STREAMS_NUM_ADDRESSES_TESTNET];
535
536/// Returns the address change period
537/// as described in [protocol specification §7.10][7.10]
538///
539/// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams
540pub fn funding_stream_address_period<N: ParameterSubsidy>(height: Height, network: &N) -> u32 {
541    // Spec equation: `address_period = floor((height - (height_for_halving(1) - post_blossom_halving_interval))/funding_stream_address_change_interval)`,
542    // <https://zips.z.cash/protocol/protocol.pdf#fundingstreams>
543    //
544    // Note that the brackets make it so the post blossom halving interval is added to the total.
545    //
546    // In Rust, "integer division rounds towards zero":
547    // <https://doc.rust-lang.org/stable/reference/expressions/operator-expr.html#arithmetic-and-logical-binary-operators>
548    // This is the same as `floor()`, because these numbers are all positive.
549
550    let height_after_first_halving = height - network.height_for_first_halving();
551
552    let address_period = (height_after_first_halving + network.post_blossom_halving_interval())
553        / network.funding_stream_address_change_interval();
554
555    address_period
556        .try_into()
557        .expect("all values are positive and smaller than the input height")
558}
559
560/// The first block height of the halving at the provided halving index for a network.
561///
562/// See `Halving(height)`, as described in [protocol specification §7.8][7.8]
563///
564/// [7.8]: https://zips.z.cash/protocol/protocol.pdf#subsidies
565pub fn height_for_halving(halving: u32, network: &Network) -> Option<Height> {
566    if halving == 0 {
567        return Some(Height(0));
568    }
569
570    let slow_start_shift = i64::from(network.slow_start_shift().0);
571    let blossom_height = i64::from(
572        NetworkUpgrade::Blossom
573            .activation_height(network)
574            .expect("blossom activation height should be available")
575            .0,
576    );
577    let pre_blossom_halving_interval = network.pre_blossom_halving_interval();
578    let halving_index = i64::from(halving);
579
580    let unscaled_height = halving_index
581        .checked_mul(pre_blossom_halving_interval)
582        .expect("Multiplication overflow: consider reducing the halving interval");
583
584    let pre_blossom_height = unscaled_height
585        .min(blossom_height)
586        .checked_add(slow_start_shift)
587        .expect("Addition overflow: consider reducing the halving interval");
588
589    let post_blossom_height = 0
590        .max(unscaled_height - blossom_height)
591        .checked_mul(i64::from(BLOSSOM_POW_TARGET_SPACING_RATIO))
592        .expect("Multiplication overflow: consider reducing the halving interval")
593        .checked_add(slow_start_shift)
594        .expect("Addition overflow: consider reducing the halving interval");
595
596    let height = pre_blossom_height
597        .checked_add(post_blossom_height)
598        .expect("Addition overflow: consider reducing the halving interval");
599
600    let height = u32::try_from(height).ok()?;
601    height.try_into().ok()
602}