From 673e41a84ac19401de32eb2e2125d5476970ad35 Mon Sep 17 00:00:00 2001 From: Samer Afach Date: Wed, 17 Apr 2024 18:57:21 +0400 Subject: [PATCH] Add multi-proof example --- examples/multi_proof.rs | 113 ++++++++++++++++++++++++++++++++++ examples/single_proof.rs | 6 +- src/merkle/proof/multi/mod.rs | 8 +++ 3 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 examples/multi_proof.rs diff --git a/examples/multi_proof.rs b/examples/multi_proof.rs new file mode 100644 index 0000000..46408e8 --- /dev/null +++ b/examples/multi_proof.rs @@ -0,0 +1,113 @@ +// Copyright (c) 2021-2024 RBB S.r.l +// opensource@mintlayer.org +// SPDX-License-Identifier: MIT +// Licensed under the MIT License; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://github.com/mintlayer/merkletree-mintlayer/blob/master/LICENSE +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This is an example where we calculate the inclusion proof of multiple-leaves in the tree and test it + +use blake2::{digest::typenum, Digest}; +use merkletree::{ + hasher::PairHasher, + proof::{ + multi::{MultiProofHashes, MultiProofNodes}, + verify_result::ProofVerifyResult, + }, + tree::MerkleTree, +}; + +// You can use any hashing function you like, we use blake2b here as an example +type Blake2bHasher = blake2::Blake2b; + +// A helper function that hashes data, not necessary for your application +pub fn hash_data>(data: T) -> TreeNode { + let mut h = Blake2bHasher::new(); + Digest::update(&mut h, data); + h.finalize_reset().into() +} + +// You can use any node type you like, as long as you use it consistently in the tree +// See the PairHasher implementation +type TreeNode = [u8; 32]; + +// You have to define a type that implements `PairHasher` trait, which will tell the tree how to combine different nodes +#[derive(Clone)] +pub struct HashAlgo(Blake2bHasher); + +impl HashAlgo { + pub fn new() -> Self { + Self(Blake2bHasher::new()) + } + + pub fn write>(&mut self, in_bytes: T) { + Digest::update(&mut self.0, in_bytes); + } + + pub fn finalize(&mut self) -> TreeNode { + self.0.finalize_reset().into() + } +} + +// This is the important part, your hasher has to implement PairHasher +impl PairHasher for HashAlgo { + type Type = TreeNode; + + fn hash_pair(left: &Self::Type, right: &Self::Type) -> Self::Type { + let mut h = Blake2bHasher::new(); + Digest::update(&mut h, left); + Digest::update(&mut h, right); + h.finalize_reset().into() + } + + fn hash_single(data: &Self::Type) -> Self::Type { + let mut h = Blake2bHasher::new(); + Digest::update(&mut h, data); + h.finalize_reset().into() + } +} + +fn main() { + // You have to hash the leaves or create them (any way you like) + let leaf0 = hash_data("0"); + let leaf1 = hash_data("1"); + let leaf2 = hash_data("2"); + let leaf3 = hash_data("3"); + + // The tree is defined from a vector of leaves, from left to right + let tree = + MerkleTree::::from_leaves(vec![leaf0, leaf1, leaf2, leaf3]).unwrap(); + + // Proof that leaves with numbers 1 and 3, this is an abstract form of the proof that depends on the tree + let inclusion_proof = MultiProofNodes::from_tree_leaves(&tree, &[1, 3]).unwrap(); + + // Now this object is self-contained, and can be used to prove that a leaf exists, so it can be serialized and transferred over wire, + // (feel free to use the Encode/Decode using scale-codec, but you have to enable the feature) + let proof_hashes = inclusion_proof.into_values(); + + // Now we pretend we serialized the data, and restore it from serialization, and attempted to prove that the leaf is included + let restored_leaf_index = proof_hashes.tree_leaf_count(); + let restored_nodes = proof_hashes.nodes().clone(); + + let inclusion_proof_reconstructed = MultiProofHashes::<_, HashAlgo>::from_leaf_count_and_nodes( + restored_leaf_index, + restored_nodes, + ); + + // Now we prove that leaf1 (of index 1) and leaf3 (of index3) exist in the tree + // We do that by creating a map of the leaves, whose presence in the tree we want to prove, and the tree root + assert_eq!( + inclusion_proof_reconstructed + .verify([(1, leaf1), (3, leaf3)].into(), tree.root()) + .unwrap(), + ProofVerifyResult::PassedDecisively + ); +} diff --git a/examples/single_proof.rs b/examples/single_proof.rs index bb2f2d7..f5d7213 100644 --- a/examples/single_proof.rs +++ b/examples/single_proof.rs @@ -89,11 +89,11 @@ fn main() { // Proof that leaf number 2, this is an abstract form of the proof that depends on the tree let inclusion_proof = SingleProofNodes::from_tree_leaf(&tree, 2).unwrap(); - // Now this object is self-contained, and can be used to prove that a leaf exists + // Now this object is self-contained, and can be used to prove that a leaf exists, so it can be serialized and transferred over wire, + // (feel free to use the Encode/Decode using scale-codec, but you have to enable the feature) let branch = inclusion_proof.into_values(); - // Now we pretend we serialized the data (feel free to use the Encode/Decode using scale-codec, but you have to enable the feature), - // and restore it from serialization, and attempted to prove that the leaf is included + // Now we pretend we serialized the data, and restore it from serialization, and attempted to prove that the leaf is included let restored_leaf_index = branch.leaf_index_in_level(); let restored_branch = branch.into_hashes(); diff --git a/src/merkle/proof/multi/mod.rs b/src/merkle/proof/multi/mod.rs index 651ed68..6f1b53d 100644 --- a/src/merkle/proof/multi/mod.rs +++ b/src/merkle/proof/multi/mod.rs @@ -236,6 +236,14 @@ impl MultiProofHashes { pub fn tree_leaf_count(&self) -> u32 { self.tree_leaf_count } + + pub fn from_leaf_count_and_nodes(tree_leaf_count: u32, nodes: BTreeMap) -> Self { + Self { + nodes, + tree_leaf_count, + _phantom: std::marker::PhantomData, + } + } } impl> MultiProofHashes {