Skip to content

Commit

Permalink
Update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
ChosunOne committed Apr 28, 2020
1 parent 03fd562 commit 364f692
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 12 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# 3.2.1
* Expand documentation
# 3.2.0
* Updated dependencies, notably rocksdb to 0.14
* Adjusted API for `verify_inclusion_proof`, it is now an associated function of a `Tree` and is now called via
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "starling"
version = "3.2.0"
version = "3.2.1"
authors = ["Josiah Evans <koreanhalo@gmail.com>"]
description = "This tree structure is a binary merkle tree with branch compression via split indexes."
repository = "https://github.com/ChosunOne/merkle_bit"
Expand Down
18 changes: 18 additions & 0 deletions src/hash_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ where
ArrayType: Array,
{
/// Creates a new `HashTree`. `depth` indicates the maximum depth of the tree.
/// # Errors
/// None.
#[inline]
pub fn new(depth: usize) -> BinaryMerkleTreeResult<Self> {
let path = PathBuf::new();
Expand All @@ -52,13 +54,17 @@ where

/// Creates a new `HashTree`. This method exists for conforming with the general API for the `MerkleBIT`
/// and does not need to be used (except for compatibility). Prefer `new` when possible.
/// # Errors
/// None.
#[inline]
pub fn open(path: &PathBuf, depth: usize) -> BinaryMerkleTreeResult<Self> {
let tree = MerkleBIT::new(path, depth)?;
Ok(Self { tree })
}

/// Gets the values associated with `keys` from the tree.
/// # Errors
/// `Exception` generated if the `get` encounters an invalid state during tree traversal.
#[inline]
pub fn get(
&self,
Expand All @@ -70,6 +76,8 @@ where

/// Inserts elements into the tree. Using `previous_root` specifies that the insert depends on
/// the state from the previous root, and will update references accordingly.
/// # Errors
/// `Exception` generated if the `insert` encounters an invalid state during tree traversal.
#[inline]
pub fn insert(
&mut self,
Expand All @@ -82,12 +90,16 @@ where

/// Removes a root from the tree. This will remove all elements with less than two references
/// under the given root.
/// # Errors
/// `Exception` generated if the `remove` encounters an invalid state during tree traversal.
#[inline]
pub fn remove(&mut self, root_hash: &ArrayType) -> BinaryMerkleTreeResult<()> {
self.tree.remove(root_hash)
}

/// Generates an inclusion proof for the given key at the specified root.
/// # Errors
/// `Exception` generated if an invalid state is encountered during tree traversal
#[inline]
pub fn generate_inclusion_proof(
&self,
Expand All @@ -98,6 +110,8 @@ where
}

/// Verifies an inclusion proof with the given root, key, and value.
/// # Errors
/// `Exception` generated if the given proof is invalid.
#[inline]
pub fn verify_inclusion_proof(
root: &ArrayType,
Expand All @@ -109,6 +123,8 @@ where
}

/// Gets a single item out of the tree.
/// # Errors
/// `Exception` generated if the `get_one` encounters an invalid state during tree traversal.
#[inline]
pub fn get_one(
&self,
Expand All @@ -119,6 +135,8 @@ where
}

/// Inserts a single item into the tree.
/// # Errors
/// `Exception` generated if the `insert_one` encounters an invalid state during tree traversal.
#[inline]
pub fn insert_one(
&mut self,
Expand Down
64 changes: 63 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,68 @@
#![allow(clippy::else_if_without_else)]
#![allow(clippy::module_name_repetitions)]

#![forbid(unsafe_code)]

//! # Merkle Binary Indexed Tree
//! ## Introduction
//! This module implements [`MerkleBIT`](merkle_bit/struct.MerkleBIT.html) with an attached storage module. The implemented [`HashTree`](hash_tree/struct.HashTree.html)
//! and [`RocksTree`](rocks_tree/struct.RocksTree.html) structures allow use with persistence in memory and storage respectively. Write
//! operations are batched together and committed at the end of each insert op. The [`MerkleBit`](merkle_bit/struct.MerkleBIT.html) API
//! abstracts all actions related to maintaining and updating the storage tree. The public APIs are
//! * [`new`](merkle_bit/struct.MerkleBIT.html#method.new)
//! * [`from_db`](merkle_bit/struct.MerkleBIT.html#method.from_db)
//! * [`get`](merkle_bit/struct.MerkleBIT.html#method.get)
//! * [`insert`](merkle_bit/struct.MerkleBIT.html#method.insert)
//! * [`remove`](merkle_bit/struct.MerkleBIT.html#method.remove)
//! * [`generate_inclusion_proof`](merkle_bit/struct.MerkleBIT.html#method.generate_inclusion_proof)
//! * [`get_one`](merkle_bit/struct.MerkleBIT.html#method.get_one)
//! * [`insert_one`](merkle_bit/struct.MerkleBIT.html#method.insert_one)
//! * and the associated function [`verify_inclusion_proof`](merkle_bit/struct.MerkleBIT.html#method.verify_inclusion_proof).
//!
//! After each call to either `insert` or `insert_one`, a new root hash will be created which can be
//! later used to access the inserted items.
//!
//! ## Internal Structure
//! Internally, the `MerkleBit` is composed of a collection of trait structs which implement the
//! [`Branch`](traits/trait.Branch.html), [`Leaf`](traits/trait.Leaf.html), and [`Data`](traits/trait.Data.html) nodes of the tree.
//!
//! A `Branch` node contains first a `split_index`
//! which indicates which bit of a given hash should be used to traverse the tree. This is an optimisation
//! that makes this a *sparse* merkle tree. It then contains pointers
//! to either the `one` side of the tree or the `zero` side of the tree. Additionally, a branch contains
//! a copy of the `key` used during creation to determine if a branch should be inserted before it, and
//! a `count` of the nodes under that branch.
//!
//! A `Leaf` node contains an associated `key` for comparison, and a pointer to a `Data` node for retrieving
//! information regarding access to the data. This is separate from the `Data` node for the purpose of only
//! accessing data information if data should be retrieved.
//!
//! A `Data` node contains the actual information to be retrieved. `Data` nodes can be arbitrary in size
//! and the only restriction is that the data must be serializable and deserializable.
//!
//! To illustrate these concepts, please refer to the diagram below:
//!
//! ```text
//! ----------------------
//! branch ---> | split_index: usize |
//! | | zero: [u8] |
//! ---------------- / \ | one: [u8] |
//! | key: [u8] | / \ | count: u64 |
//! | data: [u8] | <---- leaf leaf | key: [u8] |
//! ---------------- | ----------------------
//! |
//! V
//! data
//! |
//! V
//! ------------------
//! | value: Vec<u8> |
//! ------------------
//! ```
//!
//! The `MerkleBIT` can be extended to support a wide variety of backend storage solutions given that
//! you make implementations for the `Branch`, `Leaf`, and `Data` traits.
/// Defines constants for the tree.
pub mod constants;
/// An implementation of the `MerkleBIT` with a `HashMap` backend database.
Expand All @@ -30,6 +92,6 @@ pub mod tree_hasher;
/// Contains a collection of useful structs and functions for tree operations.
pub mod utils;

/// An implementation of the `MerkleBIT` with a `RocksDB` backend database.
#[cfg(feature = "use_rocksdb")]
/// An implementation of the `MerkleBIT` with a `RocksDB` backend database.
pub mod rocks_tree;
59 changes: 50 additions & 9 deletions src/merkle_bit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ where
ArrayType: Array,
{
/// Create a new `MerkleBIT` from a saved database
/// # Errors
/// `Exception` generated if the `open` fails.
#[inline]
pub fn new(path: &PathBuf, depth: usize) -> BinaryMerkleTreeResult<Self> {
let db = DatabaseType::open(path)?;
Expand All @@ -112,6 +114,8 @@ where
}

/// Create a new `MerkleBIT` from an already opened database
/// # Errors
/// None.
#[inline]
pub fn from_db(db: DatabaseType, depth: usize) -> BinaryMerkleTreeResult<Self> {
Ok(Self {
Expand All @@ -128,6 +132,8 @@ where
}

/// Get items from the `MerkleBIT`. Returns a map of `Option`s which may include the corresponding values.
/// # Errors
/// `Exception` generated when an invalid state is encountered during tree traversal.
#[inline]
pub fn get(
&self,
Expand Down Expand Up @@ -237,6 +243,8 @@ where
}

/// Insert items into the `MerkleBIT`. Keys must be sorted. Returns a new root hash for the `MerkleBIT`.
/// # Errors
/// `Exception` generated if an invalid state is encountered during tree traversal.
#[inline]
pub fn insert(
&mut self,
Expand Down Expand Up @@ -279,6 +287,8 @@ where
}

/// Traverses the tree and searches for nodes to include in the merkle proof.
/// # Errors
/// `Exception` generated when an invalid state is encountered during tree traversal.
fn generate_treerefs(
&mut self,
root: &ArrayType,
Expand Down Expand Up @@ -406,6 +416,10 @@ where
Ok(proof_nodes)
}

/// Splits nodes during tree traversal into either zeros or ones, depending on the selected bit
/// from the index
/// # Errors
/// `Exception` generated when an invalid state is encountered during tree traversal.
fn split_nodes<'a>(
&mut self,
depth: usize,
Expand All @@ -414,7 +428,7 @@ where
) -> Result<SplitNodeType<'a, BranchType, LeafType, DataType, NodeType, ArrayType>, Exception>
{
if let Some(node) = self.db.get_node(branch)? {
if node_list.is_empty() {
return if node_list.is_empty() {
let other_key;
let count;
let refs = node.get_references() + 1;
Expand Down Expand Up @@ -444,15 +458,15 @@ where
new_node.set_references(refs);
self.db.insert(branch, new_node)?;
let tree_ref = TreeRef::new(other_key, branch, count, 1);
return Ok(SplitNodeType::Ref(tree_ref));
Ok(SplitNodeType::Ref(tree_ref))
} else {
let new_cell = TreeCell::new::<BranchType, LeafType, DataType>(
branch,
node_list,
node,
depth + 1,
);
return Ok(SplitNodeType::Cell(new_cell));
Ok(SplitNodeType::Cell(new_cell))
}
}
Err(Exception::new("Failed to find node in database."))
Expand Down Expand Up @@ -515,11 +529,16 @@ where

/// This function generates the queue of `TreeRef`s and merges the queue together to create a
/// new tree root.
/// # Errors
/// `Exception` generated when `tree_refs` is empty or an invalid state is encountered during
/// tree traversal
fn create_tree(
&mut self,
mut tree_refs: Vec<TreeRef<ArrayType>>,
) -> BinaryMerkleTreeResult<ArrayType> {
assert!(!tree_refs.is_empty());
if tree_refs.is_empty() {
return Err(Exception::new("tree_refs should not be empty!"))
}

if tree_refs.len() == 1 {
self.db.batch_write()?;
Expand All @@ -537,12 +556,17 @@ where

let mut root = None;
for i in indices.into_iter().rev() {
let level = tree_ref_queue
.remove(&i)
.expect("Level should not be empty");
root = self.merge_nodes(&mut tree_refs, level)?;
if let Some(level) = tree_ref_queue.remove(&i){
root = self.merge_nodes(&mut tree_refs, level)?;
} else {
return Err(Exception::new("Level should not be empty."))
}
}
if let Some(r) = root {
Ok(r)
} else {
Err(Exception::new("Failed to get root."))
}
Ok(root.expect("Failed to get root"))
}

/// Performs the merging of `TreeRef`s until a single new root is left.
Expand Down Expand Up @@ -618,6 +642,8 @@ where
}

/// Remove all items with less than 1 reference under the given root.
/// # Errors
/// `Exception` generated when an invalid state is encountered during tree traversal.
#[inline]
pub fn remove(&mut self, root_hash: &ArrayType) -> BinaryMerkleTreeResult<()> {
let mut nodes = VecDeque::with_capacity(128);
Expand Down Expand Up @@ -688,6 +714,8 @@ where

/// Generates an inclusion proof. The proof consists of a list of hashes beginning with the key/value
/// pair and traveling up the tree until the level below the root is reached.
/// # Errors
/// `Exception` generated when an invalid state is encountered during tree traversal.
#[inline]
pub fn generate_inclusion_proof(
&self,
Expand Down Expand Up @@ -777,6 +805,9 @@ where
Ok(proof)
}

/// Verifies an inclusion proof.
/// # Errors
/// `Exception` generated when the given proof is invalid.
#[inline]
pub fn verify_inclusion_proof(
root: &ArrayType,
Expand Down Expand Up @@ -834,6 +865,8 @@ where
}

/// Gets a single key from the tree.
/// # Errors
/// `Exception` generated from encountering an invalid state during tree traversal.
#[inline]
pub fn get_one(
&self,
Expand Down Expand Up @@ -907,6 +940,8 @@ where
}

/// Inserts a single value into a tree.
/// # Errors
/// `Exception` generated if an invalid state is encountered during tree traversal.
#[inline]
pub fn insert_one(
&mut self,
Expand Down Expand Up @@ -936,6 +971,7 @@ where
}
}

/// Enum used for splitting nodes into either the left or right path during tree traversal
enum SplitNodeType<'a, BranchType, LeafType, DataType, NodeType, ArrayType>
where
BranchType: Branch<ArrayType>,
Expand All @@ -944,10 +980,15 @@ where
NodeType: Node<BranchType, LeafType, DataType, ArrayType>,
ArrayType: Array,
{
/// Used for building the `proof_nodes` variable during tree traversal
Ref(TreeRef<ArrayType>),
/// Used for appending to the `cell_queue` during tree traversal.
Cell(TreeCell<'a, NodeType, ArrayType>),
/// PhantomData marker
_UnusedBranch(PhantomData<BranchType>),
/// PhantomData marker
_UnusedLeaf(PhantomData<LeafType>),
/// PhantomData marker
_UnusedData(PhantomData<DataType>),
}

Expand Down
Loading

0 comments on commit 364f692

Please sign in to comment.