Skip to content

Commit

Permalink
Merge pull request #7 from rkdud007/release/0.7.0
Browse files Browse the repository at this point in the history
Release/0.7.0
  • Loading branch information
rkdud007 authored Sep 16, 2024
2 parents 4419a80 + bd5b182 commit 2e2d685
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 18 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ repository = "https://github.com/rkdud007/alloy-merkle-tree"
keywords = ["merkle", "tree", "merkle-tree"]
categories = ["cryptography", "data-structures", "blockchain"]
exclude = [".github"]
version = "0.6.0"
version = "0.7.0"
edition = "2021"

[dependencies]
Expand Down
4 changes: 4 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# alloy-merkle-tree

![CI](https://img.shields.io/github/actions/workflow/status/rkdud007/alloy-merkle-tree/ci.yml?style=flat-square&logo=githubactions&logoColor=white&label=CI)
[![Crates.io](https://img.shields.io/crates/v/alloy-merkle-tree?style=flat-square&logo=lootcrate)](https://crates.io/crates/alloy-merkle-tree)
[![Documentation](https://img.shields.io/docsrs/alloy-merkle-tree)](https://docs.rs/alloy-merkle-tree)

Minimal Merkle Tree implementation

- various tree implementation
Expand Down
78 changes: 67 additions & 11 deletions src/standard_binary_tree.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,27 @@
//! This module contains the [StandardMerkleTree], an implementation of the standard Merkle Tree data structure
//! Checkout [StandardMerkleTree](https://github.com/OpenZeppelin/merkle-tree) for more details.
//! This module contains the [StandardMerkleTree], an implementation of the standard Merkle Tree data structure.
//!
//! Check out [StandardMerkleTree](https://github.com/OpenZeppelin/merkle-tree) for more details.
//!
//! # Examples
//!
//! ```rust
//! use alloy-merkle-tree::standard_binary_tree::StandardMerkleTree;
//! use alloy::dyn_abi::DynSolValue;
//!
//! let num_leaves = 1000;
//! let mut leaves = Vec::new();
//! for i in 0..num_leaves {
//! leaves.push(DynSolValue::String(i.to_string()));
//! }
//! let tree = StandardMerkleTree::of(&leaves);
//!
//! for leaf in leaves.iter() {
//! let proof = tree.get_proof(leaf).unwrap();
//! let is_valid = tree.verify_proof(leaf, proof);
//! assert!(is_valid);
//! }
//! ```
//!
use core::panic;

use crate::alloc::string::ToString;
Expand All @@ -15,29 +36,34 @@ use hashbrown::HashMap;
/// The error type for the [StandardMerkleTree].
#[derive(Debug)]
pub enum MerkleTreeError {
/// The tree is empty.
/// The specified leaf was not found in the tree.
LeafNotFound,
/// Invalid check.
/// An invalid check occurred during tree operations.
InvalidCheck,
/// The root hash have no siblings.
/// The root node does not have any siblings.
RootHaveNoSiblings,
/// Leaf is not supported type.
/// The leaf type is not supported by the tree.
NotSupportedType,
}

/// Represents a standard Merkle tree with methods for proof generation and verification.
#[derive(Debug)]
pub struct StandardMerkleTree {
/// The internal representation of the tree as a flat vector.
tree: Vec<B256>,
/// A mapping from serialized leaf values to their indices in the tree.
tree_values: HashMap<String, usize>,
}

impl Default for StandardMerkleTree {
/// Creates a new, empty `StandardMerkleTree`.
fn default() -> Self {
Self::new(Vec::new(), Vec::new())
}
}

impl StandardMerkleTree {
/// Creates a new [`StandardMerkleTree`] with the given tree nodes and values.
pub fn new(tree: Vec<B256>, values: Vec<(&DynSolValue, usize)>) -> Self {
let mut tree_values = HashMap::new();
for (tree_key, tree_value) in values.into_iter() {
Expand All @@ -47,20 +73,25 @@ impl StandardMerkleTree {
Self { tree, tree_values }
}

/// Constructs a [`StandardMerkleTree`] from a slice of dynamic Solidity values.
pub fn of(values: &[DynSolValue]) -> Self {
// Hash each value and associate it with its index and leaf hash.
let hashed_values: Vec<(&DynSolValue, usize, B256)> = values
.iter()
.enumerate()
.map(|(i, value)| (value, i, standard_leaf_hash(value)))
.collect();

// Collect the leaf hashes into a vector.
let hashed_values_hash = hashed_values
.iter()
.map(|(_, _, hash)| *hash)
.collect::<Vec<B256>>();

// Build the Merkle tree from the leaf hashes.
let tree = make_merkle_tree(hashed_values_hash);

// Map each value to its corresponding index in the tree.
let mut indexed_values: Vec<(&DynSolValue, usize)> =
values.iter().map(|value| (value, 0)).collect();

Expand All @@ -71,10 +102,12 @@ impl StandardMerkleTree {
Self::new(tree, indexed_values)
}

/// Retrieves the root hash of the Merkle tree.
pub fn root(&self) -> B256 {
self.tree[0]
}

/// Generates a Merkle proof for a given leaf value.
pub fn get_proof(&self, value: &DynSolValue) -> Result<Vec<B256>, MerkleTreeError> {
let tree_key = Self::check_valid_value_type(value);

Expand All @@ -86,16 +119,19 @@ impl StandardMerkleTree {
make_proof(&self.tree, *tree_index)
}

/// Computes the hash of a leaf node.
fn get_leaf_hash(&self, leaf: &DynSolValue) -> B256 {
standard_leaf_hash(leaf)
}

/// Verifies a Merkle proof for a given leaf value.
pub fn verify_proof(&self, leaf: &DynSolValue, proof: Vec<B256>) -> bool {
let leaf_hash = self.get_leaf_hash(leaf);
let implied_root = process_proof(leaf_hash, proof);
self.tree[0] == implied_root
}

/// Validates and serializes a [`DynSolValue`] into a [`String`].
fn check_valid_value_type(value: &DynSolValue) -> String {
match value {
DynSolValue::String(inner_value) => inner_value.to_string(),
Expand All @@ -105,6 +141,7 @@ impl StandardMerkleTree {
}
}

/// Computes the standard leaf hash for a given value..
fn standard_leaf_hash(value: &DynSolValue) -> B256 {
let encoded = match value {
DynSolValue::String(inner_value) => inner_value.as_bytes(),
Expand All @@ -114,14 +151,17 @@ fn standard_leaf_hash(value: &DynSolValue) -> B256 {
keccak256(keccak256(encoded))
}

/// Calculates the index of the left child for a given parent index..
fn left_child_index(index: usize) -> usize {
2 * index + 1
}

/// Calculates the index of the right child for a given parent index.
fn right_child_index(index: usize) -> usize {
2 * index + 2
}

/// Determines the sibling index for a given node index..
fn sibling_index(index: usize) -> Result<usize, MerkleTreeError> {
if index == 0 {
return Err(MerkleTreeError::RootHaveNoSiblings);
Expand All @@ -133,22 +173,28 @@ fn sibling_index(index: usize) -> Result<usize, MerkleTreeError> {
Ok(index + 1)
}
}

/// Calculates the parent index for a given child index.
fn parent_index(index: usize) -> usize {
(index - 1) / 2
}

/// Checks if a given index corresponds to a node within the tree.
fn is_tree_node(tree: &[B256], index: usize) -> bool {
index < tree.len()
}

/// Checks if a given index corresponds to an internal node (non-leaf).
fn is_internal_node(tree: &[B256], index: usize) -> bool {
is_tree_node(tree, left_child_index(index))
}

/// Checks if a given index corresponds to a leaf node.
fn is_leaf_node(tree: &[B256], index: usize) -> bool {
!is_internal_node(tree, index) && is_tree_node(tree, index)
}

/// Validates that a given index corresponds to a leaf node.
fn check_leaf_node(tree: &[B256], index: usize) -> Result<(), MerkleTreeError> {
if !is_leaf_node(tree, index) {
Err(MerkleTreeError::InvalidCheck)
Expand All @@ -157,15 +203,18 @@ fn check_leaf_node(tree: &[B256], index: usize) -> Result<(), MerkleTreeError> {
}
}

/// Constructs a Merkle tree from a vector of leaf hashes.
fn make_merkle_tree(leaves: Vec<B256>) -> Vec<B256> {
let tree_len = 2 * leaves.len() - 1;
let mut tree = vec![B256::default(); tree_len];
let leaves_len = leaves.len();

// Place leaves at the end of the tree array.
for (i, leaf) in leaves.into_iter().enumerate() {
tree[tree_len - 1 - i] = leaf;
}

// Build the tree by hashing pairs of nodes from the leaves up to the root.
for i in (0..tree_len - leaves_len).rev() {
let left = tree[left_child_index(i)];
let right = tree[right_child_index(i)];
Expand All @@ -176,6 +225,7 @@ fn make_merkle_tree(leaves: Vec<B256>) -> Vec<B256> {
tree
}

/// Generates a Merkle proof for a leaf at a given index.
fn make_proof(tree: &[B256], index: usize) -> Result<Vec<B256>, MerkleTreeError> {
check_leaf_node(tree, index)?;

Expand All @@ -193,10 +243,14 @@ fn make_proof(tree: &[B256], index: usize) -> Result<Vec<B256>, MerkleTreeError>
Ok(proof)
}

/// Processes a Merkle proof to compute the implied root hash.
///
/// Returns `B256` hash of the implied Merkle root.
fn process_proof(leaf: B256, proof: Vec<B256>) -> B256 {
proof.into_iter().fold(leaf, hash_pair)
}

/// Hashes a pair of `B256` values to compute their parent hash.
fn hash_pair(left: B256, right: B256) -> B256 {
let combined = if left <= right { left } else { right };
let second = if left <= right { right } else { left };
Expand All @@ -215,6 +269,7 @@ mod test {
use alloy::dyn_abi::DynSolValue;
use alloy::primitives::{hex::FromHex, FixedBytes};

/// Tests the [`StandardMerkleTree`] with string-type leaves.
#[test]
fn test_tree_string_type() {
let num_leaves = 1000;
Expand All @@ -226,11 +281,12 @@ mod test {

for leaf in leaves.into_iter() {
let proof = tree.get_proof(&leaf).unwrap();
let bool = tree.verify_proof(&leaf, proof);
assert!(bool);
let is_valid = tree.verify_proof(&leaf, proof);
assert!(is_valid);
}
}

/// Tests the `StandardMerkleTree` with bytes32-type leaves.
#[test]
fn test_tree_bytes32_type() {
let mut leaves = Vec::new();
Expand All @@ -249,8 +305,8 @@ mod test {

for leaf in leaves.into_iter() {
let proof = tree.get_proof(&leaf).unwrap();
let bool = tree.verify_proof(&leaf, proof);
assert!(bool);
let is_valid = tree.verify_proof(&leaf, proof);
assert!(is_valid);
}
}
}
Loading

0 comments on commit 2e2d685

Please sign in to comment.