Skip to main content

snarkvm_console_program/state_path/transition_leaf/
mod.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
16mod bytes;
17mod serialize;
18mod string;
19mod to_bits;
20
21use snarkvm_console_network::prelude::*;
22use snarkvm_console_types::Field;
23
24/// The version of a transition leaf.
25#[derive(Copy, Clone, Debug, PartialEq, Eq)]
26#[repr(u8)]
27pub enum LeafVersion {
28    /// Static leaf (version 1) — standard inputs/outputs.
29    Static = 1,
30    /// Dynamic leaf (version 2) — record inputs/outputs from dynamic transition calls.
31    Dynamic = 2,
32}
33
34impl TryFrom<u8> for LeafVersion {
35    type Error = Error;
36
37    fn try_from(value: u8) -> Result<Self> {
38        match value {
39            1 => Ok(Self::Static),
40            2 => Ok(Self::Dynamic),
41            _ => bail!("Invalid transition leaf version: {value}"),
42        }
43    }
44}
45
46/// The Merkle leaf for an input or output ID in the transition.
47#[derive(Copy, Clone, PartialEq, Eq)]
48pub struct TransitionLeaf<N: Network> {
49    /// The version of the Merkle leaf.
50    version: u8,
51    /// The index of the Merkle leaf.
52    index: u8,
53    /// The variant of the Merkle leaf.
54    variant: u8,
55    /// The ID.
56    id: Field<N>,
57}
58
59impl<N: Network> TransitionLeaf<N> {
60    /// Initializes a new instance of `TransitionLeaf` with static version.
61    pub const fn new(index: u8, variant: u8, id: Field<N>) -> Self {
62        Self { version: LeafVersion::Static as u8, index, variant, id }
63    }
64
65    /// Initializes a new instance of `TransitionLeaf` for a record input/output with a dynamic ID (variant 3).
66    pub const fn new_record_with_dynamic_id(index: u8, id: Field<N>) -> Self {
67        Self { version: LeafVersion::Dynamic as u8, index, variant: 3, id }
68    }
69
70    /// Initializes a new instance of `TransitionLeaf` for an external record input/output with a dynamic ID (variant 4).
71    pub const fn new_external_record_with_dynamic_id(index: u8, id: Field<N>) -> Self {
72        Self { version: LeafVersion::Dynamic as u8, index, variant: 4, id }
73    }
74
75    /// Initializes a new instance of `TransitionLeaf` from raw fields.
76    /// Returns an error if the version/variant combination is invalid.
77    pub fn from(version: u8, index: u8, variant: u8, id: Field<N>) -> Result<Self> {
78        // Validate the version.
79        let leaf_version = LeafVersion::try_from(version)?;
80        // Dynamic version is only allowed for Record (3) and ExternalRecord (4) variants.
81        if matches!(leaf_version, LeafVersion::Dynamic) && variant != 3 && variant != 4 {
82            bail!("Dynamic transition leaf variant must be 3 (Record) or 4 (ExternalRecord), found {variant}");
83        }
84        Ok(Self { version, index, variant, id })
85    }
86
87    /// Returns the version of the Merkle leaf.
88    pub const fn version(&self) -> u8 {
89        self.version
90    }
91
92    /// Returns the index of the Merkle leaf.
93    pub const fn index(&self) -> u8 {
94        self.index
95    }
96
97    /// Returns the variant of the Merkle leaf.
98    pub const fn variant(&self) -> u8 {
99        self.variant
100    }
101
102    /// Returns the ID in the Merkle leaf.
103    pub const fn id(&self) -> Field<N> {
104        self.id
105    }
106}
107
108#[cfg(test)]
109mod test_helpers {
110    use super::*;
111    use snarkvm_console_network::MainnetV0;
112
113    type CurrentNetwork = MainnetV0;
114
115    /// Samples a transition leaf with version 1 (static).
116    pub(super) fn sample_leaf(rng: &mut TestRng) -> TransitionLeaf<CurrentNetwork> {
117        // Construct a new leaf.
118        TransitionLeaf::new(rng.r#gen(), rng.r#gen(), Uniform::rand(rng))
119    }
120
121    /// Samples a transition leaf with version 2 (dynamic).
122    pub(super) fn sample_dynamic_leaf(rng: &mut TestRng) -> TransitionLeaf<CurrentNetwork> {
123        // Randomly choose between Record (variant 3) and ExternalRecord (variant 4).
124        if rng.r#gen() {
125            TransitionLeaf::new_record_with_dynamic_id(rng.r#gen(), Uniform::rand(rng))
126        } else {
127            TransitionLeaf::new_external_record_with_dynamic_id(rng.r#gen(), Uniform::rand(rng))
128        }
129    }
130}