Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release/0.7.0 #7

Merged
merged 2 commits into from
Sep 16, 2024
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
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