snarkvm_ledger_block/transition/
merkle.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
16use super::*;
17
18impl<N: Network> Transition<N> {
19    /// Returns the transition root, by computing the root for a Merkle tree of the input and output IDs.
20    pub fn to_root(&self) -> Result<Field<N>> {
21        Ok(*self.to_tree()?.root())
22    }
23
24    /// Returns the Merkle path for the transition leaf.
25    pub fn to_path(&self, leaf: &TransitionLeaf<N>) -> Result<TransitionPath<N>> {
26        // Compute the Merkle path.
27        self.to_tree()?.prove(leaf.index() as usize, &leaf.to_bits_le())
28    }
29
30    /// Returns the Merkle leaf for the given input or output ID in the transition.
31    pub fn to_leaf(&self, id: &Field<N>, is_input: bool) -> Result<TransitionLeaf<N>> {
32        // Match on the input or output ID.
33        match is_input {
34            true => {
35                // Iterate through the transition inputs.
36                for (index, input) in self.inputs.iter().enumerate() {
37                    // Check if the input ID matches the given ID.
38                    if id == input.id() {
39                        // Return the transition leaf.
40                        return Ok(input.to_transition_leaf(u8::try_from(index)?));
41                    }
42                }
43                // Error if the input ID was not found.
44                bail!("Input ID not found in transition")
45            }
46            false => {
47                // Iterate through the transition outputs.
48                for (index, output) in self.outputs.iter().enumerate() {
49                    // Check if the output ID matches the given ID.
50                    if id == output.id() {
51                        // Return the transition leaf.
52                        return Ok(output.to_transition_leaf(u8::try_from(self.inputs.len() + index)?));
53                    }
54                }
55                // Error if the output ID was not found.
56                bail!("Output ID not found in transition")
57            }
58        }
59    }
60
61    /// The Merkle tree of input and output IDs for the transition.
62    pub fn to_tree(&self) -> Result<TransitionTree<N>> {
63        Self::function_tree(&self.inputs, &self.outputs)
64    }
65
66    /// Returns the Merkle tree for the given inputs and outputs.
67    pub(super) fn function_tree(inputs: &[Input<N>], outputs: &[Output<N>]) -> Result<TransitionTree<N>> {
68        // Ensure the number of inputs is within the allowed range.
69        ensure!(
70            inputs.len() <= N::MAX_INPUTS,
71            "Transition cannot exceed {} inputs, found {} inputs",
72            N::MAX_INPUTS,
73            inputs.len()
74        );
75        // Ensure the number of outputs is within the allowed range.
76        ensure!(
77            outputs.len() <= N::MAX_OUTPUTS,
78            "Transition cannot exceed {} outputs, found {} outputs",
79            N::MAX_OUTPUTS,
80            outputs.len()
81        );
82
83        // Prepare the input leaves.
84        let input_leaves = inputs
85            .iter()
86            .enumerate()
87            .map(|(index, input)| Ok::<_, Error>(input.to_transition_leaf(u8::try_from(index)?).to_bits_le()));
88        // Prepare the output leaves.
89        let output_leaves = outputs
90            .iter()
91            .enumerate()
92            .map(|(index, output)| Ok(output.to_transition_leaf(u8::try_from(inputs.len() + index)?).to_bits_le()));
93        // Compute the function tree.
94        N::merkle_tree_bhp::<TRANSITION_DEPTH>(&input_leaves.chain(output_leaves).collect::<Result<Vec<_>, _>>()?)
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101    use console::network::MainnetV0;
102
103    type CurrentNetwork = MainnetV0;
104
105    #[test]
106    fn test_transition_depth() {
107        // Ensure the log2 relationship between depth and the maximum number of transition inputs & outputs.
108        assert_eq!(2usize.pow(TRANSITION_DEPTH as u32), CurrentNetwork::MAX_INPUTS + CurrentNetwork::MAX_OUTPUTS);
109    }
110}