diff --git a/CHANGELOG.md b/CHANGELOG.md index cea38a85..15c15a39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - [\#59](https://github.com/arkworks-rs/crypto-primitives/pull/59) Implement `TwoToOneCRHScheme` for Bowe-Hopwood CRH. - [\#60](https://github.com/arkworks-rs/crypto-primitives/pull/60) Merkle tree no longer requires CRH to input and output bytes. Leaf can be any raw input of CRH, such as field elements. - [\#67](https://github.com/arkworks-rs/crypto-primitives/pull/67) User can access or replace leaf index variable in `PathVar`. +- [\#72](https://github.com/arkworks-rs/crypto-primitives/pull/72) Add incremental Merkle tree implementation. ### Improvements diff --git a/src/lib.rs b/src/lib.rs index f1de5d1c..93f93648 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,7 +29,7 @@ pub mod snark; pub use self::{ commitment::CommitmentScheme, crh::CRHScheme, - merkle_tree::{MerkleTree, Path}, + merkle_tree::{incremental_merkle_tree::IncrementalMerkleTree, MerkleTree, Path}, prf::PRF, signature::SignatureScheme, snark::{CircuitSpecificSetupSNARK, UniversalSetupSNARK, SNARK}, diff --git a/src/merkle_tree/incremental_merkle_tree.rs b/src/merkle_tree/incremental_merkle_tree.rs new file mode 100644 index 00000000..bf50fc61 --- /dev/null +++ b/src/merkle_tree/incremental_merkle_tree.rs @@ -0,0 +1,277 @@ +use crate::crh::TwoToOneCRHScheme; +use crate::merkle_tree::{Config, DigestConverter, LeafParam, Path, TwoToOneParam}; +use crate::CRHScheme; +use ark_std::borrow::Borrow; +use ark_std::vec::Vec; + +/// Defines an incremental merkle tree data structure. +/// This merkle tree has runtime fixed height, and assumes number of leaves is 2^height. +/// +#[derive(Derivative)] +#[derivative(Clone(bound = "P: Config"))] +pub struct IncrementalMerkleTree { + /// Store the hash of leaf nodes from left to right + leaf_nodes: Vec, + /// Store the inner hash parameters + two_to_one_hash_param: TwoToOneParam

, + /// Store the leaf hash parameters + leaf_hash_param: LeafParam

, + /// Stores the height of the MerkleTree + height: usize, + /// Stores the path of the "current leaf" + current_path: Path

, + /// Stores the root of the IMT + root: P::InnerDigest, + /// Is the IMT empty + empty: bool, +} + +impl IncrementalMerkleTree

{ + /// Check if this IMT is empty + pub fn is_empty(&self) -> bool { + self.empty + } + + /// The index of the current right most leaf + pub fn current_index(&self) -> Option { + if self.is_empty() { + None + } else { + Some(self.current_path.leaf_index) + } + } + + /// The next available index of leaf node + pub fn next_available(&self) -> Option { + let current_index = self.current_path.leaf_index; + if self.is_empty() { + Some(0) + } else if current_index < (1 << (self.height - 1)) - 1 { + Some(current_index + 1) + } else { + None + } + } + + /// Create an empty merkle tree such that all leaves are zero-filled. + pub fn blank( + leaf_hash_param: &LeafParam

, + two_to_one_hash_param: &TwoToOneParam

, + height: usize, + ) -> Result { + assert!( + height > 1, + "the height of incremental merkle tree should be at least 2" + ); + // use empty leaf digest + let leaves_digest = vec![]; + Ok(IncrementalMerkleTree { + /// blank tree doesn't have current_path + current_path: Path { + leaf_sibling_hash: P::LeafDigest::default(), + auth_path: Vec::new(), + leaf_index: 0, + }, + leaf_nodes: leaves_digest, + two_to_one_hash_param: two_to_one_hash_param.clone(), + leaf_hash_param: leaf_hash_param.clone(), + root: P::InnerDigest::default(), + height, + empty: true, + }) + } + + /// Append leaf at `next_available` + /// ```tree_diagram + /// [A] + /// / \ + /// [B] () + /// / \ / \ + /// D [E] () () + /// .. / \ .... + /// [I]{new leaf} + /// ``` + /// append({new leaf}) when the `next_availabe` is at 4, would cause a recompute [E], [B], [A] + pub fn append>(&mut self, new_leaf: T) -> Result<(), crate::Error> { + assert!(self.next_available() != None, "index out of range"); + let leaf_digest = P::LeafHash::evaluate(&self.leaf_hash_param, new_leaf)?; + let (path, root) = self.next_path(leaf_digest.clone())?; + self.leaf_nodes.push(leaf_digest); + self.current_path = path; + self.root = root; + self.empty = false; + Ok(()) + } + + /// Generate updated path of `next_available` without changing the tree + /// returns (new_path, new_root) + pub fn next_path( + &self, + new_leaf_digest: P::LeafDigest, + ) -> Result<(Path

, P::InnerDigest), crate::Error> { + assert!(self.next_available() != None, "index out of range"); + + // calculate tree_height and empty hash + let tree_height = self.height; + let hash_of_empty_node: P::InnerDigest = P::InnerDigest::default(); + let hash_of_empty_leaf: P::LeafDigest = P::LeafDigest::default(); + + // auth path has the capacity of tree_hight - 2 + let mut new_auth_path = Vec::with_capacity(tree_height - 2); + + if self.is_empty() { + // generate auth path and calculate the root + let mut current_node = P::TwoToOneHash::evaluate( + &self.two_to_one_hash_param, + P::LeafInnerDigestConverter::convert(new_leaf_digest)?, + P::LeafInnerDigestConverter::convert(P::LeafDigest::default())?, + )?; + // all the auth path node are empty nodes + for _ in 0..tree_height - 2 { + new_auth_path.push(hash_of_empty_node.clone()); + current_node = P::TwoToOneHash::compress( + &self.two_to_one_hash_param, + current_node, + hash_of_empty_node.clone(), + )?; + } + + let path = Path { + leaf_index: 0, + auth_path: new_auth_path, + leaf_sibling_hash: hash_of_empty_leaf, + }; + Ok((path, current_node)) + } else { + // compute next path of a non-empty tree + // Get the indices of the previous and propsed (new) leaf node + let mut new_index = self.next_available().unwrap(); + let mut old_index = self.current_index().unwrap(); + let old_leaf = self.leaf_nodes[old_index].clone(); + + // generate two mutable node: old_current_node, new_current_node to iterate on + let (old_left_leaf, old_right_leaf) = if is_left_child(old_index) { + ( + self.leaf_nodes[old_index].clone(), + self.current_path.leaf_sibling_hash.clone(), + ) + } else { + ( + self.current_path.leaf_sibling_hash.clone(), + self.leaf_nodes[old_index].clone(), + ) + }; + + let (new_left_leaf, new_right_leaf, leaf_sibling) = if is_left_child(new_index) { + ( + new_leaf_digest, + hash_of_empty_leaf.clone(), + hash_of_empty_leaf, + ) + } else { + (old_leaf.clone(), new_leaf_digest, old_leaf) + }; + + let mut old_current_node = P::TwoToOneHash::evaluate( + &self.two_to_one_hash_param, + P::LeafInnerDigestConverter::convert(old_left_leaf)?, + P::LeafInnerDigestConverter::convert(old_right_leaf)?, + )?; + let mut new_current_node = P::TwoToOneHash::evaluate( + &self.two_to_one_hash_param, + P::LeafInnerDigestConverter::convert(new_left_leaf)?, + P::LeafInnerDigestConverter::convert(new_right_leaf)?, + )?; + + // reverse the old_auth_path to make it bottom up + let mut old_auth_path = self.current_path.auth_path.clone(); + old_auth_path.reverse(); + + // build new_auth_path and root recursively + for x in 0..tree_height - 2 { + new_index = parent_index_on_level(new_index); + old_index = parent_index_on_level(old_index); + if new_index == old_index { + // this means the old path and new path are merged, + // as a result, no need to update the old_current_node any more + + // add the auth path node + new_auth_path.push(old_auth_path[x].clone()); + + // update the new current node (this is needed to compute the root) + let (new_left, new_right) = if is_left_child(new_index) { + (new_current_node, hash_of_empty_node.clone()) + } else { + (old_auth_path[x].clone(), new_current_node) + }; + new_current_node = P::TwoToOneHash::compress( + &self.two_to_one_hash_param, + new_left, + new_right, + )?; + } else { + // this means old path and new path haven't been merged, + // as a reulst, need to update both the new_current_node and new_current_node + let auth_node = if is_left_child(new_index) { + hash_of_empty_node.clone() + } else { + old_current_node.clone() + }; + new_auth_path.push(auth_node); + + // update both old_current_node and new_current_node + // update new_current_node + let (new_left, new_right) = if is_left_child(new_index) { + (new_current_node.clone(), hash_of_empty_node.clone()) + } else { + (old_current_node.clone(), new_current_node) + }; + new_current_node = P::TwoToOneHash::compress( + &self.two_to_one_hash_param, + new_left, + new_right, + )?; + + // We only need to update the old_current_node bottom up when it is right child + if !is_left_child(old_index) { + old_current_node = P::TwoToOneHash::compress( + &self.two_to_one_hash_param, + old_auth_path[x].clone(), + old_current_node, + )?; + } + } + } + + // reverse new_auth_path to top down + new_auth_path.reverse(); + let path = Path { + leaf_index: self.next_available().unwrap(), + auth_path: new_auth_path, + leaf_sibling_hash: leaf_sibling, + }; + Ok((path, new_current_node)) + } + } + + /// the proof of the current item + pub fn current_proof(&self) -> Path

{ + self.current_path.clone() + } + + /// root of IMT + pub fn root(&self) -> P::InnerDigest { + self.root.clone() + } +} + +/// Return true iff the given index on its current level represents a left child +#[inline] +fn is_left_child(index_on_level: usize) -> bool { + index_on_level % 2 == 0 +} + +#[inline] +fn parent_index_on_level(index_on_level: usize) -> usize { + index_on_level >> 1 +} diff --git a/src/merkle_tree/mod.rs b/src/merkle_tree/mod.rs index 998dd125..42300935 100644 --- a/src/merkle_tree/mod.rs +++ b/src/merkle_tree/mod.rs @@ -9,6 +9,8 @@ use ark_std::borrow::Borrow; use ark_std::hash::Hash; use ark_std::vec::Vec; +pub mod incremental_merkle_tree; + #[cfg(test)] mod tests; @@ -146,7 +148,7 @@ impl Path

{ leaf: L, ) -> Result { // calculate leaf hash - let claimed_leaf_hash = P::LeafHash::evaluate(&leaf_hash_params, leaf)?; + let claimed_leaf_hash = P::LeafHash::evaluate(leaf_hash_params, leaf)?; // check hash along the path from bottom to root let (left_child, right_child) = select_left_right_child(self.leaf_index, &claimed_leaf_hash, &self.leaf_sibling_hash)?; @@ -156,7 +158,7 @@ impl Path

{ let right_child = P::LeafInnerDigestConverter::convert(right_child)?; let mut curr_path_node = - P::TwoToOneHash::evaluate(&two_to_one_params, left_child, right_child)?; + P::TwoToOneHash::evaluate(two_to_one_params, left_child, right_child)?; // we will use `index` variable to track the position of path let mut index = self.leaf_index; @@ -168,7 +170,7 @@ impl Path

{ let (left, right) = select_left_right_child(index, &curr_path_node, &self.auth_path[level])?; // update curr_path_node - curr_path_node = P::TwoToOneHash::compress(&two_to_one_params, &left, &right)?; + curr_path_node = P::TwoToOneHash::compress(two_to_one_params, &left, &right)?; index >>= 1; } @@ -311,7 +313,7 @@ impl MerkleTree

{ let left_index = left_child(current_index); let right_index = right_child(current_index); non_leaf_nodes[current_index] = P::TwoToOneHash::compress( - &two_to_one_hash_param, + two_to_one_hash_param, non_leaf_nodes[left_index].clone(), non_leaf_nodes[right_index].clone(), )? diff --git a/src/merkle_tree/tests/constraints.rs b/src/merkle_tree/tests/constraints.rs index 9e7dd091..42576aed 100644 --- a/src/merkle_tree/tests/constraints.rs +++ b/src/merkle_tree/tests/constraints.rs @@ -3,7 +3,7 @@ mod byte_mt_tests { use crate::merkle_tree::constraints::{BytesVarDigestConverter, ConfigGadget}; use crate::merkle_tree::{ByteDigestConverter, Config}; - use crate::{CRHScheme, CRHSchemeGadget, MerkleTree, PathVar}; + use crate::{CRHScheme, CRHSchemeGadget, IncrementalMerkleTree, MerkleTree, PathVar}; use ark_ed_on_bls12_381::{constraints::EdwardsVar, EdwardsProjective as JubJub, Fq}; #[allow(unused)] use ark_r1cs_std::prelude::*; @@ -50,6 +50,7 @@ mod byte_mt_tests { } type JubJubMerkleTree = MerkleTree; + type JubJubIncrementalMerkleTree = IncrementalMerkleTree; /// Generate a merkle tree, its constraints, and test its constraints fn merkle_tree_test( @@ -216,6 +217,112 @@ mod byte_mt_tests { } } + /// Generate a merkle tree, its constraints, and test its constraints + fn incremental_merkle_tree_test( + updates: &[Vec], + use_bad_root: bool, + tree_height: usize, + ) -> () { + let mut rng = ark_std::test_rng(); + + let leaf_crh_params = ::setup(&mut rng).unwrap(); + let two_to_one_crh_params = ::setup(&mut rng).unwrap(); + let mut tree = JubJubIncrementalMerkleTree::blank( + &leaf_crh_params, + &two_to_one_crh_params, + tree_height, + ) + .unwrap(); + for leaf in updates { + let cs = ConstraintSystem::::new_ref(); + tree.append(leaf.as_slice()).unwrap(); + let proof = tree.current_proof(); + assert!(proof + .verify( + &leaf_crh_params, + &two_to_one_crh_params, + &tree.root(), + leaf.as_slice() + ) + .unwrap()); + + // Allocate Merkle Tree Root + let root = >::OutputVar::new_witness( + ark_relations::ns!(cs, "new_digest"), + || { + if use_bad_root { + Ok(::Output::default()) + } else { + Ok(tree.root()) + } + }, + ) + .unwrap(); + + let constraints_from_digest = cs.num_constraints(); + println!("constraints from digest: {}", constraints_from_digest); + + // Allocate Parameters for CRH + let leaf_crh_params_var = + >::ParametersVar::new_constant( + ark_relations::ns!(cs, "leaf_crh_parameter"), + &leaf_crh_params, + ) + .unwrap(); + let two_to_one_crh_params_var = + >::ParametersVar::new_constant( + ark_relations::ns!(cs, "two_to_one_crh_parameter"), + &two_to_one_crh_params, + ) + .unwrap(); + + let constraints_from_params = cs.num_constraints() - constraints_from_digest; + println!("constraints from parameters: {}", constraints_from_params); + + // Allocate Leaf + let leaf_g = UInt8::new_input_vec(cs.clone(), leaf).unwrap(); + + let constraints_from_leaf = + cs.num_constraints() - constraints_from_params - constraints_from_digest; + println!("constraints from leaf: {}", constraints_from_leaf); + + // Allocate Merkle Tree Path + let cw: PathVar = + PathVar::new_witness(ark_relations::ns!(cs, "new_witness"), || Ok(&proof)).unwrap(); + + let constraints_from_path = cs.num_constraints() + - constraints_from_params + - constraints_from_digest + - constraints_from_leaf; + println!("constraints from path: {}", constraints_from_path); + + assert!(cs.is_satisfied().unwrap()); + assert!(cw + .verify_membership( + &leaf_crh_params_var, + &two_to_one_crh_params_var, + &root, + &leaf_g, + ) + .unwrap() + .value() + .unwrap()); + let setup_constraints = constraints_from_leaf + + constraints_from_digest + + constraints_from_params + + constraints_from_path; + println!( + "number of constraints: {}", + cs.num_constraints() - setup_constraints + ); + + assert!( + cs.is_satisfied().unwrap(), + "verification constraints not satisfied" + ); + } + } + #[test] fn good_root_test() { let mut leaves = Vec::new(); @@ -226,6 +333,15 @@ mod byte_mt_tests { merkle_tree_test(&leaves, false, Some((3usize, vec![7u8; 30]))); } + #[test] + fn good_root_test_for_imt() { + let mut updates = Vec::new(); + for i in 0..4u8 { + updates.push(vec![i; 30]); + } + incremental_merkle_tree_test(&updates, false, 3); + } + #[test] #[should_panic] fn bad_root_test() { @@ -236,6 +352,16 @@ mod byte_mt_tests { } merkle_tree_test(&leaves, true, None); } + + #[test] + #[should_panic] + fn bad_root_test_for_imt() { + let mut updates = Vec::new(); + for i in 0..4u8 { + updates.push(vec![i; 30]); + } + incremental_merkle_tree_test(&updates, true, 3); + } } mod field_mt_tests { @@ -243,7 +369,7 @@ mod field_mt_tests { use crate::merkle_tree::constraints::ConfigGadget; use crate::merkle_tree::tests::test_utils::poseidon_parameters; use crate::merkle_tree::{Config, IdentityDigestConverter}; - use crate::{CRHSchemeGadget, MerkleTree, PathVar}; + use crate::{CRHSchemeGadget, IncrementalMerkleTree, MerkleTree, PathVar}; use ark_r1cs_std::alloc::AllocVar; use ark_r1cs_std::fields::fp::FpVar; use ark_r1cs_std::uint32::UInt32; @@ -281,6 +407,7 @@ mod field_mt_tests { } type FieldMT = MerkleTree; + type FieldIMT = IncrementalMerkleTree; fn merkle_tree_test( leaves: &[Vec], @@ -455,6 +582,100 @@ mod field_mt_tests { } } + fn incremental_merkle_tree_test(updates: &[Vec], use_bad_root: bool, tree_height: usize) { + let leaf_crh_params = poseidon_parameters(); + let two_to_one_params = leaf_crh_params.clone(); + let mut tree = FieldIMT::blank(&leaf_crh_params, &two_to_one_params, tree_height).unwrap(); + + for leaf in updates { + let cs = ConstraintSystem::::new_ref(); + tree.append(leaf.as_slice()).unwrap(); + let proof = tree.current_proof(); + let root = tree.root(); + assert!(proof + .verify(&leaf_crh_params, &two_to_one_params, &root, leaf.as_slice()) + .unwrap()); + // Allocate MT root + let root = FpVar::new_witness(cs.clone(), || { + if use_bad_root { + Ok(root + F::one()) + } else { + Ok(root) + } + }) + .unwrap(); + + let constraints_from_digest = cs.num_constraints(); + println!("constraints from digest: {}", constraints_from_digest); + + let leaf_crh_params_var = >::ParametersVar::new_constant( + ark_relations::ns!(cs, "leaf_crh_params"), + &leaf_crh_params, + ) + .unwrap(); + + let two_to_one_crh_params_var = + >::ParametersVar::new_constant( + ark_relations::ns!(cs, "two_to_one_params"), + &leaf_crh_params, + ) + .unwrap(); + + let constraints_from_params = cs.num_constraints() - constraints_from_digest; + println!("constraints from parameters: {}", constraints_from_params); + + // Allocate Leaf + let leaf_g: Vec<_> = leaf + .iter() + .map(|x| FpVar::new_input(cs.clone(), || Ok(*x)).unwrap()) + .collect(); + + let constraints_from_leaf = + cs.num_constraints() - constraints_from_params - constraints_from_digest; + println!("constraints from leaf: {}", constraints_from_leaf); + + // Allocate MT Path + let cw = PathVar::::new_witness( + ark_relations::ns!(cs, "new_witness"), + || Ok(&proof), + ) + .unwrap(); + + let constraints_from_path = cs.num_constraints() + - constraints_from_params + - constraints_from_digest + - constraints_from_leaf; + println!("constraints from path: {}", constraints_from_path); + assert!(cs.is_satisfied().unwrap()); + + assert!(cw + .verify_membership( + &leaf_crh_params_var, + &two_to_one_crh_params_var, + &root, + &leaf_g + ) + .unwrap() + .value() + .unwrap()); + + let setup_constraints = constraints_from_leaf + + constraints_from_digest + + constraints_from_params + + constraints_from_path; + + println!( + "number of constraints for verification: {}", + cs.num_constraints() - setup_constraints + ); + + assert!( + cs.is_satisfied().unwrap(), + "verification constraints not satisfied" + ); + } + } + #[test] fn good_root_test() { let mut rng = test_rng(); @@ -468,6 +689,19 @@ mod field_mt_tests { merkle_tree_test(&leaves, false, Some((3, rand_leaves()))) } + #[test] + fn good_root_test_for_imt() { + let mut rng = test_rng(); + let mut rand_leaves = || (0..2).map(|_| F::rand(&mut rng)).collect(); + + let mut leaves: Vec> = Vec::new(); + for _ in 0..128u8 { + leaves.push(rand_leaves()) + } + + incremental_merkle_tree_test(&leaves, false, 8); + } + #[test] #[should_panic] fn bad_root_test() { @@ -481,4 +715,18 @@ mod field_mt_tests { merkle_tree_test(&leaves, true, Some((3, rand_leaves()))) } + + #[test] + #[should_panic] + fn bad_root_test_for_imt() { + let mut rng = test_rng(); + let mut rand_leaves = || (0..2).map(|_| F::rand(&mut rng)).collect(); + + let mut leaves: Vec> = Vec::new(); + for _ in 0..128u8 { + leaves.push(rand_leaves()) + } + + incremental_merkle_tree_test(&leaves, true, 8); + } } diff --git a/src/merkle_tree/tests/mod.rs b/src/merkle_tree/tests/mod.rs index 1d1296c0..977e899f 100644 --- a/src/merkle_tree/tests/mod.rs +++ b/src/merkle_tree/tests/mod.rs @@ -6,7 +6,7 @@ mod bytes_mt_tests { use crate::{ crh::{pedersen, *}, - merkle_tree::*, + merkle_tree::{incremental_merkle_tree::*, *}, }; use ark_ed_on_bls12_381::EdwardsProjective as JubJub; use ark_ff::BigInteger256; @@ -35,6 +35,7 @@ mod bytes_mt_tests { type TwoToOneHash = CompressH; } type JubJubMerkleTree = MerkleTree; + type JubJubIncrementalMerkleTree = IncrementalMerkleTree; /// Pedersen only takes bytes as leaf, so we use `ToBytes` trait. fn merkle_tree_test(leaves: &[L], update_query: &[(usize, L)]) -> () { @@ -79,6 +80,36 @@ mod bytes_mt_tests { } } + /// Pedersen only takes bytes as leaf, so we use `ToBytes` trait. + fn incremental_merkle_tree_test( + tree_height: usize, + update_query: &[L], + ) -> () { + let mut rng = ark_std::test_rng(); + let leaf_crh_params = ::setup(&mut rng).unwrap(); + let two_to_one_params = ::setup(&mut rng) + .unwrap() + .clone(); + let mut tree = JubJubIncrementalMerkleTree::blank( + &leaf_crh_params.clone(), + &two_to_one_params.clone(), + tree_height, + ) + .unwrap(); + + // test merkle tree update functionality + for v in update_query { + let v = crate::to_unchecked_bytes!(v).unwrap(); + tree.append(v.clone()).unwrap(); + println!("{:?}", tree.next_available()); + println!("{:?}", tree.is_empty()); + let proof = tree.current_proof(); + assert!(proof + .verify(&leaf_crh_params, &two_to_one_params, &tree.root(), v) + .unwrap()); + } + } + #[test] fn good_root_test() { let mut rng = test_rng(); @@ -116,10 +147,67 @@ mod bytes_mt_tests { ], ); } + + #[test] + fn test_emptyness_for_imt() { + let mut rng = test_rng(); + let leaf_crh_params = ::setup(&mut rng).unwrap(); + let two_to_one_params = ::setup(&mut rng) + .unwrap() + .clone(); + let mut tree = JubJubIncrementalMerkleTree::blank( + &leaf_crh_params.clone(), + &two_to_one_params.clone(), + 5, + ) + .unwrap(); + assert!(tree.is_empty()); + let v = BigInteger256::rand(&mut rng); + tree.append(crate::to_unchecked_bytes!(v).unwrap()).unwrap(); + assert!(!tree.is_empty()); + } + + #[test] + fn good_root_test_for_imt() { + let mut rng = test_rng(); + + // test various sized IMTs + let mut updates = Vec::new(); + for _ in 0..2u8 { + updates.push(BigInteger256::rand(&mut rng)); + } + incremental_merkle_tree_test(2, &updates); + + let mut updates = Vec::new(); + for _ in 0..7u8 { + updates.push(BigInteger256::rand(&mut rng)); + } + incremental_merkle_tree_test(4, &updates); + + let mut updates = Vec::new(); + for _ in 0..128u8 { + updates.push(BigInteger256::rand(&mut rng)); + } + incremental_merkle_tree_test(8, &updates); + } + + #[test] + #[should_panic] + fn out_of_capacity_test_for_imt() { + let mut rng = test_rng(); + + // test various sized IMTs + let mut updates = Vec::new(); + for _ in 0..3u8 { + updates.push(BigInteger256::rand(&mut rng)); + } + incremental_merkle_tree_test(2, &updates); + } } mod field_mt_tests { use crate::crh::poseidon; + use crate::merkle_tree::incremental_merkle_tree::IncrementalMerkleTree; use crate::merkle_tree::tests::test_utils::poseidon_parameters; use crate::merkle_tree::{Config, IdentityDigestConverter}; use crate::MerkleTree; @@ -140,6 +228,7 @@ mod field_mt_tests { } type FieldMT = MerkleTree; + type FieldIMT = IncrementalMerkleTree; fn merkle_tree_test(leaves: &[Vec], update_query: &[(usize, Vec)]) -> () { let mut leaves = leaves.to_vec(); @@ -195,6 +284,41 @@ mod field_mt_tests { } } + fn incremental_merkle_tree_test(tree_height: usize, update_query: &[Vec]) -> () { + let leaf_crh_params = poseidon_parameters(); + let two_to_one_params = leaf_crh_params.clone(); + + let mut tree = FieldIMT::blank(&leaf_crh_params, &two_to_one_params, tree_height).unwrap(); + + // test incremental merkle tree append + for v in update_query { + tree.append(v.as_slice()).unwrap(); + let proof = tree.current_proof(); + assert!(proof + .verify( + &leaf_crh_params, + &two_to_one_params, + &tree.root(), + v.as_slice() + ) + .unwrap()); + } + + { + // wrong root should lead to error but do not panic + let wrong_root = tree.root() + F::one(); + let proof = tree.current_proof(); + assert!(!proof + .verify( + &leaf_crh_params, + &two_to_one_params, + &wrong_root, + update_query.last().unwrap().as_slice() + ) + .unwrap()) + } + } + #[test] fn good_root_test() { let mut rng = test_rng(); @@ -215,4 +339,16 @@ mod field_mt_tests { ], ) } + + #[test] + fn good_root_test_for_imt() { + let mut rng = test_rng(); + let mut rand_leaves = || (0..3).map(|_| F::rand(&mut rng)).collect(); + + let mut updates: Vec> = Vec::new(); + for _ in 0..128u8 { + updates.push(rand_leaves()) + } + incremental_merkle_tree_test(8, &updates) + } } diff --git a/src/signature/schnorr/mod.rs b/src/signature/schnorr/mod.rs index e5e50db2..a6f333b1 100644 --- a/src/signature/schnorr/mod.rs +++ b/src/signature/schnorr/mod.rs @@ -141,7 +141,7 @@ where let mut hash_input = Vec::new(); hash_input.extend_from_slice(¶meters.salt); hash_input.extend_from_slice(&to_bytes![claimed_prover_commitment]?); - hash_input.extend_from_slice(&message); + hash_input.extend_from_slice(message); let obtained_verifier_challenge = if let Some(obtained_verifier_challenge) = C::ScalarField::from_random_bytes(&D::digest(&hash_input))