snarkvm_console_network/
lib.rs

1// Copyright (c) 2019-2025 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16#![forbid(unsafe_code)]
17#![allow(clippy::too_many_arguments)]
18#![warn(clippy::cast_possible_truncation)]
19
20#[macro_use]
21extern crate lazy_static;
22
23pub use snarkvm_console_network_environment as environment;
24pub use snarkvm_console_network_environment::*;
25
26mod helpers;
27pub use helpers::*;
28
29mod canary_v0;
30pub use canary_v0::*;
31
32mod mainnet_v0;
33pub use mainnet_v0::*;
34
35mod testnet_v0;
36pub use testnet_v0::*;
37
38pub mod prelude {
39    pub use crate::{ConsensusVersion, Network, consensus_config_value, environment::prelude::*};
40}
41
42use crate::environment::prelude::*;
43use snarkvm_algorithms::{
44    AlgebraicSponge,
45    crypto_hash::PoseidonSponge,
46    snark::varuna::{CircuitProvingKey, CircuitVerifyingKey, VarunaHidingMode},
47    srs::{UniversalProver, UniversalVerifier},
48};
49use snarkvm_console_algorithms::{BHP512, BHP1024, Poseidon2, Poseidon4};
50use snarkvm_console_collections::merkle_tree::{MerklePath, MerkleTree};
51use snarkvm_console_types::{Field, Group, Scalar};
52use snarkvm_curves::PairingEngine;
53
54use indexmap::IndexMap;
55use once_cell::sync::OnceCell;
56use std::sync::Arc;
57
58/// A helper type for the BHP Merkle tree.
59pub type BHPMerkleTree<N, const DEPTH: u8> = MerkleTree<N, BHP1024<N>, BHP512<N>, DEPTH>;
60/// A helper type for the Poseidon Merkle tree.
61pub type PoseidonMerkleTree<N, const DEPTH: u8> = MerkleTree<N, Poseidon4<N>, Poseidon2<N>, DEPTH>;
62
63/// Helper types for the Varuna parameters.
64type Fq<N> = <<N as Environment>::PairingCurve as PairingEngine>::Fq;
65pub type FiatShamir<N> = PoseidonSponge<Fq<N>, 2, 1>;
66pub type FiatShamirParameters<N> = <FiatShamir<N> as AlgebraicSponge<Fq<N>, 2>>::Parameters;
67
68/// Helper types for the Varuna proving and verifying key.
69pub(crate) type VarunaProvingKey<N> = CircuitProvingKey<<N as Environment>::PairingCurve, VarunaHidingMode>;
70pub(crate) type VarunaVerifyingKey<N> = CircuitVerifyingKey<<N as Environment>::PairingCurve>;
71
72/// The different consensus versions.
73/// If you need the version active for a specific height, see: `N::CONSENSUS_VERSION`.
74#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
75pub enum ConsensusVersion {
76    /// V1: The initial genesis consensus version.
77    V1 = 1,
78    /// V2: Update to the block reward and execution cost algorithms.
79    V2 = 2,
80    /// V3: Update to the number of validators and finalize scope RNG seed.
81    V3 = 3,
82    /// V4: Update to the Varuna version.
83    V4 = 4,
84    /// V5: Update to the number of validators and enable batch proposal spend limits.
85    V5 = 5,
86    /// V6: Update to the number of validators.
87    V6 = 6,
88    /// V7: Update to program rules.
89    V7 = 7,
90}
91
92pub trait Network:
93    'static
94    + Environment
95    + Copy
96    + Clone
97    + Debug
98    + Eq
99    + PartialEq
100    + core::hash::Hash
101    + Serialize
102    + DeserializeOwned
103    + for<'a> Deserialize<'a>
104    + Send
105    + Sync
106{
107    /// The network ID.
108    const ID: u16;
109    /// The network name.
110    const NAME: &'static str;
111    /// The network edition.
112    const EDITION: u16;
113
114    /// The function name for the inclusion circuit.
115    const INCLUSION_FUNCTION_NAME: &'static str;
116
117    /// The fixed timestamp of the genesis block.
118    const GENESIS_TIMESTAMP: i64;
119    /// The genesis block coinbase target.
120    const GENESIS_COINBASE_TARGET: u64;
121    /// The genesis block proof target.
122    const GENESIS_PROOF_TARGET: u64;
123    /// The maximum number of solutions that can be included per block as a power of 2.
124    const MAX_SOLUTIONS_AS_POWER_OF_TWO: u8 = 2; // 4 solutions
125    /// The maximum number of solutions that can be included per block.
126    const MAX_SOLUTIONS: usize = 1 << Self::MAX_SOLUTIONS_AS_POWER_OF_TWO; // 4 solutions
127
128    /// The starting supply of Aleo credits.
129    const STARTING_SUPPLY: u64 = 1_500_000_000_000_000; // 1.5B credits
130    /// The cost in microcredits per byte for the deployment transaction.
131    const DEPLOYMENT_FEE_MULTIPLIER: u64 = 1_000; // 1 millicredit per byte
132    /// The constant that divides the storage polynomial.
133    const EXECUTION_STORAGE_FEE_SCALING_FACTOR: u64 = 5000;
134    /// The maximum size execution transactions can be before a quadratic storage penalty applies.
135    const EXECUTION_STORAGE_PENALTY_THRESHOLD: u64 = 5000;
136    /// The cost in microcredits per constraint for the deployment transaction.
137    const SYNTHESIS_FEE_MULTIPLIER: u64 = 25; // 25 microcredits per constraint
138    /// The maximum number of variables in a deployment.
139    const MAX_DEPLOYMENT_VARIABLES: u64 = 1 << 20; // 1,048,576 variables
140    /// The maximum number of constraints in a deployment.
141    const MAX_DEPLOYMENT_CONSTRAINTS: u64 = 1 << 20; // 1,048,576 constraints
142    /// The maximum number of microcredits that can be spent as a fee.
143    const MAX_FEE: u64 = 1_000_000_000_000_000;
144    /// The maximum number of microcredits that can be spent on a transaction's finalize scope.
145    const TRANSACTION_SPEND_LIMIT: u64 = 100_000_000;
146
147    /// The anchor height, defined as the expected number of blocks to reach the coinbase target.
148    const ANCHOR_HEIGHT: u32 = Self::ANCHOR_TIME as u32 / Self::BLOCK_TIME as u32;
149    /// The anchor time in seconds.
150    const ANCHOR_TIME: u16 = 25;
151    /// The expected time per block in seconds.
152    const BLOCK_TIME: u16 = 10;
153    /// The number of blocks per epoch.
154    const NUM_BLOCKS_PER_EPOCH: u32 = 3600 / Self::BLOCK_TIME as u32; // 360 blocks == ~1 hour
155
156    /// The maximum number of entries in data.
157    const MAX_DATA_ENTRIES: usize = 32;
158    /// The maximum recursive depth of an entry.
159    /// Note: This value must be strictly less than u8::MAX.
160    const MAX_DATA_DEPTH: usize = 32;
161    /// The maximum number of fields in data (must not exceed u16::MAX).
162    #[allow(clippy::cast_possible_truncation)]
163    const MAX_DATA_SIZE_IN_FIELDS: u32 = ((128 * 1024 * 8) / Field::<Self>::SIZE_IN_DATA_BITS) as u32;
164
165    /// The minimum number of entries in a struct.
166    const MIN_STRUCT_ENTRIES: usize = 1; // This ensures the struct is not empty.
167    /// The maximum number of entries in a struct.
168    const MAX_STRUCT_ENTRIES: usize = Self::MAX_DATA_ENTRIES;
169
170    /// The minimum number of elements in an array.
171    const MIN_ARRAY_ELEMENTS: usize = 1; // This ensures the array is not empty.
172    /// The maximum number of elements in an array.
173    const MAX_ARRAY_ELEMENTS: usize = Self::MAX_DATA_ENTRIES;
174
175    /// The minimum number of entries in a record.
176    const MIN_RECORD_ENTRIES: usize = 1; // This accounts for 'record.owner'.
177    /// The maximum number of entries in a record.
178    const MAX_RECORD_ENTRIES: usize = Self::MIN_RECORD_ENTRIES.saturating_add(Self::MAX_DATA_ENTRIES);
179
180    /// The maximum program size by number of characters.
181    const MAX_PROGRAM_SIZE: usize = 100_000; // 100 KB
182
183    /// The maximum number of mappings in a program.
184    const MAX_MAPPINGS: usize = 31;
185    /// The maximum number of functions in a program.
186    const MAX_FUNCTIONS: usize = 31;
187    /// The maximum number of structs in a program.
188    const MAX_STRUCTS: usize = 10 * Self::MAX_FUNCTIONS;
189    /// The maximum number of records in a program.
190    const MAX_RECORDS: usize = 10 * Self::MAX_FUNCTIONS;
191    /// The maximum number of closures in a program.
192    const MAX_CLOSURES: usize = 2 * Self::MAX_FUNCTIONS;
193    /// The maximum number of operands in an instruction.
194    const MAX_OPERANDS: usize = Self::MAX_INPUTS;
195    /// The maximum number of instructions in a closure or function.
196    const MAX_INSTRUCTIONS: usize = u16::MAX as usize;
197    /// The maximum number of commands in finalize.
198    const MAX_COMMANDS: usize = u16::MAX as usize;
199    /// The maximum number of write commands in finalize.
200    const MAX_WRITES: u16 = 16;
201
202    /// The maximum number of inputs per transition.
203    const MAX_INPUTS: usize = 16;
204    /// The maximum number of outputs per transition.
205    const MAX_OUTPUTS: usize = 16;
206
207    /// The maximum number of imports.
208    const MAX_IMPORTS: usize = 64;
209
210    /// The maximum number of bytes in a transaction.
211    // Note: This value must **not** be decreased as it would invalidate existing transactions.
212    const MAX_TRANSACTION_SIZE: usize = 128_000; // 128 kB
213
214    /// The state root type.
215    type StateRoot: Bech32ID<Field<Self>>;
216    /// The block hash type.
217    type BlockHash: Bech32ID<Field<Self>>;
218    /// The ratification ID type.
219    type RatificationID: Bech32ID<Field<Self>>;
220    /// The transaction ID type.
221    type TransactionID: Bech32ID<Field<Self>>;
222    /// The transition ID type.
223    type TransitionID: Bech32ID<Field<Self>>;
224    /// The transmission checksum type.
225    type TransmissionChecksum: IntegerType;
226
227    /// A list of (consensus_version, block_height) pairs indicating when each consensus version takes effect.
228    /// Documentation for what is changed at each version can be found in `N::CONSENSUS_VERSION`
229    const CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); 7];
230    ///  A list of (consensus_version, size) pairs indicating the maximum number of validators in a committee.
231    //  Note: This value must **not** decrease without considering the impact on serialization.
232    //  Decreasing this value will break backwards compatibility of serialization without explicit
233    //  declaration of migration based on round number rather than block height.
234    //  Increasing this value will require a migration to prevent forking during network upgrades.
235    const MAX_CERTIFICATES: [(ConsensusVersion, u16); 4];
236
237    /// Returns the consensus version which is active at the given height.
238    #[allow(non_snake_case)]
239    fn CONSENSUS_VERSION(seek_height: u32) -> anyhow::Result<ConsensusVersion> {
240        match Self::CONSENSUS_VERSION_HEIGHTS.binary_search_by(|(_, height)| height.cmp(&seek_height)) {
241            // If a consensus version was found at this height, return it.
242            Ok(index) => Ok(Self::CONSENSUS_VERSION_HEIGHTS[index].0),
243            // If the specified height was not found, determine whether to return an appropriate version.
244            Err(index) => {
245                if index == 0 {
246                    Err(anyhow!("Expected consensus version 1 to exist at height 0."))
247                } else {
248                    // Return the appropriate version belonging to the height *lower* than the sought height.
249                    Ok(Self::CONSENSUS_VERSION_HEIGHTS[index - 1].0)
250                }
251            }
252        }
253    }
254    /// Returns the height at which a specified consensus version becomes active.
255    #[allow(non_snake_case)]
256    fn CONSENSUS_HEIGHT(version: ConsensusVersion) -> Result<u32> {
257        Ok(Self::CONSENSUS_VERSION_HEIGHTS.get(version as usize - 1).ok_or(anyhow!("Invalid consensus version"))?.1)
258    }
259    /// Returns the last `MAX_CERTIFICATES` value.
260    #[allow(non_snake_case)]
261    fn LATEST_MAX_CERTIFICATES() -> Result<u16> {
262        Self::MAX_CERTIFICATES.last().map_or(Err(anyhow!("No MAX_CERTIFICATES defined.")), |(_, value)| Ok(*value))
263    }
264
265    /// Returns the genesis block bytes.
266    fn genesis_bytes() -> &'static [u8];
267
268    /// Returns the restrictions list as a JSON-compatible string.
269    fn restrictions_list_as_str() -> &'static str;
270
271    /// Returns the proving key for the given function name in `credits.aleo`.
272    fn get_credits_proving_key(function_name: String) -> Result<&'static Arc<VarunaProvingKey<Self>>>;
273
274    /// Returns the verifying key for the given function name in `credits.aleo`.
275    fn get_credits_verifying_key(function_name: String) -> Result<&'static Arc<VarunaVerifyingKey<Self>>>;
276
277    /// Returns the `proving key` for the inclusion circuit.
278    fn inclusion_proving_key() -> &'static Arc<VarunaProvingKey<Self>>;
279
280    /// Returns the `verifying key` for the inclusion circuit.
281    fn inclusion_verifying_key() -> &'static Arc<VarunaVerifyingKey<Self>>;
282
283    /// Returns the powers of `G`.
284    fn g_powers() -> &'static Vec<Group<Self>>;
285
286    /// Returns the scalar multiplication on the generator `G`.
287    fn g_scalar_multiply(scalar: &Scalar<Self>) -> Group<Self>;
288
289    /// Returns the Varuna universal prover.
290    fn varuna_universal_prover() -> &'static UniversalProver<Self::PairingCurve>;
291
292    /// Returns the Varuna universal verifier.
293    fn varuna_universal_verifier() -> &'static UniversalVerifier<Self::PairingCurve>;
294
295    /// Returns the sponge parameters for Varuna.
296    fn varuna_fs_parameters() -> &'static FiatShamirParameters<Self>;
297
298    /// Returns the encryption domain as a constant field element.
299    fn encryption_domain() -> Field<Self>;
300
301    /// Returns the graph key domain as a constant field element.
302    fn graph_key_domain() -> Field<Self>;
303
304    /// Returns the serial number domain as a constant field element.
305    fn serial_number_domain() -> Field<Self>;
306
307    /// Returns a BHP commitment with an input hasher of 256-bits and randomizer.
308    fn commit_bhp256(input: &[bool], randomizer: &Scalar<Self>) -> Result<Field<Self>>;
309
310    /// Returns a BHP commitment with an input hasher of 512-bits and randomizer.
311    fn commit_bhp512(input: &[bool], randomizer: &Scalar<Self>) -> Result<Field<Self>>;
312
313    /// Returns a BHP commitment with an input hasher of 768-bits and randomizer.
314    fn commit_bhp768(input: &[bool], randomizer: &Scalar<Self>) -> Result<Field<Self>>;
315
316    /// Returns a BHP commitment with an input hasher of 1024-bits and randomizer.
317    fn commit_bhp1024(input: &[bool], randomizer: &Scalar<Self>) -> Result<Field<Self>>;
318
319    /// Returns a Pedersen commitment for the given (up to) 64-bit input and randomizer.
320    fn commit_ped64(input: &[bool], randomizer: &Scalar<Self>) -> Result<Field<Self>>;
321
322    /// Returns a Pedersen commitment for the given (up to) 128-bit input and randomizer.
323    fn commit_ped128(input: &[bool], randomizer: &Scalar<Self>) -> Result<Field<Self>>;
324
325    /// Returns a BHP commitment with an input hasher of 256-bits and randomizer.
326    fn commit_to_group_bhp256(input: &[bool], randomizer: &Scalar<Self>) -> Result<Group<Self>>;
327
328    /// Returns a BHP commitment with an input hasher of 512-bits and randomizer.
329    fn commit_to_group_bhp512(input: &[bool], randomizer: &Scalar<Self>) -> Result<Group<Self>>;
330
331    /// Returns a BHP commitment with an input hasher of 768-bits and randomizer.
332    fn commit_to_group_bhp768(input: &[bool], randomizer: &Scalar<Self>) -> Result<Group<Self>>;
333
334    /// Returns a BHP commitment with an input hasher of 1024-bits and randomizer.
335    fn commit_to_group_bhp1024(input: &[bool], randomizer: &Scalar<Self>) -> Result<Group<Self>>;
336
337    /// Returns a Pedersen commitment for the given (up to) 64-bit input and randomizer.
338    fn commit_to_group_ped64(input: &[bool], randomizer: &Scalar<Self>) -> Result<Group<Self>>;
339
340    /// Returns a Pedersen commitment for the given (up to) 128-bit input and randomizer.
341    fn commit_to_group_ped128(input: &[bool], randomizer: &Scalar<Self>) -> Result<Group<Self>>;
342
343    /// Returns the BHP hash with an input hasher of 256-bits.
344    fn hash_bhp256(input: &[bool]) -> Result<Field<Self>>;
345
346    /// Returns the BHP hash with an input hasher of 512-bits.
347    fn hash_bhp512(input: &[bool]) -> Result<Field<Self>>;
348
349    /// Returns the BHP hash with an input hasher of 768-bits.
350    fn hash_bhp768(input: &[bool]) -> Result<Field<Self>>;
351
352    /// Returns the BHP hash with an input hasher of 1024-bits.
353    fn hash_bhp1024(input: &[bool]) -> Result<Field<Self>>;
354
355    /// Returns the Keccak hash with a 256-bit output.
356    fn hash_keccak256(input: &[bool]) -> Result<Vec<bool>>;
357
358    /// Returns the Keccak hash with a 384-bit output.
359    fn hash_keccak384(input: &[bool]) -> Result<Vec<bool>>;
360
361    /// Returns the Keccak hash with a 512-bit output.
362    fn hash_keccak512(input: &[bool]) -> Result<Vec<bool>>;
363
364    /// Returns the Pedersen hash for a given (up to) 64-bit input.
365    fn hash_ped64(input: &[bool]) -> Result<Field<Self>>;
366
367    /// Returns the Pedersen hash for a given (up to) 128-bit input.
368    fn hash_ped128(input: &[bool]) -> Result<Field<Self>>;
369
370    /// Returns the Poseidon hash with an input rate of 2.
371    fn hash_psd2(input: &[Field<Self>]) -> Result<Field<Self>>;
372
373    /// Returns the Poseidon hash with an input rate of 4.
374    fn hash_psd4(input: &[Field<Self>]) -> Result<Field<Self>>;
375
376    /// Returns the Poseidon hash with an input rate of 8.
377    fn hash_psd8(input: &[Field<Self>]) -> Result<Field<Self>>;
378
379    /// Returns the SHA-3 hash with a 256-bit output.
380    fn hash_sha3_256(input: &[bool]) -> Result<Vec<bool>>;
381
382    /// Returns the SHA-3 hash with a 384-bit output.
383    fn hash_sha3_384(input: &[bool]) -> Result<Vec<bool>>;
384
385    /// Returns the SHA-3 hash with a 512-bit output.
386    fn hash_sha3_512(input: &[bool]) -> Result<Vec<bool>>;
387
388    /// Returns the extended Poseidon hash with an input rate of 2.
389    fn hash_many_psd2(input: &[Field<Self>], num_outputs: u16) -> Vec<Field<Self>>;
390
391    /// Returns the extended Poseidon hash with an input rate of 4.
392    fn hash_many_psd4(input: &[Field<Self>], num_outputs: u16) -> Vec<Field<Self>>;
393
394    /// Returns the extended Poseidon hash with an input rate of 8.
395    fn hash_many_psd8(input: &[Field<Self>], num_outputs: u16) -> Vec<Field<Self>>;
396
397    /// Returns the BHP hash with an input hasher of 256-bits.
398    fn hash_to_group_bhp256(input: &[bool]) -> Result<Group<Self>>;
399
400    /// Returns the BHP hash with an input hasher of 512-bits.
401    fn hash_to_group_bhp512(input: &[bool]) -> Result<Group<Self>>;
402
403    /// Returns the BHP hash with an input hasher of 768-bits.
404    fn hash_to_group_bhp768(input: &[bool]) -> Result<Group<Self>>;
405
406    /// Returns the BHP hash with an input hasher of 1024-bits.
407    fn hash_to_group_bhp1024(input: &[bool]) -> Result<Group<Self>>;
408
409    /// Returns the Pedersen hash for a given (up to) 64-bit input.
410    fn hash_to_group_ped64(input: &[bool]) -> Result<Group<Self>>;
411
412    /// Returns the Pedersen hash for a given (up to) 128-bit input.
413    fn hash_to_group_ped128(input: &[bool]) -> Result<Group<Self>>;
414
415    /// Returns the Poseidon hash with an input rate of 2 on the affine curve.
416    fn hash_to_group_psd2(input: &[Field<Self>]) -> Result<Group<Self>>;
417
418    /// Returns the Poseidon hash with an input rate of 4 on the affine curve.
419    fn hash_to_group_psd4(input: &[Field<Self>]) -> Result<Group<Self>>;
420
421    /// Returns the Poseidon hash with an input rate of 8 on the affine curve.
422    fn hash_to_group_psd8(input: &[Field<Self>]) -> Result<Group<Self>>;
423
424    /// Returns the Poseidon hash with an input rate of 2 on the scalar field.
425    fn hash_to_scalar_psd2(input: &[Field<Self>]) -> Result<Scalar<Self>>;
426
427    /// Returns the Poseidon hash with an input rate of 4 on the scalar field.
428    fn hash_to_scalar_psd4(input: &[Field<Self>]) -> Result<Scalar<Self>>;
429
430    /// Returns the Poseidon hash with an input rate of 8 on the scalar field.
431    fn hash_to_scalar_psd8(input: &[Field<Self>]) -> Result<Scalar<Self>>;
432
433    /// Returns a Merkle tree with a BHP leaf hasher of 1024-bits and a BHP path hasher of 512-bits.
434    fn merkle_tree_bhp<const DEPTH: u8>(leaves: &[Vec<bool>]) -> Result<BHPMerkleTree<Self, DEPTH>>;
435
436    /// Returns a Merkle tree with a Poseidon leaf hasher with input rate of 4 and a Poseidon path hasher with input rate of 2.
437    fn merkle_tree_psd<const DEPTH: u8>(leaves: &[Vec<Field<Self>>]) -> Result<PoseidonMerkleTree<Self, DEPTH>>;
438
439    /// Returns `true` if the given Merkle path is valid for the given root and leaf.
440    #[allow(clippy::ptr_arg)]
441    fn verify_merkle_path_bhp<const DEPTH: u8>(
442        path: &MerklePath<Self, DEPTH>,
443        root: &Field<Self>,
444        leaf: &Vec<bool>,
445    ) -> bool;
446
447    /// Returns `true` if the given Merkle path is valid for the given root and leaf.
448    #[allow(clippy::ptr_arg)]
449    fn verify_merkle_path_psd<const DEPTH: u8>(
450        path: &MerklePath<Self, DEPTH>,
451        root: &Field<Self>,
452        leaf: &Vec<Field<Self>>,
453    ) -> bool;
454}
455
456/// Returns the consensus configuration value for the specified height.
457///
458/// Arguments:
459/// - `$network`: The network to use the constant of.
460/// - `$constant`: The constant to search a value of.
461/// - `$seek_height`: The block height to search the value for.
462#[macro_export]
463macro_rules! consensus_config_value {
464    ($network:ident, $constant:ident, $seek_height:expr) => {
465        // Search the consensus version enacted at the specified height.
466        $network::CONSENSUS_VERSION($seek_height).map_or(None, |seek_version| {
467            // Search the consensus value for the specified version.
468            match $network::$constant.binary_search_by(|(version, _)| version.cmp(&seek_version)) {
469                // If a value was found for this consensus version, return it.
470                Ok(index) => Some($network::$constant[index].1),
471                // If the specified version was not found exactly, determine whether to return an appropriate value anyway.
472                Err(index) => {
473                    // This constant is not yet in effect at this consensus version.
474                    if index == 0 {
475                        None
476                    // Return the appropriate value belonging to the consensus version *lower* than the sought version.
477                    } else {
478                        Some($network::$constant[index - 1].1)
479                    }
480                }
481            }
482        })
483    };
484}
485
486#[cfg(test)]
487mod tests {
488    use super::*;
489
490    /// Ensure that the consensus constants are defined and correct at genesis.
491    /// It is possible this invariant no longer holds in the future, e.g. due to pruning or novel types of constants.
492    fn consensus_constants_at_genesis<N: Network>() {
493        let height = N::CONSENSUS_VERSION_HEIGHTS.first().unwrap().1;
494        assert_eq!(height, 0);
495        let consensus_version = N::CONSENSUS_VERSION_HEIGHTS.first().unwrap().0;
496        assert_eq!(consensus_version, ConsensusVersion::V1);
497        assert_eq!(consensus_version as usize, 1);
498    }
499
500    /// Ensure that the consensus *versions* are unique, incrementing and start with 1.
501    fn consensus_versions<N: Network>() {
502        let mut previous_version = N::CONSENSUS_VERSION_HEIGHTS.first().unwrap().0;
503        // Ensure that the consensus versions start with 1.
504        assert_eq!(previous_version as usize, 1);
505        // Ensure that the consensus versions are unique and incrementing by 1.
506        for (version, _) in N::CONSENSUS_VERSION_HEIGHTS.iter().skip(1) {
507            assert_eq!(*version as usize, previous_version as usize + 1);
508            previous_version = *version;
509        }
510        // Ensure that the consensus versions are unique and incrementing.
511        let mut previous_version = N::MAX_CERTIFICATES.first().unwrap().0;
512        for (version, _) in N::MAX_CERTIFICATES.iter().skip(1) {
513            assert!(*version > previous_version);
514            previous_version = *version;
515        }
516    }
517
518    /// Ensure that consensus *heights* are unique and incrementing.
519    fn consensus_constants_increasing_heights<N: Network>() {
520        let mut previous_height = N::CONSENSUS_VERSION_HEIGHTS.first().unwrap().1;
521        for (version, height) in N::CONSENSUS_VERSION_HEIGHTS.iter().skip(1) {
522            assert!(*height > previous_height);
523            previous_height = *height;
524            // Ensure that N::CONSENSUS_VERSION returns the expected value.
525            assert_eq!(N::CONSENSUS_VERSION(*height).unwrap(), *version);
526            // Ensure that N::CONSENSUS_HEIGHT returns the expected value.
527            assert_eq!(N::CONSENSUS_HEIGHT(*version).unwrap(), *height);
528        }
529    }
530
531    /// Ensure that version of all consensus-relevant constants are present in the consensus version heights.
532    fn consensus_constants_valid_heights<N: Network>() {
533        for (version, value) in N::MAX_CERTIFICATES.iter() {
534            // Ensure that the height at which an update occurs are present in CONSENSUS_VERSION_HEIGHTS.
535            let height = N::CONSENSUS_VERSION_HEIGHTS.iter().find(|(c_version, _)| *c_version == *version).unwrap().1;
536            // Double-check that consensus_config_value returns the correct value.
537            assert_eq!(consensus_config_value!(N, MAX_CERTIFICATES, height).unwrap(), *value);
538        }
539    }
540
541    /// Ensure that `MAX_CERTIFICATES` increases and is correctly defined.
542    /// See the constant declaration for an explanation why.
543    fn max_certificates_increasing<N: Network>() {
544        let mut previous_value = N::MAX_CERTIFICATES.first().unwrap().1;
545        for (_, value) in N::MAX_CERTIFICATES.iter().skip(1) {
546            assert!(*value >= previous_value);
547            previous_value = *value;
548        }
549    }
550
551    /// Ensure that the number of constant definitions is the same across networks.
552    fn constants_equal_length<N1: Network, N2: Network, N3: Network>() {
553        // If we can construct an array, that means the underlying types must be the same.
554        let _ = [N1::CONSENSUS_VERSION_HEIGHTS, N2::CONSENSUS_VERSION_HEIGHTS, N3::CONSENSUS_VERSION_HEIGHTS];
555        let _ = [N1::MAX_CERTIFICATES, N2::MAX_CERTIFICATES, N3::MAX_CERTIFICATES];
556    }
557
558    #[test]
559    #[allow(clippy::assertions_on_constants)]
560    fn test_consensus_constants() {
561        consensus_constants_at_genesis::<MainnetV0>();
562        consensus_constants_at_genesis::<TestnetV0>();
563        consensus_constants_at_genesis::<CanaryV0>();
564
565        consensus_versions::<MainnetV0>();
566        consensus_versions::<TestnetV0>();
567        consensus_versions::<CanaryV0>();
568
569        consensus_constants_increasing_heights::<MainnetV0>();
570        consensus_constants_increasing_heights::<TestnetV0>();
571        consensus_constants_increasing_heights::<CanaryV0>();
572
573        consensus_constants_valid_heights::<MainnetV0>();
574        consensus_constants_valid_heights::<TestnetV0>();
575        consensus_constants_valid_heights::<CanaryV0>();
576
577        max_certificates_increasing::<MainnetV0>();
578        max_certificates_increasing::<TestnetV0>();
579        max_certificates_increasing::<CanaryV0>();
580
581        constants_equal_length::<MainnetV0, TestnetV0, CanaryV0>();
582    }
583}