openzeppelin_crypto/merkle.rs
1//! This module deals with verification of Merkle Tree proofs.
2//!
3//! The tree and the proofs can be generated using `OpenZeppelin`'s
4//! [merkle tree library](https://github.com/OpenZeppelin/merkle-tree). You will
5//! find a quickstart guide in its README.
6//!
7//! WARNING: You should avoid using leaf values that are 64 bytes long
8//! prior to hashing, or use a hash function other than keccak256 for
9//! hashing leaves. This is because the concatenation of a sorted pair
10//! of internal nodes in the Merkle tree could be reinterpreted as a
11//! leaf value. `OpenZeppelin`'s JavaScript library generates Merkle trees
12//! that are safe against this attack out of the box.
13use alloc::vec::Vec;
14use core::marker::PhantomData;
15
16use crate::{
17 hash::{commutative_hash_pair, BuildHasher, Hasher},
18 KeccakBuilder,
19};
20
21type Bytes32 = [u8; 32];
22
23/// Verify merkle proofs.
24pub struct Verifier<B = KeccakBuilder>(PhantomData<B>)
25where
26 B: BuildHasher;
27
28impl Verifier<KeccakBuilder> {
29 /// Verify that `leaf` is part of a Merkle tree defined by `root` by using
30 /// `proof` and the default `keccak256` hashing algorithm.
31 ///
32 /// A new root is rebuilt by traversing up the Merkle tree. The `proof`
33 /// provided must contain sibling hashes on the branch starting from the
34 /// leaf to the root of the tree. Each pair of leaves and each pair of
35 /// pre-images are assumed to be sorted.
36 ///
37 /// A `proof` is valid if and only if the rebuilt hash matches the root
38 /// of the tree.
39 ///
40 /// # Arguments
41 ///
42 /// * `proof` - A slice of hashes that constitute the merkle proof.
43 /// * `root` - The root of the merkle tree, in bytes.
44 /// * `leaf` - The leaf of the merkle tree to proof, in bytes.
45 ///
46 /// # Examples
47 ///
48 /// ```
49 /// use openzeppelin_crypto::merkle::Verifier;
50 /// use hex_literal::hex;
51 ///
52 /// let root = hex!("0000000000000000000000000000000000000000000000000000000000000000");
53 /// let leaf = hex!("0000000000000000000000000000000000000000000000000000000000000000");
54 /// let proof = hex!("0000000000000000000000000000000000000000000000000000000000000000");
55 ///
56 /// let verification = Verifier::verify(&[proof], root, leaf);
57 /// assert!(!verification);
58 /// ```
59 #[must_use]
60 pub fn verify(proof: &[Bytes32], root: Bytes32, leaf: Bytes32) -> bool {
61 Verifier::verify_with_builder(proof, root, leaf, &KeccakBuilder)
62 }
63
64 /// Verify multiple `leaves` can be simultaneously proven to be a part of
65 /// a Merkle tree defined by `root` by using a `proof` with `proof_flags`
66 /// and a `hasher`.
67 ///
68 /// The `proof` must contain the sibling hashes one would need to rebuild
69 /// the root starting from `leaves`. `proof_flags` represents whether a
70 /// hash must be computed using a `proof` member. A new root is rebuilt by
71 /// starting from the `leaves` and traversing up the Merkle tree.
72 ///
73 /// The procedure incrementally reconstructs all inner nodes by combining
74 /// a leaf/inner node with either another leaf/inner node or a `proof`
75 /// sibling node, depending on each proof flag being true or false
76 /// respectively, i.e., the `i`-th hash must be computed using the proof if
77 /// `proof_flags[i] == false`.
78 ///
79 /// CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs,
80 /// it is sufficient to ensure that:
81 /// - The tree is complete (but not necessarily perfect).
82 /// - The leaves to be proven are in the opposite order they appear in the
83 /// tree (i.e., as seen from right to left starting at the deepest layer
84 /// and continuing at the next layer).
85 ///
86 /// NOTE: This implementation is *not* equivalent to it's Solidity
87 /// counterpart. In Rust, access to uninitialized memory panics, which
88 /// means we don't need to check that the whole proof array has been
89 /// processed. Both implementations will revert for the same inputs, but
90 /// for different reasons. See <https://github.com/OpenZeppelin/openzeppelin-contracts/security/advisories/GHSA-wprv-93r4-jj2p>
91 ///
92 /// # Arguments
93 ///
94 /// * `proof` - A slice of hashes that constitute the merkle proof.
95 /// * `proof_flags` - A slice of booleans that determine whether to hash
96 /// leaves or the proof.
97 /// * `root` - The root of the merkle tree, in bytes.
98 /// * `leaves` - A slice of hashes that constitute the leaves of the merkle
99 /// tree to be proven, each leaf in bytes.
100 ///
101 /// # Errors
102 ///
103 /// * [`MultiProofError`] - If the arguments are well-formed, but invalid.
104 ///
105 /// # Panics
106 ///
107 /// * If the proof is malicious (with an out-of-bounds error). See <https://github.com/OpenZeppelin/openzeppelin-contracts/security/advisories/GHSA-wprv-93r4-jj2p>
108 ///
109 /// # Examples
110 ///
111 /// ```rust
112 /// use openzeppelin_crypto::merkle::Verifier;
113 /// use hex_literal::hex;
114 ///
115 /// let root = hex!("6deb52b5da8fd108f79fab00341f38d2587896634c646ee52e49f845680a70c8");
116 /// let leaves = [hex!("19ba6c6333e0e9a15bf67523e0676e2f23eb8e574092552d5e888c64a4bb3681"),
117 /// hex!("c62a8cfa41edc0ef6f6ae27a2985b7d39c7fea770787d7e104696c6e81f64848"),
118 /// hex!("eba909cf4bb90c6922771d7f126ad0fd11dfde93f3937a196274e1ac20fd2f5b")];
119 /// let proof = [hex!("9a4f64e953595df82d1b4f570d34c4f4f0cfaf729a61e9d60e83e579e1aa283e"),
120 /// hex!("8076923e76cf01a7c048400a2304c9a9c23bbbdac3a98ea3946340fdafbba34f")];
121 ///
122 /// let proof_flags = [false, true, false, true];
123 ///
124 /// let verification =
125 /// Verifier::verify_multi_proof(&proof, &proof_flags, root, &leaves);
126 /// assert!(verification.unwrap());
127 /// ```
128 pub fn verify_multi_proof(
129 proof: &[Bytes32],
130 proof_flags: &[bool],
131 root: Bytes32,
132 leaves: &[Bytes32],
133 ) -> Result<bool, MultiProofError> {
134 Verifier::verify_multi_proof_with_builder(
135 proof,
136 proof_flags,
137 root,
138 leaves,
139 &KeccakBuilder,
140 )
141 }
142}
143
144impl<B> Verifier<B>
145where
146 B: BuildHasher,
147 B::Hasher: Hasher<Output = Bytes32>,
148{
149 /// Verify that `leaf` is part of a Merkle tree defined by `root` by using
150 /// `proof` and a custom hashing algorithm defined by `builder`. See
151 /// [`BuildHasher`] for more information on how to construct a builder.
152 ///
153 /// Merkle tree hashing process must be constructed commutatively when using
154 /// custom hashing algorithms.
155 ///
156 /// WARNING: This is a lower-level function. For most use cases,
157 /// [`Verifier::verify`], which uses `keccak256` as a hashing algorithm,
158 /// should be enough. Using other hashing algorithm may have unexpected
159 /// results.
160 ///
161 /// # Arguments
162 ///
163 /// * `proof` - A slice of hashes that constitute the merkle proof.
164 /// * `root` - The root of the merkle tree, in bytes.
165 /// * `leaf` - The leaf of the merkle tree to proof, in bytes.
166 /// * `builder` - A [`BuildHasher`] that represents a hashing algorithm.
167 ///
168 /// # Examples
169 ///
170 /// ```
171 /// use openzeppelin_crypto::{merkle::Verifier, KeccakBuilder};
172 /// use hex_literal::hex;
173 ///
174 /// let root = hex!("0000000000000000000000000000000000000000000000000000000000000000");
175 /// let leaf = hex!("0000000000000000000000000000000000000000000000000000000000000000");
176 /// let proof = hex!("0000000000000000000000000000000000000000000000000000000000000000");
177 ///
178 /// let verification = Verifier::verify_with_builder(&[proof], root, leaf, &KeccakBuilder);
179 /// assert!(!verification);
180 /// ```
181 pub fn verify_with_builder(
182 proof: &[Bytes32],
183 root: Bytes32,
184 mut leaf: Bytes32,
185 builder: &B,
186 ) -> bool {
187 for &hash in proof {
188 leaf = commutative_hash_pair(&leaf, &hash, builder.build_hasher());
189 }
190
191 leaf == root
192 }
193
194 /// Verify multiple `leaves` can be simultaneously proven to be a part of
195 /// a Merkle tree defined by `root` by using a `proof` with `proof_flags`
196 /// and a custom hashing algorithm defined by `builder`. See
197 /// [`BuildHasher`] for more information on how to construct a builder.
198 ///
199 /// Merkle tree hashing process must be constructed commutatively when using
200 /// custom hashing algorithms.
201 ///
202 /// WARNING: This is a lower-level function. For most use cases,
203 /// [`Verifier::verify_multi_proof`], which uses `keccak256` as a hashing
204 /// algorithm, should be enough. Using other hashing algorithm may have
205 /// unexpected results.
206 ///
207 /// The `proof` must contain the sibling hashes one would need to rebuild
208 /// the root starting from `leaves`. `proof_flags` represents whether a
209 /// hash must be computed using a `proof` member. A new root is rebuilt by
210 /// starting from the `leaves` and traversing up the Merkle tree.
211 ///
212 /// The procedure incrementally reconstructs all inner nodes by combining
213 /// a leaf/inner node with either another leaf/inner node or a `proof`
214 /// sibling node, depending on each proof flag being true or false
215 /// respectively, i.e., the `i`-th hash must be computed using the proof if
216 /// `proof_flags[i] == false`.
217 ///
218 /// CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs,
219 /// it is sufficient to ensure that:
220 /// - The tree is complete (but not necessarily perfect).
221 /// - The leaves to be proven are in the opposite order they appear in the
222 /// tree (i.e., as seen from right to left starting at the deepest layer
223 /// and continuing at the next layer).
224 ///
225 /// NOTE: This implementation is *not* equivalent to it's Solidity
226 /// counterpart. In Rust, access to uninitialized memory panics, which
227 /// means we don't need to check that the whole proof array has been
228 /// processed. Both implementations will revert for the same inputs, but
229 /// for different reasons. See <https://github.com/OpenZeppelin/openzeppelin-contracts/security/advisories/GHSA-wprv-93r4-jj2p>
230 ///
231 /// # Arguments
232 ///
233 /// * `proof` - A slice of hashes that constitute the merkle proof.
234 /// * `proof_flags` - A slice of booleans that determine whether to hash
235 /// leaves or the proof.
236 /// * `root` - The root of the merkle tree, in bytes.
237 /// * `leaves` - A slice of hashes that constitute the leaves of the merkle
238 /// tree to be proven, each leaf in bytes.
239 /// * `builder` - A [`BuildHasher`] that represents a hashing algorithm.
240 ///
241 /// # Errors
242 ///
243 /// * [`MultiProofError`] - If the arguments are well-formed, but invalid.
244 ///
245 /// # Examples
246 ///
247 /// ```rust
248 /// use openzeppelin_crypto::{merkle::Verifier, KeccakBuilder};
249 /// use hex_literal::hex;
250 ///
251 /// let root = hex!("6deb52b5da8fd108f79fab00341f38d2587896634c646ee52e49f845680a70c8");
252 /// let leaves = [hex!("19ba6c6333e0e9a15bf67523e0676e2f23eb8e574092552d5e888c64a4bb3681"),
253 /// hex!("c62a8cfa41edc0ef6f6ae27a2985b7d39c7fea770787d7e104696c6e81f64848"),
254 /// hex!("eba909cf4bb90c6922771d7f126ad0fd11dfde93f3937a196274e1ac20fd2f5b")];
255 /// let proof = [hex!("9a4f64e953595df82d1b4f570d34c4f4f0cfaf729a61e9d60e83e579e1aa283e"),
256 /// hex!("8076923e76cf01a7c048400a2304c9a9c23bbbdac3a98ea3946340fdafbba34f")];
257 /// let proof_flags = [false, true, false, true];
258 ///
259 /// let verification =
260 /// Verifier::verify_multi_proof_with_builder(&proof, &proof_flags, root, &leaves, &KeccakBuilder);
261 /// assert!(verification.unwrap());
262 /// ```
263 pub fn verify_multi_proof_with_builder(
264 proof: &[Bytes32],
265 proof_flags: &[bool],
266 root: Bytes32,
267 leaves: &[Bytes32],
268 builder: &B,
269 ) -> Result<bool, MultiProofError> {
270 let total_hashes = proof_flags.len();
271 if leaves.len() + proof.len() != total_hashes + 1 {
272 return Err(MultiProofError::InvalidTotalHashes);
273 }
274 if total_hashes == 0 {
275 // We can safely assume that either `leaves` or `proof` is not empty
276 // given the previous check. We use `unwrap_or_else` to avoid
277 // eagerly evaluating `proof[0]`, which may panic.
278 let rebuilt_root = *leaves.first().unwrap_or_else(|| &proof[0]);
279 return Ok(root == rebuilt_root);
280 }
281
282 // We need at least one leaf for non-trivial trees
283 if leaves.is_empty() {
284 return Err(MultiProofError::NoLeaves);
285 }
286
287 // `hashes` represents a queue of hashes, our "main queue".
288 let mut hashes = Vec::with_capacity(total_hashes + leaves.len());
289 // Which initially gets populated with the leaves.
290 hashes.extend(leaves);
291 // The `xxx_pos` values are "pointers" to the next value to consume in
292 // each queue. We use them to mimic a queue's pop operation.
293 let mut proof_pos = 0;
294 let mut hashes_pos = 0;
295 // At each step, we compute the next hash using two values:
296 // - A value from the "main queue". Consume all the leaves, then all the
297 // hashes but the root.
298 // - A value from the "main queue" (merging branches) or a member of the
299 // `proof`, depending on `flag`.
300 for &flag in proof_flags {
301 let a = hashes[hashes_pos];
302 hashes_pos += 1;
303
304 let b;
305 if flag {
306 b = hashes
307 .get(hashes_pos)
308 .ok_or(MultiProofError::InvalidRootChild)?;
309 hashes_pos += 1;
310 } else {
311 b = proof
312 .get(proof_pos)
313 .ok_or(MultiProofError::InvalidProofLength)?;
314 proof_pos += 1;
315 }
316
317 let hash = commutative_hash_pair(&a, b, builder.build_hasher());
318 hashes.push(hash);
319 }
320
321 // We know that `total_hashes > 0`.
322 let rebuilt_root = hashes[total_hashes + leaves.len() - 1];
323 Ok(root == rebuilt_root)
324 }
325}
326
327/// An error that occurred while verifying a multi-proof.
328///
329/// TODO: Once <https://github.com/rust-lang/rust/issues/103765> is resolved,
330/// we should derive `core::error::Error`.
331#[derive(core::fmt::Debug, PartialEq)]
332pub enum MultiProofError {
333 /// The proof length does not match the flags.
334 InvalidProofLength,
335 /// Tried to access uninitialized memory.
336 ///
337 /// This happens when the proof is too long, which makes the verification
338 /// procedure try to access uninitialized memory, which may result in an
339 /// invalid root.
340 ///
341 /// For more information see [this vulnerability].
342 ///
343 /// [this vulnerability]: https://github.com/OpenZeppelin/openzeppelin-contracts/security/advisories/GHSA-wprv-93r4-jj2p
344 InvalidRootChild,
345 /// The number of leaves and proof members does not match the number of
346 /// hashes necessary to complete the verification.
347 InvalidTotalHashes,
348 /// No leaves were provided for a non-trivial tree.
349 NoLeaves,
350}
351
352impl core::fmt::Display for MultiProofError {
353 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
354 let msg = match self {
355 MultiProofError::InvalidProofLength => "invalid multi-proof length",
356 MultiProofError::InvalidRootChild => "invalid root child generated",
357 MultiProofError::InvalidTotalHashes => {
358 "leaves.len() + proof.len() != total_hashes + 1"
359 }
360 MultiProofError::NoLeaves => {
361 "no leaves were provided for a non-trivial tree"
362 }
363 };
364
365 write!(f, "{msg}")
366 }
367}
368
369#[cfg(test)]
370mod tests {
371 //! NOTE: The values used as input for these tests were all generated using
372 //! <https://github.com/OpenZeppelin/merkle-tree>.
373 use hex_literal::hex;
374 use proptest::{prelude::*, prop_compose};
375 use rand::{rng, RngCore};
376
377 use super::{Bytes32, KeccakBuilder, MultiProofError, Verifier};
378 use crate::hash::{commutative_hash_pair, BuildHasher};
379
380 /// Shorthand for declaring variables converted from a hex literal to a
381 /// fixed 32-byte slice.
382 macro_rules! bytes {
383 ($($var:ident = $hex:literal);* $(;)?) => {
384 $(
385 #[allow(non_upper_case_globals)]
386 const $var: Bytes32 = hex!($hex);
387 )*
388 };
389 }
390
391 /// Shorthand for converting from an array of hex literals to an array of
392 /// fixed 32-bytes slices.
393 macro_rules! bytes_array {
394 ($($s:literal),* $(,)?) => {
395 [
396 $(hex!($s),)*
397 ]
398 };
399 }
400
401 prop_compose! {
402 fn valid_merkle_proof(min_proof_len: usize)(
403 leaf: [u8; 32],
404 proof in prop::collection::vec(any::<[u8; 32]>(), min_proof_len..ProptestConfig::default().max_default_size_range),
405 ) -> (Vec<[u8; 32]>, [u8; 32], [u8; 32]) {
406 let mut current = leaf;
407 for &hash in &proof {
408 current = commutative_hash_pair(
409 ¤t,
410 &hash,
411 KeccakBuilder.build_hasher(),
412 );
413 }
414 let root = current;
415 (proof, root, leaf)
416 }
417 }
418
419 #[test]
420 fn proof_tampering_invalidates() {
421 proptest!(
422 |((proof, root, leaf) in valid_merkle_proof(0),
423 tamper_idx in 0..32usize)| {
424 if let Some(proof_element) = proof.first() {
425 let mut tampered_proof = proof.clone();
426 let mut tampered_element = *proof_element;
427 tampered_element[tamper_idx] =
428 tampered_element[tamper_idx].wrapping_add(1);
429 tampered_proof[0] = tampered_element;
430
431 prop_assert!(!Verifier::verify(&tampered_proof, root, leaf));
432 }
433 }
434 );
435 }
436
437 #[test]
438 fn proof_length_affects_verification() {
439 proptest!(
440 |((proof, root, leaf) in valid_merkle_proof(0),
441 extra_hash: [u8; 32])| {
442 let longer_proof = &[proof.as_slice(), &[extra_hash]].concat();
443 prop_assert!(!Verifier::verify(longer_proof, root, leaf));
444
445 if !proof.is_empty() {
446 let shorter_proof = &proof[1..];
447 prop_assert!(!Verifier::verify(shorter_proof, root, leaf));
448 let shorter_proof = &proof[..proof.len() - 1];
449 prop_assert!(!Verifier::verify(shorter_proof, root, leaf));
450 }
451 }
452 );
453 }
454
455 #[test]
456 fn proof_consistency() {
457 proptest!(
458 |(proof: Vec<[u8; 32]>,
459 proof_flags: Vec<bool>,
460 root: [u8; 32],
461 // for regular proof
462 leaf: [u8; 32],
463 // for multi-proof
464 leaves: Vec<[u8; 32]>,)| {
465 let result1 = Verifier::verify(&proof, root, leaf);
466 let result2 = Verifier::verify(&proof, root, leaf);
467 prop_assert_eq!(result1, result2);
468
469 // ensure proof_flags length is always <= proof.len()
470 let proof_flags =
471 proof_flags.into_iter().take(proof.len()).collect::<Vec<_>>();
472
473 let result1 = Verifier::verify_multi_proof(
474 &proof,
475 &proof_flags,
476 root,
477 &leaves,
478 );
479 let result2 = Verifier::verify_multi_proof(
480 &proof,
481 &proof_flags,
482 root,
483 &leaves,
484 );
485 prop_assert_eq!(result1, result2);
486 }
487 );
488 }
489
490 #[test]
491 fn single_leaf_equals_regular_verify() {
492 proptest!(|((proof, root, leaf) in valid_merkle_proof(0))| {
493 let proof_flags = vec![false; proof.len()];
494 let multi_result = Verifier::verify_multi_proof(
495 &proof,
496 &proof_flags,
497 root,
498 &[leaf],
499 );
500 let regular_result = Verifier::verify(&proof, root, leaf);
501
502 let multi_result = multi_result.unwrap();
503 prop_assert_eq!(multi_result, regular_result);
504 });
505 }
506
507 #[test]
508 fn zero_length_proof_with_matching_leaf_and_root() {
509 let root = [0u8; 32];
510 let leaf = root;
511 assert!(Verifier::verify(&[], root, leaf));
512 }
513
514 #[test]
515 fn multi_proof_empty_flags() {
516 let root = [0u8; 32];
517 let leaves = vec![[1u8; 32]];
518 let proof = vec![[2u8; 32]];
519
520 let result = Verifier::verify_multi_proof(&proof, &[], root, &leaves);
521 assert!(result.is_err());
522 }
523
524 #[test]
525 fn multi_proof_minimum_valid_case() {
526 let root = [0u8; 32];
527 let leaves = vec![[0u8; 32]];
528 let result = Verifier::verify_multi_proof(&[], &[], root, &leaves);
529 assert!(result.is_ok());
530 }
531
532 #[test]
533 fn verifies_valid_proofs() {
534 // ```js
535 // const merkleTree = StandardMerkleTree.of(
536 // toElements('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='),
537 // ['string'],
538 // );
539 //
540 // const root = merkleTree.root;
541 // const hash = merkleTree.leafHash(['A']);
542 // const proof = merkleTree.getProof(['A']);
543 // ```
544 bytes! {
545 root = "b89eb120147840e813a77109b44063488a346b4ca15686185cf314320560d3f3";
546 leaf_a = "6efbf77e320741a027b50f02224545461f97cd83762d5fbfeb894b9eb3287c16";
547 leaf_b = "7051e21dd45e25ed8c605a53da6f77de151dcbf47b0e3ced3c5d8b61f4a13dbc";
548 };
549 let proof = bytes_array! {
550 "7051e21dd45e25ed8c605a53da6f77de151dcbf47b0e3ced3c5d8b61f4a13dbc",
551 "1629d3b5b09b30449d258e35bbd09dd5e8a3abb91425ef810dc27eef995f7490",
552 "633d21baee4bbe5ed5c51ac0c68f7946b8f28d2937f0ca7ef5e1ea9dbda52e7a",
553 "8a65d3006581737a3bab46d9e4775dbc1821b1ea813d350a13fcd4f15a8942ec",
554 "d6c3f3e36cd23ba32443f6a687ecea44ebfe2b8759a62cccf7759ec1fb563c76",
555 "276141cd72b9b81c67f7182ff8a550b76eb96de9248a3ec027ac048c79649115",
556 };
557
558 let verification = Verifier::verify(&proof, root, leaf_a);
559 assert!(verification);
560
561 let builder = KeccakBuilder.build_hasher();
562 let no_such_leaf = commutative_hash_pair(&leaf_a, &leaf_b, builder);
563 let proof = &proof[1..];
564 let verification = Verifier::verify(proof, root, no_such_leaf);
565 assert!(verification);
566 }
567
568 #[test]
569 fn rejects_invalid_proofs() {
570 // ```js
571 // const correctMerkleTree = StandardMerkleTree.of(toElements('abc'), ['string']);
572 // const otherMerkleTree = StandardMerkleTree.of(toElements('def'), ['string']);
573 //
574 // const root = correctMerkleTree.root;
575 // const leaf = correctMerkleTree.leafHash(['a']);
576 // const proof = otherMerkleTree.getProof(['d']);
577 // ```
578 bytes! {
579 root = "f2129b5a697531ef818f644564a6552b35c549722385bc52aa7fe46c0b5f46b1";
580 leaf = "9c15a6a0eaeed500fd9eed4cbeab71f797cefcc67bfd46683e4d2e6ff7f06d1c";
581 proof = "7b0c6cd04b82bfc0e250030a5d2690c52585e0cc6a4f3bc7909d7723b0236ece";
582 };
583
584 let verification = Verifier::verify(&[proof], root, leaf);
585 assert!(!verification);
586 }
587
588 #[test]
589 fn rejects_proofs_with_invalid_length() {
590 // ```js
591 // const merkleTree = StandardMerkleTree.of(toElements('abc'), ['string']);
592 //
593 // const root = merkleTree.root;
594 // const leaf = merkleTree.leafHash(['a']);
595 // const proof = merkleTree.getProof(['a']);
596 // ```
597 bytes! {
598 root = "f2129b5a697531ef818f644564a6552b35c549722385bc52aa7fe46c0b5f46b1";
599 leaf = "9c15a6a0eaeed500fd9eed4cbeab71f797cefcc67bfd46683e4d2e6ff7f06d1c";
600 };
601 let proof = bytes_array! {
602 "19ba6c6333e0e9a15bf67523e0676e2f23eb8e574092552d5e888c64a4bb3681",
603 "9cf5a63718145ba968a01c1d557020181c5b252f665cf7386d370eddb176517b",
604 };
605
606 let bad_proof = &proof[..1];
607 let verification = Verifier::verify(bad_proof, root, leaf);
608 assert!(!verification);
609 }
610
611 #[test]
612 fn verifies_valid_multi_proof() {
613 // ```js
614 // const merkleTree = StandardMerkleTree.of(toElements('abcdef'), ['string']);
615 //
616 // const root = merkleTree.root;
617 // const { proof, proofFlags, leaves } = merkleTree.getMultiProof(toElements('bdf'));
618 // const hashes = leaves.map(e => merkleTree.leafHash(e));
619 // ```
620 bytes! {
621 root = "6deb52b5da8fd108f79fab00341f38d2587896634c646ee52e49f845680a70c8";
622 };
623 let leaves = bytes_array! {
624 "19ba6c6333e0e9a15bf67523e0676e2f23eb8e574092552d5e888c64a4bb3681",
625 "c62a8cfa41edc0ef6f6ae27a2985b7d39c7fea770787d7e104696c6e81f64848",
626 "eba909cf4bb90c6922771d7f126ad0fd11dfde93f3937a196274e1ac20fd2f5b",
627 };
628 let proof = bytes_array! {
629 "9a4f64e953595df82d1b4f570d34c4f4f0cfaf729a61e9d60e83e579e1aa283e",
630 "8076923e76cf01a7c048400a2304c9a9c23bbbdac3a98ea3946340fdafbba34f",
631 };
632
633 let proof_flags = [false, true, false, true];
634 let verification =
635 Verifier::verify_multi_proof(&proof, &proof_flags, root, &leaves);
636 assert!(verification.unwrap());
637 }
638
639 #[test]
640 fn rejects_invalid_multi_proof() {
641 // ```js
642 // const merkleTree = StandardMerkleTree.of(toElements('abcdef'), ['string']);
643 // const otherMerkleTree = StandardMerkleTree.of(toElements('ghi'), ['string']);
644 //
645 // const root = merkleTree.root;
646 // const { proof, proofFlags, leaves } = otherMerkleTree.getMultiProof(toElements('ghi'));
647 // const hashes = leaves.map(e => merkleTree.leafHash(e));
648 // ```
649 bytes! {
650 root = "6deb52b5da8fd108f79fab00341f38d2587896634c646ee52e49f845680a70c8";
651 };
652 let leaves = bytes_array! {
653 "34e6ce3d0d73f6bff2ee1e865833d58e283570976d70b05f45c989ef651ef742",
654 "aa28358fb75b314c899e16d7975e029d18b4457fd8fd831f2e6c17ffd17a1d7e",
655 "e0fd7e6916ff95d933525adae392a17e247819ebecc2e63202dfec7005c60560",
656 };
657 let proof = [];
658 let proof_flags = [true, true];
659
660 let verification =
661 Verifier::verify_multi_proof(&proof, &proof_flags, root, &leaves);
662 assert!(!verification.unwrap());
663 }
664
665 #[test]
666 fn multi_proof_invalid_total_hashes_length() {
667 // ```js
668 // const merkleTree = StandardMerkleTree.of(toElements('abcd'), ['string']);
669 //
670 // const root = merkleTree.root;
671 // const hashA = merkleTree.leafHash(['a']);
672 // const hashB = merkleTree.leafHash(['b']);
673 // const hashCD = hashPair(
674 // ethers.toBeArray(merkleTree.leafHash(['c'])),
675 // ethers.toBeArray(merkleTree.leafHash(['d'])),
676 // );
677 // const hashE = merkleTree.leafHash(['e']); // incorrect (not part of the tree)
678 // const fill = ethers.randomBytes(32);
679 // ```
680 bytes! {
681 root = "8f7234e8cfe39c08ca84a3a3e3274f574af26fd15165fe29e09cbab742daccd9";
682 hash_a = "9c15a6a0eaeed500fd9eed4cbeab71f797cefcc67bfd46683e4d2e6ff7f06d1c";
683 hash_b = "19ba6c6333e0e9a15bf67523e0676e2f23eb8e574092552d5e888c64a4bb3681";
684 hash_cd = "03707d7802a71ca56a8ad8028da98c4f1dbec55b31b4a25d536b5309cc20eda9";
685 hash_e = "9a4f64e953595df82d1b4f570d34c4f4f0cfaf729a61e9d60e83e579e1aa283e";
686 };
687
688 let mut random_bytes = [0u8; 32];
689 rng().fill_bytes(&mut random_bytes);
690
691 let fill = Bytes32::from(random_bytes);
692 let proof = [hash_b, fill, hash_cd];
693 let proof_flags = [false, false, false];
694 let leaves = [hash_a, hash_e];
695
696 let err =
697 Verifier::verify_multi_proof(&proof, &proof_flags, root, &leaves)
698 .unwrap_err();
699 assert!(matches!(err, MultiProofError::InvalidTotalHashes));
700 }
701
702 #[test]
703 fn multi_proof_invalid_proof_length() {
704 // ```js
705 // const merkleTree = StandardMerkleTree.of(toElements('abcd'), ['string']);
706 //
707 // const root = merkleTree.root;
708 // const hashA = merkleTree.leafHash(['a']);
709 // const hashB = merkleTree.leafHash(['b']);
710 // const hashCD = hashPair(
711 // ethers.toBeArray(merkleTree.leafHash(['c'])),
712 // ethers.toBeArray(merkleTree.leafHash(['d'])),
713 // );
714 // const hashE = merkleTree.leafHash(['e']); // incorrect (not part of the tree)
715 // const fill = ethers.randomBytes(32);
716 // ```
717 bytes! {
718 root = "8f7234e8cfe39c08ca84a3a3e3274f574af26fd15165fe29e09cbab742daccd9";
719 hash_a = "9c15a6a0eaeed500fd9eed4cbeab71f797cefcc67bfd46683e4d2e6ff7f06d1c";
720 hash_b = "19ba6c6333e0e9a15bf67523e0676e2f23eb8e574092552d5e888c64a4bb3681";
721 hash_cd = "03707d7802a71ca56a8ad8028da98c4f1dbec55b31b4a25d536b5309cc20eda9";
722 hash_e = "9a4f64e953595df82d1b4f570d34c4f4f0cfaf729a61e9d60e83e579e1aa283e";
723 };
724
725 let mut random_bytes = [0u8; 32];
726 rng().fill_bytes(&mut random_bytes);
727
728 let fill = Bytes32::from(random_bytes);
729 let proof = [hash_b, fill, hash_cd];
730 let proof_flags = [false, false, false, false];
731 let leaves = [hash_e, hash_a];
732
733 let err =
734 Verifier::verify_multi_proof(&proof, &proof_flags, root, &leaves)
735 .unwrap_err();
736 assert!(matches!(err, MultiProofError::InvalidProofLength));
737 }
738
739 #[test]
740 fn verifies_empty_leaves_multi_proof() {
741 // ```js
742 // const merkleTree = StandardMerkleTree.of(toElements('abcd'), ['string']);
743 //
744 // const root = merkleTree.root;
745 // ```
746 bytes!(root = "8f7234e8cfe39c08ca84a3a3e3274f574af26fd15165fe29e09cbab742daccd9");
747 let proof = [root];
748 let proof_flags = [];
749 let leaves = [];
750
751 let verification =
752 Verifier::verify_multi_proof(&proof, &proof_flags, root, &leaves);
753 assert!(verification.unwrap());
754 }
755
756 #[test]
757 /// Errors when processing manipulated proofs with a zero-value node at
758 /// depth 1.
759 fn errors_manipulated_multi_proof() {
760 // Create a merkle tree that contains a zero leaf at depth 1
761 //
762 // Taken from https://github.com/advisories/GHSA-wprv-93r4-jj2p
763 //
764 // ```js
765 // const { MerkleTree } = require('merkletreejs'); // v0.2.32
766 // const keccak256 = require('keccak256'); // v1.0.6
767 //
768 // const leaves = [keccak256('real leaf'), Buffer.alloc(32, 0)];
769 // const merkleTree = new MerkleTree(leaves, keccak256, { sortPairs: true });
770 // const root = merkleTree.getRoot();
771 // ```
772 bytes! {
773 root = "f2d552e1e4c59d4f0fa2b80859febc9e4bdc915dff37c56c858550d8b64659a5";
774 leaf = "5e941ddd8f313c0b39f92562c0eca709c3d91360965d396aaef584b3fa76889a"; // 'real leaf'
775 };
776 let malicious_leaves = bytes_array! {
777 "1f23ad5fc0ee6ccbe2f3d30df856758f05ad9d03408a51a99c1c9f0854309db2",
778 "4e7e8301f5d206748d1c4f822e3564ddb1124f86591a839f58dfc2f007983b61",
779 "613994f4e324d0667c07857cd5d147994bc917da5d07ee63fc3f0a1fe8a18e34",
780 };
781 let malicious_proof = [leaf, leaf];
782 let malicious_proof_flags = [true, true, false];
783
784 let verification = Verifier::verify_multi_proof(
785 &malicious_proof,
786 &malicious_proof_flags,
787 root,
788 &malicious_leaves,
789 );
790 assert!(verification.is_err());
791 }
792
793 #[test]
794 fn verify_empty_proof_should_mean_leaf_equal_to_root() {
795 // ```js
796 // const merkleTree = StandardMerkleTree.of(toElements('abc'), ['string']);
797 //
798 // const root = merkleTree.root;
799 // const leaf = merkleTree.leafHash(['a']);
800 // const proof = merkleTree.getProof(['a']);
801 // ```
802 bytes! {
803 root = "f2129b5a697531ef818f644564a6552b35c549722385bc52aa7fe46c0b5f46b1";
804 leaf = "9c15a6a0eaeed500fd9eed4cbeab71f797cefcc67bfd46683e4d2e6ff7f06d1c";
805 };
806 let proof = [];
807
808 // valid if root == leaf
809 assert!(Verifier::verify(&proof, root, root));
810
811 // invalid if root != leaf
812 assert!(!Verifier::verify(&proof, root, leaf));
813 }
814}