snarkvm_circuit_collections/merkle_tree/
verify.rs1use super::*;
17
18impl<E: Environment, const DEPTH: u8> MerklePath<E, DEPTH> {
19 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 if (*self.leaf_index.eject_value() as u128) >= (1u128 << DEPTH) {
29 E::halt("Found an out of bounds Merkle leaf index")
30 }
31 else if self.siblings.len() != DEPTH as usize {
33 E::halt("Found an incorrect Merkle path length")
34 }
35
36 let mut current_hash = leaf_hasher.hash_leaf(leaf);
38
39 let indicators = self.leaf_index.to_bits_le().into_iter().take(DEPTH as usize).map(|b| !b);
43
44 for (indicator, sibling_hash) in indicators.zip_eq(&self.siblings) {
46 let left = Field::ternary(&indicator, ¤t_hash, sibling_hash);
48 let right = Field::ternary(&indicator, sibling_hash, ¤t_hash);
49
50 current_hash = path_hasher.hash_children(&left, &right);
52 }
53
54 root.is_equal(¤t_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 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 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 let num_leaves = core::cmp::min(2u128.pow($depth as u32), i);
88 let leaves = (0..num_leaves)
90 .map(|_| (0..$num_inputs).map(|_| Uniform::rand(&mut rng)).collect::<Vec<_>>())
91 .collect::<Vec<_>>();
92 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 let merkle_path = merkle_tree.prove(index, merkle_leaf)?;
102
103 let path = MerklePath::<Circuit, $depth>::new(Mode::$mode, merkle_path.clone());
105 assert_eq!(merkle_path, path.eject_value());
106 let root = Field::new(Mode::$mode, *merkle_tree.root());
108 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 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 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}