Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,8 @@ See the [`examples/`](./examples/) directory for more usage examples:
**Basic Usage** (`how_to_use/`):
- `merkle_root.nr` - Calculate merkle root from leaves
- `merkle_proof.nr` - Generate merkle proofs for elements
- `merkle_verify.nr` - Verify merkle proofs
- `merkle_proof_check.nr` - Verify proofs using low-level API
- `merkle_verify.nr` - Verify merkle proofs using high-level API

**ZK Proof Patterns** (`how_to_proof/`):
- `membership_public_root.nr` - Membership proof with public root (whitelist, airdrops, voting)
Expand Down
36 changes: 36 additions & 0 deletions examples/how_to_use/merkle_proof_check.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use merklez::{merkle_proof, merkle_proof_check, merkle_root, Proof};
use sha256::sha256;

fn main() {
// Step 1: Create some leaves
let mut leaf_a: [u8; 32] = [0; 32];
leaf_a[0] = 1;
let mut leaf_b: [u8; 32] = [0; 32];
leaf_b[0] = 2;
let mut leaf_c: [u8; 32] = [0; 32];
leaf_c[0] = 3;
let mut leaf_d: [u8; 32] = [0; 32];
leaf_d[0] = 4;

let leaves: [[u8; 32]; 4] = [leaf_a, leaf_b, leaf_c, leaf_d];

// Step 2: Calculate merkle root
let root = merkle_root(leaves, sha256);

// Step 3: Generate proof for leaf_c
let proof: Proof<2> = merkle_proof(leaves, leaf_c, sha256);

// Step 4: Verify proof using merkle_proof_check
// This function reconstructs the root from the leaf and proof
let computed_root = merkle_proof_check(proof, leaf_c, sha256);

// Step 5: Compare computed root with expected root
assert(computed_root == root);
}

// you need add sha256 in your Nargo.toml:
//
// ```toml
// [dependencies]
// sha256 = { tag = "v0.2.1", git = "https://github.com/noir-lang/sha256" }
// ```
1 change: 1 addition & 0 deletions merklez/src/lib.nr
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ mod utils;
pub mod merkle_root;
mod merkle_proof_mixed;
pub mod merkle_proof;
pub mod merkle_check;
58 changes: 58 additions & 0 deletions merklez/src/merkle_check.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// =============================================================================
// MERKLE PROOF CHECK - Verify a Merkle proof and compute the root
// =============================================================================

use crate::side::Side;
use crate::types::{HashFn, Leaf, Proof, Root};

// =============================================================================
// MERKLE PROOF CHECK - Main function
// =============================================================================

/// Verifies a Merkle proof and computes the root hash
///
/// This function takes a proof (list of sibling nodes with their positions),
/// a leaf value, and a hash function, then reconstructs the path to the root
/// by combining the leaf with each proof node according to their side indicator.
///
/// # Arguments
/// * `proof` - A Proof struct containing sibling nodes and their positions
/// * `leaf` - The leaf value to verify
/// * `hash_fn` - Hash function to combine two hashes
///
/// # Returns
/// The computed root hash after applying all proof nodes
///
/// # Type Parameters
/// * `P` - Proof capacity (maximum number of nodes in the proof path)
///
/// # How it works
/// Starting from the leaf, for each proof node:
/// - If the node is on the LEFT, compute hash(node.data, current_hash)
/// - If the node is on the RIGHT, compute hash(current_hash, node.data)
///
/// The final hash is the Merkle root.
///
/// # Example
/// ```
/// let proof: Proof<3> = /* proof for a leaf */;
/// let leaf: Leaf = /* leaf to verify */;
/// let root = merkle_proof_check(proof, leaf, hash_fn);
/// // root is the computed Merkle root
/// ```
pub fn merkle_proof_check<let P: u32>(proof: Proof<P>, leaf: Leaf, hash_fn: HashFn) -> Root {
let mut current_hash = leaf;

for i in 0..P {
if i < proof.len {
let node = proof.nodes[i];
current_hash = if node.side.is_right {
hash_fn(current_hash, node.data)
} else {
hash_fn(node.data, current_hash)
};
}
}

current_hash
}
1 change: 1 addition & 0 deletions tests/src/lib.nr
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
mod merkle_proof;
mod merkle_root;
mod merkle_check;
136 changes: 136 additions & 0 deletions tests/src/merkle_check/mod.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
use dep::keccak256::keccak256;
use dep::merklez::merkle_check::merkle_proof_check;
use dep::merklez::node::Node;
use dep::merklez::side::Side;
use dep::merklez::types::{Hash, Leaf, Proof, Root};

fn keccak_hash(left: Hash, right: Hash) -> Hash {
let mut input: [u8; 64] = [0u8; 64];
for i in 0..32 {
input[i] = left[i];
input[i + 32] = right[i];
}
keccak256(input, 64u32)
}

// =============================================================================
// TEST: Merkle proof check with even number of leaves
// =============================================================================
#[test]
fn merkle_proof_check_leaves_even() {
let setup_root: Root = [
144, 18, 241, 225, 138, 135, 121, 13, 46, 1, 250, 172, 231, 90, 170, 202, 56, 229, 61,
244, 55, 205, 206, 44, 5, 82, 70, 77, 218, 74, 244, 156,
];

let setup_leaf: Leaf = [
168, 152, 44, 137, 216, 9, 135, 251, 154, 81, 14, 37, 152, 30, 233, 23, 2, 6, 190, 33,
175, 60, 142, 14, 179, 18, 239, 29, 51, 130, 231, 97,
];

let node1 = Node {
data: [
209, 232, 174, 183, 149, 0, 73, 110, 243, 220, 46, 87, 186, 116, 106, 131, 21,
208, 72, 183, 166, 100, 162, 191, 148, 141, 180, 250, 145, 150, 4, 131,
],
side: Side::right(),
};

let node2 = Node {
data: [
104, 32, 63, 144, 233, 208, 125, 197, 133, 146, 89, 215, 83, 110, 135, 166,
186, 157, 52, 95, 37, 82, 181, 185, 222, 41, 153, 221, 206, 156, 225, 191,
],
side: Side::left(),
};

let proof = Proof { nodes: [node1, node2], len: 2 };

let result = merkle_proof_check(proof, setup_leaf, keccak_hash);

assert(result == setup_root);
}

// =============================================================================
// TEST: Merkle proof check with odd number of leaves
// =============================================================================
#[test]
fn merkle_proof_check_leaves_odd() {
let setup_root: Root = [
29, 208, 210, 166, 174, 70, 109, 102, 92, 178, 110, 26, 49, 240, 124, 87, 174, 93, 247,
210, 188, 85, 156, 213, 130, 109, 65, 123, 233, 20, 26, 93,
];

let setup_leaf: Leaf = [
241, 145, 142, 133, 98, 35, 110, 177, 122, 220, 133, 2, 51, 47, 76, 156, 130, 188, 20,
225, 155, 252, 10, 161, 10, 182, 116, 255, 117, 179, 210, 243,
];

let node1 = Node {
data: [
11, 66, 182, 57, 60, 31, 83, 6, 15, 227, 221, 191, 205, 122, 173, 204, 168,
148, 70, 90, 90, 67, 143, 105, 200, 125, 121, 11, 34, 153, 185, 178,
],
side: Side::left(),
};

let node2 = Node {
data: [
128, 91, 33, 216, 70, 177, 137, 239, 174, 176, 55, 125, 107, 176, 210, 1, 179,
135, 42, 54, 62, 96, 124, 37, 8, 143, 2, 91, 12, 106, 225, 248,
],
side: Side::left(),
};

let node3 = Node {
data: [
168, 152, 44, 137, 216, 9, 135, 251, 154, 81, 14, 37, 152, 30, 233, 23, 2, 6,
190, 33, 175, 60, 142, 14, 179, 18, 239, 29, 51, 130, 231, 97,
],
side: Side::right(),
};

let proof = Proof { nodes: [node1, node2, node3], len: 3 };

let result = merkle_proof_check(proof, setup_leaf, keccak_hash);

assert(result == setup_root);
}

// =============================================================================
// TEST: Merkle proof check with power-of-2 number of leaves
// =============================================================================
#[test]
fn merkle_proof_check_leaves_base_2() {
let setup_root: Root = [
104, 32, 63, 144, 233, 208, 125, 197, 133, 146, 89, 215, 83, 110, 135, 166, 186, 157,
52, 95, 37, 82, 181, 185, 222, 41, 153, 221, 206, 156, 225, 191,
];

let setup_leaf: Leaf = [
58, 194, 37, 22, 141, 245, 66, 18, 162, 92, 28, 1, 253, 53, 190, 191, 234, 64, 143,
218, 194, 227, 29, 221, 111, 128, 164, 187, 249, 165, 241, 203,
];

let node1 = Node {
data: [
181, 85, 61, 227, 21, 224, 237, 245, 4, 217, 21, 10, 248, 45, 175, 165, 196,
102, 127, 166, 24, 237, 10, 111, 25, 198, 155, 65, 22, 108, 85, 16,
],
side: Side::right(),
};

let node2 = Node {
data: [
210, 83, 165, 45, 76, 176, 13, 226, 137, 94, 133, 242, 82, 158, 41, 118, 230,
170, 170, 92, 24, 16, 107, 104, 171, 102, 129, 62, 20, 65, 86, 105,
],
side: Side::right(),
};

let proof = Proof { nodes: [node1, node2], len: 2 };

let result = merkle_proof_check(proof, setup_leaf, keccak_hash);

assert(result == setup_root);
}
Loading