From 1a62327423c8b3698c912392213f3bd011f40b6c Mon Sep 17 00:00:00 2001 From: MRain Date: Tue, 15 Oct 2024 17:56:29 -0400 Subject: [PATCH] Remove index and element from Merkle proof. Temporarily remove NMT --- merkle_tree/benches/merkle_path.rs | 8 +- merkle_tree/src/append_only.rs | 165 +++--- merkle_tree/src/gadgets/mod.rs | 264 +++++----- .../src/gadgets/universal_merkle_tree.rs | 95 ++-- merkle_tree/src/hasher.rs | 4 +- merkle_tree/src/internal.rs | 470 +++++++----------- merkle_tree/src/lib.rs | 35 +- merkle_tree/src/light_weight.rs | 118 ++--- merkle_tree/src/macros.rs | 70 +-- merkle_tree/src/namespaced_merkle_tree/mod.rs | 7 +- merkle_tree/src/prelude.rs | 2 +- merkle_tree/src/universal_merkle_tree.rs | 264 +++++----- merkle_tree/tests/merkle_tree_hasher.rs | 8 +- 13 files changed, 665 insertions(+), 845 deletions(-) diff --git a/merkle_tree/benches/merkle_path.rs b/merkle_tree/benches/merkle_path.rs index aab16ff3e..a89290747 100644 --- a/merkle_tree/benches/merkle_path.rs +++ b/merkle_tree/benches/merkle_path.rs @@ -10,7 +10,7 @@ extern crate criterion; use ark_ed_on_bls12_381::Fq as Fq381; use ark_std::rand::Rng; use criterion::Criterion; -use jf_merkle_tree::{prelude::RescueMerkleTree, MerkleCommitment, MerkleTreeScheme}; +use jf_merkle_tree::{prelude::RescueMerkleTree, MerkleTreeScheme}; use std::time::Duration; const BENCH_NAME: &str = "merkle_path_height_20"; @@ -25,12 +25,12 @@ fn twenty_hashes(c: &mut Criterion) { let leaf: Fq381 = rng.gen(); let mt = RescueMerkleTree::::from_elems(Some(20), [leaf, leaf]).unwrap(); - let root = mt.commitment().digest(); - let (_, proof) = mt.lookup(0).expect_ok().unwrap(); + let commitment = mt.commitment(); + let (val, proof) = mt.lookup(0).expect_ok().unwrap(); let num_inputs = 0; benchmark_group.bench_with_input(BENCH_NAME, &num_inputs, move |b, &_num_inputs| { - b.iter(|| RescueMerkleTree::::verify(&root, 0, &proof).unwrap()) + b.iter(|| RescueMerkleTree::::verify(&commitment, 0, val, &proof).unwrap()) }); benchmark_group.finish(); } diff --git a/merkle_tree/src/append_only.rs b/merkle_tree/src/append_only.rs index ca832b5a3..9f7181d54 100644 --- a/merkle_tree/src/append_only.rs +++ b/merkle_tree/src/append_only.rs @@ -8,11 +8,11 @@ use super::{ internal::{ - build_tree_internal, MerkleNode, MerkleProof, MerkleTreeCommitment, MerkleTreeIntoIter, - MerkleTreeIter, + build_tree_internal, MerkleNode, MerkleTreeCommitment, MerkleTreeIntoIter, MerkleTreeIter, + MerkleTreeProof, }, AppendableMerkleTreeScheme, DigestAlgorithm, Element, ForgetableMerkleTreeScheme, Index, - LookupResult, MerkleCommitment, MerkleTreeScheme, NodeValue, ToTraversalPath, + LookupResult, MerkleCommitment, MerkleProof, MerkleTreeScheme, NodeValue, ToTraversalPath, }; use crate::{ errors::MerkleTreeError, impl_forgetable_merkle_tree_scheme, impl_merkle_tree_scheme, @@ -104,12 +104,10 @@ where } } -// TODO(Chengyu): extract a merkle frontier - #[cfg(test)] mod mt_tests { use crate::{ - internal::{MerkleNode, MerkleProof}, + internal::{MerkleNode, MerkleTreeProof}, prelude::{RescueMerkleTree, RescueSparseMerkleTree}, *, }; @@ -166,43 +164,36 @@ mod mt_tests { let mt = RescueMerkleTree::::from_elems(Some(2), [F::from(3u64), F::from(1u64)]).unwrap(); - let root = mt.commitment().digest(); + let commitment = mt.commitment(); let (elem, proof) = mt.lookup(0).expect_ok().unwrap(); assert_eq!(elem, &F::from(3u64)); - assert_eq!(proof.tree_height(), 3); - assert!(RescueMerkleTree::::verify(&root, 0u64, &proof) + assert_eq!(proof.height(), 2); + assert!( + RescueMerkleTree::::verify(&commitment, 0u64, elem, &proof) + .unwrap() + .is_ok() + ); + + // Wrong element value, should fail. + assert!( + RescueMerkleTree::::verify(&commitment, 0, F::from(14u64), &proof) + .unwrap() + .is_err() + ); + + // Wrong pos, should fail. + assert!(RescueMerkleTree::::verify(&commitment, 1, elem, &proof) .unwrap() - .is_ok()); + .is_err()); let mut bad_proof = proof.clone(); - if let MerkleNode::Leaf { - value: _, - pos: _, - elem, - } = &mut bad_proof.proof[0] - { - *elem = F::from(4u64); - } else { - unreachable!() - } + bad_proof.0[0][0] = F::one(); - let result = RescueMerkleTree::::verify(&root, 0, &bad_proof); - assert!(result.unwrap().is_err()); - - let mut forge_proof = MerkleProof::new(2, proof.proof); - if let MerkleNode::Leaf { - value: _, - pos, - elem, - } = &mut forge_proof.proof[0] - { - *pos = 2; - *elem = F::from(0u64); - } else { - unreachable!() - } - let result = RescueMerkleTree::::verify(&root, 0, &forge_proof); - assert!(result.unwrap().is_err()); + assert!( + RescueMerkleTree::::verify(&commitment, 0, elem, &bad_proof) + .unwrap() + .is_err() + ); } #[test] @@ -213,55 +204,67 @@ mod mt_tests { } fn test_mt_forget_remember_helper() { - let mut mt = - RescueMerkleTree::::from_elems(Some(2), [F::from(3u64), F::from(1u64)]).unwrap(); - let root = mt.commitment().digest(); - let (lookup_elem, lookup_proof) = mt.lookup(0).expect_ok().unwrap(); - let lookup_elem = *lookup_elem; - let (elem, proof) = mt.forget(0).expect_ok().unwrap(); + let mut mt = RescueMerkleTree::::from_elems( + Some(2), + [F::from(3u64), F::from(1u64), F::from(2u64), F::from(5u64)], + ) + .unwrap(); + let commitment = mt.commitment(); + let (&lookup_elem, mut lookup_proof) = mt.lookup(3).expect_ok().unwrap(); + let (elem, proof) = mt.forget(3).expect_ok().unwrap(); assert_eq!(lookup_elem, elem); assert_eq!(lookup_proof, proof); - assert_eq!(elem, F::from(3u64)); - assert_eq!(proof.tree_height(), 3); - assert!(RescueMerkleTree::::verify(&root, 0, &lookup_proof) + assert_eq!(elem, F::from(5u64)); + assert_eq!(proof.height(), 2); + assert!( + RescueMerkleTree::::verify(&commitment, 3, elem, &lookup_proof) + .unwrap() + .is_ok() + ); + assert!(RescueMerkleTree::::verify(&commitment, 3, elem, &proof) .unwrap() .is_ok()); - assert!(RescueMerkleTree::::verify(&root, 0, &proof) + + assert!(mt.forget(3).expect_ok().is_err()); + assert!(matches!(mt.lookup(3), LookupResult::NotInMemory)); + + // Wrong element + assert!(mt.remember(3, F::from(19u64), &proof).is_err()); + // Wrong pos + assert!(mt.remember(1, elem, &proof).is_err()); + // Wrong proof + lookup_proof.0[0][0] = F::one(); + assert!(mt.remember(3, elem, &lookup_proof).is_err()); + + assert!(mt.remember(3, elem, &proof).is_ok()); + assert!(mt.lookup(3).expect_ok().is_ok()); + + // test another index + let (&lookup_elem, mut lookup_proof) = mt.lookup(0).expect_ok().unwrap(); + let (elem, proof) = mt.forget(0).expect_ok().unwrap(); + assert_eq!(lookup_elem, elem); + assert_eq!(lookup_proof, proof); + assert_eq!(elem, F::from(3u64)); + assert_eq!(proof.height(), 2); + assert!( + RescueMerkleTree::::verify(&commitment, 0, elem, &lookup_proof) + .unwrap() + .is_ok() + ); + assert!(RescueMerkleTree::::verify(&commitment, 0, elem, &proof) .unwrap() .is_ok()); assert!(mt.forget(0).expect_ok().is_err()); assert!(matches!(mt.lookup(0), LookupResult::NotInMemory)); - let mut bad_proof = proof.clone(); - if let MerkleNode::Leaf { - value: _, - pos: _, - elem, - } = &mut bad_proof.proof[0] - { - *elem = F::from(4u64); - } else { - unreachable!() - } - - let result = mt.remember(0, elem, &bad_proof); - assert!(result.is_err()); - - let mut forge_proof = MerkleProof::new(2, proof.proof.clone()); - if let MerkleNode::Leaf { - value: _, - pos, - elem, - } = &mut forge_proof.proof[0] - { - *pos = 2; - *elem = F::from(0u64); - } else { - unreachable!() - } - let result = mt.remember(2, elem, &forge_proof); - assert!(result.is_err()); + // Wrong element + assert!(mt.remember(0, F::from(19u64), &proof).is_err()); + // Wrong pos + assert!(mt.remember(1, elem, &proof).is_err()); + // Wrong proof + lookup_proof.0[0][0] = F::one(); + assert!(mt.remember(0, elem, &lookup_proof).is_err()); assert!(mt.remember(0, elem, &proof).is_ok()); assert!(mt.lookup(0).expect_ok().is_ok()); @@ -278,7 +281,7 @@ mod mt_tests { let mt = RescueMerkleTree::::from_elems(Some(2), [F::from(3u64), F::from(1u64)]).unwrap(); let proof = mt.lookup(0).expect_ok().unwrap().1; - let node = &proof.proof[0]; + // let node = &proof.proof.0[0]; assert_eq!( mt, @@ -288,10 +291,10 @@ mod mt_tests { proof, bincode::deserialize(&bincode::serialize(&proof).unwrap()).unwrap() ); - assert_eq!( - *node, - bincode::deserialize(&bincode::serialize(node).unwrap()).unwrap() - ); + // assert_eq!( + // *node, + // bincode::deserialize(&bincode::serialize(node).unwrap()).unwrap() + // ); } #[test] diff --git a/merkle_tree/src/gadgets/mod.rs b/merkle_tree/src/gadgets/mod.rs index 17dc74ee8..9cc6cb9d1 100644 --- a/merkle_tree/src/gadgets/mod.rs +++ b/merkle_tree/src/gadgets/mod.rs @@ -14,9 +14,10 @@ mod universal_merkle_tree; use ark_std::{string::ToString, vec::Vec}; use crate::{ - internal::{MerkleNode, MerklePath, MerkleProof}, + internal::{MerkleNode, MerkleTreeProof}, prelude::RescueMerkleTree, - Element, Index, MerkleTreeScheme, NodeValue, ToTraversalPath, UniversalMerkleTreeScheme, + Element, Index, MerkleCommitment, MerkleProof, MerkleTreeScheme, NodeValue, ToTraversalPath, + UniversalMerkleTreeScheme, }; use jf_rescue::RescueParameter; type NodeVal = as MerkleTreeScheme>::NodeValue; @@ -36,29 +37,33 @@ use jf_rescue::gadgets::RescueNativeGadget; /// // Create a 3-ary MT, instantiated with a Rescue-based hash, of height 1. /// let elements = vec![Fq::from(1_u64), Fq::from(2_u64), Fq::from(100_u64)]; /// let mt = RescueMerkleTree::::from_elems(Some(1), elements).unwrap(); -/// let expected_root = mt.commitment().digest(); +/// let commitment = mt.commitment(); /// // Get a proof for the element in position 2 -/// let (_, proof) = mt.lookup(2).expect_ok().unwrap(); +/// let (elem, proof) = mt.lookup(2).expect_ok().unwrap(); /// /// // Circuit computation with a MT -/// let elem_idx = circuit.create_variable(2_u64.into()).unwrap(); +/// let pos = 2_u64; +/// let elem_idx = circuit.create_variable(pos.into()).unwrap(); +/// let elem_var = circuit.create_variable(*elem).unwrap(); /// let proof_var = /// MerkleTreeGadget::>::create_membership_proof_variable( /// &mut circuit, +/// &pos, /// &proof /// ) /// .unwrap(); -/// let root_var = -/// MerkleTreeGadget::>::create_root_variable( +/// let commitment_var = +/// MerkleTreeGadget::>::create_commitment_variable( /// &mut circuit, -/// expected_root +/// &commitment /// ) /// .unwrap(); /// MerkleTreeGadget::>::enforce_membership_proof( /// &mut circuit, /// elem_idx, -/// proof_var, -/// root_var +/// elem_var, +/// &proof_var, +/// commitment_var /// ) /// .unwrap(); /// assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); @@ -79,31 +84,38 @@ where /// Allocate a variable for the membership proof. fn create_membership_proof_variable( &mut self, + pos: &M::Index, membership_proof: &M::MembershipProof, ) -> Result; /// Allocate a variable for the merkle root. - fn create_root_variable(&mut self, root: M::NodeValue) -> Result; + fn create_commitment_variable( + &mut self, + commitment: &M::Commitment, + ) -> Result; /// Given variables representing: /// * an element index + /// * the element itself /// * its merkle proof - /// * root + /// * Merkle commitment /// * return `BoolVar` indicating the correctness of its membership proof. fn is_member( &mut self, elem_idx_var: Variable, - proof_var: Self::MembershipProofVar, - root_var: Variable, + elem_var: Variable, + proof_var: &Self::MembershipProofVar, + commitment_var: Variable, ) -> Result; /// Enforce correct `proof_var` for the `elem_idx_var` against - /// `expected_root_var`. + /// `commitment_var`. fn enforce_membership_proof( &mut self, elem_idx_var: Variable, - proof_var: Self::MembershipProofVar, - expected_root_var: Variable, + elem_var: Variable, + proof_var: &Self::MembershipProofVar, + commitment_var: Variable, ) -> Result<(), CircuitError>; } @@ -128,30 +140,32 @@ where /// hashmap.insert(BigUint::from(2u64), Fq::from(2u64)); /// hashmap.insert(BigUint::from(1u64), Fq::from(3u64)); /// let mt = SparseMerkleTree::::from_kv_set(2, &hashmap).unwrap(); -/// let expected_root = mt.commitment().digest(); -/// // Get a proof for the element in position 2 -/// let proof = mt.universal_lookup(&BigUint::from(3u64)).expect_not_found().unwrap(); +/// let commitment = mt.commitment(); +/// // Get a proof for the element in position 3 +/// let pos = BigUint::from(3u64); +/// let proof = mt.universal_lookup(&pos).expect_not_found().unwrap(); /// /// // Circuit computation with a MT -/// let non_elem_idx_var = circuit.create_variable(BigUint::from(3u64).into()).unwrap(); +/// let non_elem_idx_var = circuit.create_variable(pos.clone().into()).unwrap(); /// /// let proof_var = /// UniversalMerkleTreeGadget::>::create_non_membership_proof_variable( /// &mut circuit, +/// &pos, /// &proof /// ) /// .unwrap(); -/// let root_var = -/// MerkleTreeGadget::>::create_root_variable( +/// let commitment_var = +/// MerkleTreeGadget::>::create_commitment_variable( /// &mut circuit, -/// expected_root +/// &commitment /// ) /// .unwrap(); /// UniversalMerkleTreeGadget::>::enforce_non_membership_proof( /// &mut circuit, /// non_elem_idx_var, -/// proof_var, -/// root_var +/// &proof_var, +/// commitment_var /// ) /// .unwrap(); /// assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); @@ -169,24 +183,25 @@ where /// Allocate a variable for the membership proof. fn create_non_membership_proof_variable( &mut self, - membership_proof: &M::NonMembershipProof, + pos: &M::Index, + non_membership_proof: &M::NonMembershipProof, ) -> Result; /// checking non-membership proof fn is_non_member( &mut self, non_elem_idx_var: Variable, - proof_var: Self::NonMembershipProofVar, - root_var: Variable, + proof_var: &Self::NonMembershipProofVar, + commitment_var: Variable, ) -> Result; /// Enforce correct `proof_var` for the empty elem `empty_elem_idx_var` - /// against `expected_root_var`. + /// against `expected_commitment_var`. fn enforce_non_membership_proof( &mut self, non_elem_idx_var: Variable, - proof_var: Self::NonMembershipProofVar, - expected_root_var: Variable, + proof_var: &Self::NonMembershipProofVar, + expected_commitment_var: Variable, ) -> Result<(), CircuitError>; } @@ -232,24 +247,22 @@ pub struct Merkle3AryNodeVar { is_right_child: BoolVar, } -/// Circuit variable for a Merkle non-membership proof of a 3-ary Merkle tree. -/// Contains: -/// * a list of node variables in the path, -/// * a variable correseponsing to the position of the element. -#[derive(Debug, Clone)] -pub struct Merkle3AryNonMembershipProofVar { - node_vars: Vec, - pos_var: Variable, -} +// /// Circuit variable for a Merkle non-membership proof of a 3-ary Merkle tree. +// /// Contains: +// /// * a list of node variables in the path, +// /// * a variable correseponsing to the position of the element. +// #[derive(Debug, Clone)] +// pub struct Merkle3AryNonMembershipProofVar { +// node_vars: Vec, +// pos_var: Variable, +// } /// Circuit variable for a Merkle proof of a 3-ary Merkle tree. /// Contains: /// * a list of node variables in the path, -/// * a variable correseponsing to the value of the element. #[derive(Debug, Clone)] -pub struct Merkle3AryMembershipProofVar { +pub struct Merkle3AryProofVar { node_vars: Vec, - elem_var: Variable, } /// Circuit counterpart to DigestAlgorithm pub trait DigestAlgorithmGadget @@ -285,86 +298,30 @@ impl DigestAlgorithmGadget for RescueDigestGadget { } } -/// Proof of membership -pub trait MembershipProof -where - E: Element, - I: Index, - T: NodeValue, -{ - /// Get the tree height - fn tree_height(&self) -> usize; - /// Get index of element - fn index(&self) -> &I; - /// Get the element - fn elem(&self) -> Option<&E>; - /// Get the merkle path to the element - fn merkle_path(&self) -> &MerklePath; -} - -impl MembershipProof for MerkleProof -where - E: Element, - I: Index, - T: NodeValue, -{ - fn tree_height(&self) -> usize { - self.tree_height() - } - fn index(&self) -> &I { - self.index() - } - fn elem(&self) -> Option<&E> { - self.elem() - } - fn merkle_path(&self) -> &MerklePath { - &self.proof - } -} - impl MerkleTreeGadget for PlonkCircuit where T: MerkleTreeScheme, - T::MembershipProof: MembershipProof, T::NodeValue: PrimeField + RescueParameter, T::Index: ToTraversalPath<3>, { - type MembershipProofVar = Merkle3AryMembershipProofVar; + type MembershipProofVar = Merkle3AryProofVar; type DigestGadget = RescueDigestGadget; fn create_membership_proof_variable( &mut self, + pos: &::Index, merkle_proof: &::MembershipProof, - ) -> Result { - let path = merkle_proof - .index() - .to_traversal_path(merkle_proof.tree_height() - 1); - - let elem = match merkle_proof.elem() { - Some(elem) => elem, - None => { - return Err(CircuitError::InternalError( - "The proof doesn't contain a leaf element".to_string(), - )) - }, - }; - - let elem_var = self.create_variable(*elem)?; + ) -> Result { + let path = pos.to_traversal_path(merkle_proof.height()); let nodes = path .iter() - .zip(merkle_proof.merkle_path().iter().skip(1)) - .filter_map(|(branch, node)| match node { - MerkleNode::Branch { value: _, children } => Some((children, branch)), - _ => None, - }) - .map(|(children, branch)| { - let sib_branch1 = if branch == &0 { 1 } else { 0 }; - let sib_branch2 = if branch == &2 { 1 } else { 2 }; + .zip(merkle_proof.path_values()) + .map(|(branch, siblings)| { Ok(Merkle3AryNodeVar { - sibling1: self.create_variable(children[sib_branch1].value())?, - sibling2: self.create_variable(children[sib_branch2].value())?, + sibling1: self.create_variable(siblings[0])?, + sibling2: self.create_variable(siblings[1])?, is_left_child: self.create_boolean_variable(branch == &0)?, is_right_child: self.create_boolean_variable(branch == &2)?, }) @@ -381,31 +338,26 @@ where self.enforce_bool(left_plus_right)?; } - Ok(Merkle3AryMembershipProofVar { - node_vars: nodes, - elem_var, - }) + Ok(Merkle3AryProofVar { node_vars: nodes }) } - fn create_root_variable( + fn create_commitment_variable( &mut self, - root: NodeVal, + commitment: &::Commitment, ) -> Result { - self.create_variable(root) + self.create_variable(commitment.digest()) } fn is_member( &mut self, elem_idx_var: Variable, - proof_var: Merkle3AryMembershipProofVar, - root_var: Variable, + elem_var: Variable, + proof_var: &Merkle3AryProofVar, + commitment_var: Variable, ) -> Result { - let computed_root_var = { - let proof_var = &proof_var; - + let computed_commitment_var = { // elem label = H(0, uid, elem) - let mut cur_label = - Self::DigestGadget::digest_leaf(self, elem_idx_var, proof_var.elem_var)?; + let mut cur_label = Self::DigestGadget::digest_leaf(self, elem_idx_var, elem_var)?; for cur_node in proof_var.node_vars.iter() { let input_labels = constrain_sibling_order( self, @@ -421,17 +373,23 @@ where } Ok(cur_label) }?; - self.is_equal(root_var, computed_root_var) + self.is_equal(commitment_var, computed_commitment_var) } fn enforce_membership_proof( &mut self, elem_idx_var: Variable, - proof_var: Merkle3AryMembershipProofVar, - expected_root_var: Variable, + elem_var: Variable, + proof_var: &Merkle3AryProofVar, + commitment_var: Variable, ) -> Result<(), CircuitError> { - let bool_val = - MerkleTreeGadget::::is_member(self, elem_idx_var, proof_var, expected_root_var)?; + let bool_val = MerkleTreeGadget::::is_member( + self, + elem_idx_var, + elem_var, + proof_var, + commitment_var, + )?; self.enforce_true(bool_val.into()) } } @@ -439,7 +397,7 @@ where #[cfg(test)] mod test { use crate::{ - gadgets::{constrain_sibling_order, Merkle3AryMembershipProofVar, MerkleTreeGadget}, + gadgets::{constrain_sibling_order, Merkle3AryProofVar, MerkleTreeGadget}, internal::MerkleNode, prelude::RescueMerkleTree, MerkleCommitment, MerkleTreeScheme, @@ -560,36 +518,40 @@ mod test { let mut circuit = PlonkCircuit::::new_turbo_plonk(); let mut elements = (1u64..=9u64).map(|x| F::from(x)).collect::>(); elements[uid as usize] = elem; - let mt = RescueMerkleTree::::from_elems(Some(2), elements).unwrap(); - let expected_root = mt.commitment().digest(); + let mt = RescueMerkleTree::::from_elems(Some(2), &elements).unwrap(); + let commitment = mt.commitment(); let (retrieved_elem, proof) = mt.lookup(uid).expect_ok().unwrap(); assert_eq!(retrieved_elem, &elem); // Happy path // Circuit computation with a MT let elem_idx_var: Variable = circuit.create_variable(uid.into()).unwrap(); - let proof_var: Merkle3AryMembershipProofVar = + let elem_var: Variable = circuit.create_variable(elements[uid as usize]).unwrap(); + let proof_var = MerkleTreeGadget::>::create_membership_proof_variable( &mut circuit, + &uid, &proof, ) .unwrap(); - let root_var = MerkleTreeGadget::>::create_root_variable( - &mut circuit, - expected_root, - ) - .unwrap(); + let commitment_var = + MerkleTreeGadget::>::create_commitment_variable( + &mut circuit, + &commitment, + ) + .unwrap(); MerkleTreeGadget::>::enforce_membership_proof( &mut circuit, elem_idx_var, - proof_var, - root_var, + elem_var, + &proof_var, + commitment_var, ) .unwrap(); assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); - *circuit.witness_mut(root_var) = F::zero(); + *circuit.witness_mut(commitment_var) = F::zero(); assert!(circuit.check_circuit_satisfiability(&[]).is_err()); // Bad path: @@ -599,28 +561,28 @@ mod test { let elem_idx_var: Variable = circuit.create_variable(uid.into()).unwrap(); let mut bad_proof = proof.clone(); + bad_proof.0[1][0] = F::zero(); - if let MerkleNode::Branch { value: _, children } = &mut bad_proof.proof[1] { - let left_sib = if uid % 3 == 0 { 1 } else { 0 }; - children[left_sib] = Arc::new(MerkleNode::ForgettenSubtree { value: F::zero() }); - } - let path_vars: Merkle3AryMembershipProofVar = + let proof_var = MerkleTreeGadget::>::create_membership_proof_variable( &mut circuit, + &uid, &bad_proof, ) .unwrap(); - let root_var = MerkleTreeGadget::>::create_root_variable( - &mut circuit, - expected_root, - ) - .unwrap(); + let commitment_var = + MerkleTreeGadget::>::create_commitment_variable( + &mut circuit, + &commitment, + ) + .unwrap(); MerkleTreeGadget::>::enforce_membership_proof( &mut circuit, elem_idx_var, - path_vars, - root_var, + elem_var, + &proof_var, + commitment_var, ) .unwrap(); diff --git a/merkle_tree/src/gadgets/universal_merkle_tree.rs b/merkle_tree/src/gadgets/universal_merkle_tree.rs index d9da32e5f..25fa53f21 100644 --- a/merkle_tree/src/gadgets/universal_merkle_tree.rs +++ b/merkle_tree/src/gadgets/universal_merkle_tree.rs @@ -8,7 +8,9 @@ //! with a Rescue hash function. use crate::{ - internal::MerkleNode, prelude::RescueSparseMerkleTree, MerkleTreeScheme, ToTraversalPath, + internal::MerkleNode, + prelude::{MerkleTreeProof, RescueSparseMerkleTree}, + MerkleProof, MerkleTreeScheme, ToTraversalPath, }; use ark_std::vec::Vec; use jf_relation::{BoolVar, Circuit, CircuitError, PlonkCircuit, Variable}; @@ -16,8 +18,8 @@ use jf_rescue::RescueParameter; type SparseMerkleTree = RescueSparseMerkleTree; use super::{ - constrain_sibling_order, DigestAlgorithmGadget, Merkle3AryNodeVar, - Merkle3AryNonMembershipProofVar, UniversalMerkleTreeGadget, + constrain_sibling_order, DigestAlgorithmGadget, Merkle3AryNodeVar, Merkle3AryProofVar, + UniversalMerkleTreeGadget, }; use num_bigint::BigUint; @@ -25,20 +27,17 @@ impl UniversalMerkleTreeGadget> for PlonkCircuit where F: RescueParameter, { - type NonMembershipProofVar = Merkle3AryNonMembershipProofVar; + type NonMembershipProofVar = Merkle3AryProofVar; fn is_non_member( &mut self, non_elem_idx_var: Variable, - proof_var: Self::NonMembershipProofVar, - root_var: Variable, + proof_var: &Merkle3AryProofVar, + commitment_var: Variable, ) -> Result { - // constrain that the element's index is part of the proof - self.enforce_equal(proof_var.pos_var, non_elem_idx_var)?; - let computed_root_var = { - let path_vars = &proof_var; + let computed_commitment_var = { let mut cur_label = self.zero(); - for cur_node in path_vars.node_vars.iter() { + for cur_node in proof_var.node_vars.iter() { let input_labels = constrain_sibling_order( self, cur_label, @@ -47,45 +46,43 @@ where cur_node.is_left_child, cur_node.is_right_child, )?; - // check that the left child's label is non-zero - self.non_zero_gate(input_labels[0])?; + let is_zero_vars = [ + self.is_zero(input_labels[0])?, + self.is_zero(input_labels[1])?, + self.is_zero(input_labels[2])?, + ]; cur_label = Self::DigestGadget::digest(self, &input_labels)?; } Ok(cur_label) }?; - self.is_equal(computed_root_var, root_var) + self.is_equal(computed_commitment_var, commitment_var) } fn enforce_non_membership_proof( &mut self, non_elem_idx_var: Variable, - proof_var: Self::NonMembershipProofVar, - expected_root_var: Variable, + proof_var: &Merkle3AryProofVar, + expected_commitment_var: Variable, ) -> Result<(), CircuitError> { - let bool_val = self.is_non_member(non_elem_idx_var, proof_var, expected_root_var)?; + let bool_val = self.is_non_member(non_elem_idx_var, proof_var, expected_commitment_var)?; self.enforce_true(bool_val.into()) } fn create_non_membership_proof_variable( &mut self, - merkle_proof: & as MerkleTreeScheme>::MembershipProof, - ) -> Result { - let path = >::to_traversal_path( - &merkle_proof.pos, - merkle_proof.tree_height() - 1, - ); + pos: & as MerkleTreeScheme>::Index, + merkle_proof: &MerkleTreeProof, + ) -> Result { + let path = >::to_traversal_path(&pos, merkle_proof.height()); let nodes = path .iter() - .zip(merkle_proof.proof.iter().skip(1)) - .filter_map(|(branch, node)| match node { - MerkleNode::Branch { value: _, children } => Some((children, branch)), - _ => None, - }) - .map(|(children, branch)| { + .zip(merkle_proof.path_values().iter()) + .filter(|(_, v)| v.len() > 0) + .map(|(branch, siblings)| { Ok(Merkle3AryNodeVar { - sibling1: self.create_variable(children[0].value())?, - sibling2: self.create_variable(children[1].value())?, + sibling1: self.create_variable(siblings[0])?, + sibling2: self.create_variable(siblings[1])?, is_left_child: self.create_boolean_variable(branch == &0)?, is_right_child: self.create_boolean_variable(branch == &2)?, }) @@ -102,12 +99,7 @@ where self.enforce_bool(left_plus_right)?; } - let pos = self.create_variable(merkle_proof.pos.clone().into())?; - - Ok(Self::NonMembershipProofVar { - node_vars: nodes, - pos_var: pos, - }) + Ok(Merkle3AryProofVar { node_vars: nodes }) } } @@ -153,62 +145,65 @@ mod test { hashmap.insert(BigUint::from(2u64), F::from(2u64)); hashmap.insert(BigUint::from(1u64), F::from(3u64)); let mt = SparseMerkleTree::::from_kv_set(2, &hashmap).unwrap(); - let expected_root = mt.commitment().digest(); + let commitment = mt.commitment(); // proof of non-membership let proof = mt.universal_lookup(&uid).expect_not_found().unwrap(); // Circuit computation with a MT - let non_elem_idx_var = circuit.create_variable(uid.into()).unwrap(); + let non_elem_idx_var = circuit.create_variable(uid.clone().into()).unwrap(); let proof_var = UniversalMerkleTreeGadget::>::create_non_membership_proof_variable( &mut circuit, + &uid.into(), &proof, ) .unwrap(); - let root_var = MerkleTreeGadget::>::create_root_variable( + let commitment_var = MerkleTreeGadget::>::create_commitment_variable( &mut circuit, - expected_root, + &commitment, ) .unwrap(); UniversalMerkleTreeGadget::>::enforce_non_membership_proof( &mut circuit, non_elem_idx_var, - proof_var, - root_var, + &proof_var, + commitment_var, ) .unwrap(); assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); - *circuit.witness_mut(root_var) = F::zero(); + *circuit.witness_mut(commitment_var) = F::zero(); assert!(circuit.check_circuit_satisfiability(&[]).is_err()); // Bad path: // The circuit cannot be satisfied if we try to prove non-membership of an // existing element. let mut circuit = PlonkCircuit::::new_turbo_plonk(); - let elem_idx_var = circuit.create_variable(2u64.into()).unwrap(); + let pos = 2u64; + let elem_idx_var = circuit.create_variable(pos.into()).unwrap(); - let path_vars = + let proof_var = UniversalMerkleTreeGadget::>::create_non_membership_proof_variable( &mut circuit, + &pos.into(), &proof, ) .unwrap(); - let root_var = MerkleTreeGadget::>::create_root_variable( + let commitment_var = MerkleTreeGadget::>::create_commitment_variable( &mut circuit, - expected_root, + &commitment, ) .unwrap(); UniversalMerkleTreeGadget::>::enforce_non_membership_proof( &mut circuit, elem_idx_var, - path_vars, - root_var, + &proof_var, + commitment_var, ) .unwrap(); diff --git a/merkle_tree/src/hasher.rs b/merkle_tree/src/hasher.rs index cd92fc9d8..385611fb8 100644 --- a/merkle_tree/src/hasher.rs +++ b/merkle_tree/src/hasher.rs @@ -17,10 +17,10 @@ //! // payload type is `usize`, hash function is `Sha256`. //! let mt = HasherMerkleTree::::from_elems(Some(2), &my_data)?; //! -//! let root = mt.commitment().digest(); +//! let commitment = mt.commitment(); //! let (val, proof) = mt.lookup(2).expect_ok()?; //! assert_eq!(val, &3); -//! assert!(HasherMerkleTree::::verify(root, 2, proof)?.is_ok()); +//! assert!(HasherMerkleTree::::verify(commitment, 2, val, proof)?.is_ok()); //! # Ok(()) //! # } //! ``` diff --git a/merkle_tree/src/internal.rs b/merkle_tree/src/internal.rs index d07f9e22f..9b791daf5 100644 --- a/merkle_tree/src/internal.rs +++ b/merkle_tree/src/internal.rs @@ -7,7 +7,7 @@ use super::{ DigestAlgorithm, Element, Index, LookupResult, MerkleCommitment, NodeValue, ToTraversalPath, }; -use crate::{errors::MerkleTreeError, VerificationResult}; +use crate::{errors::MerkleTreeError, prelude::MerkleTree, VerificationResult}; use alloc::sync::Arc; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::{borrow::Borrow, format, iter::Peekable, string::ToString, vec, vec::Vec}; @@ -46,7 +46,7 @@ pub enum MerkleNode { elem: E, }, /// The subtree is forgotten from the memory - ForgettenSubtree { + ForgottenSubtree { /// Merkle hash value of this forgotten subtree #[serde(with = "canonical")] value: T, @@ -70,19 +70,16 @@ where elem: _, } => *value, Self::Branch { value, children: _ } => *value, - Self::ForgettenSubtree { value } => *value, + Self::ForgottenSubtree { value } => *value, } } #[inline] pub(crate) fn is_forgotten(&self) -> bool { - matches!(self, Self::ForgettenSubtree { .. }) + matches!(self, Self::ForgottenSubtree { .. }) } } -/// A merkle path is a bottom-up list of nodes from leaf to the root. -pub type MerklePath = Vec>; - /// A merkle commitment consists a root hash value, a tree height and number of /// leaves #[derive( @@ -131,52 +128,75 @@ impl MerkleCommitment for MerkleTreeCommitment { } } -/// Merkle proof struct. -#[derive(Derivative, Debug, Clone, Serialize, Deserialize)] -#[derivative(Eq, Hash, PartialEq)] -#[serde(bound = "E: CanonicalSerialize + CanonicalDeserialize, - I: CanonicalSerialize + CanonicalDeserialize,")] -pub struct MerkleProof -where - E: Element, - I: Index, - T: NodeValue, -{ - /// Proof of inclusion for element at index `pos` - #[serde(with = "canonical")] - pub pos: I, - /// Nodes of proof path, from root to leaf - pub proof: MerklePath, +/// A (non)membership Merkle proof consists of all values of siblings of a Merkle path. +#[derive( + Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, CanonicalSerialize, CanonicalDeserialize, +)] +#[tagged("MERKLE_PROOF")] +pub struct MerkleTreeProof(pub Vec>); + +impl super::MerkleProof for MerkleTreeProof { + /// Expected height of the Merkle tree. + fn height(&self) -> usize { + self.0.len() + } + + /// Return all values of siblings of this Merkle path + fn path_values(&self) -> &[Vec] { + &self.0 + } } -impl MerkleProof +/// Verify a merkle proof +/// * `commitment` - a merkle tree commitment +/// * `pos` - zero-based index of the leaf in the tree +/// * `element` - the leaf value, None if verifying a non-membership proof +/// * `proof` - a membership proof for `element` at given `pos` +/// * `returns` - Ok(true) if the proof is accepted, Ok(false) if not. Err() +/// if the proof is not well structured, E.g. not for this merkle tree. +pub(crate) fn verify_merkle_proof( + commitment: &MerkleTreeCommitment, + pos: &I, + element: Option<&E>, + proof: &[Vec], +) -> Result where E: Element, - I: Index, + I: Index + ToTraversalPath, T: NodeValue, + H: DigestAlgorithm, { - /// Return the height of this proof. - pub fn tree_height(&self) -> usize { - self.proof.len() - } - - /// Form a `MerkleProof` from a given index and Merkle path. - pub fn new(pos: I, proof: MerklePath) -> Self { - MerkleProof { pos, proof } - } - - /// Return the index of this `MerkleProof`. - pub fn index(&self) -> &I { - &self.pos - } - - /// Return the element associated with this `MerkleProof`. None if it's a - /// non-membership proof. - pub fn elem(&self) -> Option<&E> { - match self.proof.first() { - Some(MerkleNode::Leaf { elem, .. }) => Some(elem), - _ => None, + if proof.len() == commitment.height() { + let init = if let Some(elem) = element { + H::digest_leaf(pos, elem)? + } else { + T::default() + }; + let computed_root = pos + .to_traversal_path(commitment.height()) + .iter() + .zip(proof.iter()) + .try_fold( + init, + |val, (branch, values)| -> Result { + if values.len() == 0 { + Ok(T::default()) + } else { + let mut data = values.clone(); + data.insert(*branch, val); + H::digest(&data) + } + }, + )?; + if computed_root == commitment.digest() { + Ok(Ok(())) + } else { + Ok(Err(())) } + } else { + Err(MerkleTreeError::InconsistentStructureError( + "Inconsistent Merkle tree height.".to_string(), + )) } } @@ -247,9 +267,9 @@ where .chunks(ARITY) .into_iter() .map(|chunk| { - let children = chunk + let children: Vec<_> = chunk .pad_using(ARITY, |_| Arc::new(MerkleNode::::Empty)) - .collect::>(); + .collect(); Ok(Arc::new(MerkleNode::::Branch { value: digest_branch::(&children)?, children, @@ -311,7 +331,7 @@ where .map(|(pos, elem)| { let pos = pos as u64; Ok(if pos < num_leaves - 1 { - Arc::new(MerkleNode::ForgettenSubtree { + Arc::new(MerkleNode::ForgottenSubtree { value: H::digest_leaf(&pos, elem.borrow())?, }) } else { @@ -331,7 +351,7 @@ where }) .collect::, MerkleTreeError>>()?; for i in 1..cur_nodes.len() - 1 { - cur_nodes[i] = Arc::new(MerkleNode::ForgettenSubtree { + cur_nodes[i] = Arc::new(MerkleNode::ForgottenSubtree { value: cur_nodes[i].value(), }) } @@ -351,7 +371,7 @@ where }) .collect::, MerkleTreeError>>()?; for i in 1..cur_nodes.len() - 1 { - cur_nodes[i] = Arc::new(MerkleNode::ForgettenSubtree { + cur_nodes[i] = Arc::new(MerkleNode::ForgottenSubtree { value: cur_nodes[i].value(), }) } @@ -390,44 +410,37 @@ where traversal_path: &[usize], ) -> ( Arc, - LookupResult, MerklePath>, + LookupResult, MerkleTreeProof>, ) { match self { MerkleNode::Empty => ( Arc::new(self.clone()), - LookupResult::NotFound(vec![MerkleNode::Empty; height + 1]), + LookupResult::NotFound(MerkleTreeProof(vec![])), ), MerkleNode::Branch { value, children } => { let mut children = children.clone(); let (new_child, result) = children[traversal_path[height - 1]] .forget_internal(height - 1, traversal_path); match result { - LookupResult::Ok(elem, mut proof) => { - proof.push(MerkleNode::Branch { - value: T::default(), - children: children + LookupResult::Ok(elem, mut membership_proof) => { + membership_proof.0.push( + children .iter() - .map(|child| { - if let MerkleNode::Empty = **child { - Arc::new(MerkleNode::Empty) - } else { - Arc::new(MerkleNode::ForgettenSubtree { - value: child.value(), - }) - } - }) + .enumerate() + .filter(|(id, _)| *id != traversal_path[height - 1]) + .map(|(_, child)| child.value()) .collect::>(), - }); + ); children[traversal_path[height - 1]] = new_child; if children.iter().all(|child| { matches!( **child, - MerkleNode::Empty | MerkleNode::ForgettenSubtree { .. } + MerkleNode::Empty | MerkleNode::ForgottenSubtree { .. } ) }) { ( - Arc::new(MerkleNode::ForgettenSubtree { value: *value }), - LookupResult::Ok(elem, proof), + Arc::new(MerkleNode::ForgottenSubtree { value: *value }), + LookupResult::Ok(elem, membership_proof), ) } else { ( @@ -435,7 +448,7 @@ where value: *value, children, }), - LookupResult::Ok(elem, proof), + LookupResult::Ok(elem, membership_proof), ) } }, @@ -443,21 +456,14 @@ where (Arc::new(self.clone()), LookupResult::NotInMemory) }, LookupResult::NotFound(mut non_membership_proof) => { - non_membership_proof.push(MerkleNode::Branch { - value: T::default(), - children: children + non_membership_proof.0.push( + children .iter() - .map(|child| { - if let MerkleNode::Empty = **child { - Arc::new(MerkleNode::Empty) - } else { - Arc::new(MerkleNode::ForgettenSubtree { - value: child.value(), - }) - } - }) + .enumerate() + .filter(|(id, _)| *id != traversal_path[height - 1]) + .map(|(_, child)| child.value()) .collect::>(), - }); + ); ( Arc::new(self.clone()), LookupResult::NotFound(non_membership_proof), @@ -465,87 +471,96 @@ where }, } }, - MerkleNode::Leaf { value, pos, elem } => { - let elem = elem.clone(); - let proof = vec![MerkleNode::::Leaf { - value: *value, - pos: pos.clone(), - elem: elem.clone(), - }]; - ( - Arc::new(MerkleNode::ForgettenSubtree { value: *value }), - LookupResult::Ok(elem, proof), - ) - }, + MerkleNode::Leaf { value, pos, elem } => ( + Arc::new(MerkleNode::ForgottenSubtree { value: *value }), + LookupResult::Ok(elem.clone(), MerkleTreeProof(vec![])), + ), _ => (Arc::new(self.clone()), LookupResult::NotInMemory), } } - /// Re-insert a forgotten leaf to the Merkle tree. We assume that the proof - /// is valid and already checked. + /// Re-insert a forgotten leaf to the Merkle tree. + /// It also fails if the Merkle proof is invalid. pub(crate) fn remember_internal( &self, height: usize, traversal_path: &[usize], - path_values: &[T], - proof: &[MerkleNode], + pos: &I, + element: Option<&E>, + proof: &[Vec], ) -> Result, MerkleTreeError> where H: DigestAlgorithm, { - if self.value() != path_values[height] { - return Err(MerkleTreeError::InconsistentStructureError(format!( - "Invalid proof. Hash differs at height {}: (expected: {:?}, received: {:?})", - height, - self.value(), - path_values[height] - ))); - } - - match (self, &proof[height]) { - (Self::ForgettenSubtree { value }, Self::Branch { children, .. }) => { - // Recurse into the appropriate sub-tree to remember the rest of the path. - let mut children = children.clone(); - children[traversal_path[height - 1]] = children[traversal_path[height - 1]] - .remember_internal::( - height - 1, - traversal_path, - path_values, - proof, - )?; - // Remember `*self`. - Ok(Arc::new(Self::Branch { - value: *value, - children, - })) - }, - (Self::ForgettenSubtree { .. }, node) => { - // Replace forgotten sub-tree with a hopefully-less-forgotten sub-tree from the - // proof. Safe because we already checked our hash value matches the proof. - Ok(Arc::new(node.clone())) + match self { + MerkleNode::Empty => Ok(Arc::new(self.clone())), + MerkleNode::Leaf { + value, + pos: leaf_pos, + elem, + } => { + if height != 0 { + // Reach a leaf before it should + Err(MerkleTreeError::InconsistentStructureError( + "Malformed Merkle tree or proof".to_string(), + )) + } else { + Ok(Arc::new(self.clone())) + } }, - (Self::Branch { value, children }, Self::Branch { .. }) => { - let mut children = children.clone(); - children[traversal_path[height - 1]] = children[traversal_path[height - 1]] - .remember_internal::( + MerkleNode::Branch { value, children } => { + if height == 0 { + // Reach a branch when there should be a leaf + Err(MerkleTreeError::InconsistentStructureError( + "Malformed merkle tree".to_string(), + )) + } else { + let branch = traversal_path[height - 1]; + let mut children = children.clone(); + children[branch] = children[branch].remember_internal::( height - 1, traversal_path, - path_values, + pos, + element, proof, )?; - Ok(Arc::new(Self::Branch { - value: *value, - children, - })) - }, - (Self::Leaf { .. }, Self::Leaf { .. }) | (Self::Empty, Self::Empty) => { - // This node is already a complete sub-tree, so there's nothing to remember. The - // proof matches, so just return success. - Ok(Arc::new(self.clone())) + Ok(Arc::new(MerkleNode::Branch { + value: *value, + children, + })) + } }, - (..) => Err(MerkleTreeError::InconsistentStructureError( - "Invalid proof".into(), - )), + MerkleNode::ForgottenSubtree { value } => Ok(Arc::new(if height == 0 { + if let Some(element) = element { + MerkleNode::Leaf { + value: H::digest_leaf(pos, element)?, + pos: pos.clone(), + elem: element.clone(), + } + } else { + MerkleNode::Empty + } + } else { + let branch = traversal_path[height - 1]; + let mut values = proof[height - 1].clone(); + values.insert(branch, *value); + let mut children = values + .iter() + .map(|&value| Arc::new(MerkleNode::ForgottenSubtree { value })) + .collect::>(); + children[branch] = children[branch].remember_internal::( + height - 1, + traversal_path, + pos, + element, + proof, + )?; + values[branch] = children[branch].value(); + MerkleNode::Branch { + value: H::digest(&values)?, + children, + } + })), } } @@ -557,50 +572,34 @@ where &self, height: usize, traversal_path: &[usize], - ) -> LookupResult<&E, MerklePath, MerklePath> { + ) -> LookupResult<&E, MerkleTreeProof, MerkleTreeProof> { match self { - MerkleNode::Empty => { - LookupResult::NotFound(vec![MerkleNode::::Empty; height + 1]) - }, + MerkleNode::Empty => LookupResult::NotFound(MerkleTreeProof(vec![vec![]; height])), MerkleNode::Branch { value: _, children } => { match children[traversal_path[height - 1]] .lookup_internal(height - 1, traversal_path) { - LookupResult::Ok(elem, mut proof) => { - proof.push(MerkleNode::Branch { - value: T::default(), - children: children + LookupResult::Ok(elem, mut membership_proof) => { + membership_proof.0.push( + children .iter() - .map(|child| { - if let MerkleNode::Empty = **child { - Arc::new(MerkleNode::Empty) - } else { - Arc::new(MerkleNode::ForgettenSubtree { - value: child.value(), - }) - } - }) + .enumerate() + .filter(|(id, _)| *id != traversal_path[height - 1]) + .map(|(_, child)| child.value()) .collect::>(), - }); - LookupResult::Ok(elem, proof) + ); + LookupResult::Ok(elem, membership_proof) }, LookupResult::NotInMemory => LookupResult::NotInMemory, LookupResult::NotFound(mut non_membership_proof) => { - non_membership_proof.push(MerkleNode::Branch { - value: T::default(), - children: children + non_membership_proof.0.push( + children .iter() - .map(|child| { - if let MerkleNode::Empty = **child { - Arc::new(MerkleNode::Empty) - } else { - Arc::new(MerkleNode::ForgettenSubtree { - value: child.value(), - }) - } - }) + .enumerate() + .filter(|(id, _)| *id != traversal_path[height - 1]) + .map(|(_, child)| child.value()) .collect::>(), - }); + ); LookupResult::NotFound(non_membership_proof) }, } @@ -609,7 +608,7 @@ where elem, value: _, pos: _, - } => LookupResult::Ok(elem, vec![self.clone()]), + } => LookupResult::Ok(elem, MerkleTreeProof(vec![])), _ => LookupResult::NotInMemory, } } @@ -662,7 +661,7 @@ where )?; let mut children = children.clone(); children[branch] = result.0; - if matches!(*children[branch], MerkleNode::ForgettenSubtree { .. }) { + if matches!(*children[branch], MerkleNode::ForgottenSubtree { .. }) { // If the branch containing the update was forgotten by // user, the update failed and nothing was changed, so we // can short-circuit without recomputing this node's value. @@ -714,9 +713,7 @@ where } } else { let branch = traversal_path[height - 1]; - let mut children = (0..ARITY) - .map(|_| Arc::new(Self::Empty)) - .collect::>(); + let mut children: Vec<_> = (0..ARITY).map(|_| Arc::new(Self::Empty)).collect(); // Inserting new leave here, shortcutting let result = children[branch].update_with_internal::( height - 1, @@ -740,7 +737,7 @@ where } } }, - MerkleNode::ForgettenSubtree { .. } => Err(MerkleTreeError::ForgottenLeaf), + MerkleNode::ForgottenSubtree { .. } => Err(MerkleTreeError::ForgottenLeaf), } } } @@ -812,7 +809,7 @@ where 0 }; let cap = ARITY; - let mut children = (0..cap).map(|_| Arc::new(Self::Empty)).collect::>(); + let mut children: Vec<_> = (0..cap).map(|_| Arc::new(Self::Empty)).collect(); while data.peek().is_some() && frontier < cap { let (new_child, increment) = children[frontier] .extend_internal::( @@ -837,7 +834,7 @@ where } }, MerkleNode::Leaf { .. } => Err(MerkleTreeError::ExistingLeaf), - MerkleNode::ForgettenSubtree { .. } => Err(MerkleTreeError::ForgottenLeaf), + MerkleNode::ForgottenSubtree { .. } => Err(MerkleTreeError::ForgottenLeaf), } } @@ -871,7 +868,7 @@ where while data.peek().is_some() && frontier < cap { if frontier > 0 && !children[frontier - 1].is_forgotten() { children[frontier - 1] = - Arc::new(MerkleNode::::ForgettenSubtree { + Arc::new(MerkleNode::::ForgottenSubtree { value: children[frontier - 1].value(), }); } @@ -911,11 +908,11 @@ where 0 }; let cap = ARITY; - let mut children = (0..cap).map(|_| Arc::new(Self::Empty)).collect::>(); + let mut children: Vec<_> = (0..cap).map(|_| Arc::new(Self::Empty)).collect(); while data.peek().is_some() && frontier < cap { if frontier > 0 && !children[frontier - 1].is_forgotten() { children[frontier - 1] = - Arc::new(MerkleNode::::ForgettenSubtree { + Arc::new(MerkleNode::::ForgottenSubtree { value: children[frontier - 1].value(), }); } @@ -942,98 +939,7 @@ where } }, MerkleNode::Leaf { .. } => Err(MerkleTreeError::ExistingLeaf), - MerkleNode::ForgettenSubtree { .. } => Err(MerkleTreeError::ForgottenLeaf), - } - } -} - -impl MerkleProof -where - E: Element, - I: Index + ToTraversalPath, - T: NodeValue, -{ - /// Verify a membership proof by comparing the computed root value to the - /// expected one. - pub(crate) fn verify_membership_proof( - &self, - expected_root: &T, - ) -> Result - where - H: DigestAlgorithm, - { - if let MerkleNode::::Leaf { - value: _, - pos, - elem, - } = &self.proof[0] - { - let init = H::digest_leaf(pos, elem)?; - let computed_root = self - .pos - .to_traversal_path(self.tree_height() - 1) - .iter() - .zip(self.proof.iter().skip(1)) - .try_fold(init, |val, (branch, node)| -> Result { - match node { - MerkleNode::Branch { value: _, children } => { - let mut data = - children.iter().map(|node| node.value()).collect::>(); - data[*branch] = val; - H::digest(&data) - }, - _ => Err(MerkleTreeError::InconsistentStructureError( - "Incompatible proof for this merkle tree".to_string(), - )), - } - })?; - if computed_root == *expected_root { - Ok(Ok(())) - } else { - Ok(Err(())) - } - } else { - Err(MerkleTreeError::InconsistentStructureError( - "Invalid proof type".to_string(), - )) - } - } - - /// Verify a non membership proof by comparing the computed root value - /// to the expected one. - pub(crate) fn verify_non_membership_proof( - &self, - expected_root: &T, - ) -> Result - where - H: DigestAlgorithm, - { - if let MerkleNode::::Empty = &self.proof[0] { - let init = T::default(); - let computed_root = self - .pos - .to_traversal_path(self.tree_height() - 1) - .iter() - .zip(self.proof.iter().skip(1)) - .try_fold(init, |val, (branch, node)| -> Result { - match node { - MerkleNode::Branch { value: _, children } => { - let mut data = - children.iter().map(|node| node.value()).collect::>(); - data[*branch] = val; - H::digest(&data) - }, - MerkleNode::Empty => Ok(init), - _ => Err(MerkleTreeError::InconsistentStructureError( - "Incompatible proof for this merkle tree".to_string(), - )), - } - })?; - Ok(computed_root == *expected_root) - } else { - Err(MerkleTreeError::InconsistentStructureError( - "Invalid proof type".to_string(), - )) + MerkleNode::ForgottenSubtree { .. } => Err(MerkleTreeError::ForgottenLeaf), } } } diff --git a/merkle_tree/src/lib.rs b/merkle_tree/src/lib.rs index 9dd699642..d598da712 100644 --- a/merkle_tree/src/lib.rs +++ b/merkle_tree/src/lib.rs @@ -25,7 +25,7 @@ pub mod gadgets; pub mod hasher; pub mod light_weight; pub mod macros; -pub mod namespaced_merkle_tree; +// pub mod namespaced_merkle_tree; pub mod universal_merkle_tree; pub(crate) mod internal; @@ -182,6 +182,24 @@ pub trait MerkleCommitment: fn size(&self) -> u64; } +/// Trait for a Merkle proof +pub trait MerkleProof: + Eq + + PartialEq + + Hash + + Clone + + CanonicalSerialize + + CanonicalDeserialize + + Serialize + + for<'a> Deserialize<'a> +{ + /// Expected height of the Merkle tree. + fn height(&self) -> usize; + + /// Return all values of siblings of this Merkle path + fn path_values(&self) -> &[Vec]; +} + /// Basic functionalities for a merkle tree implementation. Abstracted as an /// accumulator for fixed-length array. Supports generate membership proof at a /// given position and verify a membership proof. @@ -193,7 +211,7 @@ pub trait MerkleTreeScheme: Sized { /// Internal and root node value type NodeValue: NodeValue; /// Merkle proof - type MembershipProof: Clone + Eq + Hash; + type MembershipProof: MerkleProof; /// Batch proof type BatchMembershipProof: Clone; /// Merkle tree commitment @@ -223,15 +241,16 @@ pub trait MerkleTreeScheme: Sized { ) -> LookupResult<&Self::Element, Self::MembershipProof, ()>; /// Verify an element is a leaf of a Merkle tree given the proof - /// * `root` - a merkle tree root, usually obtained from - /// `Self::commitment().digest()` + /// * `commitment` - a merkle tree commitment /// * `pos` - zero-based index of the leaf in the tree - /// * `proof` - a merkle tree proof + /// * `element` - the leaf value + /// * `proof` - a membership proof for `element` at given `pos` /// * `returns` - Ok(true) if the proof is accepted, Ok(false) if not. Err() /// if the proof is not well structured, E.g. not for this merkle tree. fn verify( - root: impl Borrow, + commitment: impl Borrow, pos: impl Borrow, + element: impl Borrow, proof: impl Borrow, ) -> Result; @@ -342,10 +361,10 @@ pub trait UniversalMerkleTreeScheme: MerkleTreeScheme { /// * `returns` - Ok(true) if the proof is accepted, Ok(false) if not. Err() /// if the proof is not well structured, E.g. not for this merkle tree. fn non_membership_verify( - &self, + commitment: impl Borrow, pos: impl Borrow, proof: impl Borrow, - ) -> Result; + ) -> Result; // TODO(Chengyu): non-membership proof interfaces } diff --git a/merkle_tree/src/light_weight.rs b/merkle_tree/src/light_weight.rs index e00d2f048..593f7ed80 100644 --- a/merkle_tree/src/light_weight.rs +++ b/merkle_tree/src/light_weight.rs @@ -9,11 +9,11 @@ use super::{ internal::{ - build_light_weight_tree_internal, MerkleNode, MerkleProof, MerkleTreeCommitment, - MerkleTreeIntoIter, MerkleTreeIter, + build_light_weight_tree_internal, MerkleNode, MerkleTreeCommitment, MerkleTreeIntoIter, + MerkleTreeIter, MerkleTreeProof, }, AppendableMerkleTreeScheme, DigestAlgorithm, Element, ForgetableMerkleTreeScheme, Index, - LookupResult, MerkleCommitment, MerkleTreeScheme, NodeValue, ToTraversalPath, + LookupResult, MerkleCommitment, MerkleProof, MerkleTreeScheme, NodeValue, ToTraversalPath, }; use crate::{ errors::MerkleTreeError, impl_forgetable_merkle_tree_scheme, impl_merkle_tree_scheme, @@ -110,7 +110,7 @@ where #[cfg(test)] mod mt_tests { use crate::{ - internal::{MerkleNode, MerkleProof}, + internal::{MerkleNode, MerkleTreeProof}, prelude::{RescueLightWeightMerkleTree, RescueMerkleTree}, *, }; @@ -175,81 +175,73 @@ mod mt_tests { let mut mt = RescueLightWeightMerkleTree::::from_elems(Some(2), [F::from(3u64), F::from(1u64)]) .unwrap(); - let mut mock_mt = + let mut full_mt = RescueMerkleTree::::from_elems(Some(2), [F::from(3u64), F::from(1u64)]).unwrap(); assert!(mt.lookup(0).expect_not_in_memory().is_ok()); assert!(mt.lookup(1).expect_ok().is_ok()); - assert!(mt.extend(&[F::from(3u64), F::from(1u64)]).is_ok()); - assert!(mock_mt.extend(&[F::from(3u64), F::from(1u64)]).is_ok()); + assert!(mt.extend(&[F::from(33u64), F::from(41u64)]).is_ok()); + assert!(full_mt.extend(&[F::from(33u64), F::from(41u64)]).is_ok()); assert!(mt.lookup(0).expect_not_in_memory().is_ok()); assert!(mt.lookup(1).expect_not_in_memory().is_ok()); assert!(mt.lookup(2).expect_not_in_memory().is_ok()); assert!(mt.lookup(3).expect_ok().is_ok()); - let (elem, proof) = mock_mt.lookup(0).expect_ok().unwrap(); + + // Should have the same commitment + assert_eq!(mt.commitment(), full_mt.commitment()); + + let commitment = mt.commitment(); + let (elem, proof) = full_mt.lookup(0).expect_ok().unwrap(); assert_eq!(elem, &F::from(3u64)); - assert_eq!(proof.tree_height(), 3); assert!( - RescueLightWeightMerkleTree::::verify(&mt.root.value(), 0, &proof) + RescueLightWeightMerkleTree::::verify(&commitment, 0, elem, &proof) .unwrap() .is_ok() ); - let mut bad_proof = proof.clone(); - if let MerkleNode::Leaf { - value: _, - pos: _, - elem, - } = &mut bad_proof.proof[0] - { - *elem = F::from(4u64); - } else { - unreachable!() - } - - let result = RescueLightWeightMerkleTree::::verify(&mt.root.value(), 0, &bad_proof); - assert!(result.unwrap().is_err()); - - let mut forge_proof = MerkleProof::new(2, proof.proof); - if let MerkleNode::Leaf { - value: _, - pos, - elem, - } = &mut forge_proof.proof[0] - { - *pos = 2; - *elem = F::from(0u64); - } else { - unreachable!() - } - let result = RescueLightWeightMerkleTree::::verify(&mt.root.value(), 2, &forge_proof); - assert!(result.unwrap().is_err()); - } + // Wrong element value, should fail. + assert!( + RescueLightWeightMerkleTree::::verify(&commitment, 0, F::from(14u64), &proof) + .unwrap() + .is_err() + ); - #[test] - fn test_light_mt_serde() { - test_light_mt_serde_helper::(); - test_light_mt_serde_helper::(); - test_light_mt_serde_helper::(); - } + // Wrong pos, should fail. + assert!( + RescueLightWeightMerkleTree::::verify(&commitment, 2, elem, &proof) + .unwrap() + .is_err() + ); - fn test_light_mt_serde_helper() { - let mt = - RescueLightWeightMerkleTree::::from_elems(Some(2), [F::from(3u64), F::from(1u64)]) - .unwrap(); - let proof = mt.lookup(1).expect_ok().unwrap().1; - let node = &proof.proof[0]; + let mut bad_proof = proof.clone(); + bad_proof.0[0][0] = F::one(); - assert_eq!( - mt, - bincode::deserialize(&bincode::serialize(&mt).unwrap()).unwrap() - ); - assert_eq!( - proof, - bincode::deserialize(&bincode::serialize(&proof).unwrap()).unwrap() - ); - assert_eq!( - *node, - bincode::deserialize(&bincode::serialize(node).unwrap()).unwrap() + assert!( + RescueLightWeightMerkleTree::::verify(&commitment, 0, elem, &bad_proof) + .unwrap() + .is_err() ); } + + // #[test] + // fn test_light_mt_serde() { + // test_light_mt_serde_helper::(); + // test_light_mt_serde_helper::(); + // test_light_mt_serde_helper::(); + // } + + // fn test_light_mt_serde_helper() { + // let mt = + // RescueLightWeightMerkleTree::::from_elems(Some(2), [F::from(3u64), F::from(1u64)]) + // .unwrap(); + // let (elem, proof) = mt.lookup(1).expect_ok().unwrap(); + + // assert_eq!( + // mt, + // bincode::deserialize(&bincode::serialize(&mt).unwrap()).unwrap() + // ); + // assert_eq!( + // proof, + // bincode::deserialize(&bincode::serialize(&proof).unwrap()).unwrap() + // ); + // } } diff --git a/merkle_tree/src/macros.rs b/merkle_tree/src/macros.rs index 9f372ca72..471626a29 100644 --- a/merkle_tree/src/macros.rs +++ b/merkle_tree/src/macros.rs @@ -40,7 +40,7 @@ macro_rules! impl_merkle_tree_scheme { type Element = E; type Index = I; type NodeValue = T; - type MembershipProof = MerkleProof; + type MembershipProof = MerkleTreeProof; // TODO(Chengyu): implement batch membership proof type BatchMembershipProof = (); type Commitment = MerkleTreeCommitment; @@ -71,7 +71,7 @@ macro_rules! impl_merkle_tree_scheme { let traversal_path = pos.to_traversal_path(self.height); match self.root.lookup_internal(self.height, &traversal_path) { LookupResult::Ok(value, proof) => { - LookupResult::Ok(&value, MerkleProof::new(pos.clone(), proof)) + LookupResult::Ok(&value, proof) }, LookupResult::NotInMemory => LookupResult::NotInMemory, LookupResult::NotFound(_) => LookupResult::NotFound(()), @@ -79,14 +79,12 @@ macro_rules! impl_merkle_tree_scheme { } fn verify( - root: impl Borrow, + commitment: impl Borrow, pos: impl Borrow, + element: impl Borrow, proof: impl Borrow, ) -> Result { - if *pos.borrow() != proof.borrow().pos { - return Ok(Err(())); // invalid proof for the given pos - } - proof.borrow().verify_membership_proof::(root.borrow()) + crate::internal::verify_merkle_proof::(commitment.borrow(), pos.borrow(), Some(element.borrow()), proof.borrow().path_values()) } fn iter(&self) -> MerkleTreeIter { @@ -143,7 +141,7 @@ macro_rules! impl_forgetable_merkle_tree_scheme { fn from_commitment(com: impl Borrow) -> Self { let com = com.borrow(); $name { - root: Arc::new(MerkleNode::ForgettenSubtree { + root: Arc::new(MerkleNode::ForgottenSubtree { value: com.digest(), }), height: com.height(), @@ -161,9 +159,7 @@ macro_rules! impl_forgetable_merkle_tree_scheme { let (new_root, result) = self.root.forget_internal(self.height, &traversal_path); self.root = new_root; match result { - LookupResult::Ok(elem, proof) => { - LookupResult::Ok(elem, MerkleProof::new(pos.clone(), proof)) - }, + LookupResult::Ok(elem, proof) => LookupResult::Ok(elem, proof), LookupResult::NotInMemory => LookupResult::NotInMemory, LookupResult::NotFound(_) => LookupResult::NotFound(()), } @@ -175,53 +171,23 @@ macro_rules! impl_forgetable_merkle_tree_scheme { element: impl Borrow, proof: impl Borrow, ) -> Result<(), MerkleTreeError> { + let pos = pos.borrow(); + let element = element.borrow(); let proof = proof.borrow(); - let traversal_path = pos.borrow().to_traversal_path(self.height); - if let MerkleNode::::Leaf { - value: _, - pos, - elem, - } = &proof.proof[0] - { - if !elem.eq(element.borrow()) { - return Err(MerkleTreeError::InconsistentStructureError( - "Element does not match the proof.".to_string(), - )); - } - let proof_leaf_value = H::digest_leaf(pos, elem)?; - let mut path_values = vec![proof_leaf_value]; - traversal_path.iter().zip(proof.proof.iter().skip(1)).fold( - Ok(proof_leaf_value), - |result, (branch, node)| -> Result { - match result { - Ok(val) => match node { - MerkleNode::Branch { value: _, children } => { - let mut data: Vec<_> = - children.iter().map(|node| node.value()).collect(); - data[*branch] = val; - let digest = H::digest(&data)?; - path_values.push(digest); - Ok(digest) - }, - _ => Err(MerkleTreeError::InconsistentStructureError( - "Incompatible proof for this merkle tree".to_string(), - )), - }, - Err(e) => Err(e), - } - }, - )?; + if Self::verify(&self.commitment(), pos, element, proof)?.is_err() { + Err(MerkleTreeError::InconsistentStructureError( + "Wrong proof".to_string(), + )) + } else { + let traversal_path = pos.to_traversal_path(self.height); self.root = self.root.remember_internal::( self.height, &traversal_path, - &path_values, - &proof.proof, + pos, + Some(element), + proof.path_values(), )?; Ok(()) - } else { - Err(MerkleTreeError::InconsistentStructureError( - "Invalid proof type".to_string(), - )) } } } diff --git a/merkle_tree/src/namespaced_merkle_tree/mod.rs b/merkle_tree/src/namespaced_merkle_tree/mod.rs index ea86ed456..c72998fa3 100644 --- a/merkle_tree/src/namespaced_merkle_tree/mod.rs +++ b/merkle_tree/src/namespaced_merkle_tree/mod.rs @@ -212,11 +212,12 @@ where } fn verify( - root: impl Borrow, + commitment: impl Borrow, pos: impl Borrow, + element: impl Borrow, proof: impl Borrow, ) -> Result { - as MerkleTreeScheme>::verify(root, pos, proof) + as MerkleTreeScheme>::verify(commitment, pos, element, proof) } fn iter(&self) -> MerkleTreeIter { @@ -257,7 +258,7 @@ where { // Helper function to lookup a proof that should be in the tree because of NMT // invariants - fn lookup_proof(&self, idx: u64) -> MerkleProof, ARITY> { + fn lookup_proof(&self, idx: u64) -> MerkleProof> { if let LookupResult::Ok(_, proof) = self.inner.lookup(idx) { proof } else { diff --git a/merkle_tree/src/prelude.rs b/merkle_tree/src/prelude.rs index 5925cf060..166426781 100644 --- a/merkle_tree/src/prelude.rs +++ b/merkle_tree/src/prelude.rs @@ -9,7 +9,7 @@ pub use crate::{ append_only::MerkleTree, impl_to_traversal_path_biguint, impl_to_traversal_path_primitives, - internal::{MerkleNode, MerklePath, MerkleProof}, + internal::{MerkleNode, MerkleTreeProof}, universal_merkle_tree::UniversalMerkleTree, AppendableMerkleTreeScheme, DigestAlgorithm, Element, ForgetableMerkleTreeScheme, ForgetableUniversalMerkleTreeScheme, Index, LookupResult, MerkleCommitment, MerkleTreeScheme, diff --git a/merkle_tree/src/universal_merkle_tree.rs b/merkle_tree/src/universal_merkle_tree.rs index 939006bbe..fd3c3c77b 100644 --- a/merkle_tree/src/universal_merkle_tree.rs +++ b/merkle_tree/src/universal_merkle_tree.rs @@ -6,9 +6,11 @@ //! Implementation of a typical Sparse Merkle Tree. use super::{ - internal::{MerkleNode, MerkleProof, MerkleTreeCommitment, MerkleTreeIntoIter, MerkleTreeIter}, + internal::{ + MerkleNode, MerkleTreeCommitment, MerkleTreeIntoIter, MerkleTreeIter, MerkleTreeProof, + }, DigestAlgorithm, Element, ForgetableMerkleTreeScheme, ForgetableUniversalMerkleTreeScheme, - Index, LookupResult, MerkleCommitment, MerkleTreeScheme, NodeValue, + Index, LookupResult, MerkleCommitment, MerkleProof, MerkleTreeScheme, NodeValue, PersistentUniversalMerkleTreeScheme, ToTraversalPath, UniversalMerkleTreeScheme, }; use crate::{ @@ -70,7 +72,7 @@ where I: Index + ToTraversalPath, T: NodeValue, { - type NonMembershipProof = MerkleProof; + type NonMembershipProof = MerkleTreeProof; type BatchNonMembershipProof = (); fn update_with( @@ -92,23 +94,16 @@ where } fn non_membership_verify( - &self, + commitment: impl Borrow, pos: impl Borrow, proof: impl Borrow, - ) -> Result { - let pos = pos.borrow(); - let proof = proof.borrow(); - if self.height != proof.tree_height() - 1 { - return Err(MerkleTreeError::InconsistentStructureError( - "Incompatible membership proof for this merkle tree".to_string(), - )); - } - if *pos != proof.pos { - return Err(MerkleTreeError::InconsistentStructureError( - "Inconsistent proof index".to_string(), - )); - } - proof.verify_non_membership_proof::(&self.root.value()) + ) -> Result { + crate::internal::verify_merkle_proof::( + commitment.borrow(), + pos.borrow(), + None, + proof.borrow().path_values(), + ) } fn universal_lookup( @@ -117,15 +112,7 @@ where ) -> LookupResult<&Self::Element, Self::MembershipProof, Self::NonMembershipProof> { let pos = pos.borrow(); let traversal_path = pos.to_traversal_path(self.height); - match self.root.lookup_internal(self.height, &traversal_path) { - LookupResult::Ok(value, proof) => { - LookupResult::Ok(value, MerkleProof::new(pos.clone(), proof)) - }, - LookupResult::NotInMemory => LookupResult::NotInMemory, - LookupResult::NotFound(non_membership_proof) => { - LookupResult::NotFound(MerkleProof::new(pos.clone(), non_membership_proof)) - }, - } + self.root.lookup_internal(self.height, &traversal_path) } } @@ -176,11 +163,7 @@ where let traversal_path = pos.to_traversal_path(self.height); let (root, result) = self.root.forget_internal(self.height, &traversal_path); self.root = root; - match result { - LookupResult::Ok(elem, proof) => LookupResult::Ok(elem, MerkleProof::new(pos, proof)), - LookupResult::NotInMemory => LookupResult::NotInMemory, - LookupResult::NotFound(proof) => LookupResult::NotFound(MerkleProof::new(pos, proof)), - } + result } fn non_membership_remember( @@ -188,47 +171,22 @@ where pos: Self::Index, proof: impl Borrow, ) -> Result<(), MerkleTreeError> { + let pos = pos.borrow(); let proof = proof.borrow(); - let traversal_path = pos.to_traversal_path(self.height); - if matches!(&proof.proof[0], MerkleNode::Empty) { - let empty_value = T::default(); - let mut path_values = vec![empty_value]; - traversal_path - .iter() - .zip(proof.proof.iter().skip(1)) - .try_fold( - empty_value, - |val: T, (branch, node)| -> Result { - match node { - MerkleNode::Branch { value: _, children } => { - let mut data: Vec<_> = - children.iter().map(|node| node.value()).collect(); - data[*branch] = val; - let digest = H::digest(&data)?; - path_values.push(digest); - Ok(digest) - }, - MerkleNode::Empty => { - path_values.push(empty_value); - Ok(empty_value) - }, - _ => Err(MerkleTreeError::InconsistentStructureError( - "Incompatible proof for this merkle tree".to_string(), - )), - } - }, - )?; + if Self::non_membership_verify(&self.commitment(), pos, proof)?.is_err() { + Err(MerkleTreeError::InconsistentStructureError( + "Wrong proof".to_string(), + )) + } else { + let traversal_path = pos.to_traversal_path(self.height); self.root = self.root.remember_internal::( self.height, &traversal_path, - &path_values, - &proof.proof, + pos, + None, + proof.path_values(), )?; Ok(()) - } else { - Err(MerkleTreeError::InconsistentStructureError( - "Invalid proof type".to_string(), - )) } } } @@ -236,11 +194,11 @@ where #[cfg(test)] mod mt_tests { use crate::{ - internal::{MerkleNode, MerkleProof}, + internal::{MerkleNode, MerkleTreeProof}, prelude::{RescueHash, RescueSparseMerkleTree}, DigestAlgorithm, ForgetableMerkleTreeScheme, ForgetableUniversalMerkleTreeScheme, Index, - LookupResult, MerkleCommitment, MerkleTreeScheme, PersistentUniversalMerkleTreeScheme, - ToTraversalPath, UniversalMerkleTreeScheme, + LookupResult, MerkleCommitment, MerkleProof, MerkleTreeScheme, + PersistentUniversalMerkleTreeScheme, ToTraversalPath, UniversalMerkleTreeScheme, }; use ark_bls12_377::Fr as Fr377; use ark_bls12_381::Fr as Fr381; @@ -287,18 +245,27 @@ mod mt_tests { let mt = RescueSparseMerkleTree::::from_kv_set(10, &hashmap).unwrap(); assert_eq!(mt.num_leaves(), hashmap.len() as u64); + let commitment = mt.commitment(); + let mut proof = mt .universal_lookup(BigUint::from(3u64)) .expect_not_found() .unwrap(); - let verify_result = mt.non_membership_verify(BigUint::from(3u64), &proof); - assert!(verify_result.is_ok() && verify_result.unwrap()); - proof.pos = BigUint::from(1u64); - let verify_result = mt.non_membership_verify(BigUint::from(1u64), &proof); - assert!(verify_result.is_ok() && !verify_result.unwrap()); + let verify_result = RescueSparseMerkleTree::::non_membership_verify( + &commitment, + BigUint::from(3u64), + &proof, + ) + .unwrap(); + assert!(verify_result.is_ok()); - let verify_result = mt.non_membership_verify(BigUint::from(4u64), proof); + let verify_result = RescueSparseMerkleTree::::non_membership_verify( + &commitment, + BigUint::from(1u64), + &proof, + ) + .unwrap(); assert!(verify_result.is_err()); } @@ -323,13 +290,14 @@ mod mt_tests { for i in 0..2 { mt.update(F::from(i as u64), F::from(i as u64)).unwrap(); } + let commitment = mt.commitment(); for i in 0..2 { let (val, proof) = mt.universal_lookup(F::from(i as u64)).expect_ok().unwrap(); assert_eq!(val, &F::from(i as u64)); - assert_eq!(proof.elem().unwrap(), val); assert!(RescueSparseMerkleTree::::verify( - &mt.root.value(), + &commitment, F::from(i as u64), + val, &proof ) .unwrap() @@ -343,12 +311,12 @@ mod mt_tests { .unwrap(); } assert_eq!(mt.num_leaves(), 10); + let commitment = mt.commitment(); // test lookup at index 7 let (val, proof) = mt.universal_lookup(F::from(7u64)).expect_ok().unwrap(); assert_eq!(val, &F::from(7u64)); - assert_eq!(proof.elem().unwrap(), val); assert!( - RescueSparseMerkleTree::::verify(&mt.root.value(), F::from(7u64), &proof) + RescueSparseMerkleTree::::verify(&commitment, F::from(7u64), val, &proof) .unwrap() .is_ok() ); @@ -378,7 +346,7 @@ mod mt_tests { ], ) .unwrap(); - let root = mt.commitment().digest(); + let commitment = mt.commitment(); // Look up and forget an element that is in the tree. let (lookup_elem, lookup_mem_proof) = mt @@ -390,17 +358,19 @@ mod mt_tests { assert_eq!(lookup_elem, elem); assert_eq!(lookup_mem_proof, mem_proof); assert_eq!(elem, 1u64.into()); - assert_eq!(mem_proof.tree_height(), 11); + assert_eq!(mem_proof.height(), 10); assert!(RescueSparseMerkleTree::::verify( - &root, + &commitment, BigUint::from(0u64), + &elem, &lookup_mem_proof ) .unwrap() .is_ok()); assert!(RescueSparseMerkleTree::::verify( - &root, + &commitment, BigUint::from(0u64), + &elem, &mem_proof ) .unwrap() @@ -422,11 +392,14 @@ mod mt_tests { .expect_ok() .unwrap(); assert_eq!(elem, &3u64.into()); - assert!( - RescueSparseMerkleTree::::verify(&root, BigUint::from(2u64), &proof) - .unwrap() - .is_ok() - ); + assert!(RescueSparseMerkleTree::::verify( + &commitment, + BigUint::from(2u64), + elem, + &proof + ) + .unwrap() + .is_ok()); // Look up and forget an empty sub-tree. let lookup_non_mem_proof = match mt.universal_lookup(BigUint::from(1u64)) { @@ -438,13 +411,21 @@ mod mt_tests { res => panic!("expected NotFound, got {:?}", res), }; assert_eq!(lookup_non_mem_proof, non_mem_proof); - assert_eq!(non_mem_proof.tree_height(), 11); - assert!(mt - .non_membership_verify(BigUint::from(1u64), &lookup_non_mem_proof) - .unwrap()); - assert!(mt - .non_membership_verify(BigUint::from(1u64), &non_mem_proof) - .unwrap()); + assert_eq!(non_mem_proof.height(), 10); + assert!(RescueSparseMerkleTree::::non_membership_verify( + &commitment, + BigUint::from(1u64), + &lookup_non_mem_proof + ) + .unwrap() + .is_ok()); + assert!(RescueSparseMerkleTree::::non_membership_verify( + &commitment, + BigUint::from(1u64), + &non_mem_proof + ) + .unwrap() + .is_ok()); // Forgetting an empty sub-tree will never actually cause any new entries to be // forgotten, since empty sub-trees are _already_ treated as if they @@ -452,9 +433,13 @@ mod mt_tests { // though we "forgot" it, the empty sub-tree is still in memory. match mt.universal_lookup(BigUint::from(1u64)) { LookupResult::NotFound(proof) => { - assert!(mt - .non_membership_verify(BigUint::from(1u64), &proof) - .unwrap()); + assert!(RescueSparseMerkleTree::::non_membership_verify( + &commitment, + BigUint::from(1u64), + &proof + ) + .unwrap() + .is_ok()); }, res => { panic!("expected NotFound, got {:?}", res); @@ -467,11 +452,14 @@ mod mt_tests { .expect_ok() .unwrap(); assert_eq!(elem, &3u64.into()); - assert!( - RescueSparseMerkleTree::::verify(&root, BigUint::from(2u64), &proof) - .unwrap() - .is_ok() - ); + assert!(RescueSparseMerkleTree::::verify( + &commitment, + BigUint::from(2u64), + elem, + &proof + ) + .unwrap() + .is_ok()); // Now if we forget the last entry, which is the only thing keeping the root // branch in memory, every entry will be forgotten. @@ -492,39 +480,22 @@ mod mt_tests { )); // Remember should fail if the proof is invalid. + mt.remember(BigUint::from(0u64), F::from(2u64), &mem_proof) + .unwrap_err(); + mt.remember(BigUint::from(1u64), F::from(1u64), &mem_proof) + .unwrap_err(); let mut bad_mem_proof = mem_proof.clone(); - if let MerkleNode::Leaf { elem, .. } = &mut bad_mem_proof.proof[0] { - *elem = F::from(4u64); - } else { - panic!("expected membership proof to end in a Leaf"); - } + bad_mem_proof.0[0][0] = F::one(); mt.remember(BigUint::from(0u64), F::from(1u64), &bad_mem_proof) .unwrap_err(); + mt.non_membership_remember(0u64.into(), &non_mem_proof) + .unwrap_err(); let mut bad_non_mem_proof = non_mem_proof.clone(); - bad_non_mem_proof.proof[0] = MerkleNode::Leaf { - pos: 1u64.into(), - elem: Default::default(), - value: Default::default(), - }; + bad_non_mem_proof.0[0][0] = F::one(); mt.non_membership_remember(1u64.into(), &bad_non_mem_proof) .unwrap_err(); - let mut forge_mem_proof = MerkleProof::new(1u64.into(), mem_proof.proof.clone()); - if let MerkleNode::Leaf { pos, elem, .. } = &mut forge_mem_proof.proof[0] { - *pos = 1u64.into(); - *elem = F::from(0u64); - } else { - panic!("expected membership proof to end in a Leaf"); - } - mt.remember(BigUint::from(1u64), F::from(0u64), &forge_mem_proof) - .unwrap_err(); - - let forge_non_mem_proof = non_mem_proof.clone(); - assert!(matches!(forge_non_mem_proof.proof[0], MerkleNode::Empty)); - mt.non_membership_remember(0u64.into(), &forge_non_mem_proof) - .unwrap_err(); - // Remember an occupied and an empty sub-tree. mt.remember(BigUint::from(0u64), F::from(1u64), &mem_proof) .unwrap(); @@ -537,17 +508,24 @@ mod mt_tests { .expect_ok() .unwrap(); assert_eq!(elem, &1u64.into()); - assert!( - RescueSparseMerkleTree::::verify(&root, BigUint::from(0u64), &proof) - .unwrap() - .is_ok() - ); + assert!(RescueSparseMerkleTree::::verify( + &commitment, + BigUint::from(0u64), + elem, + &proof + ) + .unwrap() + .is_ok()); match mt.universal_lookup(BigUint::from(1u64)) { LookupResult::NotFound(proof) => { - assert!(mt - .non_membership_verify(BigUint::from(1u64), &proof) - .unwrap()); + assert!(RescueSparseMerkleTree::::non_membership_verify( + &commitment, + BigUint::from(1u64), + &proof + ) + .unwrap() + .is_ok()); }, res => { panic!("expected NotFound, got {:?}", res); @@ -610,8 +588,8 @@ mod mt_tests { hashmap.insert(F::from(1u64), F::from(2u64)); hashmap.insert(F::from(10u64), F::from(3u64)); let mt = RescueSparseMerkleTree::::from_kv_set(3, &hashmap).unwrap(); - let mem_proof = mt.lookup(F::from(10u64)).expect_ok().unwrap().1; - let node = &mem_proof.proof[0]; + let (_elem, mem_proof) = mt.lookup(F::from(10u64)).expect_ok().unwrap(); + // let node = (F::from(10u64), elem.clone()); let non_mem_proof = match mt.universal_lookup(F::from(9u64)) { LookupResult::NotFound(proof) => proof, res => panic!("expected NotFound, got {:?}", res), @@ -629,9 +607,9 @@ mod mt_tests { non_mem_proof, bincode::deserialize(&bincode::serialize(&non_mem_proof).unwrap()).unwrap() ); - assert_eq!( - *node, - bincode::deserialize(&bincode::serialize(node).unwrap()).unwrap() - ); + // assert_eq!( + // node, + // bincode::deserialize(&bincode::serialize(&node).unwrap()).unwrap() + // ); } } diff --git a/merkle_tree/tests/merkle_tree_hasher.rs b/merkle_tree/tests/merkle_tree_hasher.rs index 3831e56d1..f586ecda1 100644 --- a/merkle_tree/tests/merkle_tree_hasher.rs +++ b/merkle_tree/tests/merkle_tree_hasher.rs @@ -1,6 +1,4 @@ -use jf_merkle_tree::{ - errors::MerkleTreeError, hasher::HasherMerkleTree, MerkleCommitment, MerkleTreeScheme, -}; +use jf_merkle_tree::{errors::MerkleTreeError, hasher::HasherMerkleTree, MerkleTreeScheme}; use sha2::Sha256; #[test] @@ -10,9 +8,9 @@ fn doctest_example() -> Result<(), MerkleTreeError> { // payload type is `usize`, hash function is `Sha256`. let mt = HasherMerkleTree::::from_elems(Some(2), my_data)?; - let root = mt.commitment().digest(); + let commitment = mt.commitment(); let (val, proof) = mt.lookup(2).expect_ok()?; assert_eq!(val, &3); - assert!(HasherMerkleTree::::verify(root, 2, proof)?.is_ok()); + assert!(HasherMerkleTree::::verify(commitment, 2, val, proof)?.is_ok()); Ok(()) }