From 60ba9d8786ef5380351270842718c74d3e04a522 Mon Sep 17 00:00:00 2001 From: stechu Date: Fri, 20 Aug 2021 00:11:52 -0700 Subject: [PATCH 01/14] IMT implementation --- src/merkle_tree/incremental_merkle_tree.rs | 262 +++++++++++++++++++++ src/merkle_tree/mod.rs | 2 + 2 files changed, 264 insertions(+) create mode 100644 src/merkle_tree/incremental_merkle_tree.rs diff --git a/src/merkle_tree/incremental_merkle_tree.rs b/src/merkle_tree/incremental_merkle_tree.rs new file mode 100644 index 00000000..360e42ef --- /dev/null +++ b/src/merkle_tree/incremental_merkle_tree.rs @@ -0,0 +1,262 @@ +use crate::crh::TwoToOneCRHScheme; +use crate::merkle_tree::{tree_height, Config, DigestConverter, LeafParam, Path, TwoToOneParam}; +use crate::CRHScheme; +use ark_std::borrow::Borrow; + +/// 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" + /// if current_path.auth_path.len() == mean, means the IMT is empty + current_path: Path

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

{ + /// Check if this IMT is empty + pub fn is_empty(&self) -> bool { + self.current_path.auth_path.len() == 0 + } + + /// 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 < self.leaf_nodes.len() - 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 { + // use empty leaf digest + let capacity: usize = 1 << (height - 1); + let leaves_digest = vec![P::LeafDigest::default(); capacity]; + 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, + }) + } + + /// 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], [A], [B] + 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)?; + self.current_path = path; + self.root = root; + 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 = tree_height(self.leaf_nodes.len()); + 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 interate 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.clone(), + ) + } else { + (old_leaf.clone(), new_leaf_digest, old_leaf.clone()) + }; + + 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)) + } + } +} + +/// 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 a51b5cd3..6a82dd35 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; From 0c425711bf1a19f0df84e0402aed44e60a465aa4 Mon Sep 17 00:00:00 2001 From: stechu Date: Fri, 20 Aug 2021 17:03:03 -0700 Subject: [PATCH 02/14] IMT tests --- src/merkle_tree/incremental_merkle_tree.rs | 19 +++- src/merkle_tree/tests/mod.rs | 110 ++++++++++++++++++++- 2 files changed, 124 insertions(+), 5 deletions(-) diff --git a/src/merkle_tree/incremental_merkle_tree.rs b/src/merkle_tree/incremental_merkle_tree.rs index 360e42ef..7c546870 100644 --- a/src/merkle_tree/incremental_merkle_tree.rs +++ b/src/merkle_tree/incremental_merkle_tree.rs @@ -57,6 +57,10 @@ impl IncrementalMerkleTree

{ 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 capacity: usize = 1 << (height - 1); let leaves_digest = vec![P::LeafDigest::default(); capacity]; @@ -101,10 +105,7 @@ impl IncrementalMerkleTree

{ &self, new_leaf_digest: P::LeafDigest, ) -> Result<(Path

, P::InnerDigest), crate::Error> { - assert!( - self.next_available() != None, - "index out of range" - ); + assert!(self.next_available() != None, "index out of range"); // calculate tree_height and empty hash let tree_height = tree_height(self.leaf_nodes.len()); @@ -248,6 +249,16 @@ impl IncrementalMerkleTree

{ Ok((path, new_current_node)) } } + + /// FIXME + pub fn current_proof(&self) -> Path

{ + self.current_path.clone() + } + + /// FIXME + pub fn root(&self) -> P::InnerDigest { + self.root.clone() + } } /// Return true iff the given index on its current level represents a left child diff --git a/src/merkle_tree/tests/mod.rs b/src/merkle_tree/tests/mod.rs index 1d1296c0..dc5bf7bb 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,34 @@ 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(); + 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 +145,40 @@ mod bytes_mt_tests { ], ); } + + #[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] + fn out_of_capacity_test_for_imt(){ + + } } 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 +199,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 +255,40 @@ 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 +309,18 @@ 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 + ) + } } From 28491512772c7ac5e061e02cd6fad76e5ce6463c Mon Sep 17 00:00:00 2001 From: stechu Date: Fri, 20 Aug 2021 17:39:05 -0700 Subject: [PATCH 03/14] add test when it should panic --- src/merkle_tree/incremental_merkle_tree.rs | 10 +++++++--- src/merkle_tree/tests/mod.rs | 12 +++++++++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/merkle_tree/incremental_merkle_tree.rs b/src/merkle_tree/incremental_merkle_tree.rs index 7c546870..34d4f190 100644 --- a/src/merkle_tree/incremental_merkle_tree.rs +++ b/src/merkle_tree/incremental_merkle_tree.rs @@ -22,12 +22,14 @@ pub struct IncrementalMerkleTree { 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.current_path.auth_path.len() == 0 + self.empty } /// The index of the current right most leaf @@ -76,6 +78,7 @@ impl IncrementalMerkleTree

{ leaf_hash_param: leaf_hash_param.clone(), root: P::InnerDigest::default(), height, + empty: true }) } @@ -96,6 +99,7 @@ impl IncrementalMerkleTree

{ let (path, root) = self.next_path(leaf_digest)?; self.current_path = path; self.root = root; + self.empty = false; Ok(()) } @@ -250,12 +254,12 @@ impl IncrementalMerkleTree

{ } } - /// FIXME + /// the proof of the current item pub fn current_proof(&self) -> Path

{ self.current_path.clone() } - /// FIXME + /// root of IMT pub fn root(&self) -> P::InnerDigest { self.root.clone() } diff --git a/src/merkle_tree/tests/mod.rs b/src/merkle_tree/tests/mod.rs index dc5bf7bb..5bcce220 100644 --- a/src/merkle_tree/tests/mod.rs +++ b/src/merkle_tree/tests/mod.rs @@ -101,6 +101,8 @@ mod bytes_mt_tests { 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) @@ -171,8 +173,16 @@ mod bytes_mt_tests { } #[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); } } From c807e4134d4a4f54e5141a4f06f15320861bf594 Mon Sep 17 00:00:00 2001 From: stechu Date: Sun, 22 Aug 2021 19:12:16 -0700 Subject: [PATCH 04/14] IMT tests --- src/lib.rs | 2 +- src/merkle_tree/tests/constraints.rs | 260 ++++++++++++++++++++++++++- 2 files changed, 259 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f1de5d1c..308e904a 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::{MerkleTree, incremental_merkle_tree::IncrementalMerkleTree, Path}, prf::PRF, signature::SignatureScheme, snark::{CircuitSpecificSetupSNARK, UniversalSetupSNARK, SNARK}, diff --git a/src/merkle_tree/tests/constraints.rs b/src/merkle_tree/tests/constraints.rs index f5846d38..5bbdec4d 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,108 @@ 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 +329,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 +348,17 @@ 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 +366,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::R1CSVar; @@ -280,6 +403,7 @@ mod field_mt_tests { } type FieldMT = MerkleTree; + type FieldIMT = IncrementalMerkleTree; fn merkle_tree_test( leaves: &[Vec], @@ -442,6 +566,111 @@ 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(); @@ -455,6 +684,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() { @@ -468,4 +710,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); + } } From dd6c5212e29083392757b998acc467e0da379dae Mon Sep 17 00:00:00 2001 From: stechu Date: Sun, 22 Aug 2021 19:47:32 -0700 Subject: [PATCH 05/14] add one more test --- src/merkle_tree/tests/mod.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/merkle_tree/tests/mod.rs b/src/merkle_tree/tests/mod.rs index 5bcce220..939d784f 100644 --- a/src/merkle_tree/tests/mod.rs +++ b/src/merkle_tree/tests/mod.rs @@ -148,6 +148,24 @@ 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(); + } + #[test] fn good_root_test_for_imt() { let mut rng = test_rng(); From 9a07b1bfba6a4a1934868fcfe8c603b531152e2a Mon Sep 17 00:00:00 2001 From: stechu Date: Sun, 22 Aug 2021 20:52:18 -0700 Subject: [PATCH 06/14] add change log --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7fa1176..a56374d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Pending +- [\#1](https://github.com/Manta-Network/crypto-primitives/pull/1) add incremental merkle tree implementation. + ### Breaking changes - [\#56](https://github.com/arkworks-rs/crypto-primitives/pull/56) Compress the output of the Bowe-Hopwood-Pedersen CRH to a single field element, in line with the Zcash specification. From 217cf11c326ab31bdacb25fab9728c19275ce2c5 Mon Sep 17 00:00:00 2001 From: stechu Date: Mon, 23 Aug 2021 13:58:29 -0700 Subject: [PATCH 07/14] fix clippy warnings --- src/merkle_tree/incremental_merkle_tree.rs | 5 ++--- src/merkle_tree/mod.rs | 8 ++++---- src/signature/schnorr/mod.rs | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/merkle_tree/incremental_merkle_tree.rs b/src/merkle_tree/incremental_merkle_tree.rs index 34d4f190..0ba115f9 100644 --- a/src/merkle_tree/incremental_merkle_tree.rs +++ b/src/merkle_tree/incremental_merkle_tree.rs @@ -18,7 +18,6 @@ pub struct IncrementalMerkleTree { /// Stores the height of the MerkleTree height: usize, /// Stores the path of the "current leaf" - /// if current_path.auth_path.len() == mean, means the IMT is empty current_path: Path

, /// Stores the root of the IMT root: P::InnerDigest, @@ -166,10 +165,10 @@ impl IncrementalMerkleTree

{ ( new_leaf_digest, hash_of_empty_leaf.clone(), - hash_of_empty_leaf.clone(), + hash_of_empty_leaf, ) } else { - (old_leaf.clone(), new_leaf_digest, old_leaf.clone()) + (old_leaf.clone(), new_leaf_digest, old_leaf) }; let mut old_current_node = P::TwoToOneHash::evaluate( diff --git a/src/merkle_tree/mod.rs b/src/merkle_tree/mod.rs index 5602b13b..40b9542f 100644 --- a/src/merkle_tree/mod.rs +++ b/src/merkle_tree/mod.rs @@ -144,7 +144,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)?; @@ -154,7 +154,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; @@ -166,7 +166,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; } @@ -309,7 +309,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/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)) From 5994e66a55083a5b40afe7f3f2c6bc01fefd8429 Mon Sep 17 00:00:00 2001 From: Shumo Chu Date: Mon, 23 Aug 2021 17:03:10 -0400 Subject: [PATCH 08/14] Incremental Merkle Tree Implementation (#1) * IMT implementation * IMT tests --- CHANGELOG.md | 2 + src/lib.rs | 2 +- src/merkle_tree/incremental_merkle_tree.rs | 276 +++++++++++++++++++++ src/merkle_tree/mod.rs | 10 +- src/merkle_tree/tests/constraints.rs | 260 ++++++++++++++++++- src/merkle_tree/tests/mod.rs | 138 ++++++++++- src/signature/schnorr/mod.rs | 2 +- 7 files changed, 681 insertions(+), 9 deletions(-) create mode 100644 src/merkle_tree/incremental_merkle_tree.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index c7fa1176..a56374d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Pending +- [\#1](https://github.com/Manta-Network/crypto-primitives/pull/1) add incremental merkle tree implementation. + ### Breaking changes - [\#56](https://github.com/arkworks-rs/crypto-primitives/pull/56) Compress the output of the Bowe-Hopwood-Pedersen CRH to a single field element, in line with the Zcash specification. diff --git a/src/lib.rs b/src/lib.rs index f1de5d1c..308e904a 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::{MerkleTree, incremental_merkle_tree::IncrementalMerkleTree, 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..0ba115f9 --- /dev/null +++ b/src/merkle_tree/incremental_merkle_tree.rs @@ -0,0 +1,276 @@ +use crate::crh::TwoToOneCRHScheme; +use crate::merkle_tree::{tree_height, Config, DigestConverter, LeafParam, Path, TwoToOneParam}; +use crate::CRHScheme; +use ark_std::borrow::Borrow; + +/// 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 < self.leaf_nodes.len() - 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 capacity: usize = 1 << (height - 1); + let leaves_digest = vec![P::LeafDigest::default(); capacity]; + 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], [A], [B] + 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)?; + 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 = tree_height(self.leaf_nodes.len()); + 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 interate 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 067dcffe..40b9542f 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; @@ -142,7 +144,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)?; @@ -152,7 +154,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; @@ -164,7 +166,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; } @@ -307,7 +309,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 f5846d38..5bbdec4d 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,108 @@ 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 +329,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 +348,17 @@ 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 +366,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::R1CSVar; @@ -280,6 +403,7 @@ mod field_mt_tests { } type FieldMT = MerkleTree; + type FieldIMT = IncrementalMerkleTree; fn merkle_tree_test( leaves: &[Vec], @@ -442,6 +566,111 @@ 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(); @@ -455,6 +684,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() { @@ -468,4 +710,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..939d784f 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,66 @@ 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(); + } + + #[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 +227,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 +283,40 @@ 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 +337,18 @@ 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)) From 130cb93d9de5f4ad5bef6ad53fcac868b1192c05 Mon Sep 17 00:00:00 2001 From: stechu Date: Mon, 23 Aug 2021 17:11:01 -0700 Subject: [PATCH 09/14] fmt --- src/lib.rs | 2 +- src/merkle_tree/incremental_merkle_tree.rs | 3 +- src/merkle_tree/tests/constraints.rs | 34 +++++++++------------- src/merkle_tree/tests/mod.rs | 25 ++++++++-------- 4 files changed, 28 insertions(+), 36 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 308e904a..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, incremental_merkle_tree::IncrementalMerkleTree, 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 index 0ba115f9..392d1c75 100644 --- a/src/merkle_tree/incremental_merkle_tree.rs +++ b/src/merkle_tree/incremental_merkle_tree.rs @@ -2,6 +2,7 @@ use crate::crh::TwoToOneCRHScheme; use crate::merkle_tree::{tree_height, 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. @@ -77,7 +78,7 @@ impl IncrementalMerkleTree

{ leaf_hash_param: leaf_hash_param.clone(), root: P::InnerDigest::default(), height, - empty: true + empty: true, }) } diff --git a/src/merkle_tree/tests/constraints.rs b/src/merkle_tree/tests/constraints.rs index 5bbdec4d..5fe30a6f 100644 --- a/src/merkle_tree/tests/constraints.rs +++ b/src/merkle_tree/tests/constraints.rs @@ -227,7 +227,12 @@ mod byte_mt_tests { 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(); + 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(); @@ -318,7 +323,6 @@ mod byte_mt_tests { } } - #[test] fn good_root_test() { let mut leaves = Vec::new(); @@ -330,7 +334,7 @@ mod byte_mt_tests { } #[test] - fn good_root_test_for_imt(){ + fn good_root_test_for_imt() { let mut updates = Vec::new(); for i in 0..4u8 { updates.push(vec![i; 30]); @@ -351,14 +355,13 @@ mod byte_mt_tests { #[test] #[should_panic] - fn bad_root_test_for_imt(){ + 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 { @@ -566,20 +569,11 @@ mod field_mt_tests { } } - fn incremental_merkle_tree_test( - updates: &[Vec], - use_bad_root: bool, - tree_height: usize, - ) { + 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(); - + 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(); @@ -667,9 +661,7 @@ mod field_mt_tests { "verification constraints not satisfied" ); } - - } - + } #[test] fn good_root_test() { @@ -713,7 +705,7 @@ mod field_mt_tests { #[test] #[should_panic] - fn bad_root_test_for_imt(){ + fn bad_root_test_for_imt() { let mut rng = test_rng(); let mut rand_leaves = || (0..2).map(|_| F::rand(&mut rng)).collect(); diff --git a/src/merkle_tree/tests/mod.rs b/src/merkle_tree/tests/mod.rs index 939d784f..0ff20b76 100644 --- a/src/merkle_tree/tests/mod.rs +++ b/src/merkle_tree/tests/mod.rs @@ -192,7 +192,7 @@ mod bytes_mt_tests { #[test] #[should_panic] - fn out_of_capacity_test_for_imt(){ + fn out_of_capacity_test_for_imt() { let mut rng = test_rng(); // test various sized IMTs @@ -287,18 +287,20 @@ mod field_mt_tests { 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(); + 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()); + assert!(proof + .verify( + &leaf_crh_params, + &two_to_one_params, + &tree.root(), + v.as_slice() + ) + .unwrap()); } { @@ -314,7 +316,6 @@ mod field_mt_tests { ) .unwrap()) } - } #[test] @@ -339,7 +340,7 @@ mod field_mt_tests { } #[test] - fn good_root_test_for_imt(){ + fn good_root_test_for_imt() { let mut rng = test_rng(); let mut rand_leaves = || (0..3).map(|_| F::rand(&mut rng)).collect(); @@ -347,8 +348,6 @@ mod field_mt_tests { for _ in 0..128u8 { updates.push(rand_leaves()) } - incremental_merkle_tree_test( - 8, &updates - ) + incremental_merkle_tree_test(8, &updates) } } From 39b65c50ecb037a95181231c2e3ce0e587fa0c64 Mon Sep 17 00:00:00 2001 From: stechu Date: Thu, 2 Sep 2021 14:14:07 -0700 Subject: [PATCH 10/14] address comments --- src/merkle_tree/incremental_merkle_tree.rs | 2 +- src/merkle_tree/tests/mod.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/merkle_tree/incremental_merkle_tree.rs b/src/merkle_tree/incremental_merkle_tree.rs index 392d1c75..9230eeca 100644 --- a/src/merkle_tree/incremental_merkle_tree.rs +++ b/src/merkle_tree/incremental_merkle_tree.rs @@ -92,7 +92,7 @@ impl IncrementalMerkleTree

{ /// .. / \ .... /// [I]{new leaf} /// ``` - /// append({new leaf}) when the `next_availabe` is at 4, would cause a recompute [E], [A], [B] + /// 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)?; diff --git a/src/merkle_tree/tests/mod.rs b/src/merkle_tree/tests/mod.rs index 0ff20b76..977e899f 100644 --- a/src/merkle_tree/tests/mod.rs +++ b/src/merkle_tree/tests/mod.rs @@ -164,6 +164,7 @@ mod bytes_mt_tests { assert!(tree.is_empty()); let v = BigInteger256::rand(&mut rng); tree.append(crate::to_unchecked_bytes!(v).unwrap()).unwrap(); + assert!(!tree.is_empty()); } #[test] From 781c16401f59d27fb8e98fdaa436f3614640691b Mon Sep 17 00:00:00 2001 From: stechu Date: Thu, 2 Sep 2021 17:00:37 -0700 Subject: [PATCH 11/14] fix markdown lint --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 234fe807..6cc570d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -93,14 +93,14 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Install Rust (${{ matrix.rust }}) + - name: Install Rust uses: actions-rs/toolchain@v1 with: toolchain: stable target: thumbv6m-none-eabi override: true - - name: Install Rust ARM64 (${{ matrix.rust }}) + - name: Install Rust ARM64 uses: actions-rs/toolchain@v1 with: toolchain: stable From 6f91e03b125082278a318b52ac46fce57b2549fd Mon Sep 17 00:00:00 2001 From: stechu Date: Sun, 5 Sep 2021 21:44:19 -0700 Subject: [PATCH 12/14] fix typo --- src/merkle_tree/incremental_merkle_tree.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/merkle_tree/incremental_merkle_tree.rs b/src/merkle_tree/incremental_merkle_tree.rs index 9230eeca..b4fc63f5 100644 --- a/src/merkle_tree/incremental_merkle_tree.rs +++ b/src/merkle_tree/incremental_merkle_tree.rs @@ -149,7 +149,7 @@ impl IncrementalMerkleTree

{ 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 interate on + // 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(), From b3166a9726b13816b8e7aba18c35a83db42e7fb5 Mon Sep 17 00:00:00 2001 From: stechu Date: Sat, 11 Sep 2021 23:57:18 -0700 Subject: [PATCH 13/14] bug fix: leaf_nodes wrong behavor --- src/merkle_tree/incremental_merkle_tree.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/merkle_tree/incremental_merkle_tree.rs b/src/merkle_tree/incremental_merkle_tree.rs index b4fc63f5..bf50fc61 100644 --- a/src/merkle_tree/incremental_merkle_tree.rs +++ b/src/merkle_tree/incremental_merkle_tree.rs @@ -1,5 +1,5 @@ use crate::crh::TwoToOneCRHScheme; -use crate::merkle_tree::{tree_height, Config, DigestConverter, LeafParam, Path, TwoToOneParam}; +use crate::merkle_tree::{Config, DigestConverter, LeafParam, Path, TwoToOneParam}; use crate::CRHScheme; use ark_std::borrow::Borrow; use ark_std::vec::Vec; @@ -46,7 +46,7 @@ impl IncrementalMerkleTree

{ let current_index = self.current_path.leaf_index; if self.is_empty() { Some(0) - } else if current_index < self.leaf_nodes.len() - 1 { + } else if current_index < (1 << (self.height - 1)) - 1 { Some(current_index + 1) } else { None @@ -64,8 +64,7 @@ impl IncrementalMerkleTree

{ "the height of incremental merkle tree should be at least 2" ); // use empty leaf digest - let capacity: usize = 1 << (height - 1); - let leaves_digest = vec![P::LeafDigest::default(); capacity]; + let leaves_digest = vec![]; Ok(IncrementalMerkleTree { /// blank tree doesn't have current_path current_path: Path { @@ -96,7 +95,8 @@ impl IncrementalMerkleTree

{ 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)?; + 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; @@ -112,7 +112,7 @@ impl IncrementalMerkleTree

{ assert!(self.next_available() != None, "index out of range"); // calculate tree_height and empty hash - let tree_height = tree_height(self.leaf_nodes.len()); + 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(); From fd9a0decc470a7c92d2ed2ef7a5fffa6952c9c83 Mon Sep 17 00:00:00 2001 From: Weikeng Chen Date: Mon, 13 Sep 2021 15:00:08 -0700 Subject: [PATCH 14/14] Update CHANGELOG.md --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02ab79e3..15c15a39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,6 @@ ## Pending -- [\#1](https://github.com/Manta-Network/crypto-primitives/pull/1) add incremental merkle tree implementation. - ### Breaking changes - [\#56](https://github.com/arkworks-rs/crypto-primitives/pull/56) Compress the output of the Bowe-Hopwood-Pedersen CRH to a single field element, in line with the Zcash specification. @@ -16,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