Skip to main content

snarkvm_console_network/
consensus_heights.rs

1// Copyright (c) 2019-2026 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
16use crate::{FromBytes, ToBytes, io_error};
17
18use enum_iterator::{Sequence, last};
19use std::io;
20
21/// The different consensus versions.
22/// If you need the version active for a specific height, see: `N::CONSENSUS_VERSION`.
23#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Sequence)]
24#[repr(u16)]
25pub enum ConsensusVersion {
26    /// V1: The initial genesis consensus version.
27    V1 = 1,
28    /// V2: Update to the block reward and execution cost algorithms.
29    V2 = 2,
30    /// V3: Update to the number of validators and finalize scope RNG seed.
31    V3 = 3,
32    /// V4: Update to the Varuna version.
33    V4 = 4,
34    /// V5: Update to the number of validators and enable batch proposal spend limits.
35    V5 = 5,
36    /// V6: Update to the number of validators.
37    V6 = 6,
38    /// V7: Update to program rules.
39    V7 = 7,
40    /// V8: Update to inclusion version, record commitment version, and introduces sender ciphertexts.
41    V8 = 8,
42    /// V9: Support for program upgradability.
43    V9 = 9,
44    /// V10: Lower fees, appropriate record output type checking.
45    V10 = 10,
46    /// V11: Expand array size limit to 512 and introduce ECDSA signature verification opcodes.
47    V11 = 11,
48    /// V12: Prevent connection to forked nodes, disable StringType, enable block timestamp.
49    V12 = 12,
50    /// V13: Introduces external structs.
51    V13 = 13,
52    /// V14: Increase the program size limit to 512 kB, the transaction size limit to 540 kB,
53    ///      the array size limit to 2048, and the `Future` argument bit size to 32 bits.
54    ///      Introduces `aleo::GENERATOR`, `aleo::GENERATOR_POWERS`, `snark.verify` opcodes,
55    ///      and dynamic dispatch, and identifier literal types.
56    V14 = 14,
57    /// V15: Introduces the record-existence check and `commit.*.raw` instruction variants.
58    ///      Increase the anchor time to 35.
59    V15 = 15,
60    /// V16: Moves the block's spend limit check to the finalize phase.
61    ///      Supports storing of transaction rejection reasons.
62    ///      Increase the program size limit to 2048 kB and the transaction size limit to 2304 kB.
63    ///      Update the deployment storage cost for programs exceeding 512 kB.
64    V16 = 16,
65    /// V17: NOTE: V17 landed chronologically on mainnet before it landed on testnet.
66    ///      Reverts the anchor time to 25.
67    V17 = 17,
68}
69
70impl ToBytes for ConsensusVersion {
71    fn write_le<W: io::Write>(&self, writer: W) -> io::Result<()> {
72        (*self as u16).write_le(writer)
73    }
74}
75
76impl FromBytes for ConsensusVersion {
77    fn read_le<R: io::Read>(reader: R) -> io::Result<Self> {
78        match u16::read_le(reader)? {
79            0 => Err(io_error("Zero is not a valid consensus version")),
80            1 => Ok(Self::V1),
81            2 => Ok(Self::V2),
82            3 => Ok(Self::V3),
83            4 => Ok(Self::V4),
84            5 => Ok(Self::V5),
85            6 => Ok(Self::V6),
86            7 => Ok(Self::V7),
87            8 => Ok(Self::V8),
88            9 => Ok(Self::V9),
89            10 => Ok(Self::V10),
90            11 => Ok(Self::V11),
91            12 => Ok(Self::V12),
92            13 => Ok(Self::V13),
93            14 => Ok(Self::V14),
94            15 => Ok(Self::V15),
95            16 => Ok(Self::V16),
96            17 => Ok(Self::V17),
97            _ => Err(io_error("Invalid consensus version")),
98        }
99    }
100}
101
102impl ConsensusVersion {
103    pub fn latest() -> Self {
104        last::<ConsensusVersion>().expect("At least one ConsensusVersion should be defined.")
105    }
106}
107
108impl std::fmt::Display for ConsensusVersion {
109    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110        // Use Debug formatting for Display.
111        write!(f, "{self:?}")
112    }
113}
114
115/// The number of consensus versions.
116pub(crate) const NUM_CONSENSUS_VERSIONS: usize = enum_iterator::cardinality::<ConsensusVersion>();
117
118/// The consensus version height for `CanaryV0`.
119pub const CANARY_V0_CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); NUM_CONSENSUS_VERSIONS] = [
120    (ConsensusVersion::V1, 0),
121    (ConsensusVersion::V2, 2_900_000),
122    (ConsensusVersion::V3, 4_560_000),
123    (ConsensusVersion::V4, 5_730_000),
124    (ConsensusVersion::V5, 5_780_000),
125    (ConsensusVersion::V6, 6_240_000),
126    (ConsensusVersion::V7, 6_880_000),
127    (ConsensusVersion::V8, 7_565_000),
128    (ConsensusVersion::V9, 8_028_000),
129    (ConsensusVersion::V10, 8_600_000),
130    (ConsensusVersion::V11, 9_510_000),
131    (ConsensusVersion::V12, 10_030_000),
132    (ConsensusVersion::V13, 10_881_000),
133    (ConsensusVersion::V14, 11_960_000),
134    (ConsensusVersion::V15, u32::MAX),
135    (ConsensusVersion::V16, u32::MAX),
136    (ConsensusVersion::V17, u32::MAX),
137];
138
139/// The consensus version height for `MainnetV0`.
140pub const MAINNET_V0_CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); NUM_CONSENSUS_VERSIONS] = [
141    (ConsensusVersion::V1, 0),
142    (ConsensusVersion::V2, 2_800_000),
143    (ConsensusVersion::V3, 4_900_000),
144    (ConsensusVersion::V4, 6_135_000),
145    (ConsensusVersion::V5, 7_060_000),
146    (ConsensusVersion::V6, 7_560_000),
147    (ConsensusVersion::V7, 7_570_000),
148    (ConsensusVersion::V8, 9_430_000),
149    (ConsensusVersion::V9, 10_272_000),
150    (ConsensusVersion::V10, 11_205_000),
151    (ConsensusVersion::V11, 12_870_000),
152    (ConsensusVersion::V12, 13_815_000),
153    (ConsensusVersion::V13, 16_850_000),
154    (ConsensusVersion::V14, 17_700_000),
155    (ConsensusVersion::V15, 19_264_000),
156    (ConsensusVersion::V16, 19_860_000),
157    (ConsensusVersion::V17, 19_860_001),
158];
159
160/// The consensus version heights for `TestnetV0`.
161pub const TESTNET_V0_CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); NUM_CONSENSUS_VERSIONS] = [
162    (ConsensusVersion::V1, 0),
163    (ConsensusVersion::V2, 2_950_000),
164    (ConsensusVersion::V3, 4_800_000),
165    (ConsensusVersion::V4, 6_625_000),
166    (ConsensusVersion::V5, 6_765_000),
167    (ConsensusVersion::V6, 7_600_000),
168    (ConsensusVersion::V7, 8_365_000),
169    (ConsensusVersion::V8, 9_173_000),
170    (ConsensusVersion::V9, 9_800_000),
171    (ConsensusVersion::V10, 10_525_000),
172    (ConsensusVersion::V11, 11_952_000),
173    (ConsensusVersion::V12, 12_669_000),
174    (ConsensusVersion::V13, 14_906_000),
175    (ConsensusVersion::V14, 15_370_000),
176    (ConsensusVersion::V15, 16_886_000),
177    (ConsensusVersion::V16, 17_319_000),
178    (ConsensusVersion::V17, u32::MAX),
179];
180
181/// The consensus version heights when the `test_consensus_heights` feature is enabled.
182pub const TEST_CONSENSUS_VERSION_HEIGHTS: [(ConsensusVersion, u32); NUM_CONSENSUS_VERSIONS] = [
183    (ConsensusVersion::V1, 0),
184    (ConsensusVersion::V2, 5),
185    (ConsensusVersion::V3, 6),
186    (ConsensusVersion::V4, 7),
187    (ConsensusVersion::V5, 8),
188    (ConsensusVersion::V6, 9),
189    (ConsensusVersion::V7, 10),
190    (ConsensusVersion::V8, 11),
191    (ConsensusVersion::V9, 12),
192    (ConsensusVersion::V10, 13),
193    (ConsensusVersion::V11, 14),
194    (ConsensusVersion::V12, 15),
195    (ConsensusVersion::V13, 16),
196    (ConsensusVersion::V14, 17),
197    (ConsensusVersion::V15, 18),
198    (ConsensusVersion::V16, 19),
199    (ConsensusVersion::V17, 20),
200];
201
202#[cfg(any(test, feature = "test", feature = "test_consensus_heights"))]
203pub fn load_test_consensus_heights() -> [(ConsensusVersion, u32); NUM_CONSENSUS_VERSIONS] {
204    // Attempt to read the test consensus heights from the environment variable.
205    load_test_consensus_heights_inner(std::env::var("CONSENSUS_VERSION_HEIGHTS").ok())
206}
207
208#[cfg(any(test, feature = "test", feature = "test_consensus_heights", feature = "wasm"))]
209pub(crate) fn load_test_consensus_heights_inner(
210    consensus_version_heights: Option<String>,
211) -> [(ConsensusVersion, u32); NUM_CONSENSUS_VERSIONS] {
212    // Define a closure to verify the consensus heights.
213    let verify_consensus_heights = |heights: &[(ConsensusVersion, u32); NUM_CONSENSUS_VERSIONS]| {
214        // Assert that the genesis height is 0.
215        assert_eq!(heights[0].1, 0, "Genesis height must be 0.");
216        // Assert that the consensus heights are strictly increasing.
217        for window in heights.windows(2) {
218            if window[0] >= window[1] {
219                panic!("Heights must be strictly increasing, but found: {window:?}");
220            }
221        }
222    };
223
224    // Define consensus version heights container used for testing.
225    let mut test_consensus_heights = TEST_CONSENSUS_VERSION_HEIGHTS;
226
227    // If version heights have been specified, verify and return them.
228    match consensus_version_heights {
229        Some(height_string) => {
230            let parsing_error = format!("Expected exactly {NUM_CONSENSUS_VERSIONS} ConsensusVersion heights.");
231            // Parse the heights from the environment variable.
232            let parsed_test_consensus_heights: [u32; NUM_CONSENSUS_VERSIONS] = height_string
233                .replace(" ", "")
234                .split(",")
235                .map(|height| height.parse::<u32>().expect("Heights should be valid u32 values."))
236                .collect::<Vec<u32>>()
237                .try_into()
238                .expect(&parsing_error);
239            // Set the parsed heights in the test consensus heights.
240            for (i, height) in parsed_test_consensus_heights.into_iter().enumerate() {
241                test_consensus_heights[i] = (TEST_CONSENSUS_VERSION_HEIGHTS[i].0, height);
242            }
243            // Verify and return the parsed test consensus heights.
244            verify_consensus_heights(&test_consensus_heights);
245            test_consensus_heights
246        }
247        None => {
248            // Verify and return the default test consensus heights.
249            verify_consensus_heights(&test_consensus_heights);
250            test_consensus_heights
251        }
252    }
253}
254
255/// Returns the consensus configuration value for the specified height.
256///
257/// Arguments:
258/// - `$network`: The network to use the constant of.
259/// - `$constant`: The constant to search a value of.
260/// - `$seek_height`: The block height to search the value for.
261#[macro_export]
262macro_rules! consensus_config_value {
263    ($network:ident, $constant:ident, $seek_height:expr) => {
264        // Search the consensus version enacted at the specified height.
265        $network::CONSENSUS_VERSION($seek_height).map_or(None, |seek_version| {
266            // Search the consensus value for the specified version.
267            // NOTE: calling `consensus_config_value_by_version!` here would require callers to import both macros.
268            match $network::$constant.binary_search_by(|(version, _)| version.cmp(&seek_version)) {
269                // If a value was found for this consensus version, return it.
270                Ok(index) => Some($network::$constant[index].1),
271                // If the specified version was not found exactly, determine whether to return an appropriate value anyway.
272                Err(index) => {
273                    // This constant is not yet in effect at this consensus version.
274                    if index == 0 {
275                        None
276                    // Return the appropriate value belonging to the consensus version *lower* than the sought version.
277                    } else {
278                        Some($network::$constant[index - 1].1)
279                    }
280                }
281            }
282        })
283    };
284}
285
286/// Returns the consensus configuration value for the specified ConsensusVersion.
287///
288/// Arguments:
289/// - `$network`: The network to use the constant of.
290/// - `$constant`: The constant to search a value of.
291/// - `$seek_version`: The ConsensusVersion to search the value for.
292#[macro_export]
293macro_rules! consensus_config_value_by_version {
294    ($network:ident, $constant:ident, $seek_version:expr) => {
295        // Search the consensus value for the specified version.
296        match $network::$constant.binary_search_by(|(version, _)| version.cmp(&$seek_version)) {
297            // If a value was found for this consensus version, return it.
298            Ok(index) => Some($network::$constant[index].1),
299            // If the specified version was not found exactly, determine whether to return an appropriate value anyway.
300            Err(index) => {
301                // This constant is not yet in effect at this consensus version.
302                if index == 0 {
303                    None
304                // Return the appropriate value belonging to the consensus version *lower* than the sought version.
305                } else {
306                    Some($network::$constant[index - 1].1)
307                }
308            }
309        }
310    };
311}
312
313#[cfg(test)]
314mod tests {
315    use super::*;
316    use crate::{CanaryV0, MainnetV0, Network, TestnetV0};
317
318    /// Ensure that the consensus constants are defined and correct at genesis.
319    /// It is possible this invariant no longer holds in the future, e.g. due to pruning or novel types of constants.
320    fn consensus_constants_at_genesis<N: Network>() {
321        let height = N::_CONSENSUS_VERSION_HEIGHTS.first().unwrap().1;
322        assert_eq!(height, 0);
323        let consensus_version = N::_CONSENSUS_VERSION_HEIGHTS.first().unwrap().0;
324        assert_eq!(consensus_version, ConsensusVersion::V1);
325        assert_eq!(consensus_version as usize, 1);
326    }
327
328    /// Ensure that the consensus *versions* are unique, incrementing and start with 1.
329    fn consensus_versions<N: Network>() {
330        let mut previous_version = N::_CONSENSUS_VERSION_HEIGHTS.first().unwrap().0;
331        // Ensure that the consensus versions start with 1.
332        assert_eq!(previous_version as usize, 1);
333        // Ensure that the consensus versions are unique and incrementing by 1.
334        for (version, _) in N::_CONSENSUS_VERSION_HEIGHTS.iter().skip(1) {
335            assert_eq!(*version as usize, previous_version as usize + 1);
336            previous_version = *version;
337        }
338        // Ensure that the consensus versions are unique and incrementing.
339        let mut previous_version = N::MAX_CERTIFICATES.first().unwrap().0;
340        for (version, _) in N::MAX_CERTIFICATES.iter().skip(1) {
341            assert!(*version > previous_version);
342            previous_version = *version;
343        }
344        let mut previous_version = N::TRANSACTION_SPEND_LIMIT.first().unwrap().0;
345        for (version, _) in N::TRANSACTION_SPEND_LIMIT.iter().skip(1) {
346            assert!(*version > previous_version);
347            previous_version = *version;
348        }
349        let mut previous_version = N::CREDITS_PER_SECOND_OF_RUNTIME.first().unwrap().0;
350        for (version, _) in N::CREDITS_PER_SECOND_OF_RUNTIME.iter().skip(1) {
351            assert!(*version > previous_version);
352            previous_version = *version;
353        }
354        let mut previous_version = N::MAX_ARRAY_ELEMENTS.first().unwrap().0;
355        for (version, _) in N::MAX_ARRAY_ELEMENTS.iter().skip(1) {
356            assert!(*version > previous_version);
357            previous_version = *version;
358        }
359        let mut previous_version = N::MAX_PROGRAM_SIZE.first().unwrap().0;
360        for (version, _) in N::MAX_PROGRAM_SIZE.iter().skip(1) {
361            assert!(*version > previous_version);
362            previous_version = *version;
363        }
364        let mut previous_version = N::MAX_TRANSACTION_SIZE.first().unwrap().0;
365        for (version, _) in N::MAX_TRANSACTION_SIZE.iter().skip(1) {
366            assert!(*version > previous_version);
367            previous_version = *version;
368        }
369        let mut previous_version = N::MAX_WRITES.first().unwrap().0;
370        for (version, _) in N::MAX_WRITES.iter().skip(1) {
371            assert!(*version > previous_version);
372            previous_version = *version;
373        }
374        let mut previous_version = N::ANCHOR_TIMES.first().unwrap().0;
375        for (version, _) in N::ANCHOR_TIMES.iter().skip(1) {
376            assert!(*version > previous_version);
377            previous_version = *version;
378        }
379    }
380
381    /// Ensure that consensus *heights* are unique and incrementing.
382    fn consensus_constants_increasing_heights<N: Network>() {
383        let mut previous_height = N::CONSENSUS_VERSION_HEIGHTS().first().unwrap().1;
384        for (version, height) in N::CONSENSUS_VERSION_HEIGHTS().iter().skip(1) {
385            assert!(*height > previous_height);
386            previous_height = *height;
387            // Ensure that N::CONSENSUS_VERSION returns the expected value.
388            assert_eq!(N::CONSENSUS_VERSION(*height).unwrap(), *version);
389            // Ensure that N::CONSENSUS_HEIGHT returns the expected value.
390            assert_eq!(N::CONSENSUS_HEIGHT(*version).unwrap(), *height);
391        }
392    }
393
394    /// Ensure that version of all consensus-relevant constants are present in the consensus version heights.
395    fn consensus_constants_valid_heights<N: Network>() {
396        for (version, value) in N::MAX_CERTIFICATES.iter() {
397            // Ensure that the height at which an update occurs are present in CONSENSUS_VERSION_HEIGHTS.
398            let height = N::CONSENSUS_VERSION_HEIGHTS().iter().find(|(c_version, _)| *c_version == *version).unwrap().1;
399            // Double-check that consensus_config_value returns the correct value.
400            assert_eq!(consensus_config_value!(N, MAX_CERTIFICATES, height).unwrap(), *value);
401        }
402        for (version, value) in N::TRANSACTION_SPEND_LIMIT.iter() {
403            // Ensure that the height at which an update occurs are present in CONSENSUS_VERSION_HEIGHTS.
404            let height = N::CONSENSUS_VERSION_HEIGHTS().iter().find(|(c_version, _)| *c_version == *version).unwrap().1;
405            // Double-check that consensus_config_value returns the correct value.
406            assert_eq!(consensus_config_value!(N, TRANSACTION_SPEND_LIMIT, height).unwrap(), *value);
407        }
408        for (version, value) in N::CREDITS_PER_SECOND_OF_RUNTIME.iter() {
409            // Ensure that the height at which an update occurs are present in CONSENSUS_VERSION_HEIGHTS.
410            let height = N::CONSENSUS_VERSION_HEIGHTS().iter().find(|(c_version, _)| *c_version == *version).unwrap().1;
411            // Double-check that consensus_config_value returns the correct value.
412            assert_eq!(consensus_config_value!(N, CREDITS_PER_SECOND_OF_RUNTIME, height).unwrap(), *value);
413        }
414        for (version, value) in N::MAX_ARRAY_ELEMENTS.iter() {
415            // Ensure that the height at which an update occurs are present in CONSENSUS_VERSION_HEIGHTS.
416            let height = N::CONSENSUS_VERSION_HEIGHTS().iter().find(|(c_version, _)| *c_version == *version).unwrap().1;
417            // Double-check that consensus_config_value returns the correct value.
418            assert_eq!(consensus_config_value!(N, MAX_ARRAY_ELEMENTS, height).unwrap(), *value);
419        }
420        for (version, value) in N::MAX_PROGRAM_SIZE.iter() {
421            // Ensure that the height at which an update occurs are present in CONSENSUS_VERSION_HEIGHTS.
422            let height = N::CONSENSUS_VERSION_HEIGHTS().iter().find(|(c_version, _)| *c_version == *version).unwrap().1;
423            // Double-check that consensus_config_value returns the correct value.
424            assert_eq!(consensus_config_value!(N, MAX_PROGRAM_SIZE, height).unwrap(), *value);
425        }
426        for (version, value) in N::MAX_TRANSACTION_SIZE.iter() {
427            // Ensure that the height at which an update occurs are present in CONSENSUS_VERSION_HEIGHTS.
428            let height = N::CONSENSUS_VERSION_HEIGHTS().iter().find(|(c_version, _)| *c_version == *version).unwrap().1;
429            // Double-check that consensus_config_value returns the correct value.
430            assert_eq!(consensus_config_value!(N, MAX_TRANSACTION_SIZE, height).unwrap(), *value);
431        }
432        for (version, value) in N::MAX_WRITES.iter() {
433            // Ensure that the height at which an update occurs are present in CONSENSUS_VERSION_HEIGHTS.
434            let height = N::CONSENSUS_VERSION_HEIGHTS().iter().find(|(c_version, _)| *c_version == *version).unwrap().1;
435            // Double-check that consensus_config_value returns the correct value.
436            assert_eq!(consensus_config_value!(N, MAX_WRITES, height).unwrap(), *value);
437        }
438        for (version, value) in N::ANCHOR_TIMES.iter() {
439            let height = N::CONSENSUS_VERSION_HEIGHTS().iter().find(|(c_version, _)| c_version == version).unwrap().1;
440            assert_eq!(consensus_config_value!(N, ANCHOR_TIMES, height).unwrap(), *value);
441        }
442    }
443
444    /// Ensure that consensus_config_value returns a valid value for all consensus versions.
445    fn consensus_config_returns_some<N: Network>() {
446        for (_, height) in N::CONSENSUS_VERSION_HEIGHTS().iter() {
447            assert!(consensus_config_value!(N, MAX_CERTIFICATES, *height).is_some());
448            assert!(consensus_config_value!(N, TRANSACTION_SPEND_LIMIT, *height).is_some());
449            assert!(consensus_config_value!(N, CREDITS_PER_SECOND_OF_RUNTIME, *height).is_some());
450            assert!(consensus_config_value!(N, MAX_ARRAY_ELEMENTS, *height).is_some());
451            assert!(consensus_config_value!(N, MAX_PROGRAM_SIZE, *height).is_some());
452            assert!(consensus_config_value!(N, MAX_TRANSACTION_SIZE, *height).is_some());
453            assert!(consensus_config_value!(N, MAX_WRITES, *height).is_some());
454            assert!(consensus_config_value!(N, ANCHOR_TIMES, *height).is_some());
455        }
456    }
457
458    /// Ensure that `MAX_CERTIFICATES` increases and is correctly defined.
459    /// See the constant declaration for an explanation why.
460    fn max_certificates_increasing<N: Network>() {
461        let mut previous_value = N::MAX_CERTIFICATES.first().unwrap().1;
462        for (_, value) in N::MAX_CERTIFICATES.iter().skip(1) {
463            assert!(*value >= previous_value);
464            previous_value = *value;
465        }
466    }
467
468    /// Ensure that `MAX_ARRAY_ELEMENTS` increases and is correctly defined.
469    /// See the constant declaration for an explanation why.
470    fn max_array_elements_increasing<N: Network>() {
471        let mut previous_value = N::MAX_ARRAY_ELEMENTS.first().unwrap().1;
472        for (_, value) in N::MAX_ARRAY_ELEMENTS.iter().skip(1) {
473            assert!(*value >= previous_value);
474            previous_value = *value;
475        }
476    }
477
478    /// Ensure that `MAX_TRANSACTION_SIZE` is at least 28KB greater than `MAX_PROGRAM_SIZE` for all consensus versions.
479    /// This overhead accounts for proofs, signatures, and other transaction metadata.
480    fn transaction_size_exceeds_program_size<N: Network>() {
481        const MIN_OVERHEAD: usize = 28_000; // 28 kB minimum overhead
482
483        for (_, height) in N::CONSENSUS_VERSION_HEIGHTS().iter() {
484            let max_program_size = consensus_config_value!(N, MAX_PROGRAM_SIZE, *height).unwrap();
485            let max_transaction_size = consensus_config_value!(N, MAX_TRANSACTION_SIZE, *height).unwrap();
486
487            assert!(
488                max_transaction_size >= max_program_size + MIN_OVERHEAD,
489                "At height {height}: MAX_TRANSACTION_SIZE ({max_transaction_size}) must be at least {MIN_OVERHEAD} bytes greater than MAX_PROGRAM_SIZE ({max_program_size})"
490            );
491        }
492    }
493
494    /// Ensure that `MAX_PROGRAM_SIZE` and `MAX_TRANSACTION_SIZE` are defined in lockstep:
495    /// the same number of entries, keyed by the same consensus versions in the same order, with the
496    /// transaction size strictly exceeding the program size at every version. The latest transaction
497    /// size must likewise exceed the latest program size, since a transaction must hold a program
498    /// plus its proofs, signatures, and metadata.
499    fn program_and_transaction_size_aligned<N: Network>() {
500        // Ensure both constants define the same number of entries.
501        assert_eq!(
502            N::MAX_PROGRAM_SIZE.len(),
503            N::MAX_TRANSACTION_SIZE.len(),
504            "MAX_PROGRAM_SIZE and MAX_TRANSACTION_SIZE must define the same number of entries"
505        );
506        // Ensure both constants are keyed by the same consensus versions, in the same order, and that
507        // the transaction size exceeds the program size at each corresponding version.
508        for (index, (program_version, program_size)) in N::MAX_PROGRAM_SIZE.iter().enumerate() {
509            let (transaction_version, transaction_size) = &N::MAX_TRANSACTION_SIZE[index];
510            assert_eq!(
511                program_version, transaction_version,
512                "MAX_PROGRAM_SIZE and MAX_TRANSACTION_SIZE must be keyed by the same consensus version at index {index}, but found {program_version} and {transaction_version}"
513            );
514            assert!(
515                transaction_size > program_size,
516                "At consensus version {program_version}: MAX_TRANSACTION_SIZE ({transaction_size}) must be greater than MAX_PROGRAM_SIZE ({program_size})"
517            );
518        }
519        // Ensure the latest transaction size exceeds the latest program size.
520        assert!(
521            N::LATEST_MAX_TRANSACTION_SIZE() > N::LATEST_MAX_PROGRAM_SIZE(),
522            "LATEST_MAX_TRANSACTION_SIZE ({}) must be greater than LATEST_MAX_PROGRAM_SIZE ({})",
523            N::LATEST_MAX_TRANSACTION_SIZE(),
524            N::LATEST_MAX_PROGRAM_SIZE()
525        );
526    }
527
528    /// Ensure that the number of constant definitions is the same across networks.
529    fn constants_equal_length<N1: Network, N2: Network, N3: Network>() {
530        // If we can construct an array, that means the underlying types must be the same.
531        let _ = [N1::CONSENSUS_VERSION_HEIGHTS, N2::CONSENSUS_VERSION_HEIGHTS, N3::CONSENSUS_VERSION_HEIGHTS];
532        let _ = [N1::MAX_CERTIFICATES, N2::MAX_CERTIFICATES, N3::MAX_CERTIFICATES];
533        let _ = [N1::TRANSACTION_SPEND_LIMIT, N2::TRANSACTION_SPEND_LIMIT, N3::TRANSACTION_SPEND_LIMIT];
534        let _ =
535            [N1::CREDITS_PER_SECOND_OF_RUNTIME, N2::CREDITS_PER_SECOND_OF_RUNTIME, N3::CREDITS_PER_SECOND_OF_RUNTIME];
536        let _ = [N1::MAX_ARRAY_ELEMENTS, N2::MAX_ARRAY_ELEMENTS, N3::MAX_ARRAY_ELEMENTS];
537        let _ = [N1::MAX_PROGRAM_SIZE, N2::MAX_PROGRAM_SIZE, N3::MAX_PROGRAM_SIZE];
538        let _ = [N1::MAX_TRANSACTION_SIZE, N2::MAX_TRANSACTION_SIZE, N3::MAX_TRANSACTION_SIZE];
539        let _ = [N1::MAX_WRITES, N2::MAX_WRITES, N3::MAX_WRITES];
540        let _ = [N1::ANCHOR_TIMES, N2::ANCHOR_TIMES, N3::ANCHOR_TIMES];
541    }
542
543    /// Ensure that `LATEST_MAX_*` functions return valid values without panicking.
544    /// These functions use `.expect()` internally, so this test verifies the arrays are non-empty.
545    fn latest_max_functions_are_safe<N: Network>() {
546        // Verify LATEST_MAX_CERTIFICATES returns a positive value.
547        assert!(N::LATEST_MAX_CERTIFICATES() > 0, "LATEST_MAX_CERTIFICATES must be positive");
548        // Verify LATEST_MAX_PROGRAM_SIZE returns a positive value.
549        assert!(N::LATEST_MAX_PROGRAM_SIZE() > 0, "LATEST_MAX_PROGRAM_SIZE must be positive");
550        // Verify LATEST_MAX_TRANSACTION_SIZE returns a positive value.
551        assert!(N::LATEST_MAX_TRANSACTION_SIZE() > 0, "LATEST_MAX_TRANSACTION_SIZE must be positive");
552        // Verify LATEST_MAX_WRITES returns a positive value.
553        assert!(N::LATEST_MAX_WRITES() > 0, "LATEST_MAX_WRITES must be positive");
554    }
555
556    #[test]
557    #[allow(clippy::assertions_on_constants)]
558    fn test_consensus_constants() {
559        consensus_constants_at_genesis::<MainnetV0>();
560        consensus_constants_at_genesis::<TestnetV0>();
561        consensus_constants_at_genesis::<CanaryV0>();
562
563        consensus_versions::<MainnetV0>();
564        consensus_versions::<TestnetV0>();
565        consensus_versions::<CanaryV0>();
566
567        consensus_constants_increasing_heights::<MainnetV0>();
568        consensus_constants_increasing_heights::<TestnetV0>();
569        consensus_constants_increasing_heights::<CanaryV0>();
570
571        consensus_constants_valid_heights::<MainnetV0>();
572        consensus_constants_valid_heights::<TestnetV0>();
573        consensus_constants_valid_heights::<CanaryV0>();
574
575        consensus_config_returns_some::<MainnetV0>();
576        consensus_config_returns_some::<TestnetV0>();
577        consensus_config_returns_some::<CanaryV0>();
578
579        max_certificates_increasing::<MainnetV0>();
580        max_certificates_increasing::<TestnetV0>();
581        max_certificates_increasing::<CanaryV0>();
582
583        max_array_elements_increasing::<MainnetV0>();
584        max_array_elements_increasing::<TestnetV0>();
585        max_array_elements_increasing::<CanaryV0>();
586
587        transaction_size_exceeds_program_size::<MainnetV0>();
588        transaction_size_exceeds_program_size::<TestnetV0>();
589        transaction_size_exceeds_program_size::<CanaryV0>();
590
591        program_and_transaction_size_aligned::<MainnetV0>();
592        program_and_transaction_size_aligned::<TestnetV0>();
593        program_and_transaction_size_aligned::<CanaryV0>();
594
595        latest_max_functions_are_safe::<MainnetV0>();
596        latest_max_functions_are_safe::<TestnetV0>();
597        latest_max_functions_are_safe::<CanaryV0>();
598
599        constants_equal_length::<MainnetV0, TestnetV0, CanaryV0>();
600    }
601
602    /// Ensure (de-)serialization works correctly.
603    #[test]
604    fn test_to_bytes() {
605        let version = ConsensusVersion::V8;
606        let bytes = version.to_bytes_le().unwrap();
607        let result = ConsensusVersion::from_bytes_le(&bytes).unwrap();
608        assert_eq!(result, version);
609
610        let version = ConsensusVersion::latest();
611        let bytes = version.to_bytes_le().unwrap();
612        let result = ConsensusVersion::from_bytes_le(&bytes).unwrap();
613        assert_eq!(result, version);
614
615        let invalid_bytes = u16::MAX.to_bytes_le().unwrap();
616        let result = ConsensusVersion::from_bytes_le(&invalid_bytes);
617        assert!(result.is_err());
618    }
619
620    #[test]
621    fn test_reward_anchor_time() {
622        assert_eq!(MainnetV0::REWARD_ANCHOR_TIME, MainnetV0::ANCHOR_TIMES.first().unwrap().1);
623        assert_eq!(TestnetV0::REWARD_ANCHOR_TIME, TestnetV0::ANCHOR_TIMES.first().unwrap().1);
624        assert_eq!(CanaryV0::REWARD_ANCHOR_TIME, CanaryV0::ANCHOR_TIMES.first().unwrap().1);
625    }
626}