From 0bc1ca0ef410f74e2e403436d16743c7466b6a9b Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Wed, 14 Aug 2024 01:27:37 +0200 Subject: [PATCH 01/29] feat: add main logic --- packages/utils/src/common.cairo | 27 ++++ packages/utils/src/cryptography.cairo | 2 + packages/utils/src/cryptography/hashes.cairo | 41 +++++ .../utils/src/cryptography/merkle_proof.cairo | 142 ++++++++++++++++++ packages/utils/src/lib.cairo | 3 +- 5 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 packages/utils/src/common.cairo create mode 100644 packages/utils/src/cryptography/hashes.cairo create mode 100644 packages/utils/src/cryptography/merkle_proof.cairo diff --git a/packages/utils/src/common.cairo b/packages/utils/src/common.cairo new file mode 100644 index 000000000..960474210 --- /dev/null +++ b/packages/utils/src/common.cairo @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.15.0 (utils/common.cairo) + +use core::traits::PartialOrd; + +pub impl Felt252PartialOrd of PartialOrd { + #[inline(always)] + fn le(lhs: felt252, rhs: felt252) -> bool { + let lhs: u256 = lhs.into(); + lhs <= rhs.into() + } + #[inline(always)] + fn ge(lhs: felt252, rhs: felt252) -> bool { + let lhs: u256 = lhs.into(); + lhs >= rhs.into() + } + #[inline(always)] + fn lt(lhs: felt252, rhs: felt252) -> bool { + let lhs: u256 = lhs.into(); + lhs < rhs.into() + } + #[inline(always)] + fn gt(lhs: felt252, rhs: felt252) -> bool { + let lhs: u256 = lhs.into(); + lhs > rhs.into() + } +} diff --git a/packages/utils/src/cryptography.cairo b/packages/utils/src/cryptography.cairo index 99d2f79b5..0c7fe5ae7 100644 --- a/packages/utils/src/cryptography.cairo +++ b/packages/utils/src/cryptography.cairo @@ -1,3 +1,5 @@ +pub mod hashes; pub mod interface; +pub mod merkle_proof; pub mod nonces; pub mod snip12; diff --git a/packages/utils/src/cryptography/hashes.cairo b/packages/utils/src/cryptography/hashes.cairo new file mode 100644 index 000000000..974068bb8 --- /dev/null +++ b/packages/utils/src/cryptography/hashes.cairo @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.15.0 (utils/cryptography/hashes.cairo) + +use core::pedersen::pedersen; +use core::poseidon::poseidon_hash_span; +use openzeppelin_utils::common::Felt252PartialOrd; + +/// Commutative hash of a sorted pair of felt252. +/// +/// This is usually implemented as an extension of a non-commutative hash function, like +/// Pedersen or Poseidon, returning the hash of the concatenation of the two values by first +/// sorting them. +/// +/// Frequently used when working with merkle proofs. +pub trait CommutativeHash { + fn commutative_hash(a: felt252, b: felt252) -> felt252; +} + +/// Pedersen commutative hash of a sorted pair of felt252. +pub impl Pedersen of CommutativeHash { + /// Computes the Pedersen hash of the concatenation of two values, sorting the pair first. + fn commutative_hash(a: felt252, b: felt252) -> felt252 { + if a < b { + pedersen(a, b) + } else { + pedersen(b, a) + } + } +} + +/// Poseidon commutative hash of a sorted pair of felt252. +pub impl Poseidon of CommutativeHash { + /// Computes the Poseidon hash of the concatenation of two values, sorting the pair first. + fn commutative_hash(a: felt252, b: felt252) -> felt252 { + if a < b { + poseidon_hash_span([a, b].span()) + } else { + poseidon_hash_span([b, a].span()) + } + } +} diff --git a/packages/utils/src/cryptography/merkle_proof.cairo b/packages/utils/src/cryptography/merkle_proof.cairo new file mode 100644 index 000000000..304fc4d6e --- /dev/null +++ b/packages/utils/src/cryptography/merkle_proof.cairo @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts for Cairo v0.15.0 (utils/cryptography/merkle_proof.cairo) + +/// These functions deal with verification of Merkle Tree proofs. +/// +/// WARNING: You should avoid using leaf values that are 62 bytes long prior to +/// hashing, or use a different hash function for hashing leaves and pre-images. +/// This is because the concatenation of a sorted pair of internal nodes in +/// the Merkle tree could be reinterpreted as a leaf value. +/// +/// NOTE: This library supports proof verification for merkle trees built using +/// custom _commutative_ hashing functions (i.e. `H(a, b) == H(b, a)`). Proving +/// leaf inclusion in trees built using non-commutative hashing functions requires +/// additional logic that is not supported by this library. + +use openzeppelin_utils::cryptography::hashes::{CommutativeHash, Pedersen, Poseidon}; + +/// Version of `verify` using perdersen as the hashing function. +pub fn verify_pedersen(proof: Span, root: felt252, leaf: felt252) -> bool { + verify::(proof, root, leaf) +} + +/// Version of `verify` using poseidon as the hashing function. +pub fn verify_poseidon(proof: Span, root: felt252, leaf: felt252) -> bool { + verify::(proof, root, leaf) +} + +/// Returns true if a `leaf` can be proved to be a part of a Merkle tree +/// defined by `root`. For this, a `proof` must be provided, containing +/// sibling hashes on the branch from the leaf to the root of the tree. Each +/// pair of leaves and each pair of pre-images are assumed to be sorted. +pub fn verify( + proof: Span, root: felt252, leaf: felt252 +) -> bool { + process_proof::(proof, leaf) == root +} + +/// Returns the rebuilt hash obtained by traversing a Merkle tree up +/// from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt +/// hash matches the root of the tree. When processing the proof, the pairs +/// of leaves & pre-images are assumed to be sorted. +pub fn process_proof(proof: Span, leaf: felt252) -> felt252 { + let mut computed_hash = leaf; + for hash in proof { + computed_hash = Hash::commutative_hash(computed_hash, *hash); + }; + computed_hash +} + +/// Returns True if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined +/// by `root`, according to `proof` and `proof_flags` as described in `process_multi_proof`. +/// +/// CAUTION: Not all Merkle trees admit multiproofs. See `process_multi_proof` for details. +/// +/// NOTE: Consider the case where `root == proof[0] && leaves.len() == 0` as it will return `True`. +/// The `leaves` must be validated independently. See `process_multi_proof`. +fn verify_multi_proof( + proof: Span, proof_flags: Span, root: felt252, leaves: Span +) -> bool { + process_multi_proof::(proof, proof_flags, leaves) == root +} + +/// Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The +/// reconstruction proceeds by incrementally reconstructing all inner nodes by combining a +/// leaf/inner node with either another leaf/inner node or a proof sibling node, depending on +/// whether each `proof_flags` item is true or false respectively. +/// +/// CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure +/// that: 1) the tree is complete (but not necessarily perfect), 2) the leaves to be proven are in +/// the opposite order they are in the tree (i.e., as seen from right to left starting at the +/// deepest layer and continuing at the next layer). +/// +/// NOTE: The _empty set_ (i.e. the case where `proof.len() == 1 && leaves.len() == 0`) is +/// considered a no-op, and therefore a valid multiproof (i.e. it returns `proof.at(0)`). Consider +/// disallowing this case if you're not validating the leaves elsewhere. +pub fn process_multi_proof( + proof: Span, proof_flags: Span, leaves: Span +) -> felt252 { + // This function rebuilds the root hash by traversing the tree up from the leaves. The root is + // rebuilt by consuming and producing values on a queue. The queue starts with the `leaves` + // span, then goes onto the `hashes` span. At the end of the process, the last hash in the + // `hashes` span should contain the root of the Merkle tree. + let leaves_len = leaves.len(); + let proof_flags_len = proof_flags.len(); + + // Check proof validity. + if (leaves_len + proof.len() != proof_flags_len + 1) { + panic!("MerkleProof: invalid multi proof"); + } + + // The x_pos values are "pointers" to the next value to consume in each array. + // By incrementing the value we simulate a queue's pop operation. + let mut hashes = array![]; + let mut leaf_pos = 0; + let mut hash_pos = 0; + let mut proof_pos = 0; + let mut i = 0; + + // At each step, we compute the next hash using two values: + // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, + // otherwise we get the next hash. + // - depending on the flag, either another value from the "main queue" (merging branches) or an + // element from the `proof` array. + while i < proof_flags_len { + let a = if leaf_pos < leaves_len { + leaf_pos += 1; + leaves.at(leaf_pos - 1) + } else { + hash_pos += 1; + hashes.at(hash_pos - 1) + }; + + let b = if *proof_flags.at(i) { + if leaf_pos < leaves_len { + leaf_pos += 1; + leaves.at(leaf_pos - 1) + } else { + hash_pos += 1; + hashes.at(hash_pos - 1) + } + } else { + proof_pos += 1; + proof.at(proof_pos - 1) + }; + + hashes.append(Hash::commutative_hash(*a, *b)); + i += 1; + }; + + let root = if proof_flags_len > 0 { + if proof_pos != proof.len() { + panic!("MerkleProof: invalid multi proof"); + } + hashes.at(proof_flags_len - 1) + } else if leaves_len > 0 { + leaves.at(0) + } else { + proof.at(0) + }; + + *root +} diff --git a/packages/utils/src/lib.cairo b/packages/utils/src/lib.cairo index 8161615dc..8b1593e7e 100644 --- a/packages/utils/src/lib.cairo +++ b/packages/utils/src/lib.cairo @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.15.0 (utils.cairo) +// OpenZeppelin Contracts for Cairo v0.15.0 (utils/utils.cairo) +pub mod common; pub mod cryptography; pub mod deployments; pub mod interfaces; From 3199da1d047b775de55cc8fe8be85f1bd050d082 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Wed, 14 Aug 2024 14:47:43 +0200 Subject: [PATCH 02/29] feat: add hashes tests --- packages/utils/src/cryptography/hashes.cairo | 12 +++--- .../utils/src/cryptography/merkle_proof.cairo | 24 ++++++----- packages/utils/src/deployments.cairo | 2 +- packages/utils/src/tests.cairo | 1 + packages/utils/src/tests/test_hashes.cairo | 40 +++++++++++++++++++ 5 files changed, 61 insertions(+), 18 deletions(-) create mode 100644 packages/utils/src/tests/test_hashes.cairo diff --git a/packages/utils/src/cryptography/hashes.cairo b/packages/utils/src/cryptography/hashes.cairo index 974068bb8..362a98459 100644 --- a/packages/utils/src/cryptography/hashes.cairo +++ b/packages/utils/src/cryptography/hashes.cairo @@ -5,19 +5,19 @@ use core::pedersen::pedersen; use core::poseidon::poseidon_hash_span; use openzeppelin_utils::common::Felt252PartialOrd; -/// Commutative hash of a sorted pair of felt252. +/// Computes a commutative hash of a sorted pair of felt252. /// /// This is usually implemented as an extension of a non-commutative hash function, like /// Pedersen or Poseidon, returning the hash of the concatenation of the two values by first /// sorting them. /// /// Frequently used when working with merkle proofs. -pub trait CommutativeHash { +pub trait CommutativeHasher { fn commutative_hash(a: felt252, b: felt252) -> felt252; } -/// Pedersen commutative hash of a sorted pair of felt252. -pub impl Pedersen of CommutativeHash { +/// Computes Pedersen's commutative hash of a sorted pair of felt252. +pub impl PedersenCHasher of CommutativeHasher { /// Computes the Pedersen hash of the concatenation of two values, sorting the pair first. fn commutative_hash(a: felt252, b: felt252) -> felt252 { if a < b { @@ -28,8 +28,8 @@ pub impl Pedersen of CommutativeHash { } } -/// Poseidon commutative hash of a sorted pair of felt252. -pub impl Poseidon of CommutativeHash { +/// Computes Poseidon's commutative hash of a sorted pair of felt252. +pub impl PoseidonCHasher of CommutativeHasher { /// Computes the Poseidon hash of the concatenation of two values, sorting the pair first. fn commutative_hash(a: felt252, b: felt252) -> felt252 { if a < b { diff --git a/packages/utils/src/cryptography/merkle_proof.cairo b/packages/utils/src/cryptography/merkle_proof.cairo index 304fc4d6e..e9929bb33 100644 --- a/packages/utils/src/cryptography/merkle_proof.cairo +++ b/packages/utils/src/cryptography/merkle_proof.cairo @@ -13,36 +13,38 @@ /// leaf inclusion in trees built using non-commutative hashing functions requires /// additional logic that is not supported by this library. -use openzeppelin_utils::cryptography::hashes::{CommutativeHash, Pedersen, Poseidon}; +use openzeppelin_utils::cryptography::hashes::{CommutativeHasher, PedersenCHasher, PoseidonCHasher}; /// Version of `verify` using perdersen as the hashing function. pub fn verify_pedersen(proof: Span, root: felt252, leaf: felt252) -> bool { - verify::(proof, root, leaf) + verify::(proof, root, leaf) } /// Version of `verify` using poseidon as the hashing function. pub fn verify_poseidon(proof: Span, root: felt252, leaf: felt252) -> bool { - verify::(proof, root, leaf) + verify::(proof, root, leaf) } /// Returns true if a `leaf` can be proved to be a part of a Merkle tree /// defined by `root`. For this, a `proof` must be provided, containing /// sibling hashes on the branch from the leaf to the root of the tree. Each /// pair of leaves and each pair of pre-images are assumed to be sorted. -pub fn verify( +pub fn verify( proof: Span, root: felt252, leaf: felt252 ) -> bool { - process_proof::(proof, leaf) == root + process_proof::(proof, leaf) == root } /// Returns the rebuilt hash obtained by traversing a Merkle tree up /// from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt /// hash matches the root of the tree. When processing the proof, the pairs /// of leaves & pre-images are assumed to be sorted. -pub fn process_proof(proof: Span, leaf: felt252) -> felt252 { +pub fn process_proof( + proof: Span, leaf: felt252 +) -> felt252 { let mut computed_hash = leaf; for hash in proof { - computed_hash = Hash::commutative_hash(computed_hash, *hash); + computed_hash = Hasher::commutative_hash(computed_hash, *hash); }; computed_hash } @@ -54,10 +56,10 @@ pub fn process_proof(proof: Span, leaf: fel /// /// NOTE: Consider the case where `root == proof[0] && leaves.len() == 0` as it will return `True`. /// The `leaves` must be validated independently. See `process_multi_proof`. -fn verify_multi_proof( +fn verify_multi_proof( proof: Span, proof_flags: Span, root: felt252, leaves: Span ) -> bool { - process_multi_proof::(proof, proof_flags, leaves) == root + process_multi_proof::(proof, proof_flags, leaves) == root } /// Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The @@ -73,7 +75,7 @@ fn verify_multi_proof( /// NOTE: The _empty set_ (i.e. the case where `proof.len() == 1 && leaves.len() == 0`) is /// considered a no-op, and therefore a valid multiproof (i.e. it returns `proof.at(0)`). Consider /// disallowing this case if you're not validating the leaves elsewhere. -pub fn process_multi_proof( +pub fn process_multi_proof( proof: Span, proof_flags: Span, leaves: Span ) -> felt252 { // This function rebuilds the root hash by traversing the tree up from the leaves. The root is @@ -123,7 +125,7 @@ pub fn process_multi_proof( proof.at(proof_pos - 1) }; - hashes.append(Hash::commutative_hash(*a, *b)); + hashes.append(Hasher::commutative_hash(*a, *b)); i += 1; }; diff --git a/packages/utils/src/deployments.cairo b/packages/utils/src/deployments.cairo index d77ed2f37..b63a5fea2 100644 --- a/packages/utils/src/deployments.cairo +++ b/packages/utils/src/deployments.cairo @@ -19,8 +19,8 @@ pub const CONTRACT_ADDRESS_PREFIX: felt252 = 'STARKNET_CONTRACT_ADDRESS'; /// Returns the contract address from a `deploy_syscall`. /// `deployer_address` should be the zero address if the deployment is origin-independent (deployed /// from zero). -/// For more information, see /// +/// For more information, see /// https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/contract-address/ pub fn calculate_contract_address_from_deploy_syscall( salt: felt252, diff --git a/packages/utils/src/tests.cairo b/packages/utils/src/tests.cairo index 37cf414d3..4889b8f35 100644 --- a/packages/utils/src/tests.cairo +++ b/packages/utils/src/tests.cairo @@ -1,4 +1,5 @@ pub(crate) mod mocks; +mod test_hashes; mod test_nonces; mod test_snip12; diff --git a/packages/utils/src/tests/test_hashes.cairo b/packages/utils/src/tests/test_hashes.cairo new file mode 100644 index 000000000..0b81348e9 --- /dev/null +++ b/packages/utils/src/tests/test_hashes.cairo @@ -0,0 +1,40 @@ +use core::pedersen::pedersen; +use core::poseidon::poseidon_hash_span; +use openzeppelin_utils::cryptography::hashes::{PedersenCHasher, PoseidonCHasher}; + +#[test] +fn test_pedersen_commutative_hash_is_commutative() { + let a = 'a'; + let b = 'b'; + let hash = PedersenCHasher::commutative_hash(a, b); + assert_eq!(hash, PedersenCHasher::commutative_hash(b, a)); +} + +#[test] +fn test_pedersen_commutative_hash_smaller_first() { + let a = 'a'; + let b = 'b'; + + // Expected hash is pedersen(a, b), since a < b + let hash = PedersenCHasher::commutative_hash(b, a); + assert_eq!(hash, pedersen(a, b)); +} + +#[test] +fn test_poseidon_commutative_hash_is_commutative() { + let a = 'a'; + let b = 'b'; + let hash = PoseidonCHasher::commutative_hash(a, b); + assert_eq!(hash, PoseidonCHasher::commutative_hash(b, a)); +} + + +#[test] +fn test_poseidon_commutative_hash_smaller_first() { + let a = 'a'; + let b = 'b'; + + // Expected hash is pedersen(a, b), since a < b + let hash = PoseidonCHasher::commutative_hash(a, b); + assert_eq!(hash, poseidon_hash_span([a, b].span())); +} From c3453759fc77f72c8c7ee406e1ef7e98ab1bacb5 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Thu, 15 Aug 2024 16:28:38 +0200 Subject: [PATCH 03/29] feat: add some tests and update hashes --- Scarb.toml | 6 +- packages/utils/src/cryptography/hashes.cairo | 11 ++- packages/utils/src/tests.cairo | 1 + packages/utils/src/tests/test_hashes.cairo | 9 +- .../utils/src/tests/test_merkle_proof.cairo | 96 +++++++++++++++++++ 5 files changed, 113 insertions(+), 10 deletions(-) create mode 100644 packages/utils/src/tests/test_merkle_proof.cairo diff --git a/Scarb.toml b/Scarb.toml index 20a5c15f5..811ac03e3 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -20,8 +20,8 @@ version.workspace = true [workspace.package] version = "0.15.0" edition = "2023_11" -cairo-version = "2.7.0" -scarb-version = "2.7.0" +cairo-version = "2.7.1" +scarb-version = "2.7.1" authors = ["OpenZeppelin Community "] description = "OpenZeppelin Contracts written in Cairo for Starknet, a decentralized ZK Rollup" documentation = "https://docs.openzeppelin.com/contracts-cairo" @@ -38,7 +38,7 @@ keywords = [ ] [workspace.dependencies] -starknet = "2.7.0" +starknet = "2.7.1" snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.26.0" } [dependencies] diff --git a/packages/utils/src/cryptography/hashes.cairo b/packages/utils/src/cryptography/hashes.cairo index 362a98459..9566b9099 100644 --- a/packages/utils/src/cryptography/hashes.cairo +++ b/packages/utils/src/cryptography/hashes.cairo @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.15.0 (utils/cryptography/hashes.cairo) -use core::pedersen::pedersen; +use core::hash::{HashStateTrait}; +use core::pedersen::PedersenTrait; use core::poseidon::poseidon_hash_span; use openzeppelin_utils::common::Felt252PartialOrd; @@ -18,12 +19,14 @@ pub trait CommutativeHasher { /// Computes Pedersen's commutative hash of a sorted pair of felt252. pub impl PedersenCHasher of CommutativeHasher { - /// Computes the Pedersen hash of the concatenation of two values, sorting the pair first. + /// Computes the Pedersen hash of chaining the two values + /// with the len, sorting the pair first. fn commutative_hash(a: felt252, b: felt252) -> felt252 { + let hash_state = PedersenTrait::new(0); if a < b { - pedersen(a, b) + hash_state.update(a).update(b).update(2).finalize() } else { - pedersen(b, a) + hash_state.update(b).update(a).update(2).finalize() } } } diff --git a/packages/utils/src/tests.cairo b/packages/utils/src/tests.cairo index 4889b8f35..9421ce87b 100644 --- a/packages/utils/src/tests.cairo +++ b/packages/utils/src/tests.cairo @@ -1,5 +1,6 @@ pub(crate) mod mocks; mod test_hashes; +mod test_merkle_proof; mod test_nonces; mod test_snip12; diff --git a/packages/utils/src/tests/test_hashes.cairo b/packages/utils/src/tests/test_hashes.cairo index 0b81348e9..c83423254 100644 --- a/packages/utils/src/tests/test_hashes.cairo +++ b/packages/utils/src/tests/test_hashes.cairo @@ -1,4 +1,5 @@ -use core::pedersen::pedersen; +use core::hash::{HashStateTrait}; +use core::pedersen::PedersenTrait; use core::poseidon::poseidon_hash_span; use openzeppelin_utils::cryptography::hashes::{PedersenCHasher, PoseidonCHasher}; @@ -15,9 +16,11 @@ fn test_pedersen_commutative_hash_smaller_first() { let a = 'a'; let b = 'b'; - // Expected hash is pedersen(a, b), since a < b + let hash_state = PedersenTrait::new(0); + let expected = hash_state.update(a).update(b).update(2).finalize(); + let hash = PedersenCHasher::commutative_hash(b, a); - assert_eq!(hash, pedersen(a, b)); + assert_eq!(hash, expected); } #[test] diff --git a/packages/utils/src/tests/test_merkle_proof.cairo b/packages/utils/src/tests/test_merkle_proof.cairo new file mode 100644 index 000000000..12d52a8d8 --- /dev/null +++ b/packages/utils/src/tests/test_merkle_proof.cairo @@ -0,0 +1,96 @@ +use core::hash::{HashStateTrait, HashStateExTrait}; + +use core::pedersen::PedersenTrait; +use core::pedersen::pedersen; +use openzeppelin_utils::cryptography::hashes::PedersenCHasher; +use openzeppelin_utils::cryptography::merkle_proof::{process_proof, verify, verify_pedersen}; +use starknet::{ContractAddress, contract_address_const}; + +#[derive(Serde, Copy, Drop, Hash)] +struct Leaf { + address: ContractAddress, + amount: u128, +} + +fn LEAVES() -> Span { + [ + Leaf { + address: contract_address_const::< + 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8 + >(), + amount: 0xfc104e31d098d1ab488fc1acaeb0269 + }, + Leaf { + address: contract_address_const::< + 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffc66ca5c000 + >(), + amount: 0xfc104e31d098d1ab488fc1acaeb0269 + }, + Leaf { + address: contract_address_const::< + 0x6a1f098854799debccf2d3c4059ff0f02dbfef6673dc1fcbfffffffffffffc8 + >(), + amount: 0xfc104e31d098d1ab488fc1acaeb0269 + }, + Leaf { + address: contract_address_const::< + 0xfa6541b7909bfb5e8585f1222fcf272eea352c7e0e8ed38c988bd1e2a85e82 + >(), + amount: 0xaa8565d732c2c9fa5f6c001d89d5c219 + }, + ].span() +} + +// +// verify +// + +#[test] +fn test_valid_merkle_proof() { + let leaves = LEAVES(); + let hash = leaf_hash(*leaves.at(0)); + // `root` and `proof` were computed using @ericnordelo/strk-merkle-tree + let root = 0x02a40717603180fa52f40a55508cd360d301840f3e502665cf0132ef412911de; + let proof = [ + 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, + 0x02b0ee474cf2ab27501e54a661d17ac1dc162571c111fe2455d09fe23471099e + ].span(); + + assert_eq!(process_proof::(proof, hash), root); + assert!(verify::(proof, root, hash)); + assert!(verify_pedersen(proof, root, hash)); + + // For demonstration, it is also possible to create valid + // proofs for certain values *NOT* in elements: + let hash_state = PedersenTrait::new(0); + let no_such_leaf = hash_state.update_with(hash).update_with(*proof.at(0)).update(2).finalize(); + let second_proof = [0x02b0ee474cf2ab27501e54a661d17ac1dc162571c111fe2455d09fe23471099e].span(); + + assert_eq!(process_proof::(second_proof, no_such_leaf), root); + assert!(verify::(second_proof, root, no_such_leaf)); + assert!(verify_pedersen(second_proof, root, no_such_leaf)); +} + +#[test] +fn test_invalid_merkle_proof() { + let leaves = LEAVES(); + let hash = leaf_hash(*leaves.at(0)); + // `root` was computed using @ericnordelo/strk-merkle-tree + let root = 0x02a40717603180fa52f40a55508cd360d301840f3e502665cf0132ef412911de; + let invalid_proof = [ + 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, 'invalid' + ].span(); + + assert!(process_proof::(invalid_proof, hash) != root); + assert!(!verify::(invalid_proof, root, hash)); + assert!(!verify_pedersen(invalid_proof, root, hash)); +} + +// +// Helpers +// + +fn leaf_hash(leaf: Leaf) -> felt252 { + let hash_state = PedersenTrait::new(0); + pedersen(0, hash_state.update_with(leaf).update(2).finalize()) +} From c564b66f79c7a0357d90db3f0ac1e9a1c4776234 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Thu, 15 Aug 2024 17:39:50 +0200 Subject: [PATCH 04/29] feat: add tests for multi proofs --- .../utils/src/cryptography/merkle_proof.cairo | 7 +- .../utils/src/tests/test_merkle_proof.cairo | 154 +++++++++++++++++- 2 files changed, 159 insertions(+), 2 deletions(-) diff --git a/packages/utils/src/cryptography/merkle_proof.cairo b/packages/utils/src/cryptography/merkle_proof.cairo index e9929bb33..6bd0b42ac 100644 --- a/packages/utils/src/cryptography/merkle_proof.cairo +++ b/packages/utils/src/cryptography/merkle_proof.cairo @@ -56,7 +56,7 @@ pub fn process_proof( /// /// NOTE: Consider the case where `root == proof[0] && leaves.len() == 0` as it will return `True`. /// The `leaves` must be validated independently. See `process_multi_proof`. -fn verify_multi_proof( +pub fn verify_multi_proof( proof: Span, proof_flags: Span, root: felt252, leaves: Span ) -> bool { process_multi_proof::(proof, proof_flags, leaves) == root @@ -130,11 +130,16 @@ pub fn process_multi_proof( }; let root = if proof_flags_len > 0 { + // If `proof_flags` is not empty, assert that every + // proof was used in the validation process. if proof_pos != proof.len() { + // TODO: check if this is not unrechable code. panic!("MerkleProof: invalid multi proof"); } hashes.at(proof_flags_len - 1) } else if leaves_len > 0 { + // If `proof_flags_len` is zero, and `leaves_len` is greater then zero, + // then `leaves_len` can only be 1, because of the proof validity check. leaves.at(0) } else { proof.at(0) diff --git a/packages/utils/src/tests/test_merkle_proof.cairo b/packages/utils/src/tests/test_merkle_proof.cairo index 12d52a8d8..9658e8f66 100644 --- a/packages/utils/src/tests/test_merkle_proof.cairo +++ b/packages/utils/src/tests/test_merkle_proof.cairo @@ -3,7 +3,9 @@ use core::hash::{HashStateTrait, HashStateExTrait}; use core::pedersen::PedersenTrait; use core::pedersen::pedersen; use openzeppelin_utils::cryptography::hashes::PedersenCHasher; -use openzeppelin_utils::cryptography::merkle_proof::{process_proof, verify, verify_pedersen}; +use openzeppelin_utils::cryptography::merkle_proof::{ + process_proof, process_multi_proof, verify, verify_multi_proof, verify_pedersen +}; use starknet::{ContractAddress, contract_address_const}; #[derive(Serde, Copy, Drop, Hash)] @@ -86,6 +88,156 @@ fn test_invalid_merkle_proof() { assert!(!verify_pedersen(invalid_proof, root, hash)); } +// +// multi_proof_verify +// + +#[test] +fn test_valid_merkle_multi_proof() { + let leaves = LEAVES(); + let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1))].span(); + // `root`, `proof`, and `proof_flags` were computed using @ericnordelo/strk-merkle-tree + let root = 0x02a40717603180fa52f40a55508cd360d301840f3e502665cf0132ef412911de; + let proof = [ + 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, + 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b + ].span(); + let proof_flags = [false, false, true].span(); + + assert_eq!(process_multi_proof::(proof, proof_flags, leaves_to_prove), root); + assert!(verify_multi_proof::(proof, proof_flags, root, leaves_to_prove)); +} + +#[test] +fn test_invalid_merkle_multi_proof() { + let leaves = LEAVES(); + let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1))].span(); + // `root` and `proof_flags` were computed using @ericnordelo/strk-merkle-tree + let root = 0x02a40717603180fa52f40a55508cd360d301840f3e502665cf0132ef412911de; + let proof = [ + 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, 'invalid' + ].span(); + let proof_flags = [false, false, true].span(); + + assert!(process_multi_proof::(proof, proof_flags, leaves_to_prove) != root); + assert!(!verify_multi_proof::(proof, proof_flags, root, leaves_to_prove)); +} + +#[test] +fn test_invalid_merkle_multi_proof_flags() { + let leaves = LEAVES(); + let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1))].span(); + // `root` and `proof` were computed using @ericnordelo/strk-merkle-tree + let root = 0x02a40717603180fa52f40a55508cd360d301840f3e502665cf0132ef412911de; + let proof = [ + 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, + 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b + ].span(); + let proof_flags = [false, true, false].span(); + + assert!(process_multi_proof::(proof, proof_flags, leaves_to_prove) != root); + assert!(!verify_multi_proof::(proof, proof_flags, root, leaves_to_prove)); +} + +#[test] +#[should_panic(expected: ("MerkleProof: invalid multi proof",))] +fn test_process_multi_proof_invalid_len_proof_flags_panics() { + let leaves = LEAVES(); + let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1))].span(); + let proof = [ + 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, + 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b + ].span(); + + // `proof_flags.len()` is not equal to `proof.len() + leaves_to_prove.len() + 1` + let proof_flags = [true, false].span(); + + process_multi_proof::(proof, proof_flags, leaves_to_prove); +} + +#[test] +#[should_panic(expected: ("MerkleProof: invalid multi proof",))] +fn test_verify_multi_proof_invalid_len_proof_flags_panics() { + let leaves = LEAVES(); + let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1))].span(); + let root = 0x02a40717603180fa52f40a55508cd360d301840f3e502665cf0132ef412911de; + let proof = [ + 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, + 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b + ].span(); + + // `proof_flags.len()` is not equal to `proof.len() + leaves_to_prove.len() + 1` + let proof_flags = [true, false].span(); + + verify_multi_proof::(proof, proof_flags, root, leaves_to_prove); +} + +#[test] +#[should_panic(expected: ('Index out of bounds',))] +fn test_process_multi_proof_flags_extra_leaves_expected() { + let leaves = LEAVES(); + let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2))].span(); + let proof = [ + 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, + 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b + ].span(); + + // For each true one leaf is expected + let proof_flags = [true, true, true, true].span(); + + process_multi_proof::(proof, proof_flags, leaves_to_prove); +} + +#[test] +#[should_panic(expected: ('Index out of bounds',))] +fn test_process_multi_proof_flags_extra_proofs_expected() { + let leaves = LEAVES(); + let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2))].span(); + let proof = [ + 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, + 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b + ].span(); + + // For each false one leaf is expected + let proof_flags = [true, false, false, false].span(); + + process_multi_proof::(proof, proof_flags, leaves_to_prove); +} + +#[test] +#[should_panic(expected: ('Index out of bounds',))] +fn test_verify_multi_proof_flags_extra_leaves_expected() { + let leaves = LEAVES(); + let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2))].span(); + let root = 0x02a40717603180fa52f40a55508cd360d301840f3e502665cf0132ef412911de; + let proof = [ + 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, + 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b + ].span(); + + // For each true one leaf is expected + let proof_flags = [true, true, true, true].span(); + + verify_multi_proof::(proof, proof_flags, root, leaves_to_prove); +} + +#[test] +#[should_panic(expected: ('Index out of bounds',))] +fn test_verify_multi_proof_flags_extra_proofs_expected() { + let leaves = LEAVES(); + let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2))].span(); + let root = 0x02a40717603180fa52f40a55508cd360d301840f3e502665cf0132ef412911de; + let proof = [ + 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, + 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b + ].span(); + + // For each false one leaf is expected + let proof_flags = [true, false, false, false].span(); + + verify_multi_proof::(proof, proof_flags, root, leaves_to_prove); +} + // // Helpers // From 69f1ef3dca48c0d73abdbcd2d003f71750947a73 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Thu, 15 Aug 2024 17:40:22 +0200 Subject: [PATCH 05/29] feat: format files --- packages/utils/src/tests/test_merkle_proof.cairo | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/utils/src/tests/test_merkle_proof.cairo b/packages/utils/src/tests/test_merkle_proof.cairo index 9658e8f66..3d2b0ec08 100644 --- a/packages/utils/src/tests/test_merkle_proof.cairo +++ b/packages/utils/src/tests/test_merkle_proof.cairo @@ -176,7 +176,9 @@ fn test_verify_multi_proof_invalid_len_proof_flags_panics() { #[should_panic(expected: ('Index out of bounds',))] fn test_process_multi_proof_flags_extra_leaves_expected() { let leaves = LEAVES(); - let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2))].span(); + let leaves_to_prove = [ + leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2)) + ].span(); let proof = [ 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b @@ -192,7 +194,9 @@ fn test_process_multi_proof_flags_extra_leaves_expected() { #[should_panic(expected: ('Index out of bounds',))] fn test_process_multi_proof_flags_extra_proofs_expected() { let leaves = LEAVES(); - let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2))].span(); + let leaves_to_prove = [ + leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2)) + ].span(); let proof = [ 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b @@ -208,7 +212,9 @@ fn test_process_multi_proof_flags_extra_proofs_expected() { #[should_panic(expected: ('Index out of bounds',))] fn test_verify_multi_proof_flags_extra_leaves_expected() { let leaves = LEAVES(); - let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2))].span(); + let leaves_to_prove = [ + leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2)) + ].span(); let root = 0x02a40717603180fa52f40a55508cd360d301840f3e502665cf0132ef412911de; let proof = [ 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, @@ -225,7 +231,9 @@ fn test_verify_multi_proof_flags_extra_leaves_expected() { #[should_panic(expected: ('Index out of bounds',))] fn test_verify_multi_proof_flags_extra_proofs_expected() { let leaves = LEAVES(); - let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2))].span(); + let leaves_to_prove = [ + leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2)) + ].span(); let root = 0x02a40717603180fa52f40a55508cd360d301840f3e502665cf0132ef412911de; let proof = [ 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, From 1b4b8ec376fabe06a23ee2a3c78305a14e2e2e2a Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Thu, 15 Aug 2024 20:06:18 +0200 Subject: [PATCH 06/29] feat: add more tests --- packages/utils/src/tests.cairo | 2 +- packages/utils/src/tests/merkle_proof.cairo | 2 + .../test_with_pedersen.cairo} | 4 +- .../merkle_proof/test_with_poseidon.cairo | 252 ++++++++++++++++++ 4 files changed, 256 insertions(+), 4 deletions(-) create mode 100644 packages/utils/src/tests/merkle_proof.cairo rename packages/utils/src/tests/{test_merkle_proof.cairo => merkle_proof/test_with_pedersen.cairo} (99%) create mode 100644 packages/utils/src/tests/merkle_proof/test_with_poseidon.cairo diff --git a/packages/utils/src/tests.cairo b/packages/utils/src/tests.cairo index 9421ce87b..b9faa1571 100644 --- a/packages/utils/src/tests.cairo +++ b/packages/utils/src/tests.cairo @@ -1,6 +1,6 @@ pub(crate) mod mocks; +mod merkle_proof; mod test_hashes; -mod test_merkle_proof; mod test_nonces; mod test_snip12; diff --git a/packages/utils/src/tests/merkle_proof.cairo b/packages/utils/src/tests/merkle_proof.cairo new file mode 100644 index 000000000..32940ed7c --- /dev/null +++ b/packages/utils/src/tests/merkle_proof.cairo @@ -0,0 +1,2 @@ +mod test_with_pedersen; +mod test_with_poseidon; \ No newline at end of file diff --git a/packages/utils/src/tests/test_merkle_proof.cairo b/packages/utils/src/tests/merkle_proof/test_with_pedersen.cairo similarity index 99% rename from packages/utils/src/tests/test_merkle_proof.cairo rename to packages/utils/src/tests/merkle_proof/test_with_pedersen.cairo index 3d2b0ec08..a1f9e12fb 100644 --- a/packages/utils/src/tests/test_merkle_proof.cairo +++ b/packages/utils/src/tests/merkle_proof/test_with_pedersen.cairo @@ -1,7 +1,5 @@ use core::hash::{HashStateTrait, HashStateExTrait}; - -use core::pedersen::PedersenTrait; -use core::pedersen::pedersen; +use core::pedersen::{PedersenTrait, pedersen}; use openzeppelin_utils::cryptography::hashes::PedersenCHasher; use openzeppelin_utils::cryptography::merkle_proof::{ process_proof, process_multi_proof, verify, verify_multi_proof, verify_pedersen diff --git a/packages/utils/src/tests/merkle_proof/test_with_poseidon.cairo b/packages/utils/src/tests/merkle_proof/test_with_poseidon.cairo new file mode 100644 index 000000000..458f2fc04 --- /dev/null +++ b/packages/utils/src/tests/merkle_proof/test_with_poseidon.cairo @@ -0,0 +1,252 @@ +use core::hash::{HashStateTrait, HashStateExTrait}; +use core::poseidon::poseidon_hash_span; +use openzeppelin_utils::cryptography::hashes::PoseidonCHasher; +use openzeppelin_utils::cryptography::merkle_proof::{ + process_proof, process_multi_proof, verify, verify_multi_proof, verify_poseidon +}; +use starknet::{ContractAddress, contract_address_const}; + +#[derive(Serde, Copy, Drop, Hash)] +struct Leaf { + address: ContractAddress, + amount: u128, +} + +fn LEAVES() -> Span { + [ + Leaf { + address: contract_address_const::< + 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8 + >(), + amount: 0xfc104e31d098d1ab488fc1acaeb0269 + }, + Leaf { + address: contract_address_const::< + 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffc66ca5c000 + >(), + amount: 0xfc104e31d098d1ab488fc1acaeb0269 + }, + Leaf { + address: contract_address_const::< + 0x6a1f098854799debccf2d3c4059ff0f02dbfef6673dc1fcbfffffffffffffc8 + >(), + amount: 0xfc104e31d098d1ab488fc1acaeb0269 + }, + Leaf { + address: contract_address_const::< + 0xfa6541b7909bfb5e8585f1222fcf272eea352c7e0e8ed38c988bd1e2a85e82 + >(), + amount: 0xaa8565d732c2c9fa5f6c001d89d5c219 + }, + ].span() +} + +// +// verify +// + +#[test] +fn test_valid_merkle_proof() { + let leaves = LEAVES(); + let hash = leaf_hash(*leaves.at(0)); + // `root` and `proof` were computed using @ericnordelo/strk-merkle-tree + let root = 0x02fde31926e7ef7ac8c0ff1d438e4a177a4cf1a79960381806e3d546071bbc47; + let proof = [ + 0x0610491af77d9d95e10b0f9183a1d94c7472eda1ea081384ff48e6b8dbda73d3, + 0x82d19fa40550cddbb066d587210180c68fcbaa221176e885b5519274580c25 + ].span(); + + assert_eq!(process_proof::(proof, hash), root); + assert!(verify::(proof, root, hash)); + assert!(verify_poseidon(proof, root, hash)); + + // For demonstration, it is also possible to create valid + // proofs for certain values *NOT* in elements: + let no_such_leaf = poseidon_hash_span([hash, *proof.at(0)].span()); + let second_proof = [0x82d19fa40550cddbb066d587210180c68fcbaa221176e885b5519274580c25].span(); + + assert_eq!(process_proof::(second_proof, no_such_leaf), root); + assert!(verify::(second_proof, root, no_such_leaf)); + assert!(verify_poseidon(second_proof, root, no_such_leaf)); +} + +#[test] +fn test_invalid_merkle_proof() { + let leaves = LEAVES(); + let hash = leaf_hash(*leaves.at(0)); + // `root` was computed using @ericnordelo/strk-merkle-tree + let root = 0x02fde31926e7ef7ac8c0ff1d438e4a177a4cf1a79960381806e3d546071bbc47; + let invalid_proof = [ + 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, 'invalid' + ].span(); + + assert!(process_proof::(invalid_proof, hash) != root); + assert!(!verify::(invalid_proof, root, hash)); + assert!(!verify_poseidon(invalid_proof, root, hash)); +} + +// +// multi_proof_verify +// + +#[test] +fn test_valid_merkle_multi_proof() { + let leaves = LEAVES(); + let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1))].span(); + // `root`, `proof`, and `proof_flags` were computed using @ericnordelo/strk-merkle-tree + let root = 0x02fde31926e7ef7ac8c0ff1d438e4a177a4cf1a79960381806e3d546071bbc47; + let proof = [ + 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, + 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b + ].span(); + let proof_flags = [false, false, true].span(); + + assert_eq!(process_multi_proof::(proof, proof_flags, leaves_to_prove), root); + assert!(verify_multi_proof::(proof, proof_flags, root, leaves_to_prove)); +} + +#[test] +fn test_invalid_merkle_multi_proof() { + let leaves = LEAVES(); + let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1))].span(); + // `root` and `proof_flags` were computed using @ericnordelo/strk-merkle-tree + let root = 0x02fde31926e7ef7ac8c0ff1d438e4a177a4cf1a79960381806e3d546071bbc47; + let proof = [ + 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, 'invalid' + ].span(); + let proof_flags = [false, false, true].span(); + + assert!(process_multi_proof::(proof, proof_flags, leaves_to_prove) != root); + assert!(!verify_multi_proof::(proof, proof_flags, root, leaves_to_prove)); +} + +#[test] +fn test_invalid_merkle_multi_proof_flags() { + let leaves = LEAVES(); + let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1))].span(); + // `root` and `proof` were computed using @ericnordelo/strk-merkle-tree + let root = 0x02fde31926e7ef7ac8c0ff1d438e4a177a4cf1a79960381806e3d546071bbc47; + let proof = [ + 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, + 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b + ].span(); + let proof_flags = [false, true, false].span(); + + assert!(process_multi_proof::(proof, proof_flags, leaves_to_prove) != root); + assert!(!verify_multi_proof::(proof, proof_flags, root, leaves_to_prove)); +} + +#[test] +#[should_panic(expected: ("MerkleProof: invalid multi proof",))] +fn test_process_multi_proof_invalid_len_proof_flags_panics() { + let leaves = LEAVES(); + let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1))].span(); + let proof = [ + 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, + 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b + ].span(); + + // `proof_flags.len()` is not equal to `proof.len() + leaves_to_prove.len() + 1` + let proof_flags = [true, false].span(); + + process_multi_proof::(proof, proof_flags, leaves_to_prove); +} + +#[test] +#[should_panic(expected: ("MerkleProof: invalid multi proof",))] +fn test_verify_multi_proof_invalid_len_proof_flags_panics() { + let leaves = LEAVES(); + let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1))].span(); + let root = 0x02fde31926e7ef7ac8c0ff1d438e4a177a4cf1a79960381806e3d546071bbc47; + let proof = [ + 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, + 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b + ].span(); + + // `proof_flags.len()` is not equal to `proof.len() + leaves_to_prove.len() + 1` + let proof_flags = [true, false].span(); + + verify_multi_proof::(proof, proof_flags, root, leaves_to_prove); +} + +#[test] +#[should_panic(expected: ('Index out of bounds',))] +fn test_process_multi_proof_flags_extra_leaves_expected() { + let leaves = LEAVES(); + let leaves_to_prove = [ + leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2)) + ].span(); + let proof = [ + 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, + 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b + ].span(); + + // For each true one leaf is expected + let proof_flags = [true, true, true, true].span(); + + process_multi_proof::(proof, proof_flags, leaves_to_prove); +} + +#[test] +#[should_panic(expected: ('Index out of bounds',))] +fn test_process_multi_proof_flags_extra_proofs_expected() { + let leaves = LEAVES(); + let leaves_to_prove = [ + leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2)) + ].span(); + let proof = [ + 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, + 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b + ].span(); + + // For each false one leaf is expected + let proof_flags = [true, false, false, false].span(); + + process_multi_proof::(proof, proof_flags, leaves_to_prove); +} + +#[test] +#[should_panic(expected: ('Index out of bounds',))] +fn test_verify_multi_proof_flags_extra_leaves_expected() { + let leaves = LEAVES(); + let leaves_to_prove = [ + leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2)) + ].span(); + let root = 0x02fde31926e7ef7ac8c0ff1d438e4a177a4cf1a79960381806e3d546071bbc47; + let proof = [ + 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, + 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b + ].span(); + + // For each true one leaf is expected + let proof_flags = [true, true, true, true].span(); + + verify_multi_proof::(proof, proof_flags, root, leaves_to_prove); +} + +#[test] +#[should_panic(expected: ('Index out of bounds',))] +fn test_verify_multi_proof_flags_extra_proofs_expected() { + let leaves = LEAVES(); + let leaves_to_prove = [ + leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2)) + ].span(); + let root = 0x02fde31926e7ef7ac8c0ff1d438e4a177a4cf1a79960381806e3d546071bbc47; + let proof = [ + 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, + 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b + ].span(); + + // For each false one leaf is expected + let proof_flags = [true, false, false, false].span(); + + verify_multi_proof::(proof, proof_flags, root, leaves_to_prove); +} + +// +// Helpers +// + +fn leaf_hash(leaf: Leaf) -> felt252 { + poseidon_hash_span([0, poseidon_hash_span([leaf.address.into(), leaf.amount.into()].span())].span()) +} From d67535c71f918317e2d6979b438299e8448ea153 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Fri, 16 Aug 2024 15:15:29 +0200 Subject: [PATCH 07/29] feat: finish poseidon tests --- packages/utils/src/cryptography/hashes.cairo | 9 +-- packages/utils/src/tests.cairo | 3 +- packages/utils/src/tests/merkle_proof.cairo | 4 +- .../utils/src/tests/merkle_proof/common.cairo | 36 ++++++++++ .../merkle_proof/test_with_pedersen.cairo | 39 +--------- .../merkle_proof/test_with_poseidon.cairo | 72 +++++-------------- 6 files changed, 65 insertions(+), 98 deletions(-) create mode 100644 packages/utils/src/tests/merkle_proof/common.cairo diff --git a/packages/utils/src/cryptography/hashes.cairo b/packages/utils/src/cryptography/hashes.cairo index 9566b9099..59bdce1f4 100644 --- a/packages/utils/src/cryptography/hashes.cairo +++ b/packages/utils/src/cryptography/hashes.cairo @@ -1,9 +1,9 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.15.0 (utils/cryptography/hashes.cairo) -use core::hash::{HashStateTrait}; +use core::hash::HashStateTrait; use core::pedersen::PedersenTrait; -use core::poseidon::poseidon_hash_span; +use core::poseidon::PoseidonTrait; use openzeppelin_utils::common::Felt252PartialOrd; /// Computes a commutative hash of a sorted pair of felt252. @@ -35,10 +35,11 @@ pub impl PedersenCHasher of CommutativeHasher { pub impl PoseidonCHasher of CommutativeHasher { /// Computes the Poseidon hash of the concatenation of two values, sorting the pair first. fn commutative_hash(a: felt252, b: felt252) -> felt252 { + let hash_state = PoseidonTrait::new(); if a < b { - poseidon_hash_span([a, b].span()) + hash_state.update(a).update(b).finalize() } else { - poseidon_hash_span([b, a].span()) + hash_state.update(b).update(a).finalize() } } } diff --git a/packages/utils/src/tests.cairo b/packages/utils/src/tests.cairo index b9faa1571..81b38eca5 100644 --- a/packages/utils/src/tests.cairo +++ b/packages/utils/src/tests.cairo @@ -1,6 +1,5 @@ -pub(crate) mod mocks; - mod merkle_proof; +pub(crate) mod mocks; mod test_hashes; mod test_nonces; mod test_snip12; diff --git a/packages/utils/src/tests/merkle_proof.cairo b/packages/utils/src/tests/merkle_proof.cairo index 32940ed7c..c1327c921 100644 --- a/packages/utils/src/tests/merkle_proof.cairo +++ b/packages/utils/src/tests/merkle_proof.cairo @@ -1,2 +1,4 @@ +pub(crate) mod common; + mod test_with_pedersen; -mod test_with_poseidon; \ No newline at end of file +mod test_with_poseidon; diff --git a/packages/utils/src/tests/merkle_proof/common.cairo b/packages/utils/src/tests/merkle_proof/common.cairo new file mode 100644 index 000000000..d3dc09939 --- /dev/null +++ b/packages/utils/src/tests/merkle_proof/common.cairo @@ -0,0 +1,36 @@ +use starknet::{ContractAddress, contract_address_const}; + +#[derive(Serde, Copy, Drop, Hash)] +pub(crate) struct Leaf { + pub address: ContractAddress, + pub amount: u128, +} + +pub(crate) fn LEAVES() -> Span { + [ + Leaf { + address: contract_address_const::< + 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8 + >(), + amount: 0xfc104e31d098d1ab488fc1acaeb0269 + }, + Leaf { + address: contract_address_const::< + 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffc66ca5c000 + >(), + amount: 0xfc104e31d098d1ab488fc1acaeb0269 + }, + Leaf { + address: contract_address_const::< + 0x6a1f098854799debccf2d3c4059ff0f02dbfef6673dc1fcbfffffffffffffc8 + >(), + amount: 0xfc104e31d098d1ab488fc1acaeb0269 + }, + Leaf { + address: contract_address_const::< + 0xfa6541b7909bfb5e8585f1222fcf272eea352c7e0e8ed38c988bd1e2a85e82 + >(), + amount: 0xaa8565d732c2c9fa5f6c001d89d5c219 + }, + ].span() +} diff --git a/packages/utils/src/tests/merkle_proof/test_with_pedersen.cairo b/packages/utils/src/tests/merkle_proof/test_with_pedersen.cairo index a1f9e12fb..c09ce3c04 100644 --- a/packages/utils/src/tests/merkle_proof/test_with_pedersen.cairo +++ b/packages/utils/src/tests/merkle_proof/test_with_pedersen.cairo @@ -5,41 +5,7 @@ use openzeppelin_utils::cryptography::merkle_proof::{ process_proof, process_multi_proof, verify, verify_multi_proof, verify_pedersen }; use starknet::{ContractAddress, contract_address_const}; - -#[derive(Serde, Copy, Drop, Hash)] -struct Leaf { - address: ContractAddress, - amount: u128, -} - -fn LEAVES() -> Span { - [ - Leaf { - address: contract_address_const::< - 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8 - >(), - amount: 0xfc104e31d098d1ab488fc1acaeb0269 - }, - Leaf { - address: contract_address_const::< - 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffc66ca5c000 - >(), - amount: 0xfc104e31d098d1ab488fc1acaeb0269 - }, - Leaf { - address: contract_address_const::< - 0x6a1f098854799debccf2d3c4059ff0f02dbfef6673dc1fcbfffffffffffffc8 - >(), - amount: 0xfc104e31d098d1ab488fc1acaeb0269 - }, - Leaf { - address: contract_address_const::< - 0xfa6541b7909bfb5e8585f1222fcf272eea352c7e0e8ed38c988bd1e2a85e82 - >(), - amount: 0xaa8565d732c2c9fa5f6c001d89d5c219 - }, - ].span() -} +use super::common::{Leaf, LEAVES}; // // verify @@ -62,8 +28,7 @@ fn test_valid_merkle_proof() { // For demonstration, it is also possible to create valid // proofs for certain values *NOT* in elements: - let hash_state = PedersenTrait::new(0); - let no_such_leaf = hash_state.update_with(hash).update_with(*proof.at(0)).update(2).finalize(); + let no_such_leaf = PedersenCHasher::commutative_hash(hash, *proof.at(0)); let second_proof = [0x02b0ee474cf2ab27501e54a661d17ac1dc162571c111fe2455d09fe23471099e].span(); assert_eq!(process_proof::(second_proof, no_such_leaf), root); diff --git a/packages/utils/src/tests/merkle_proof/test_with_poseidon.cairo b/packages/utils/src/tests/merkle_proof/test_with_poseidon.cairo index 458f2fc04..8ab6b8b27 100644 --- a/packages/utils/src/tests/merkle_proof/test_with_poseidon.cairo +++ b/packages/utils/src/tests/merkle_proof/test_with_poseidon.cairo @@ -1,45 +1,10 @@ -use core::hash::{HashStateTrait, HashStateExTrait}; use core::poseidon::poseidon_hash_span; use openzeppelin_utils::cryptography::hashes::PoseidonCHasher; use openzeppelin_utils::cryptography::merkle_proof::{ process_proof, process_multi_proof, verify, verify_multi_proof, verify_poseidon }; use starknet::{ContractAddress, contract_address_const}; - -#[derive(Serde, Copy, Drop, Hash)] -struct Leaf { - address: ContractAddress, - amount: u128, -} - -fn LEAVES() -> Span { - [ - Leaf { - address: contract_address_const::< - 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8 - >(), - amount: 0xfc104e31d098d1ab488fc1acaeb0269 - }, - Leaf { - address: contract_address_const::< - 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffc66ca5c000 - >(), - amount: 0xfc104e31d098d1ab488fc1acaeb0269 - }, - Leaf { - address: contract_address_const::< - 0x6a1f098854799debccf2d3c4059ff0f02dbfef6673dc1fcbfffffffffffffc8 - >(), - amount: 0xfc104e31d098d1ab488fc1acaeb0269 - }, - Leaf { - address: contract_address_const::< - 0xfa6541b7909bfb5e8585f1222fcf272eea352c7e0e8ed38c988bd1e2a85e82 - >(), - amount: 0xaa8565d732c2c9fa5f6c001d89d5c219 - }, - ].span() -} +use super::common::{Leaf, LEAVES}; // // verify @@ -50,10 +15,10 @@ fn test_valid_merkle_proof() { let leaves = LEAVES(); let hash = leaf_hash(*leaves.at(0)); // `root` and `proof` were computed using @ericnordelo/strk-merkle-tree - let root = 0x02fde31926e7ef7ac8c0ff1d438e4a177a4cf1a79960381806e3d546071bbc47; + let root = 0x013f43fdca44b32f5334414b385b46aa1016d0172a1f066eab4cc93636426fcc; let proof = [ - 0x0610491af77d9d95e10b0f9183a1d94c7472eda1ea081384ff48e6b8dbda73d3, - 0x82d19fa40550cddbb066d587210180c68fcbaa221176e885b5519274580c25 + 0x05b151ebb9201ce27c56a70f5d0571ccfb9d9d62f12b8ccab7801ba87ec21a2f, + 0x2b7d689bd2ff488fd06dfb8eb22f5cdaba1e5d9698d3fabff2f1801852dbb2 ].span(); assert_eq!(process_proof::(proof, hash), root); @@ -62,8 +27,8 @@ fn test_valid_merkle_proof() { // For demonstration, it is also possible to create valid // proofs for certain values *NOT* in elements: - let no_such_leaf = poseidon_hash_span([hash, *proof.at(0)].span()); - let second_proof = [0x82d19fa40550cddbb066d587210180c68fcbaa221176e885b5519274580c25].span(); + let no_such_leaf = PoseidonCHasher::commutative_hash(hash, *proof.at(0)); + let second_proof = [0x2b7d689bd2ff488fd06dfb8eb22f5cdaba1e5d9698d3fabff2f1801852dbb2].span(); assert_eq!(process_proof::(second_proof, no_such_leaf), root); assert!(verify::(second_proof, root, no_such_leaf)); @@ -75,7 +40,7 @@ fn test_invalid_merkle_proof() { let leaves = LEAVES(); let hash = leaf_hash(*leaves.at(0)); // `root` was computed using @ericnordelo/strk-merkle-tree - let root = 0x02fde31926e7ef7ac8c0ff1d438e4a177a4cf1a79960381806e3d546071bbc47; + let root = 0x013f43fdca44b32f5334414b385b46aa1016d0172a1f066eab4cc93636426fcc; let invalid_proof = [ 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, 'invalid' ].span(); @@ -94,12 +59,9 @@ fn test_valid_merkle_multi_proof() { let leaves = LEAVES(); let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1))].span(); // `root`, `proof`, and `proof_flags` were computed using @ericnordelo/strk-merkle-tree - let root = 0x02fde31926e7ef7ac8c0ff1d438e4a177a4cf1a79960381806e3d546071bbc47; - let proof = [ - 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, - 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b - ].span(); - let proof_flags = [false, false, true].span(); + let root = 0x013f43fdca44b32f5334414b385b46aa1016d0172a1f066eab4cc93636426fcc; + let proof = [0x2b7d689bd2ff488fd06dfb8eb22f5cdaba1e5d9698d3fabff2f1801852dbb2].span(); + let proof_flags = [true, false].span(); assert_eq!(process_multi_proof::(proof, proof_flags, leaves_to_prove), root); assert!(verify_multi_proof::(proof, proof_flags, root, leaves_to_prove)); @@ -110,7 +72,7 @@ fn test_invalid_merkle_multi_proof() { let leaves = LEAVES(); let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1))].span(); // `root` and `proof_flags` were computed using @ericnordelo/strk-merkle-tree - let root = 0x02fde31926e7ef7ac8c0ff1d438e4a177a4cf1a79960381806e3d546071bbc47; + let root = 0x013f43fdca44b32f5334414b385b46aa1016d0172a1f066eab4cc93636426fcc; let proof = [ 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, 'invalid' ].span(); @@ -125,7 +87,7 @@ fn test_invalid_merkle_multi_proof_flags() { let leaves = LEAVES(); let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1))].span(); // `root` and `proof` were computed using @ericnordelo/strk-merkle-tree - let root = 0x02fde31926e7ef7ac8c0ff1d438e4a177a4cf1a79960381806e3d546071bbc47; + let root = 0x013f43fdca44b32f5334414b385b46aa1016d0172a1f066eab4cc93636426fcc; let proof = [ 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b @@ -157,7 +119,7 @@ fn test_process_multi_proof_invalid_len_proof_flags_panics() { fn test_verify_multi_proof_invalid_len_proof_flags_panics() { let leaves = LEAVES(); let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1))].span(); - let root = 0x02fde31926e7ef7ac8c0ff1d438e4a177a4cf1a79960381806e3d546071bbc47; + let root = 0x013f43fdca44b32f5334414b385b46aa1016d0172a1f066eab4cc93636426fcc; let proof = [ 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b @@ -212,7 +174,7 @@ fn test_verify_multi_proof_flags_extra_leaves_expected() { let leaves_to_prove = [ leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2)) ].span(); - let root = 0x02fde31926e7ef7ac8c0ff1d438e4a177a4cf1a79960381806e3d546071bbc47; + let root = 0x013f43fdca44b32f5334414b385b46aa1016d0172a1f066eab4cc93636426fcc; let proof = [ 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b @@ -231,7 +193,7 @@ fn test_verify_multi_proof_flags_extra_proofs_expected() { let leaves_to_prove = [ leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2)) ].span(); - let root = 0x02fde31926e7ef7ac8c0ff1d438e4a177a4cf1a79960381806e3d546071bbc47; + let root = 0x013f43fdca44b32f5334414b385b46aa1016d0172a1f066eab4cc93636426fcc; let proof = [ 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b @@ -248,5 +210,7 @@ fn test_verify_multi_proof_flags_extra_proofs_expected() { // fn leaf_hash(leaf: Leaf) -> felt252 { - poseidon_hash_span([0, poseidon_hash_span([leaf.address.into(), leaf.amount.into()].span())].span()) + poseidon_hash_span( + [poseidon_hash_span([leaf.address.into(), leaf.amount.into()].span())].span() + ) } From 9e70146a52a3228a937302ea0c0c477eacd36db1 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Fri, 16 Aug 2024 15:24:24 +0200 Subject: [PATCH 08/29] fix: version --- packages/utils/src/common.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/utils/src/common.cairo b/packages/utils/src/common.cairo index 960474210..2337f29c7 100644 --- a/packages/utils/src/common.cairo +++ b/packages/utils/src/common.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.15.0 (utils/common.cairo) +// OpenZeppelin Contracts for Cairo v0.15.1 (utils/common.cairo) use core::traits::PartialOrd; From a76ef715f316a1c92fc6bd8db50d81a717e5a8d5 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Fri, 16 Aug 2024 15:54:42 +0200 Subject: [PATCH 09/29] fix: typo --- packages/utils/src/cryptography/merkle_proof.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/utils/src/cryptography/merkle_proof.cairo b/packages/utils/src/cryptography/merkle_proof.cairo index 9611625b3..ff8254e7d 100644 --- a/packages/utils/src/cryptography/merkle_proof.cairo +++ b/packages/utils/src/cryptography/merkle_proof.cairo @@ -133,7 +133,7 @@ pub fn process_multi_proof( // If `proof_flags` is not empty, assert that every // proof was used in the validation process. if proof_pos != proof.len() { - // TODO: check if this is not unrechable code. + // TODO: check if this is not unreachable code. panic!("MerkleProof: invalid multi proof"); } hashes.at(proof_flags_len - 1) From 4c3068d0876078ced4ad223dbad78bd54500fcc6 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Wed, 21 Aug 2024 15:35:53 +0200 Subject: [PATCH 10/29] refactor: remove unnecessary check --- packages/utils/src/cryptography/merkle_proof.cairo | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/utils/src/cryptography/merkle_proof.cairo b/packages/utils/src/cryptography/merkle_proof.cairo index ff8254e7d..ae44a8403 100644 --- a/packages/utils/src/cryptography/merkle_proof.cairo +++ b/packages/utils/src/cryptography/merkle_proof.cairo @@ -130,12 +130,6 @@ pub fn process_multi_proof( }; let root = if proof_flags_len > 0 { - // If `proof_flags` is not empty, assert that every - // proof was used in the validation process. - if proof_pos != proof.len() { - // TODO: check if this is not unreachable code. - panic!("MerkleProof: invalid multi proof"); - } hashes.at(proof_flags_len - 1) } else if leaves_len > 0 { // If `proof_flags_len` is zero, and `leaves_len` is greater then zero, From 2b2a035c709adb1e5a32b6ff3f4ee8b80a214dc9 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Wed, 21 Aug 2024 15:49:45 +0200 Subject: [PATCH 11/29] refactor: merkle_tree into a separated package --- Scarb.lock | 7 ++++++ Scarb.toml | 2 ++ packages/merkle_tree/Scarb.toml | 23 +++++++++++++++++ .../src}/hashes.cairo | 25 ++++++++++++++++++- packages/merkle_tree/src/lib.cairo | 5 ++++ .../src}/merkle_proof.cairo | 2 +- packages/merkle_tree/src/tests.cairo | 2 ++ .../merkle_tree/src/tests/merkle_proof.cairo | 4 +++ .../src/tests/merkle_proof/common.cairo | 0 .../merkle_proof/test_with_pedersen.cairo | 4 +-- .../merkle_proof/test_with_poseidon.cairo | 4 +-- .../src/tests/test_hashes.cairo | 4 +-- packages/utils/src/cryptography.cairo | 2 -- packages/utils/src/tests.cairo | 3 +-- 14 files changed, 75 insertions(+), 12 deletions(-) create mode 100644 packages/merkle_tree/Scarb.toml rename packages/{utils/src/cryptography => merkle_tree/src}/hashes.cairo (72%) create mode 100644 packages/merkle_tree/src/lib.cairo rename packages/{utils/src/cryptography => merkle_tree/src}/merkle_proof.cairo (98%) create mode 100644 packages/merkle_tree/src/tests.cairo create mode 100644 packages/merkle_tree/src/tests/merkle_proof.cairo rename packages/{utils => merkle_tree}/src/tests/merkle_proof/common.cairo (100%) rename packages/{utils => merkle_tree}/src/tests/merkle_proof/test_with_pedersen.cairo (98%) rename packages/{utils => merkle_tree}/src/tests/merkle_proof/test_with_poseidon.cairo (98%) rename packages/{utils => merkle_tree}/src/tests/test_hashes.cairo (90%) diff --git a/Scarb.lock b/Scarb.lock index 0fa419a96..0f7d1cef4 100644 --- a/Scarb.lock +++ b/Scarb.lock @@ -58,6 +58,13 @@ dependencies = [ "snforge_std", ] +[[package]] +name = "openzeppelin_merkle_tree" +version = "0.15.1" +dependencies = [ + "snforge_std", +] + [[package]] name = "openzeppelin_presets" version = "0.15.1" diff --git a/Scarb.toml b/Scarb.toml index 86afb1121..5c9dba194 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -4,6 +4,7 @@ members = [ "packages/account", "packages/governance", "packages/introspection", + "packages/merkle_tree", "packages/presets", "packages/security", "packages/token", @@ -35,6 +36,7 @@ keywords = [ "contracts", "security", "standards", + "merkle_tree" ] [workspace.dependencies] diff --git a/packages/merkle_tree/Scarb.toml b/packages/merkle_tree/Scarb.toml new file mode 100644 index 000000000..8f3f788e4 --- /dev/null +++ b/packages/merkle_tree/Scarb.toml @@ -0,0 +1,23 @@ + +[package] +name = "openzeppelin_merkle_tree" +version.workspace = true +edition.workspace = true +cairo-version.workspace = true +scarb-version.workspace = true +authors.workspace = true +description.workspace = true +documentation.workspace = true +readme.workspace = true +repository.workspace = true +license-file.workspace = true +keywords.workspace = true + +[tool] +fmt.workspace = true + +[dependencies] + +[dev-dependencies] +starknet.workspace = true +snforge_std.workspace = true diff --git a/packages/utils/src/cryptography/hashes.cairo b/packages/merkle_tree/src/hashes.cairo similarity index 72% rename from packages/utils/src/cryptography/hashes.cairo rename to packages/merkle_tree/src/hashes.cairo index 4996d2f6e..f0692917e 100644 --- a/packages/utils/src/cryptography/hashes.cairo +++ b/packages/merkle_tree/src/hashes.cairo @@ -4,7 +4,7 @@ use core::hash::HashStateTrait; use core::pedersen::PedersenTrait; use core::poseidon::PoseidonTrait; -use openzeppelin_utils::common::Felt252PartialOrd; +use core::traits::PartialOrd; /// Computes a commutative hash of a sorted pair of felt252. /// @@ -43,3 +43,26 @@ pub impl PoseidonCHasher of CommutativeHasher { } } } + +impl Felt252PartialOrd of PartialOrd { + #[inline(always)] + fn le(lhs: felt252, rhs: felt252) -> bool { + let lhs: u256 = lhs.into(); + lhs <= rhs.into() + } + #[inline(always)] + fn ge(lhs: felt252, rhs: felt252) -> bool { + let lhs: u256 = lhs.into(); + lhs >= rhs.into() + } + #[inline(always)] + fn lt(lhs: felt252, rhs: felt252) -> bool { + let lhs: u256 = lhs.into(); + lhs < rhs.into() + } + #[inline(always)] + fn gt(lhs: felt252, rhs: felt252) -> bool { + let lhs: u256 = lhs.into(); + lhs > rhs.into() + } +} diff --git a/packages/merkle_tree/src/lib.cairo b/packages/merkle_tree/src/lib.cairo new file mode 100644 index 000000000..c09b1787e --- /dev/null +++ b/packages/merkle_tree/src/lib.cairo @@ -0,0 +1,5 @@ +pub mod hashes; +pub mod merkle_proof; + +#[cfg(test)] +mod tests; diff --git a/packages/utils/src/cryptography/merkle_proof.cairo b/packages/merkle_tree/src/merkle_proof.cairo similarity index 98% rename from packages/utils/src/cryptography/merkle_proof.cairo rename to packages/merkle_tree/src/merkle_proof.cairo index ae44a8403..0628fb455 100644 --- a/packages/utils/src/cryptography/merkle_proof.cairo +++ b/packages/merkle_tree/src/merkle_proof.cairo @@ -13,7 +13,7 @@ /// leaf inclusion in trees built using non-commutative hashing functions requires /// additional logic that is not supported by this library. -use openzeppelin_utils::cryptography::hashes::{CommutativeHasher, PedersenCHasher, PoseidonCHasher}; +use openzeppelin_merkle_tree::hashes::{CommutativeHasher, PedersenCHasher, PoseidonCHasher}; /// Version of `verify` using perdersen as the hashing function. pub fn verify_pedersen(proof: Span, root: felt252, leaf: felt252) -> bool { diff --git a/packages/merkle_tree/src/tests.cairo b/packages/merkle_tree/src/tests.cairo new file mode 100644 index 000000000..982f48c1e --- /dev/null +++ b/packages/merkle_tree/src/tests.cairo @@ -0,0 +1,2 @@ +mod merkle_proof; +mod test_hashes; diff --git a/packages/merkle_tree/src/tests/merkle_proof.cairo b/packages/merkle_tree/src/tests/merkle_proof.cairo new file mode 100644 index 000000000..c1327c921 --- /dev/null +++ b/packages/merkle_tree/src/tests/merkle_proof.cairo @@ -0,0 +1,4 @@ +pub(crate) mod common; + +mod test_with_pedersen; +mod test_with_poseidon; diff --git a/packages/utils/src/tests/merkle_proof/common.cairo b/packages/merkle_tree/src/tests/merkle_proof/common.cairo similarity index 100% rename from packages/utils/src/tests/merkle_proof/common.cairo rename to packages/merkle_tree/src/tests/merkle_proof/common.cairo diff --git a/packages/utils/src/tests/merkle_proof/test_with_pedersen.cairo b/packages/merkle_tree/src/tests/merkle_proof/test_with_pedersen.cairo similarity index 98% rename from packages/utils/src/tests/merkle_proof/test_with_pedersen.cairo rename to packages/merkle_tree/src/tests/merkle_proof/test_with_pedersen.cairo index c09ce3c04..b546f187f 100644 --- a/packages/utils/src/tests/merkle_proof/test_with_pedersen.cairo +++ b/packages/merkle_tree/src/tests/merkle_proof/test_with_pedersen.cairo @@ -1,7 +1,7 @@ use core::hash::{HashStateTrait, HashStateExTrait}; use core::pedersen::{PedersenTrait, pedersen}; -use openzeppelin_utils::cryptography::hashes::PedersenCHasher; -use openzeppelin_utils::cryptography::merkle_proof::{ +use openzeppelin_merkle_tree::hashes::PedersenCHasher; +use openzeppelin_merkle_tree::merkle_proof::{ process_proof, process_multi_proof, verify, verify_multi_proof, verify_pedersen }; use starknet::{ContractAddress, contract_address_const}; diff --git a/packages/utils/src/tests/merkle_proof/test_with_poseidon.cairo b/packages/merkle_tree/src/tests/merkle_proof/test_with_poseidon.cairo similarity index 98% rename from packages/utils/src/tests/merkle_proof/test_with_poseidon.cairo rename to packages/merkle_tree/src/tests/merkle_proof/test_with_poseidon.cairo index 8ab6b8b27..326476351 100644 --- a/packages/utils/src/tests/merkle_proof/test_with_poseidon.cairo +++ b/packages/merkle_tree/src/tests/merkle_proof/test_with_poseidon.cairo @@ -1,6 +1,6 @@ use core::poseidon::poseidon_hash_span; -use openzeppelin_utils::cryptography::hashes::PoseidonCHasher; -use openzeppelin_utils::cryptography::merkle_proof::{ +use openzeppelin_merkle_tree::hashes::PoseidonCHasher; +use openzeppelin_merkle_tree::merkle_proof::{ process_proof, process_multi_proof, verify, verify_multi_proof, verify_poseidon }; use starknet::{ContractAddress, contract_address_const}; diff --git a/packages/utils/src/tests/test_hashes.cairo b/packages/merkle_tree/src/tests/test_hashes.cairo similarity index 90% rename from packages/utils/src/tests/test_hashes.cairo rename to packages/merkle_tree/src/tests/test_hashes.cairo index c83423254..916e35c76 100644 --- a/packages/utils/src/tests/test_hashes.cairo +++ b/packages/merkle_tree/src/tests/test_hashes.cairo @@ -1,7 +1,7 @@ -use core::hash::{HashStateTrait}; +use core::hash::HashStateTrait; use core::pedersen::PedersenTrait; use core::poseidon::poseidon_hash_span; -use openzeppelin_utils::cryptography::hashes::{PedersenCHasher, PoseidonCHasher}; +use openzeppelin_merkle_tree::hashes::{PedersenCHasher, PoseidonCHasher}; #[test] fn test_pedersen_commutative_hash_is_commutative() { diff --git a/packages/utils/src/cryptography.cairo b/packages/utils/src/cryptography.cairo index 0c7fe5ae7..99d2f79b5 100644 --- a/packages/utils/src/cryptography.cairo +++ b/packages/utils/src/cryptography.cairo @@ -1,5 +1,3 @@ -pub mod hashes; pub mod interface; -pub mod merkle_proof; pub mod nonces; pub mod snip12; diff --git a/packages/utils/src/tests.cairo b/packages/utils/src/tests.cairo index 81b38eca5..37cf414d3 100644 --- a/packages/utils/src/tests.cairo +++ b/packages/utils/src/tests.cairo @@ -1,5 +1,4 @@ -mod merkle_proof; pub(crate) mod mocks; -mod test_hashes; + mod test_nonces; mod test_snip12; From a6155ad564f87956754cec8459b815e6bc5815b9 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Wed, 21 Aug 2024 15:51:36 +0200 Subject: [PATCH 12/29] feat: update CHANGELOG --- CHANGELOG.md | 2 +- packages/merkle_tree/Scarb.toml | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48aa9bcd8..dd56e22cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Merkle tree utilities to verify proofs and multi proofs (#1101) +- `merkle_tree` package with utilities to verify proofs and multi proofs (#1101) ### Changed (Breaking) diff --git a/packages/merkle_tree/Scarb.toml b/packages/merkle_tree/Scarb.toml index 8f3f788e4..39472084f 100644 --- a/packages/merkle_tree/Scarb.toml +++ b/packages/merkle_tree/Scarb.toml @@ -16,8 +16,6 @@ keywords.workspace = true [tool] fmt.workspace = true -[dependencies] - [dev-dependencies] starknet.workspace = true snforge_std.workspace = true From 17322829152fc9042353874cd786cdc60f91d616 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Thu, 22 Aug 2024 12:43:07 +0200 Subject: [PATCH 13/29] feat: remove common module from utils --- packages/utils/src/common.cairo | 27 --------------------------- packages/utils/src/lib.cairo | 1 - 2 files changed, 28 deletions(-) delete mode 100644 packages/utils/src/common.cairo diff --git a/packages/utils/src/common.cairo b/packages/utils/src/common.cairo deleted file mode 100644 index 2337f29c7..000000000 --- a/packages/utils/src/common.cairo +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.15.1 (utils/common.cairo) - -use core::traits::PartialOrd; - -pub impl Felt252PartialOrd of PartialOrd { - #[inline(always)] - fn le(lhs: felt252, rhs: felt252) -> bool { - let lhs: u256 = lhs.into(); - lhs <= rhs.into() - } - #[inline(always)] - fn ge(lhs: felt252, rhs: felt252) -> bool { - let lhs: u256 = lhs.into(); - lhs >= rhs.into() - } - #[inline(always)] - fn lt(lhs: felt252, rhs: felt252) -> bool { - let lhs: u256 = lhs.into(); - lhs < rhs.into() - } - #[inline(always)] - fn gt(lhs: felt252, rhs: felt252) -> bool { - let lhs: u256 = lhs.into(); - lhs > rhs.into() - } -} diff --git a/packages/utils/src/lib.cairo b/packages/utils/src/lib.cairo index 8ca3df08c..9ac71e296 100644 --- a/packages/utils/src/lib.cairo +++ b/packages/utils/src/lib.cairo @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT // OpenZeppelin Contracts for Cairo v0.15.1 (utils/utils.cairo) -pub mod common; pub mod cryptography; pub mod deployments; pub mod interfaces; From 8cd2b518cb5780a50d1988d17171cf3a5c24b0aa Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Thu, 22 Aug 2024 14:20:41 +0200 Subject: [PATCH 14/29] docs: add page for merkle tree --- docs/modules/ROOT/nav.adoc | 2 + docs/modules/ROOT/pages/api/merkle-tree.adoc | 200 +++++++++++++++++++ packages/merkle_tree/src/merkle_proof.cairo | 24 +-- packages/utils/src/tests/merkle_proof.cairo | 4 - 4 files changed, 214 insertions(+), 16 deletions(-) create mode 100644 docs/modules/ROOT/pages/api/merkle-tree.adoc delete mode 100644 packages/utils/src/tests/merkle_proof.cairo diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index d74be18aa..b005c159f 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -24,6 +24,8 @@ *** xref:/guides/src5-migration.adoc[Migrating ERC165 to SRC5] *** xref:/api/introspection.adoc[API Reference] +** xref:/api/merkle-tree.adoc[Merkle Tree] + ** xref:security.adoc[Security] *** xref:/api/security.adoc[API Reference] diff --git a/docs/modules/ROOT/pages/api/merkle-tree.adoc b/docs/modules/ROOT/pages/api/merkle-tree.adoc new file mode 100644 index 000000000..e9b69bd64 --- /dev/null +++ b/docs/modules/ROOT/pages/api/merkle-tree.adoc @@ -0,0 +1,200 @@ +:github-icon: pass:[] +:strk-merkle-tree: https://github.com/ericnordelo/strk-merkle-tree[JavaScript library] +:verify: xref:#merkle_proof-verify[verify] +:verify_pedersen: xref:#merkle_proof-verify_perdersen[verify_pedersen] +:verify_poseidon: xref:#merkle_proof-verify_poseidon[verify_poseidon] +:verify_multi_proof: xref:#merkle_proof-verify_multi_proof[verify_multi_proof] +:process_multi_proof: xref:#merkle_proof-process_multi_proof[process_multi_proof] + += Merkle Tree + +OpenZeppelin Contracts for Cairo provides a `merkle_tree` package with a set of utilities for verifying Merkle Tree proofs on-chain. The tree and the proofs can be generated using this {strk-merkle-tree}. + +This module provides: + +- `{verify}` - can prove that some value is part of a Merkle tree. + +- `{verify_multi_proof}` - can prove multiple values are part of a Merkle tree. + +NOTE: `openzeppelin_merkle_tree` doesn't have dependencies outside of `corelib`, and can be used in projects that are not Starknet-related. + +[TIP] +==== +To use it as a standalone package, you can add it in your `Scarb.toml` as follows: + +`openzeppelin_merkle_tree = { git = "https://github.com/openzeppelin/cairo-contracts.git", tag = "v0.X.X" }` +==== + +== Modules + +[.contract] +[[merkle_proof]] +=== `++merkle_proof++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.1/packages/merkle_tree/src/merkle_proof.cairo[{github-icon},role=heading-link] + +```cairo +use openzeppelin_merkle_tree::merkle_proof; +``` + +These functions deal with verification of Merkle Tree proofs. + +The tree and the proofs can be generated using this {strk-merkle-tree}. You will find a quickstart guide in the readme. + +WARNING: You should use a hash function other than the one used to hash internal nodes for hashing leaves. This is because the concatenation of a sorted pair of internal nodes in the Merkle tree could be reinterpreted as a leaf value. the JavaScript library generates Merkle trees that are safe against this attack out of the box. + +[.contract-index] +.Functions +-- +* xref:#merkle_proof-verify[`++verify(proof, root, leaf)++`] +* xref:#merkle_proof-verify_pedersen[`++verify_pedersen(proof, root, leaf)++`] +* xref:#merkle_proof-verify_poseidon[`++verify_poseidon(proof, root, leaf)++`] +* xref:#merkle_proof-process_proof[`++process_proof(proof, leaf)++`] +* xref:#merkle_proof-verify_multi_proof[`++verify_multi_proof(proof, proof_flags, root, leaves)++`] +* xref:#merkle_proof-process_multi_proof[`++process_multi_proof(proof, proof_flags, leaf)++`] +-- + +[#merkle_proof-Functions] +==== Functions + +[.contract-item] +[[merkle_proof-verify]] +==== `[.contract-item-name]#++verify<+CommutativeHasher>++#++(proof: Span, root: felt252, leaf: felt252) → bool++` [.item-kind]#public# + +Returns true if a `leaf` can be proved to be a part of a Merkle tree defined by `root`. + +For this, a `proof` must be provided, containing sibling hashes on the branch from the leaf to the root of the tree. + +Each pair of leaves and each pair of pre-images are assumed to be sorted. + +[NOTE] +==== +This function expects a `CommutativeHasher` implementation. See xref:#hashes-CommutativeHasher[hashes::CommutativeHasher] for more information. + +`{verify_pedersen}` and `{verify_poseidon}` already include the corresponding `Hasher` implementations. +==== + +[.contract-item] +[[merkle_proof-verify_pedersen]] +==== `[.contract-item-name]#++verify_pedersen++#++(proof: Span, root: felt252, leaf: felt252) → bool++` [.item-kind]#public# + +Version of `{verify}` using Perdersen as the hashing function. + +[.contract-item] +[[merkle_proof-verify_poseidon]] +==== `[.contract-item-name]#++verify_poseidon++#++(proof: Span, root: felt252, leaf: felt252) → bool++` [.item-kind]#public# + +Version of `{verify}` using Poseidon as the hashing function. + +[.contract-item] +[[merkle_proof-process_proof]] +==== `[.contract-item-name]#++process_proof<+CommutativeHasher>++#++(proof: Span, leaf: felt252) → felt252++` [.item-kind]#public# + +Returns the rebuilt hash obtained by traversing a Merkle tree up from `leaf` using `proof`. + +A `proof` is valid if and only if the rebuilt hash matches the root of the tree. + +When processing the proof, the pairs of leaves & pre-images are assumed to be sorted. + +NOTE: This function expects a `CommutativeHasher` implementation. See xref:#hashes-CommutativeHasher[hashes::CommutativeHasher] for more information. + +[.contract-item] +[[merkle_proof-verify_multi_proof]] +==== `[.contract-item-name]#++verify_multi_proof<+CommutativeHasher>++#++(proof: Span, proof_flags: Span, root: felt252, leaves: Span) → bool++` [.item-kind]#public# + +Returns True if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined +by `root`, according to `proof` and `proof_flags` as described in `{process_multi_proof}`. + +The `leaves` must be validated independently. See `{process_multi_proof}`. + +CAUTION: Not all Merkle trees admit multiproofs. See `{process_multi_proof}` for details. + +NOTE: Consider the case where `root == proof.at(0) && leaves.len() == 0` as it will return `True`. + +NOTE: This function expects a `CommutativeHasher` implementation. See xref:#hashes-CommutativeHasher[hashes::CommutativeHasher] for more information. + +[.contract-item] +[[merkle_proof-process_multi_proof]] +==== `[.contract-item-name]#++process_multi_proof<+CommutativeHasher>++#++(proof: Span, proof_flags: Span, leaves: Span) → felt252++` [.item-kind]#public# + +Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. + +The reconstruction proceeds by incrementally reconstructing all inner nodes by combining a +leaf/inner node with either another leaf/inner node or a proof sibling node, depending on +whether each `proof_flags` item is true or false respectively. + +CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure +that: 1) the tree is complete (but not necessarily perfect), 2) the leaves to be proven are in +the opposite order they are in the tree (i.e., as seen from right to left starting at the +deepest layer and continuing at the next layer). + +NOTE: The _empty set_ (i.e. the case where `proof.len() == 1 && leaves.len() == 0`) is +considered a no-op, and therefore a valid multiproof (i.e. it returns `proof.at(0)`). Consider +disallowing this case if you're not validating the leaves elsewhere. + +NOTE: This function expects a `CommutativeHasher` implementation. See xref:#hashes-CommutativeHasher[hashes::CommutativeHasher] for more information. + + +[.contract] +[[hashes]] +=== `++hashes++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.15.1/packages/merkle_tree/src/hashes.cairo[{github-icon},role=heading-link] + +```cairo +use openzeppelin_merkle_tree::hashes; +``` + +:pedersen-hasher: xref:#hashes-PedersenCHasher[PedersenCHasher] +:poseidon-hasher: xref:#hashes-PoseidonCHasher[PoseidonCHasher] + +Module providing the trait and default implementations for the commutative hash functions used in +xref:#merkle_proof[`merkle_proof`]. + +NOTE: The `{pedersen-hasher}` implementation matches the default node hashing function used in the {strk-merkle-tree}. + +[.contract-index] +.Traits +-- +* xref:#hashes-CommutativeHasher[`++CommutativeHasher++`] +-- + +[.contract-index] +.Impls +-- +* xref:#hashes-PedersenCHasher[`++PedersenCHasher++`] +* xref:#hashes-PoseidonCHasher[`++PoseidonCHasher++`] +-- + +[#hashes-Traits] +==== Traits + +[.contract-item] +[[hashes-CommutativeHasher]] +==== `[.contract-item-name]#++CommutativeHasher++#` [.item-kind]#trait# + +Declares a commutative hash function with the following signature: + +`commutative_hash(a: felt252, b: felt252) -> felt252;` + +which computes a commutative hash of a sorted pair of `felt252`. + +This is usually implemented as an extension of a non-commutative hash function, like +Pedersen or Poseidon, returning the hash of the concatenation of the two values by first +sorting them. + +Frequently used when working with merkle proofs. + +NOTE: The `commutative_hash` function MUST follow the invariant that `commutative_hash(a, b) == commutative_hash(b, a)`. + +[#hashes-Impls] +==== Impls + +[.contract-item] +[[hashes-PedersenCHasher]] +==== `[.contract-item-name]#++PedersenCHasher++#` [.item-kind]#impl# + +Implementation of the `CommutativeHasher` trait which computes the Pedersen hash of chaining the two input values +with the len (2), sorting the pair first. + +[.contract-item] +[[hashes-PoseidonCHasher]] +==== `[.contract-item-name]#++PoseidonCHasher++#` [.item-kind]#impl# + +Implementation of the `CommutativeHasher` trait which computes the Poseidon hash of the concatenation of two values, sorting the pair first. diff --git a/packages/merkle_tree/src/merkle_proof.cairo b/packages/merkle_tree/src/merkle_proof.cairo index 0628fb455..0ad08e384 100644 --- a/packages/merkle_tree/src/merkle_proof.cairo +++ b/packages/merkle_tree/src/merkle_proof.cairo @@ -3,7 +3,7 @@ /// These functions deal with verification of Merkle Tree proofs. /// -/// WARNING: You should avoid using leaf values that are 62 bytes long prior to +/// WARNING: You should avoid using leaf values that are 64 bytes long prior to /// hashing, or use a different hash function for hashing leaves and pre-images. /// This is because the concatenation of a sorted pair of internal nodes in /// the Merkle tree could be reinterpreted as a leaf value. @@ -15,16 +15,6 @@ use openzeppelin_merkle_tree::hashes::{CommutativeHasher, PedersenCHasher, PoseidonCHasher}; -/// Version of `verify` using perdersen as the hashing function. -pub fn verify_pedersen(proof: Span, root: felt252, leaf: felt252) -> bool { - verify::(proof, root, leaf) -} - -/// Version of `verify` using poseidon as the hashing function. -pub fn verify_poseidon(proof: Span, root: felt252, leaf: felt252) -> bool { - verify::(proof, root, leaf) -} - /// Returns true if a `leaf` can be proved to be a part of a Merkle tree /// defined by `root`. For this, a `proof` must be provided, containing /// sibling hashes on the branch from the leaf to the root of the tree. Each @@ -35,6 +25,16 @@ pub fn verify( process_proof::(proof, leaf) == root } +/// Version of `verify` using Perdersen as the hashing function. +pub fn verify_pedersen(proof: Span, root: felt252, leaf: felt252) -> bool { + verify::(proof, root, leaf) +} + +/// Version of `verify` using Poseidon as the hashing function. +pub fn verify_poseidon(proof: Span, root: felt252, leaf: felt252) -> bool { + verify::(proof, root, leaf) +} + /// Returns the rebuilt hash obtained by traversing a Merkle tree up /// from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt /// hash matches the root of the tree. When processing the proof, the pairs @@ -54,7 +54,7 @@ pub fn process_proof( /// /// CAUTION: Not all Merkle trees admit multiproofs. See `process_multi_proof` for details. /// -/// NOTE: Consider the case where `root == proof[0] && leaves.len() == 0` as it will return `True`. +/// NOTE: Consider the case where `root == proof.at(0) && leaves.len() == 0` as it will return `True`. /// The `leaves` must be validated independently. See `process_multi_proof`. pub fn verify_multi_proof( proof: Span, proof_flags: Span, root: felt252, leaves: Span diff --git a/packages/utils/src/tests/merkle_proof.cairo b/packages/utils/src/tests/merkle_proof.cairo deleted file mode 100644 index c1327c921..000000000 --- a/packages/utils/src/tests/merkle_proof.cairo +++ /dev/null @@ -1,4 +0,0 @@ -pub(crate) mod common; - -mod test_with_pedersen; -mod test_with_poseidon; From 902013994848425521c1e5103f9516f5d8aaf4e2 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Thu, 22 Aug 2024 14:28:17 +0200 Subject: [PATCH 15/29] feat: format files --- packages/merkle_tree/src/merkle_proof.cairo | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/merkle_tree/src/merkle_proof.cairo b/packages/merkle_tree/src/merkle_proof.cairo index 0ad08e384..0574ebac0 100644 --- a/packages/merkle_tree/src/merkle_proof.cairo +++ b/packages/merkle_tree/src/merkle_proof.cairo @@ -54,7 +54,9 @@ pub fn process_proof( /// /// CAUTION: Not all Merkle trees admit multiproofs. See `process_multi_proof` for details. /// -/// NOTE: Consider the case where `root == proof.at(0) && leaves.len() == 0` as it will return `True`. +/// NOTE: Consider the case where `root == proof.at(0) && leaves.len() == 0` +/// as it will return `True`. +/// /// The `leaves` must be validated independently. See `process_multi_proof`. pub fn verify_multi_proof( proof: Span, proof_flags: Span, root: felt252, leaves: Span From 11cb1cc2de3032ad83d7a2e4106864ecd8c7ded1 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Thu, 22 Aug 2024 14:52:05 +0200 Subject: [PATCH 16/29] feat: update index from scarb bump --- docs/modules/ROOT/pages/index.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc index 0099eb284..01ba6ad81 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/modules/ROOT/pages/index.adoc @@ -21,8 +21,8 @@ before proceeding, and run the following command to check that the installation ---- $ scarb --version -scarb 2.7.0 (e9a2b8716 2024-08-01) -cairo: 2.7.0 (https://crates.io/crates/cairo-lang-compiler/2.7.0) +scarb 2.7.1 (e288874ba 2024-08-13) +cairo: 2.7.1 (https://crates.io/crates/cairo-lang-compiler/2.7.1) sierra: 1.6.0 ---- From 31cdc10a045577c452a32408869541a54564e851 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Thu, 22 Aug 2024 15:36:23 +0200 Subject: [PATCH 17/29] feat: add typos config file --- _typos.toml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 _typos.toml diff --git a/_typos.toml b/_typos.toml new file mode 100644 index 000000000..86863e410 --- /dev/null +++ b/_typos.toml @@ -0,0 +1,4 @@ +[default] +extend-ignore-identifiers-re = [ + "e288874ba", +] \ No newline at end of file From ec11531c92095a61ec3937a57db7b82b4fd79761 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Thu, 22 Aug 2024 15:36:40 +0200 Subject: [PATCH 18/29] refactor: add empty line --- _typos.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_typos.toml b/_typos.toml index 86863e410..e05c33bd2 100644 --- a/_typos.toml +++ b/_typos.toml @@ -1,4 +1,4 @@ [default] extend-ignore-identifiers-re = [ "e288874ba", -] \ No newline at end of file +] From 34664b356a3ffaf03e1176f245d594a029c61b1d Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Fri, 23 Aug 2024 12:13:16 +0200 Subject: [PATCH 19/29] Update packages/utils/src/lib.cairo Co-authored-by: Andrew Fleming --- packages/utils/src/lib.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/utils/src/lib.cairo b/packages/utils/src/lib.cairo index 9ac71e296..8892e3b70 100644 --- a/packages/utils/src/lib.cairo +++ b/packages/utils/src/lib.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.15.1 (utils/utils.cairo) +// OpenZeppelin Contracts for Cairo v0.15.1 (utils/lib.cairo) pub mod cryptography; pub mod deployments; From 7976a2df33a7f493b862054c98e8af541cdfe75c Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Fri, 23 Aug 2024 12:14:29 +0200 Subject: [PATCH 20/29] Update packages/merkle_tree/src/hashes.cairo Co-authored-by: Andrew Fleming --- packages/merkle_tree/src/hashes.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/merkle_tree/src/hashes.cairo b/packages/merkle_tree/src/hashes.cairo index a13f752b4..d65a8029d 100644 --- a/packages/merkle_tree/src/hashes.cairo +++ b/packages/merkle_tree/src/hashes.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.15.1 (utils/cryptography/hashes.cairo) +// OpenZeppelin Contracts for Cairo v0.15.1 (merkle_tree/hashes.cairo) use core::hash::HashStateTrait; use core::pedersen::PedersenTrait; From 65c67cc47d0e13f25fe9b37475644444ef83e944 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Fri, 23 Aug 2024 20:24:07 +0200 Subject: [PATCH 21/29] feat: apple review updates --- Scarb.toml | 3 +-- docs/modules/ROOT/pages/api/merkle-tree.adoc | 11 +++++++---- docs/modules/ROOT/pages/guides/snip12.adoc | 2 +- packages/merkle_tree/src/hashes.cairo | 2 +- packages/merkle_tree/src/merkle_proof.cairo | 8 ++++---- .../src/tests/merkle_proof/test_with_pedersen.cairo | 4 ++-- packages/merkle_tree/src/tests/test_hashes.cairo | 1 - packages/token/src/erc20/extensions/erc20_votes.cairo | 2 +- 8 files changed, 17 insertions(+), 16 deletions(-) diff --git a/Scarb.toml b/Scarb.toml index 63451ec89..f7b94f96a 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -35,8 +35,7 @@ keywords = [ "cairo", "contracts", "security", - "standards", - "merkle_tree" + "standards" ] [workspace.dependencies] diff --git a/docs/modules/ROOT/pages/api/merkle-tree.adoc b/docs/modules/ROOT/pages/api/merkle-tree.adoc index e9b69bd64..ac7830758 100644 --- a/docs/modules/ROOT/pages/api/merkle-tree.adoc +++ b/docs/modules/ROOT/pages/api/merkle-tree.adoc @@ -39,7 +39,10 @@ These functions deal with verification of Merkle Tree proofs. The tree and the proofs can be generated using this {strk-merkle-tree}. You will find a quickstart guide in the readme. -WARNING: You should use a hash function other than the one used to hash internal nodes for hashing leaves. This is because the concatenation of a sorted pair of internal nodes in the Merkle tree could be reinterpreted as a leaf value. the JavaScript library generates Merkle trees that are safe against this attack out of the box. +WARNING: You should avoid using leaf values that are two felt252 long prior to hashing, or use a hash function +other than the one used to hash internal nodes for hashing leaves. This is because the concatenation of a sorted pair +of internal nodes in the Merkle tree could be reinterpreted as a leaf value. the JavaScript library generates Merkle +trees that are safe against this attack out of the box. [.contract-index] .Functions @@ -100,14 +103,14 @@ NOTE: This function expects a `CommutativeHasher` implementation. See xref:#hash [[merkle_proof-verify_multi_proof]] ==== `[.contract-item-name]#++verify_multi_proof<+CommutativeHasher>++#++(proof: Span, proof_flags: Span, root: felt252, leaves: Span) → bool++` [.item-kind]#public# -Returns True if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined +Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by `root`, according to `proof` and `proof_flags` as described in `{process_multi_proof}`. -The `leaves` must be validated independently. See `{process_multi_proof}`. +The `leaves` must be validated independently. CAUTION: Not all Merkle trees admit multiproofs. See `{process_multi_proof}` for details. -NOTE: Consider the case where `root == proof.at(0) && leaves.len() == 0` as it will return `True`. +NOTE: Consider the case where `root == proof.at(0) && leaves.len() == 0` as it will return `true`. NOTE: This function expects a `CommutativeHasher` implementation. See xref:#hashes-CommutativeHasher[hashes::CommutativeHasher] for more information. diff --git a/docs/modules/ROOT/pages/guides/snip12.adoc b/docs/modules/ROOT/pages/guides/snip12.adoc index 2cf8e9b88..190c66456 100644 --- a/docs/modules/ROOT/pages/guides/snip12.adoc +++ b/docs/modules/ROOT/pages/guides/snip12.adoc @@ -351,7 +351,7 @@ mod CustomERC20 { let is_valid_signature_felt = DualCaseAccount { contract_address: owner } .is_valid_signature(hash, signature); - // Check either 'VALID' or True for backwards compatibility + // Check either 'VALID' or true for backwards compatibility let is_valid_signature = is_valid_signature_felt == starknet::VALIDATED || is_valid_signature_felt == 1; assert(is_valid_signature, 'Invalid signature'); diff --git a/packages/merkle_tree/src/hashes.cairo b/packages/merkle_tree/src/hashes.cairo index a13f752b4..d65a8029d 100644 --- a/packages/merkle_tree/src/hashes.cairo +++ b/packages/merkle_tree/src/hashes.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.15.1 (utils/cryptography/hashes.cairo) +// OpenZeppelin Contracts for Cairo v0.15.1 (merkle_tree/hashes.cairo) use core::hash::HashStateTrait; use core::pedersen::PedersenTrait; diff --git a/packages/merkle_tree/src/merkle_proof.cairo b/packages/merkle_tree/src/merkle_proof.cairo index 0574ebac0..250a67842 100644 --- a/packages/merkle_tree/src/merkle_proof.cairo +++ b/packages/merkle_tree/src/merkle_proof.cairo @@ -1,9 +1,9 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.15.1 (utils/cryptography/merkle_proof.cairo) +// OpenZeppelin Contracts for Cairo v0.15.1 (merkle_tree/merkle_proof.cairo) /// These functions deal with verification of Merkle Tree proofs. /// -/// WARNING: You should avoid using leaf values that are 64 bytes long prior to +/// WARNING: You should avoid using leaf values that are two felt252 long prior to /// hashing, or use a different hash function for hashing leaves and pre-images. /// This is because the concatenation of a sorted pair of internal nodes in /// the Merkle tree could be reinterpreted as a leaf value. @@ -49,13 +49,13 @@ pub fn process_proof( computed_hash } -/// Returns True if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined +/// Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined /// by `root`, according to `proof` and `proof_flags` as described in `process_multi_proof`. /// /// CAUTION: Not all Merkle trees admit multiproofs. See `process_multi_proof` for details. /// /// NOTE: Consider the case where `root == proof.at(0) && leaves.len() == 0` -/// as it will return `True`. +/// as it will return `true`. /// /// The `leaves` must be validated independently. See `process_multi_proof`. pub fn verify_multi_proof( diff --git a/packages/merkle_tree/src/tests/merkle_proof/test_with_pedersen.cairo b/packages/merkle_tree/src/tests/merkle_proof/test_with_pedersen.cairo index b546f187f..3f884ac90 100644 --- a/packages/merkle_tree/src/tests/merkle_proof/test_with_pedersen.cairo +++ b/packages/merkle_tree/src/tests/merkle_proof/test_with_pedersen.cairo @@ -165,7 +165,7 @@ fn test_process_multi_proof_flags_extra_proofs_expected() { 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b ].span(); - // For each false one leaf is expected + // For each false one proof is expected let proof_flags = [true, false, false, false].span(); process_multi_proof::(proof, proof_flags, leaves_to_prove); @@ -203,7 +203,7 @@ fn test_verify_multi_proof_flags_extra_proofs_expected() { 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b ].span(); - // For each false one leaf is expected + // For each false one proof is expected let proof_flags = [true, false, false, false].span(); verify_multi_proof::(proof, proof_flags, root, leaves_to_prove); diff --git a/packages/merkle_tree/src/tests/test_hashes.cairo b/packages/merkle_tree/src/tests/test_hashes.cairo index 916e35c76..edeb129b5 100644 --- a/packages/merkle_tree/src/tests/test_hashes.cairo +++ b/packages/merkle_tree/src/tests/test_hashes.cairo @@ -37,7 +37,6 @@ fn test_poseidon_commutative_hash_smaller_first() { let a = 'a'; let b = 'b'; - // Expected hash is pedersen(a, b), since a < b let hash = PoseidonCHasher::commutative_hash(a, b); assert_eq!(hash, poseidon_hash_span([a, b].span())); } diff --git a/packages/token/src/erc20/extensions/erc20_votes.cairo b/packages/token/src/erc20/extensions/erc20_votes.cairo index b13cd160d..d30a937ba 100644 --- a/packages/token/src/erc20/extensions/erc20_votes.cairo +++ b/packages/token/src/erc20/extensions/erc20_votes.cairo @@ -163,7 +163,7 @@ pub mod ERC20VotesComponent { let is_valid_signature_felt = DualCaseAccount { contract_address: delegator } .is_valid_signature(hash, signature); - // Check either 'VALID' or True for backwards compatibility. + // Check either 'VALID' or true for backwards compatibility. let is_valid_signature = is_valid_signature_felt == starknet::VALIDATED || is_valid_signature_felt == 1; From e4758fd65e3314cda24bc56741324f4f13bf9f11 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Mon, 26 Aug 2024 15:32:48 +0200 Subject: [PATCH 22/29] feat: update test --- packages/merkle_tree/src/tests/test_hashes.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/merkle_tree/src/tests/test_hashes.cairo b/packages/merkle_tree/src/tests/test_hashes.cairo index edeb129b5..ebe43866e 100644 --- a/packages/merkle_tree/src/tests/test_hashes.cairo +++ b/packages/merkle_tree/src/tests/test_hashes.cairo @@ -37,6 +37,6 @@ fn test_poseidon_commutative_hash_smaller_first() { let a = 'a'; let b = 'b'; - let hash = PoseidonCHasher::commutative_hash(a, b); + let hash = PoseidonCHasher::commutative_hash(b, a); assert_eq!(hash, poseidon_hash_span([a, b].span())); } From 198918c8db0820d0cad6f3bbad0d7027dde25947 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Tue, 27 Aug 2024 13:23:23 +0200 Subject: [PATCH 23/29] Update docs/modules/ROOT/pages/api/merkle-tree.adoc Co-authored-by: Andrew Fleming --- docs/modules/ROOT/pages/api/merkle-tree.adoc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/modules/ROOT/pages/api/merkle-tree.adoc b/docs/modules/ROOT/pages/api/merkle-tree.adoc index ac7830758..fefea0a07 100644 --- a/docs/modules/ROOT/pages/api/merkle-tree.adoc +++ b/docs/modules/ROOT/pages/api/merkle-tree.adoc @@ -124,10 +124,15 @@ The reconstruction proceeds by incrementally reconstructing all inner nodes by c leaf/inner node with either another leaf/inner node or a proof sibling node, depending on whether each `proof_flags` item is true or false respectively. -CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure -that: 1) the tree is complete (but not necessarily perfect), 2) the leaves to be proven are in -the opposite order they are in the tree (i.e., as seen from right to left starting at the -deepest layer and continuing at the next layer). +[CAUTION] +==== +Not all Merkle trees admit multiproofs. +To use multiproofs, it is sufficient to ensure that: + +1. The tree is complete (but not necessarily perfect). +2. the leaves to be proven are in the opposite order they are in the tree +(i.e., as seen from right to left starting at the deepest layer and continuing at the next layer). +==== NOTE: The _empty set_ (i.e. the case where `proof.len() == 1 && leaves.len() == 0`) is considered a no-op, and therefore a valid multiproof (i.e. it returns `proof.at(0)`). Consider From b2a8fd69fe7101c0f61952893fdd5ad8b1546625 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Tue, 27 Aug 2024 13:23:29 +0200 Subject: [PATCH 24/29] Update packages/merkle_tree/src/tests/merkle_proof/test_with_poseidon.cairo Co-authored-by: Andrew Fleming --- .../merkle_tree/src/tests/merkle_proof/test_with_poseidon.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/merkle_tree/src/tests/merkle_proof/test_with_poseidon.cairo b/packages/merkle_tree/src/tests/merkle_proof/test_with_poseidon.cairo index 326476351..f26058f94 100644 --- a/packages/merkle_tree/src/tests/merkle_proof/test_with_poseidon.cairo +++ b/packages/merkle_tree/src/tests/merkle_proof/test_with_poseidon.cairo @@ -161,7 +161,7 @@ fn test_process_multi_proof_flags_extra_proofs_expected() { 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b ].span(); - // For each false one leaf is expected + // For each false one proof is expected let proof_flags = [true, false, false, false].span(); process_multi_proof::(proof, proof_flags, leaves_to_prove); From de1a13d12629fb6b35e8f4a3d6f44e7f73cab6d3 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Tue, 27 Aug 2024 13:23:35 +0200 Subject: [PATCH 25/29] Update packages/merkle_tree/src/tests/merkle_proof/test_with_poseidon.cairo Co-authored-by: Andrew Fleming --- .../merkle_tree/src/tests/merkle_proof/test_with_poseidon.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/merkle_tree/src/tests/merkle_proof/test_with_poseidon.cairo b/packages/merkle_tree/src/tests/merkle_proof/test_with_poseidon.cairo index f26058f94..affe89a90 100644 --- a/packages/merkle_tree/src/tests/merkle_proof/test_with_poseidon.cairo +++ b/packages/merkle_tree/src/tests/merkle_proof/test_with_poseidon.cairo @@ -199,7 +199,7 @@ fn test_verify_multi_proof_flags_extra_proofs_expected() { 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b ].span(); - // For each false one leaf is expected + // For each false one proof is expected let proof_flags = [true, false, false, false].span(); verify_multi_proof::(proof, proof_flags, root, leaves_to_prove); From 63928f9982e6a3c78cf4be74ea9cbb3902a26569 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Wed, 28 Aug 2024 21:32:24 +0200 Subject: [PATCH 26/29] Update docs/modules/ROOT/pages/api/merkle-tree.adoc Co-authored-by: immrsd <103599616+immrsd@users.noreply.github.com> --- docs/modules/ROOT/pages/api/merkle-tree.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/api/merkle-tree.adoc b/docs/modules/ROOT/pages/api/merkle-tree.adoc index fefea0a07..d639fd17f 100644 --- a/docs/modules/ROOT/pages/api/merkle-tree.adoc +++ b/docs/modules/ROOT/pages/api/merkle-tree.adoc @@ -41,7 +41,7 @@ The tree and the proofs can be generated using this {strk-merkle-tree}. You will WARNING: You should avoid using leaf values that are two felt252 long prior to hashing, or use a hash function other than the one used to hash internal nodes for hashing leaves. This is because the concatenation of a sorted pair -of internal nodes in the Merkle tree could be reinterpreted as a leaf value. the JavaScript library generates Merkle +of internal nodes in the Merkle tree could be reinterpreted as a leaf value. The JavaScript library generates Merkle trees that are safe against this attack out of the box. [.contract-index] From 56f849117e49f123c77d98f896141a7252941c87 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Wed, 28 Aug 2024 21:32:29 +0200 Subject: [PATCH 27/29] Update docs/modules/ROOT/pages/api/merkle-tree.adoc Co-authored-by: immrsd <103599616+immrsd@users.noreply.github.com> --- docs/modules/ROOT/pages/api/merkle-tree.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/api/merkle-tree.adoc b/docs/modules/ROOT/pages/api/merkle-tree.adoc index d639fd17f..563a33208 100644 --- a/docs/modules/ROOT/pages/api/merkle-tree.adoc +++ b/docs/modules/ROOT/pages/api/merkle-tree.adoc @@ -130,7 +130,7 @@ Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1. The tree is complete (but not necessarily perfect). -2. the leaves to be proven are in the opposite order they are in the tree +2. The leaves to be proven are in the opposite order they are in the tree. (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer). ==== From f9cae0ec2eee0dcb92ff2a0a4b5d399bd4bc2271 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Thu, 29 Aug 2024 13:36:12 +0200 Subject: [PATCH 28/29] feat: apply review updates --- .../merkle_proof/test_with_pedersen.cairo | 115 +++++++----------- .../merkle_proof/test_with_poseidon.cairo | 106 +++++++--------- 2 files changed, 89 insertions(+), 132 deletions(-) diff --git a/packages/merkle_tree/src/tests/merkle_proof/test_with_pedersen.cairo b/packages/merkle_tree/src/tests/merkle_proof/test_with_pedersen.cairo index 3f884ac90..11eae6183 100644 --- a/packages/merkle_tree/src/tests/merkle_proof/test_with_pedersen.cairo +++ b/packages/merkle_tree/src/tests/merkle_proof/test_with_pedersen.cairo @@ -7,6 +7,21 @@ use openzeppelin_merkle_tree::merkle_proof::{ use starknet::{ContractAddress, contract_address_const}; use super::common::{Leaf, LEAVES}; +// `ROOT`, `PROOF`, and `MULTI_PROOF` were computed using @ericnordelo/strk-merkle-tree +const ROOT: felt252 = 0x02a40717603180fa52f40a55508cd360d301840f3e502665cf0132ef412911de; +const PROOF: [ + felt252 + ; 2] = [ + 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, + 0x02b0ee474cf2ab27501e54a661d17ac1dc162571c111fe2455d09fe23471099e +]; +const MULTI_PROOF: [ + felt252 + ; 2] = [ + 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, + 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b +]; + // // verify // @@ -15,40 +30,33 @@ use super::common::{Leaf, LEAVES}; fn test_valid_merkle_proof() { let leaves = LEAVES(); let hash = leaf_hash(*leaves.at(0)); - // `root` and `proof` were computed using @ericnordelo/strk-merkle-tree - let root = 0x02a40717603180fa52f40a55508cd360d301840f3e502665cf0132ef412911de; - let proof = [ - 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, - 0x02b0ee474cf2ab27501e54a661d17ac1dc162571c111fe2455d09fe23471099e - ].span(); + let proof = PROOF.span(); - assert_eq!(process_proof::(proof, hash), root); - assert!(verify::(proof, root, hash)); - assert!(verify_pedersen(proof, root, hash)); + assert_eq!(process_proof::(proof, hash), ROOT); + assert!(verify::(proof, ROOT, hash)); + assert!(verify_pedersen(proof, ROOT, hash)); // For demonstration, it is also possible to create valid // proofs for certain values *NOT* in elements: let no_such_leaf = PedersenCHasher::commutative_hash(hash, *proof.at(0)); let second_proof = [0x02b0ee474cf2ab27501e54a661d17ac1dc162571c111fe2455d09fe23471099e].span(); - assert_eq!(process_proof::(second_proof, no_such_leaf), root); - assert!(verify::(second_proof, root, no_such_leaf)); - assert!(verify_pedersen(second_proof, root, no_such_leaf)); + assert_eq!(process_proof::(second_proof, no_such_leaf), ROOT); + assert!(verify::(second_proof, ROOT, no_such_leaf)); + assert!(verify_pedersen(second_proof, ROOT, no_such_leaf)); } #[test] fn test_invalid_merkle_proof() { let leaves = LEAVES(); let hash = leaf_hash(*leaves.at(0)); - // `root` was computed using @ericnordelo/strk-merkle-tree - let root = 0x02a40717603180fa52f40a55508cd360d301840f3e502665cf0132ef412911de; let invalid_proof = [ 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, 'invalid' ].span(); - assert!(process_proof::(invalid_proof, hash) != root); - assert!(!verify::(invalid_proof, root, hash)); - assert!(!verify_pedersen(invalid_proof, root, hash)); + assert!(process_proof::(invalid_proof, hash) != ROOT); + assert!(!verify::(invalid_proof, ROOT, hash)); + assert!(!verify_pedersen(invalid_proof, ROOT, hash)); } // @@ -59,47 +67,39 @@ fn test_invalid_merkle_proof() { fn test_valid_merkle_multi_proof() { let leaves = LEAVES(); let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1))].span(); - // `root`, `proof`, and `proof_flags` were computed using @ericnordelo/strk-merkle-tree - let root = 0x02a40717603180fa52f40a55508cd360d301840f3e502665cf0132ef412911de; - let proof = [ - 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, - 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b - ].span(); + let proof = MULTI_PROOF.span(); let proof_flags = [false, false, true].span(); - assert_eq!(process_multi_proof::(proof, proof_flags, leaves_to_prove), root); - assert!(verify_multi_proof::(proof, proof_flags, root, leaves_to_prove)); + assert_eq!(process_multi_proof::(proof, proof_flags, leaves_to_prove), ROOT); + assert!(verify_multi_proof::(proof, proof_flags, ROOT, leaves_to_prove)); } #[test] fn test_invalid_merkle_multi_proof() { let leaves = LEAVES(); let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1))].span(); - // `root` and `proof_flags` were computed using @ericnordelo/strk-merkle-tree - let root = 0x02a40717603180fa52f40a55508cd360d301840f3e502665cf0132ef412911de; - let proof = [ + let invalid_proof = [ 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, 'invalid' ].span(); let proof_flags = [false, false, true].span(); - assert!(process_multi_proof::(proof, proof_flags, leaves_to_prove) != root); - assert!(!verify_multi_proof::(proof, proof_flags, root, leaves_to_prove)); + assert!( + process_multi_proof::(invalid_proof, proof_flags, leaves_to_prove) != ROOT + ); + assert!( + !verify_multi_proof::(invalid_proof, proof_flags, ROOT, leaves_to_prove) + ); } #[test] fn test_invalid_merkle_multi_proof_flags() { let leaves = LEAVES(); let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1))].span(); - // `root` and `proof` were computed using @ericnordelo/strk-merkle-tree - let root = 0x02a40717603180fa52f40a55508cd360d301840f3e502665cf0132ef412911de; - let proof = [ - 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, - 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b - ].span(); + let proof = MULTI_PROOF.span(); let proof_flags = [false, true, false].span(); - assert!(process_multi_proof::(proof, proof_flags, leaves_to_prove) != root); - assert!(!verify_multi_proof::(proof, proof_flags, root, leaves_to_prove)); + assert!(process_multi_proof::(proof, proof_flags, leaves_to_prove) != ROOT); + assert!(!verify_multi_proof::(proof, proof_flags, ROOT, leaves_to_prove)); } #[test] @@ -107,10 +107,7 @@ fn test_invalid_merkle_multi_proof_flags() { fn test_process_multi_proof_invalid_len_proof_flags_panics() { let leaves = LEAVES(); let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1))].span(); - let proof = [ - 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, - 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b - ].span(); + let proof = MULTI_PROOF.span(); // `proof_flags.len()` is not equal to `proof.len() + leaves_to_prove.len() + 1` let proof_flags = [true, false].span(); @@ -123,16 +120,12 @@ fn test_process_multi_proof_invalid_len_proof_flags_panics() { fn test_verify_multi_proof_invalid_len_proof_flags_panics() { let leaves = LEAVES(); let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1))].span(); - let root = 0x02a40717603180fa52f40a55508cd360d301840f3e502665cf0132ef412911de; - let proof = [ - 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, - 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b - ].span(); + let proof = MULTI_PROOF.span(); // `proof_flags.len()` is not equal to `proof.len() + leaves_to_prove.len() + 1` let proof_flags = [true, false].span(); - verify_multi_proof::(proof, proof_flags, root, leaves_to_prove); + verify_multi_proof::(proof, proof_flags, ROOT, leaves_to_prove); } #[test] @@ -142,10 +135,7 @@ fn test_process_multi_proof_flags_extra_leaves_expected() { let leaves_to_prove = [ leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2)) ].span(); - let proof = [ - 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, - 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b - ].span(); + let proof = MULTI_PROOF.span(); // For each true one leaf is expected let proof_flags = [true, true, true, true].span(); @@ -160,10 +150,7 @@ fn test_process_multi_proof_flags_extra_proofs_expected() { let leaves_to_prove = [ leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2)) ].span(); - let proof = [ - 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, - 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b - ].span(); + let proof = MULTI_PROOF.span(); // For each false one proof is expected let proof_flags = [true, false, false, false].span(); @@ -178,16 +165,12 @@ fn test_verify_multi_proof_flags_extra_leaves_expected() { let leaves_to_prove = [ leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2)) ].span(); - let root = 0x02a40717603180fa52f40a55508cd360d301840f3e502665cf0132ef412911de; - let proof = [ - 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, - 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b - ].span(); + let proof = MULTI_PROOF.span(); // For each true one leaf is expected let proof_flags = [true, true, true, true].span(); - verify_multi_proof::(proof, proof_flags, root, leaves_to_prove); + verify_multi_proof::(proof, proof_flags, ROOT, leaves_to_prove); } #[test] @@ -197,16 +180,12 @@ fn test_verify_multi_proof_flags_extra_proofs_expected() { let leaves_to_prove = [ leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2)) ].span(); - let root = 0x02a40717603180fa52f40a55508cd360d301840f3e502665cf0132ef412911de; - let proof = [ - 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, - 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b - ].span(); + let proof = MULTI_PROOF.span(); // For each false one proof is expected let proof_flags = [true, false, false, false].span(); - verify_multi_proof::(proof, proof_flags, root, leaves_to_prove); + verify_multi_proof::(proof, proof_flags, ROOT, leaves_to_prove); } // diff --git a/packages/merkle_tree/src/tests/merkle_proof/test_with_poseidon.cairo b/packages/merkle_tree/src/tests/merkle_proof/test_with_poseidon.cairo index 326476351..c49865afc 100644 --- a/packages/merkle_tree/src/tests/merkle_proof/test_with_poseidon.cairo +++ b/packages/merkle_tree/src/tests/merkle_proof/test_with_poseidon.cairo @@ -6,6 +6,21 @@ use openzeppelin_merkle_tree::merkle_proof::{ use starknet::{ContractAddress, contract_address_const}; use super::common::{Leaf, LEAVES}; +// `ROOT`, `PROOF`, and `MULTI_PROOF` were computed using @ericnordelo/strk-merkle-tree +const ROOT: felt252 = 0x013f43fdca44b32f5334414b385b46aa1016d0172a1f066eab4cc93636426fcc; +const PROOF: [ + felt252 + ; 2] = [ + 0x05b151ebb9201ce27c56a70f5d0571ccfb9d9d62f12b8ccab7801ba87ec21a2f, + 0x2b7d689bd2ff488fd06dfb8eb22f5cdaba1e5d9698d3fabff2f1801852dbb2 +]; +const MULTI_PROOF: [ + felt252 + ; 2] = [ + 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, + 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b +]; + // // verify // @@ -14,40 +29,33 @@ use super::common::{Leaf, LEAVES}; fn test_valid_merkle_proof() { let leaves = LEAVES(); let hash = leaf_hash(*leaves.at(0)); - // `root` and `proof` were computed using @ericnordelo/strk-merkle-tree - let root = 0x013f43fdca44b32f5334414b385b46aa1016d0172a1f066eab4cc93636426fcc; - let proof = [ - 0x05b151ebb9201ce27c56a70f5d0571ccfb9d9d62f12b8ccab7801ba87ec21a2f, - 0x2b7d689bd2ff488fd06dfb8eb22f5cdaba1e5d9698d3fabff2f1801852dbb2 - ].span(); + let proof = PROOF.span(); - assert_eq!(process_proof::(proof, hash), root); - assert!(verify::(proof, root, hash)); - assert!(verify_poseidon(proof, root, hash)); + assert_eq!(process_proof::(proof, hash), ROOT); + assert!(verify::(proof, ROOT, hash)); + assert!(verify_poseidon(proof, ROOT, hash)); // For demonstration, it is also possible to create valid // proofs for certain values *NOT* in elements: let no_such_leaf = PoseidonCHasher::commutative_hash(hash, *proof.at(0)); let second_proof = [0x2b7d689bd2ff488fd06dfb8eb22f5cdaba1e5d9698d3fabff2f1801852dbb2].span(); - assert_eq!(process_proof::(second_proof, no_such_leaf), root); - assert!(verify::(second_proof, root, no_such_leaf)); - assert!(verify_poseidon(second_proof, root, no_such_leaf)); + assert_eq!(process_proof::(second_proof, no_such_leaf), ROOT); + assert!(verify::(second_proof, ROOT, no_such_leaf)); + assert!(verify_poseidon(second_proof, ROOT, no_such_leaf)); } #[test] fn test_invalid_merkle_proof() { let leaves = LEAVES(); let hash = leaf_hash(*leaves.at(0)); - // `root` was computed using @ericnordelo/strk-merkle-tree - let root = 0x013f43fdca44b32f5334414b385b46aa1016d0172a1f066eab4cc93636426fcc; let invalid_proof = [ 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, 'invalid' ].span(); - assert!(process_proof::(invalid_proof, hash) != root); - assert!(!verify::(invalid_proof, root, hash)); - assert!(!verify_poseidon(invalid_proof, root, hash)); + assert!(process_proof::(invalid_proof, hash) != ROOT); + assert!(!verify::(invalid_proof, ROOT, hash)); + assert!(!verify_poseidon(invalid_proof, ROOT, hash)); } // @@ -58,44 +66,35 @@ fn test_invalid_merkle_proof() { fn test_valid_merkle_multi_proof() { let leaves = LEAVES(); let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1))].span(); - // `root`, `proof`, and `proof_flags` were computed using @ericnordelo/strk-merkle-tree - let root = 0x013f43fdca44b32f5334414b385b46aa1016d0172a1f066eab4cc93636426fcc; let proof = [0x2b7d689bd2ff488fd06dfb8eb22f5cdaba1e5d9698d3fabff2f1801852dbb2].span(); let proof_flags = [true, false].span(); - assert_eq!(process_multi_proof::(proof, proof_flags, leaves_to_prove), root); - assert!(verify_multi_proof::(proof, proof_flags, root, leaves_to_prove)); + assert_eq!(process_multi_proof::(proof, proof_flags, leaves_to_prove), ROOT); + assert!(verify_multi_proof::(proof, proof_flags, ROOT, leaves_to_prove)); } #[test] fn test_invalid_merkle_multi_proof() { let leaves = LEAVES(); let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1))].span(); - // `root` and `proof_flags` were computed using @ericnordelo/strk-merkle-tree - let root = 0x013f43fdca44b32f5334414b385b46aa1016d0172a1f066eab4cc93636426fcc; - let proof = [ + let invalid_proof = [ 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, 'invalid' ].span(); let proof_flags = [false, false, true].span(); - assert!(process_multi_proof::(proof, proof_flags, leaves_to_prove) != root); - assert!(!verify_multi_proof::(proof, proof_flags, root, leaves_to_prove)); + assert!(process_multi_proof::(invalid_proof, proof_flags, leaves_to_prove) != ROOT); + assert!(!verify_multi_proof::(invalid_proof, proof_flags, ROOT, leaves_to_prove)); } #[test] fn test_invalid_merkle_multi_proof_flags() { let leaves = LEAVES(); let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1))].span(); - // `root` and `proof` were computed using @ericnordelo/strk-merkle-tree - let root = 0x013f43fdca44b32f5334414b385b46aa1016d0172a1f066eab4cc93636426fcc; - let proof = [ - 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, - 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b - ].span(); + let proof = MULTI_PROOF.span(); let proof_flags = [false, true, false].span(); - assert!(process_multi_proof::(proof, proof_flags, leaves_to_prove) != root); - assert!(!verify_multi_proof::(proof, proof_flags, root, leaves_to_prove)); + assert!(process_multi_proof::(proof, proof_flags, leaves_to_prove) != ROOT); + assert!(!verify_multi_proof::(proof, proof_flags, ROOT, leaves_to_prove)); } #[test] @@ -103,10 +102,7 @@ fn test_invalid_merkle_multi_proof_flags() { fn test_process_multi_proof_invalid_len_proof_flags_panics() { let leaves = LEAVES(); let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1))].span(); - let proof = [ - 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, - 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b - ].span(); + let proof = MULTI_PROOF.span(); // `proof_flags.len()` is not equal to `proof.len() + leaves_to_prove.len() + 1` let proof_flags = [true, false].span(); @@ -119,16 +115,12 @@ fn test_process_multi_proof_invalid_len_proof_flags_panics() { fn test_verify_multi_proof_invalid_len_proof_flags_panics() { let leaves = LEAVES(); let leaves_to_prove = [leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1))].span(); - let root = 0x013f43fdca44b32f5334414b385b46aa1016d0172a1f066eab4cc93636426fcc; - let proof = [ - 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, - 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b - ].span(); + let proof = MULTI_PROOF.span(); // `proof_flags.len()` is not equal to `proof.len() + leaves_to_prove.len() + 1` let proof_flags = [true, false].span(); - verify_multi_proof::(proof, proof_flags, root, leaves_to_prove); + verify_multi_proof::(proof, proof_flags, ROOT, leaves_to_prove); } #[test] @@ -138,10 +130,7 @@ fn test_process_multi_proof_flags_extra_leaves_expected() { let leaves_to_prove = [ leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2)) ].span(); - let proof = [ - 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, - 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b - ].span(); + let proof = MULTI_PROOF.span(); // For each true one leaf is expected let proof_flags = [true, true, true, true].span(); @@ -156,10 +145,7 @@ fn test_process_multi_proof_flags_extra_proofs_expected() { let leaves_to_prove = [ leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2)) ].span(); - let proof = [ - 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, - 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b - ].span(); + let proof = MULTI_PROOF.span(); // For each false one leaf is expected let proof_flags = [true, false, false, false].span(); @@ -174,16 +160,12 @@ fn test_verify_multi_proof_flags_extra_leaves_expected() { let leaves_to_prove = [ leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2)) ].span(); - let root = 0x013f43fdca44b32f5334414b385b46aa1016d0172a1f066eab4cc93636426fcc; - let proof = [ - 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, - 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b - ].span(); + let proof = MULTI_PROOF.span(); // For each true one leaf is expected let proof_flags = [true, true, true, true].span(); - verify_multi_proof::(proof, proof_flags, root, leaves_to_prove); + verify_multi_proof::(proof, proof_flags, ROOT, leaves_to_prove); } #[test] @@ -193,16 +175,12 @@ fn test_verify_multi_proof_flags_extra_proofs_expected() { let leaves_to_prove = [ leaf_hash(*leaves.at(0)), leaf_hash(*leaves.at(1)), leaf_hash(*leaves.at(2)) ].span(); - let root = 0x013f43fdca44b32f5334414b385b46aa1016d0172a1f066eab4cc93636426fcc; - let proof = [ - 0x044fdc540a81d0189ed30b49d64136f9e8bd499c942ba170404ef0b9406e524c, - 0x05fb6a626bb2c1e12fc2d6fa7f218ec06928ba5febf4d5677c2c5060827e383b - ].span(); + let proof = MULTI_PROOF.span(); // For each false one leaf is expected let proof_flags = [true, false, false, false].span(); - verify_multi_proof::(proof, proof_flags, root, leaves_to_prove); + verify_multi_proof::(proof, proof_flags, ROOT, leaves_to_prove); } // From f7b839c1d857bc3caa973badf3a9f795c9914dfa Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Thu, 29 Aug 2024 13:36:40 +0200 Subject: [PATCH 29/29] feat: format files --- .../src/tests/merkle_proof/test_with_poseidon.cairo | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/merkle_tree/src/tests/merkle_proof/test_with_poseidon.cairo b/packages/merkle_tree/src/tests/merkle_proof/test_with_poseidon.cairo index c49865afc..22458c7d4 100644 --- a/packages/merkle_tree/src/tests/merkle_proof/test_with_poseidon.cairo +++ b/packages/merkle_tree/src/tests/merkle_proof/test_with_poseidon.cairo @@ -82,8 +82,12 @@ fn test_invalid_merkle_multi_proof() { ].span(); let proof_flags = [false, false, true].span(); - assert!(process_multi_proof::(invalid_proof, proof_flags, leaves_to_prove) != ROOT); - assert!(!verify_multi_proof::(invalid_proof, proof_flags, ROOT, leaves_to_prove)); + assert!( + process_multi_proof::(invalid_proof, proof_flags, leaves_to_prove) != ROOT + ); + assert!( + !verify_multi_proof::(invalid_proof, proof_flags, ROOT, leaves_to_prove) + ); } #[test]