Skip to content

Commit

Permalink
Add single node inclusion proof example
Browse files Browse the repository at this point in the history
  • Loading branch information
TheQuantumPhysicist committed Apr 17, 2024
1 parent 208f727 commit 25b985d
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 7 deletions.
16 changes: 9 additions & 7 deletions examples/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// This is a simple example where we calculate the root of a merkle tree

use blake2::{digest::typenum, Digest};
use merkletree::{hasher::PairHasher, tree::MerkleTree};

Expand Down Expand Up @@ -68,14 +70,14 @@ impl PairHasher for HashAlgo {

fn main() {
// You have to hash the leaves or create them (any way you like)
let leaf1 = hash_data("0");
let leaf2 = hash_data("1");
let leaf3 = hash_data("2");
let leaf4 = hash_data("3");
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::<TreeNode, HashAlgo>::from_leaves(vec![leaf1, leaf2, leaf3, leaf4]).unwrap();
MerkleTree::<TreeNode, HashAlgo>::from_leaves(vec![leaf0, leaf1, leaf2, leaf3]).unwrap();

// Let's get the root
let tree_root = tree.root();
Expand All @@ -91,12 +93,12 @@ fn main() {

// We attempt to recreate the expected root manually
let mut node10 = HashAlgo::new();
node10.write(leaf0);
node10.write(leaf1);
node10.write(leaf2);

let mut node11 = HashAlgo::new();
node11.write(leaf2);
node11.write(leaf3);
node11.write(leaf4);

let mut node00 = HashAlgo::new();
let n10 = node10.finalize();
Expand Down
111 changes: 111 additions & 0 deletions examples/single_proof.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// 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 a leaf in the tree and test it

use blake2::{digest::typenum, Digest};
use merkletree::{
hasher::PairHasher,
proof::{
single::{SingleProofHashes, SingleProofNodes},
verify_result::ProofVerifyResult,
},
tree::MerkleTree,
};

// You can use any hashing function you like, we use blake2b here as an example
type Blake2bHasher = blake2::Blake2b<typenum::U32>;

// A helper function that hashes data, not necessary for your application
pub fn hash_data<T: AsRef<[u8]>>(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<T: AsRef<[u8]>>(&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::<TreeNode, HashAlgo>::from_leaves(vec![leaf0, leaf1, leaf2, leaf3]).unwrap();

// 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
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
let restored_leaf_index = branch.leaf_index_in_level();
let restored_branch = branch.into_hashes();

let inclusion_proof_reconstructed =
SingleProofHashes::<_, HashAlgo>::from_leaf_index_and_branch(
restored_leaf_index,
restored_branch,
);

// Now we prove that leaf2 exists in the tree
assert_eq!(
inclusion_proof_reconstructed.verify(leaf2, tree.root()),
ProofVerifyResult::PassedDecisively
);
}
8 changes: 8 additions & 0 deletions src/merkle/proof/single/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,14 @@ impl<T: Eq, H: PairHasher<Type = T>> SingleProofHashes<T, H> {
pub fn leaf_index_in_level(&self) -> u32 {
self.leaf_index_in_level
}

pub fn from_leaf_index_and_branch(leaf_index: u32, branch_nodes: Vec<T>) -> Self {
Self {
leaf_index_in_level: leaf_index,
branch: branch_nodes,
_hasher: std::marker::PhantomData,
}
}
}

impl<T: Eq, H: PairHasher<Type = T>> SingleProofHashes<T, H> {
Expand Down

0 comments on commit 25b985d

Please sign in to comment.