Skip to content

Commit

Permalink
feat: add simple sparse merkle tree
Browse files Browse the repository at this point in the history
This commit introduces the `SparseMerkleTree` and `TreeStorage`.

The sparse merkle tree is a concrete structure that will provide the
functionalities of a keyed naive SMT with inclusion proof. It introduces
the `MerklePath` structure to fully encapsulate a merkle opening
verification - however, it can be just converted into a vector of
digests so it will be compatible with the opening prior to this commit.

It also introduces the tree storage trait, a backend definition that
will be the responsible to manage the state of a tree. This brings the
advantage of decoupling the implementation logic of the merkle tree with
its backend requirements, and will use `alloc::borrow::Cow` instead of
either references or owned values to give further flexibility to the
storage. Some storages will lock its own state to fetch a value, and
they might provide values to multi-threaded applications - hence,
extending the lock might not be desirable for fetched values. In such
cases, the storage will have the option to return the value as owned,
freeing it from its lifetime bound.

There is a memory storage added that will provide in-memory tree
storage, backed by `BTreeMap`.

closes #21
  • Loading branch information
vlopes11 committed Dec 8, 2022
1 parent a41329f commit 463e90b
Show file tree
Hide file tree
Showing 8 changed files with 705 additions and 14 deletions.
3 changes: 1 addition & 2 deletions src/hash/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use super::{Felt, FieldElement, StarkField, ONE, ZERO};
use winter_crypto::{Digest, ElementHasher, Hasher};
use super::{Digest, ElementHasher, Felt, FieldElement, Hasher, StarkField, ONE, ZERO};

pub mod blake;
pub mod rpo;
14 changes: 12 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#![cfg_attr(not(feature = "std"), no_std)]

#[cfg(not(feature = "std"))]
#[cfg_attr(test, macro_use)]
#[macro_use]
extern crate alloc;

pub mod hash;
Expand All @@ -10,24 +10,34 @@ pub mod merkle;
// RE-EXPORTS
// ================================================================================================

pub use winter_crypto::{Digest, ElementHasher, Hasher};
pub use winter_math::{fields::f64::BaseElement as Felt, FieldElement, StarkField};

pub mod utils {
pub use winter_utils::{
collections, string, uninit_vector, ByteReader, ByteWriter, Deserializable,
DeserializationError, Serializable, SliceReader,
};

#[cfg(not(feature = "std"))]
pub use alloc::borrow::Cow;

#[cfg(feature = "std")]
pub use std::borrow::Cow;
}

// TYPE ALIASES
// ================================================================================================

/// A group of four field elements in the Miden base field.
pub type Word = [Felt; 4];
pub type Word = [Felt; WORD_SIZE];

// CONSTANTS
// ================================================================================================

/// Number of field elements in a word.
pub const WORD_SIZE: usize = 4;

/// Field element representing ZERO in the Miden base filed.
pub const ZERO: Felt = Felt::ZERO;

Expand Down
13 changes: 7 additions & 6 deletions src/merkle/merkle_tree.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{Digest, Felt, MerkleError, Rpo256, Vec, Word};
use super::{Felt, MerkleError, Rpo256, RpoDigest, Vec, Word};
use crate::{utils::uninit_vector, FieldElement};
use core::slice;
use winter_math::log2;
Expand All @@ -22,7 +22,7 @@ impl MerkleTree {
pub fn new(leaves: Vec<Word>) -> Result<Self, MerkleError> {
let n = leaves.len();
if n <= 1 {
return Err(MerkleError::DepthTooSmall);
return Err(MerkleError::DepthTooSmall(n as u32));
} else if !n.is_power_of_two() {
return Err(MerkleError::NumLeavesNotPowerOfTwo(n));
}
Expand All @@ -35,7 +35,8 @@ impl MerkleTree {
nodes[n..].copy_from_slice(&leaves);

// re-interpret nodes as an array of two nodes fused together
let two_nodes = unsafe { slice::from_raw_parts(nodes.as_ptr() as *const [Digest; 2], n) };
let two_nodes =
unsafe { slice::from_raw_parts(nodes.as_ptr() as *const [RpoDigest; 2], n) };

// calculate all internal tree nodes
for i in (1..n).rev() {
Expand Down Expand Up @@ -68,7 +69,7 @@ impl MerkleTree {
/// * The specified index not valid for the specified depth.
pub fn get_node(&self, depth: u32, index: u64) -> Result<Word, MerkleError> {
if depth == 0 {
return Err(MerkleError::DepthTooSmall);
return Err(MerkleError::DepthTooSmall(depth));
} else if depth > self.depth() {
return Err(MerkleError::DepthTooBig(depth));
}
Expand All @@ -89,7 +90,7 @@ impl MerkleTree {
/// * The specified index not valid for the specified depth.
pub fn get_path(&self, depth: u32, index: u64) -> Result<Vec<Word>, MerkleError> {
if depth == 0 {
return Err(MerkleError::DepthTooSmall);
return Err(MerkleError::DepthTooSmall(depth));
} else if depth > self.depth() {
return Err(MerkleError::DepthTooBig(depth));
}
Expand Down Expand Up @@ -123,7 +124,7 @@ impl MerkleTree {

let n = self.nodes.len() / 2;
let two_nodes =
unsafe { slice::from_raw_parts(self.nodes.as_ptr() as *const [Digest; 2], n) };
unsafe { slice::from_raw_parts(self.nodes.as_ptr() as *const [RpoDigest; 2], n) };

for _ in 0..depth {
index /= 2;
Expand Down
50 changes: 46 additions & 4 deletions src/merkle/mod.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,71 @@
use super::{
hash::rpo::{Rpo256, RpoDigest as Digest},
utils::collections::{BTreeMap, Vec},
Felt, Word, ZERO,
hash::rpo::{Rpo256, RpoDigest},
utils::{
collections::{BTreeMap, Vec},
string::String,
Cow,
},
Digest, Felt, Hasher, Word, ZERO,
};
use core::{convert::Infallible, fmt};

mod merkle_tree;
pub use merkle_tree::MerkleTree;

mod merkle_path_set;
pub use merkle_path_set::MerklePathSet;

mod simple_smt;
pub use simple_smt::{DepthDigest, MemoryTreeStorage, MerklePath, SparseMerkleTree, TreeStorage};

// ERRORS
// ================================================================================================

#[derive(Clone, Debug)]
pub enum MerkleError {
DepthTooSmall,
DepthTooSmall(u32),
DepthTooBig(u32),
NumLeavesNotPowerOfTwo(usize),
InvalidIndex(u32, u64),
InvalidDepth(u32, u32),
InvalidPath(Vec<Word>),
NodeNotInSet(u64),
StorageInconsistency(String),
}

impl From<Infallible> for MerkleError {
fn from(_e: Infallible) -> Self {
unreachable!()
}
}

impl fmt::Display for MerkleError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use MerkleError::*;
match self {
DepthTooSmall(depth) => write!(f, "the provided depth {depth} is too small"),
DepthTooBig(depth) => write!(f, "the provided depth {depth} is too big"),
NumLeavesNotPowerOfTwo(leaves) => {
write!(f, "the leaves count {leaves} is not a power of 2")
}
InvalidIndex(depth, index) => write!(
f,
"the leaf index {index} is not valid for the depth {depth}"
),
InvalidDepth(expected, provided) => write!(
f,
"the provided depth {provided} is not valid for {expected}"
),
InvalidPath(_path) => write!(f, "the provided path is not valid"),
NodeNotInSet(index) => write!(f, "the node indexed by {index} is not in the set"),
StorageInconsistency(message) => write!(f, "storage inconsistency: {message}"),
}
}
}

#[cfg(feature = "std")]
impl std::error::Error for MerkleError {}

// HELPER FUNCTIONS
// ================================================================================================

Expand Down
Loading

0 comments on commit 463e90b

Please sign in to comment.