snarkvm_circuit_collections/merkle_tree/
verify.rs

1// Copyright 2024-2025 Aleo Network Foundation
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<E: Environment, const DEPTH: u8> MerklePath<E, DEPTH> {
19    /// Returns `true` if the Merkle path is valid for the given root and leaf.
20    pub fn verify<LH: LeafHash<E, Hash = PH::Hash>, PH: PathHash<E, Hash = Field<E>>>(
21        &self,
22        leaf_hasher: &LH,
23        path_hasher: &PH,
24        root: &PH::Hash,
25        leaf: &LH::Leaf,
26    ) -> Boolean<E> {
27        // Ensure the leaf index is within the tree depth.
28        if (*self.leaf_index.eject_value() as u128) >= (1u128 << DEPTH) {
29            E::halt("Found an out of bounds Merkle leaf index")
30        }
31        // Ensure the path length matches the expected depth.
32        else if self.siblings.len() != DEPTH as usize {
33            E::halt("Found an incorrect Merkle path length")
34        }
35
36        // Initialize a tracker for the current hash, by computing the leaf hash to start.
37        let mut current_hash = leaf_hasher.hash_leaf(leaf);
38
39        // Compute the ordering of the current hash and sibling hash on each level.
40        // If the indicator bit is `true`, then the ordering is (current_hash, sibling_hash).
41        // If the indicator bit is `false`, then the ordering is (sibling_hash, current_hash).
42        let indicators = self.leaf_index.to_bits_le().into_iter().take(DEPTH as usize).map(|b| !b);
43
44        // Check levels between leaf level and root.
45        for (indicator, sibling_hash) in indicators.zip_eq(&self.siblings) {
46            // Construct the ordering of the left & right child hash for this level.
47            let left = Field::ternary(&indicator, &current_hash, sibling_hash);
48            let right = Field::ternary(&indicator, sibling_hash, &current_hash);
49
50            // Update the current hash for the next level.
51            current_hash = path_hasher.hash_children(&left, &right);
52        }
53
54        // Ensure the final hash matches the given root.
55        root.is_equal(&current_hash)
56    }
57}
58
59#[cfg(all(test, feature = "console"))]
60mod tests {
61    use super::*;
62    use snarkvm_circuit_algorithms::{BHP512, BHP1024, Poseidon2, Poseidon4};
63    use snarkvm_circuit_types::environment::Circuit;
64    use snarkvm_utilities::{TestRng, Uniform};
65
66    use anyhow::Result;
67
68    const ITERATIONS: u128 = 10;
69    const DOMAIN: &str = "MerkleTreeCircuit0";
70
71    macro_rules! check_verify {
72        ($lh:ident, $ph:ident, $mode:ident, $depth:expr, $num_inputs:expr, ($num_constants:expr, $num_public:expr, $num_private:expr, $num_constraints:expr)) => {{
73            // Initialize the leaf hasher.
74            let native_leaf_hasher =
75                snarkvm_console_algorithms::$lh::<<Circuit as Environment>::Network>::setup(DOMAIN)?;
76            let circuit_leaf_hasher = $lh::<Circuit>::constant(native_leaf_hasher.clone());
77
78            let mut rng = TestRng::default();
79
80            // Initialize the path hasher.
81            let native_path_hasher =
82                snarkvm_console_algorithms::$ph::<<Circuit as Environment>::Network>::setup(DOMAIN)?;
83            let circuit_path_hasher = $ph::<Circuit>::constant(native_path_hasher.clone());
84
85            for i in 0..ITERATIONS {
86                // Determine the number of leaves.
87                let num_leaves = core::cmp::min(2u128.pow($depth as u32), i);
88                // Compute the leaves.
89                let leaves = (0..num_leaves)
90                    .map(|_| (0..$num_inputs).map(|_| Uniform::rand(&mut rng)).collect::<Vec<_>>())
91                    .collect::<Vec<_>>();
92                // Compute the Merkle tree.
93                let merkle_tree = console::merkle_tree::MerkleTree::<_, _, _, $depth>::new(
94                    &native_leaf_hasher,
95                    &native_path_hasher,
96                    &leaves,
97                )?;
98
99                for (index, merkle_leaf) in leaves.iter().enumerate() {
100                    // Compute the Merkle path.
101                    let merkle_path = merkle_tree.prove(index, merkle_leaf)?;
102
103                    // Initialize the Merkle path.
104                    let path = MerklePath::<Circuit, $depth>::new(Mode::$mode, merkle_path.clone());
105                    assert_eq!(merkle_path, path.eject_value());
106                    // Initialize the Merkle root.
107                    let root = Field::new(Mode::$mode, *merkle_tree.root());
108                    // Initialize the Merkle leaf.
109                    let leaf: Vec<_> = Inject::new(Mode::$mode, merkle_leaf.clone());
110
111                    Circuit::scope(format!("Verify {}", Mode::$mode), || {
112                        let candidate = path.verify(&circuit_leaf_hasher, &circuit_path_hasher, &root, &leaf);
113                        assert!(candidate.eject_value());
114                        assert_scope!($num_constants, $num_public, $num_private, $num_constraints);
115                    });
116                    Circuit::reset();
117
118                    // Initialize an incorrect Merkle root.
119                    let incorrect_root = root.clone() + Field::one();
120
121                    Circuit::scope(format!("Verify (Incorrect Root) {}", Mode::$mode), || {
122                        let candidate = path.verify(&circuit_leaf_hasher, &circuit_path_hasher, &incorrect_root, &leaf);
123                        assert!(!candidate.eject_value());
124                        assert_scope!($num_constants, $num_public, $num_private, $num_constraints);
125                    });
126                    Circuit::reset();
127
128                    // Initialize an incorrect Merkle leaf.
129                    let mut incorrect_leaf = leaf.clone();
130                    let mut incorrect_value = Uniform::rand(&mut rng);
131                    while incorrect_value == incorrect_leaf[0].eject_value() {
132                        incorrect_value = Uniform::rand(&mut rng);
133                    }
134                    incorrect_leaf[0] = Inject::new(Mode::$mode, incorrect_value);
135
136                    Circuit::scope(format!("Verify (Incorrect Leaf) {}", Mode::$mode), || {
137                        let candidate = path.verify(&circuit_leaf_hasher, &circuit_path_hasher, &root, &incorrect_leaf);
138                        assert!(!candidate.eject_value());
139                        assert_scope!($num_constants, $num_public, $num_private, $num_constraints);
140                    });
141                    Circuit::reset();
142                }
143            }
144            Ok(())
145        }};
146    }
147
148    #[test]
149    fn test_verify_bhp512_constant() -> Result<()> {
150        check_verify!(BHP1024, BHP512, Constant, 32, 1024, (52960, 0, 0, 0))
151    }
152
153    #[test]
154    fn test_verify_bhp512_public() -> Result<()> {
155        check_verify!(BHP1024, BHP512, Public, 32, 1024, (13501, 0, 61938, 62066))
156    }
157
158    #[test]
159    fn test_verify_bhp512_private() -> Result<()> {
160        check_verify!(BHP1024, BHP512, Private, 32, 1024, (13501, 0, 61938, 62066))
161    }
162
163    #[test]
164    fn test_verify_poseidon2_constant() -> Result<()> {
165        check_verify!(Poseidon4, Poseidon2, Constant, 32, 4, (34, 0, 0, 0))
166    }
167
168    #[test]
169    fn test_verify_poseidon2_public() -> Result<()> {
170        check_verify!(Poseidon4, Poseidon2, Public, 32, 4, (33, 0, 18046, 18046))
171    }
172
173    #[test]
174    fn test_verify_poseidon2_private() -> Result<()> {
175        check_verify!(Poseidon4, Poseidon2, Private, 32, 4, (33, 0, 18046, 18046))
176    }
177}