Skip to content

Commit

Permalink
Finish missing docs
Browse files Browse the repository at this point in the history
  • Loading branch information
preston-evans98 committed Nov 10, 2023
1 parent d2e4d38 commit 4889a7a
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 15 deletions.
14 changes: 10 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#![cfg_attr(not(feature = "std"), no_std)]
// #![deny(missing_docs)]
#![deny(missing_docs)]
//! This crate implements a Namespaced Merkle Tree compatible with <https://github.com/celestiaorg/nmt>. To quote from their documentation:
//!
//! > A Namespaced Merkle Tree is an ordered Merkle tree that uses a modified hash function so that each node in the tree
Expand Down Expand Up @@ -357,13 +357,16 @@ where
proof.start_idx() as usize,
)?;
}
NamespaceProof::PresenceProof { .. } => {
NamespaceProof::PresenceProof { ignore_max_ns, .. } => {
if !root.contains::<M>(namespace) {
return Err(RangeProofError::TreeDoesNotContainLeaf);
}
let leaf_hashes: Vec<NamespacedHash<NS_ID_SIZE>> = raw_leaves
.iter()
.map(|data| NamespacedHash::hash_leaf(data.as_ref(), namespace))
.map(|data| {
M::with_ignore_max_ns(*ignore_max_ns)
.hash_leaf_with_namespace(data.as_ref(), namespace)
})
.collect();
let proof_type = self.check_range_proof(
root,
Expand Down Expand Up @@ -412,6 +415,7 @@ pub enum RangeProofType {
#[cfg(test)]
mod tests {
use crate::maybestd::{format, vec::Vec};
use crate::NamespaceMerkleHasher;
use crate::{
namespaced_hash::{NamespaceId, NamespacedSha2Hasher},
nmt_proof::NamespaceProof,
Expand Down Expand Up @@ -487,7 +491,9 @@ mod tests {
unreachable!();
};
let data = format!("leaf_{i}").as_bytes().to_vec();
*leaf = Some(NamespacedHash::hash_leaf(&data, ns_id_from_u64(i)));
*leaf = Some(
NamespacedSha2Hasher::default().hash_leaf_with_namespace(&data, ns_id_from_u64(i)),
);
proof
.verify_complete_namespace(&tree.root(), no_leaves, ns_id_from_u64(2))
.unwrap_err();
Expand Down
25 changes: 15 additions & 10 deletions src/namespaced_hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,12 @@ impl<const NS_ID_SIZE: usize> NamespaceMerkleHasher<NS_ID_SIZE>
data: &[u8],
namespace: NamespaceId<NS_ID_SIZE>,
) -> <Self as MerkleHash>::Output {
NamespacedHash::hash_leaf(data, namespace)
let mut output = NamespacedHash::with_min_and_max_ns(namespace, namespace);
let mut hasher = Sha256::new_with_prefix(LEAF_DOMAIN_SEPARATOR);
hasher.update(namespace.as_ref());
hasher.update(data.as_ref());
output.set_hash(hasher.finalize().as_ref());
output
}
}

Expand Down Expand Up @@ -303,6 +308,7 @@ impl<const NS_ID_SIZE: usize> NamespacedHash<NS_ID_SIZE> {
2 * NS_ID_SIZE + HASH_LEN
}

/// Construct a new namespaced hash from the provided components
pub const fn new(
min_ns: NamespaceId<NS_ID_SIZE>,
max_ns: NamespaceId<NS_ID_SIZE>,
Expand All @@ -315,6 +321,7 @@ impl<const NS_ID_SIZE: usize> NamespacedHash<NS_ID_SIZE> {
}
}

/// Construct a namespaced hash with the provided namespace range and the zero hash
pub fn with_min_and_max_ns(
min_ns: NamespaceId<NS_ID_SIZE>,
max_ns: NamespaceId<NS_ID_SIZE>,
Expand All @@ -326,14 +333,17 @@ impl<const NS_ID_SIZE: usize> NamespacedHash<NS_ID_SIZE> {
}
}

/// Returns the min namespace id of the hash
pub fn min_namespace(&self) -> NamespaceId<NS_ID_SIZE> {
self.min_ns
}

/// Returns the max namespace id of the hash
pub fn max_namespace(&self) -> NamespaceId<NS_ID_SIZE> {
self.max_ns
}

/// Returns the hash without the namespace range
pub fn hash(&self) -> [u8; HASH_LEN] {
self.hash
}
Expand All @@ -342,6 +352,7 @@ impl<const NS_ID_SIZE: usize> NamespacedHash<NS_ID_SIZE> {
self.hash.copy_from_slice(new_hash)
}

/// Check if the given hash includes the provided namespace under the given hasher
pub fn contains<M: NamespaceMerkleHasher<NS_ID_SIZE, Output = Self>>(
&self,
namespace: NamespaceId<NS_ID_SIZE>,
Expand All @@ -351,19 +362,12 @@ impl<const NS_ID_SIZE: usize> NamespacedHash<NS_ID_SIZE> {
&& !self.is_empty_root::<M>()
}

/// Check if the hash is the empty root under the given hasher
pub fn is_empty_root<M: NamespaceMerkleHasher<NS_ID_SIZE, Output = Self>>(&self) -> bool {
self == &M::EMPTY_ROOT
}

pub fn hash_leaf(raw_data: impl AsRef<[u8]>, namespace: NamespaceId<NS_ID_SIZE>) -> Self {
let mut output = NamespacedHash::with_min_and_max_ns(namespace, namespace);
let mut hasher = DefaultHasher::new_with_prefix(LEAF_DOMAIN_SEPARATOR);
hasher.update(namespace.as_ref());
hasher.update(raw_data.as_ref());
output.set_hash(hasher.finalize().as_ref());
output
}

/// Returns an iterator of the bytes of the namespaced hash
pub fn iter(&self) -> impl Iterator<Item = u8> {
self.min_ns
.0
Expand All @@ -373,6 +377,7 @@ impl<const NS_ID_SIZE: usize> NamespacedHash<NS_ID_SIZE> {
}
}

/// The error returned when failing to convert a slice to a namespaced hash
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct InvalidNamespacedHash;

Expand Down
5 changes: 4 additions & 1 deletion src/nmt_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@ where

let leaf_hashes: Vec<_> = raw_leaves
.iter()
.map(|data| NamespacedHash::hash_leaf(data.as_ref(), leaf_namespace))
.map(|data| {
M::with_ignore_max_ns(self.ignores_max_ns())
.hash_leaf_with_namespace(data.as_ref(), leaf_namespace)
})
.collect();
let tree = NamespaceMerkleTree::<NoopDb, M, NS_ID_SIZE>::with_hasher(
M::with_ignore_max_ns(self.ignores_max_ns()),
Expand Down
6 changes: 6 additions & 0 deletions src/simple_merkle/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
//! Implements a simple [RFC 6962](https://www.rfc-editor.org/rfc/rfc6962#section-2.1) compatible merkle tree
//! over an in-memory data store which maps preimages to hashes.
/// Defines traits and types for storing hashes and preimages.
pub mod db;
/// Defines errors that might arise in proof verification.
pub mod error;
/// Defines proofs on the tree.
pub mod proof;
/// Defines the merkle tree itself.
pub mod tree;
/// Utilities for computing facts about trees from proofs.
pub mod utils;
8 changes: 8 additions & 0 deletions src/simple_merkle/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ use crate::maybestd::vec::Vec;
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Proof<M: MerkleHash> {
/// The siblings to be used to build the path to the root.
pub siblings: Vec<M::Output>,
/// The range of indices covered by the proof.
pub range: Range<u32>,
}

Expand Down Expand Up @@ -80,22 +82,27 @@ where
)
}

/// Returns the siblings provided as part of the proof.
pub fn siblings(&self) -> &Vec<M::Output> {
&self.siblings
}

/// Returns the index of the first leaf covered by the proof.
pub fn start_idx(&self) -> u32 {
self.range.start
}

/// Returns the index *after* the last leaf included in the proof.
pub fn end_idx(&self) -> u32 {
self.range.end
}

/// Returns the length of the range covered by the proof.
pub fn range_len(&self) -> usize {
self.range.end.saturating_sub(self.range.start) as usize
}

/// Returns the leftmost node to the right of the proven range, if one exists.
pub fn leftmost_right_sibling(&self) -> Option<&M::Output> {
let siblings = self.siblings();
let num_left_siblings = compute_num_left_siblings(self.start_idx() as usize);
Expand All @@ -105,6 +112,7 @@ where
None
}

/// Returns the rightmost node to the left of the proven range, if one exists.
pub fn rightmost_left_sibling(&self) -> Option<&M::Output> {
let siblings = self.siblings();
let num_left_siblings = compute_num_left_siblings(self.start_idx() as usize);
Expand Down
18 changes: 18 additions & 0 deletions src/simple_merkle/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ impl<T> TakeLast<T> for [T] {

type BoxedVisitor<M> = Box<dyn Fn(&<M as MerkleHash>::Output)>;

/// Implments an RFC 6962 compatible merkle tree over an in-memory data store which maps preimages to hashes.
pub struct MerkleTree<Db, M>
where
M: MerkleHash,
Expand All @@ -45,13 +46,17 @@ impl<Db: PreimageDb<<M as MerkleHash>::Output>, M: MerkleHash + Default> Default
}
}

/// A trait for hashing data into a merkle tree
pub trait MerkleHash {
/// The output of this hasher
#[cfg(all(not(feature = "serde"), feature = "std"))]
type Output: Debug + PartialEq + Eq + Clone + Default + Hash;

/// The output of this hasher
#[cfg(all(not(feature = "serde"), not(feature = "std")))]
type Output: Debug + PartialEq + Eq + Clone + Default + Hash + Ord;

/// The output of this hasher
#[cfg(all(feature = "serde", not(feature = "std")))]
type Output: Debug
+ PartialEq
Expand All @@ -62,6 +67,7 @@ pub trait MerkleHash {
+ serde::Serialize
+ serde::de::DeserializeOwned;

/// The output of this hasher
#[cfg(all(feature = "serde", feature = "std"))]
type Output: Debug
+ PartialEq
Expand All @@ -73,9 +79,12 @@ pub trait MerkleHash {
+ serde::Serialize
+ serde::de::DeserializeOwned;

/// The hash of the empty tree. This is often defined as the hash of the empty string
const EMPTY_ROOT: Self::Output;

/// Hashes data as a "leaf" of the tree. This operation *should* be domain separated
fn hash_leaf(&self, data: &[u8]) -> Self::Output;
/// Hashes two digests into one. This operation *should* be domain separated
fn hash_nodes(&self, l: &Self::Output, r: &Self::Output) -> Self::Output;
}

Expand All @@ -84,6 +93,7 @@ where
Db: PreimageDb<M::Output>,
M: MerkleHash + Default,
{
/// Constructs an empty merkle tree with a default hasher
pub fn new() -> Self {
Self::with_hasher(Default::default())
}
Expand All @@ -94,6 +104,7 @@ where
Db: PreimageDb<M::Output>,
M: MerkleHash,
{
/// Constructs an empty merkle tree with the given hasher
pub fn with_hasher(hasher: M) -> Self {
Self {
leaves: Vec::new(),
Expand All @@ -104,16 +115,19 @@ where
}
}

/// Appends the given leaf to the tree
pub fn push_raw_leaf(&mut self, raw_leaf: &[u8]) {
let leaf = LeafWithHash::with_hasher(raw_leaf.to_vec(), &self.hasher);
self.push_leaf_with_hash(leaf);
}

/// Appends a pre-hashed leaf to the tree
pub fn push_leaf_with_hash(&mut self, leaf_with_hash: LeafWithHash<M>) {
self.root = None;
self.leaves.push(leaf_with_hash);
}

/// Returns the root of the tree, computing it if necessary. Repeated queries return a cached result.
pub fn root(&mut self) -> M::Output {
if let Some(inner) = &self.root {
return inner.clone();
Expand All @@ -123,11 +137,13 @@ where
inner
}

/// Returns the requested range of leaves
pub fn get_leaves(&self, range: Range<usize>) -> Vec<Vec<u8>> {
let leaves = &self.leaves[range];
leaves.iter().map(|leaf| leaf.data().to_vec()).collect()
}

/// Returns all leaves in the tree
pub fn leaves(&self) -> &[LeafWithHash<M>] {
&self.leaves[..]
}
Expand Down Expand Up @@ -370,12 +386,14 @@ where
}
}

/// Fetches the requested range of leaves, along with a proof of correctness.
pub fn get_range_with_proof(&mut self, leaf_range: Range<usize>) -> (Vec<Vec<u8>>, Proof<M>) {
let leaves = &self.leaves[leaf_range.clone()];
let leaves = leaves.iter().map(|leaf| leaf.data().to_vec()).collect();
(leaves, self.build_range_proof(leaf_range))
}

/// Fetches the leaf at the given index, along with a proof of inclusion.
pub fn get_index_with_proof(&mut self, idx: usize) -> (Vec<u8>, Proof<M>) {
(
self.leaves[idx].data().to_vec(),
Expand Down

0 comments on commit 4889a7a

Please sign in to comment.