Skip to content

Commit

Permalink
feat: bring documentation to accordance with guidelines (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
antouhou authored Oct 18, 2021
1 parent cbda7a0 commit 93f93a9
Show file tree
Hide file tree
Showing 13 changed files with 1,067 additions and 327 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "rs_merkle"
version = "0.2.2"
authors = ["Anton Suprunchuk <[email protected]>"]
description = "The most advanced Merkle Tree librray for Rust"
description = "The most advanced Merkle Tree library for Rust. Supports creating and verifying proofs, multi-proofs, as well as advanced features, such as tree diffs, transactional changes, and rollbacks"
edition = "2018"
license = "Apache-2.0/MIT"
repository = "https://github.com/antouhou/rs-merkle"
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ single and several elements, i.e. multi-proofs. Advanced features include making
transactional changes to the tree and rolling back to any previously committed
tree state, similarly to Git.

The library is highly customizable. Hashing function and the way how the tree
is built can be easily configured through a special trait.

`rs-merkle` is
[available on crates.io](https://crates.io/crates/rs_merkle), and
[API Documentation is available on docs.rs](https://docs.rs/rs_merkle/).
Expand Down
4 changes: 3 additions & 1 deletion src/algorithms/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
//! This module contains built-in implementations of `rs_merkle::Hasher`
//! This module contains built-in implementations of the [`Hasher`]
//!
//! [`Hasher`]: crate::Hasher
mod sha256;

pub use sha256::Sha256Algorithm as Sha256;
28 changes: 27 additions & 1 deletion src/algorithms/sha256.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,33 @@
use crate::Hasher;
use sha2::{digest::FixedOutput, Digest, Sha256};

/// Sha256 implementation of the `rs_merkle::Hasher` trait
/// Sha256 implementation of the [`Hasher`] trait.
///
/// # Examples
///
/// ```
/// # use rs_merkle::{MerkleTree, MerkleProof, algorithms::Sha256, Hasher, Error, utils};
/// # use std::convert::TryFrom;
/// #
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let tree = MerkleTree::<Sha256>::new();
/// let other_tree: MerkleTree<Sha256> = MerkleTree::new();
///
/// let proof_bytes: Vec<u8> = vec![
/// 46, 125, 44, 3, 169, 80, 122, 226, 101, 236, 245, 181, 53, 104, 133, 165, 51, 147, 162,
/// 2, 157, 36, 19, 148, 153, 114, 101, 161, 162, 90, 239, 198, 37, 47, 16, 200, 54, 16,
/// 235, 202, 26, 5, 156, 11, 174, 130, 85, 235, 162, 249, 91, 228, 209, 215, 188, 250,
/// 137, 215, 36, 138, 130, 217, 241, 17, 229, 160, 31, 238, 20, 224, 237, 92, 72, 113, 79,
/// 34, 24, 15, 37, 173, 131, 101, 181, 63, 151, 121, 247, 157, 196, 163, 215, 233, 57, 99,
/// 249, 74,
/// ];
///
/// let proof_result = MerkleProof::<Sha256>::from_bytes(&proof_bytes);
/// # Ok(())
/// # }
/// ```
///
/// [`Hasher`]: crate::Hasher
#[derive(Clone)]
pub struct Sha256Algorithm {}

Expand Down
32 changes: 31 additions & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,33 @@
use std::fmt::{Debug, Display, Formatter};

#[derive(Copy, Clone, Debug)]
/// A list specifying general categories of tree traversals/parsing errors.
///
/// This list is intended to grow over time and it is not recommended to
/// exhaustively match against it.
///
/// It is used with the [`Error`] type.
///
/// [`Error`]: crate::Error
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[non_exhaustive]
pub enum ErrorKind {
/// Serialized to bytes merkle proof can't be parsed because it can not be divided
SerializedProofSizeIsIncorrect,
/// Not enough helper nodes to calculate the root was passed to the [`PartialTree`].
///
/// [`PartialTree`]: crate::PartialTree
NotEnoughHelperNodes,
HashConversionError,
NotEnoughHashesToCalculateRoot,
LeavesIndicesCountMismatch,
}

/// The error type for tree traversals/parsing errors of the [`MerkleProof`] and [`PartialTree`].
///
/// Errors mostly originate from the data being insufficient to traverse the partial tree
///
/// [`MerkleProof`]: crate::MerkleProof
/// [`PartialTree`]: crate::PartialTree
#[derive(Clone, Debug)]
pub struct Error {
kind: ErrorKind,
Expand Down Expand Up @@ -50,6 +70,16 @@ impl Error {
)
}

pub fn leaves_indices_count_mismatch(indices_len: usize, leaves_len: usize) -> Self {
Self::new(
ErrorKind::LeavesIndicesCountMismatch,
format!(
"leaves indices count doesn't match leaves count: {} and {}",
indices_len, leaves_len
),
)
}

pub fn kind(&self) -> ErrorKind {
self.kind
}
Expand Down
17 changes: 10 additions & 7 deletions src/hasher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::mem;
///
/// # Example
///
/// This example shows how to implement sha256 algorithm
/// This example shows how to implement the sha256 algorithm
///
/// ```
/// use rs_merkle::{Hasher};
Expand All @@ -27,9 +27,9 @@ use std::mem;
/// ```
pub trait Hasher: Clone {
/// This type is used as a hash type in the library.
/// It is recommended to use fixed size u8 array as hash. For example,
/// It is recommended to use fixed size u8 array as a hash type. For example,
/// for sha256 the type would be `[u8; 32]`, representing 32 bytes,
/// which is the size of sha256 digest. Also, fixed sized arrays of `u8`
/// which is the size of the sha256 digest. Also, fixed sized arrays of `u8`
/// by default satisfy all trait bounds required by this type.
///
/// # Trait bounds
Expand All @@ -40,18 +40,21 @@ pub trait Hasher: Clone {
/// `TryFrom<Vec<u8>>` is required to parse hashes from a serialized proof
type Hash: Copy + PartialEq + Into<Vec<u8>> + TryFrom<Vec<u8>>;

/// This associated function takes arbitrary bytes and returns hash of it.
/// This associated function takes a slice of bytes and returns a hash of it.
/// Used by `concat_and_hash` function to build a tree from concatenated hashes
fn hash(data: &[u8]) -> Self::Hash;

/// Used by `MerkleTree` and `MerkleProof` when calculating the root.
/// The provided default implementation follows propagates left node if it doesn't
/// have a sibling. Left node should always be present. Right node is optional.
/// Used by [`MerkleTree`] and [`PartialTree`] when calculating the root.
/// The provided default implementation propagates the left node if it doesn't
/// have a sibling. The left node should always be present. The right node is optional.
///
/// For the tree to be compatible with different types of proofs this function
/// needs to be overridden. For example, in Bitcoin implementation,
/// if the left node doesn't have a sibling it is concatenated to itself and
/// then hashed instead of just being propagated to the next level.
///
/// [`MerkleTree`]: crate::MerkleTree
/// [`PartialTree`]: crate::PartialTree
fn concat_and_hash(left: &Self::Hash, right: Option<&Self::Hash>) -> Self::Hash {
let mut concatenated: Vec<u8> = (*left).into();

Expand Down
140 changes: 132 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,145 @@
//! Merkle Trees, also known as Hash Trees, are used to verify that two or more parties have
//! the same data without exchanging the entire data collection.
//! `rs-merkle` is the most advanced Merkle tree library for Rust. Basic features
//! include building a Merkle tree, creation, and verification of Merkle proofs for
//! single and multiple elements, i.e. multi-proofs. Advanced features include making
//! transactional changes to the tree and rolling back to any previously committed
//! tree state, similarly to Git.
//!
//! Merkle Trees are used in Git, Mercurial,ZFS, IPFS, Bitcoin, Ethereum, Cassandra and many more.
//! In Git, for example, Merkle Trees are used to find a delta between the local and remote states,
//! and transfer only the delta. In Bitcoin, Merkle Trees are used to verify that a transaction was
//! included into the block without downloading the whole block contents.
//! The library has two main structs. The first one is [`MerkleTree`],
//! which builds the tree that can be used to verify data integrity and produce a Merkle proof. The
//! second is [`MerkleProof`], which can be used to verify the inclusion of an item in a set.
//!
//! The library is highly customizable. Hashing algorithm and the way how the tree is built
//! can be configured through the [`Hasher`] trait.
//!
//! ## About Merkle trees
//!
//! Merkle trees, also known as hash trees, are used to verify that two or more
//! parties have the same data without exchanging the entire data collection.
//!
//! Merkle trees are used in Git, Mercurial, ZFS, IPFS, Bitcoin, Ethereum, Cassandra,
//! and many more. In Git, for example, Merkle trees are used to find a delta
//! between the local and remote repository states to transfer only the difference
//! between them over the network. In Bitcoin, Merkle trees are used to verify that
//! a transaction was included in the block without downloading the whole block
//! contents. ZFS uses Merkle trees to quickly verify data integrity, offering
//! protection from silent data corruption caused by phantom writes, bugs in disk
//! firmware, power surges, and other causes.
//!
//! ## Examples
//!
//! Basic usage for verifying Merkle proofs:
//!
//! ```
//! # use rs_merkle::{MerkleTree, MerkleProof, algorithms::Sha256, Hasher, Error, utils};
//! # use std::convert::TryFrom;
//! #
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let leaf_values = ["a", "b", "c", "d", "e", "f"];
//! let leaves: Vec<[u8; 32]> = leaf_values
//! .iter()
//! .map(|x| Sha256::hash(x.as_bytes()))
//! .collect();
//!
//! let merkle_tree = MerkleTree::<Sha256>::from_leaves(&leaves);
//! let indices_to_prove = vec![3, 4];
//! let leaves_to_prove = leaves.get(3..5).ok_or("can't get leaves to prove")?;
//! let merkle_proof = merkle_tree.proof(&indices_to_prove);
//! let merkle_root = merkle_tree.root().ok_or("couldn't get the merkle root")?;
//! // Serialize proof to pass it to the client
//! let proof_bytes = merkle_proof.to_bytes();
//!
//! // Parse proof back on the client
//! let proof = MerkleProof::<Sha256>::try_from(proof_bytes)?;
//!
//! assert!(proof.verify(merkle_root, &indices_to_prove, leaves_to_prove, leaves.len()));
//! # Ok(())
//! # }
//! ```
//!
//! Advanced usage with rolling several commits back:
//!
//! ```
//! # use rs_merkle::{MerkleTree, algorithms::Sha256, Hasher, Error};
//! #
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let elements = ["a", "b", "c", "d", "e", "f"];
//! let mut leaves: Vec<[u8; 32]> = elements
//! .iter()
//! .map(|x| Sha256::hash(x.as_bytes()))
//! .collect();
//!
//! let mut merkle_tree: MerkleTree<Sha256> = MerkleTree::new();
//!
//! // Appending leaves to the tree without committing
//! merkle_tree.append(&mut leaves);
//!
//! // Without committing changes we can get the root for the uncommitted data, but committed
//! // tree still doesn't have any elements
//! assert_eq!(merkle_tree.root(), None);
//! assert_eq!(
//! merkle_tree.uncommitted_root_hex(),
//! Some("1f7379539707bcaea00564168d1d4d626b09b73f8a2a365234c62d763f854da2".to_string())
//! );
//!
//! // Committing the changes
//! merkle_tree.commit();
//!
//! // Changes applied to the tree after the commit, and there's no uncommitted changes anymore
//! assert_eq!(
//! merkle_tree.root_hex(),
//! Some("1f7379539707bcaea00564168d1d4d626b09b73f8a2a365234c62d763f854da2".to_string())
//! );
//! assert_eq!(merkle_tree.uncommitted_root_hex(), None);
//!
//! // Adding a new leaf
//! merkle_tree.insert(Sha256::hash("g".as_bytes())).commit();
//!
//! // Root was updated after insertion
//! assert_eq!(
//! merkle_tree.root_hex(),
//! Some("e2a80e0e872a6c6eaed37b4c1f220e1935004805585b5f99617e48e9c8fe4034".to_string())
//! );
//!
//! // Adding some more leaves
//! merkle_tree.append(vec![
//! Sha256::hash("h".as_bytes()),
//! Sha256::hash("k".as_bytes()),
//! ].as_mut()).commit();
//! assert_eq!(
//! merkle_tree.root_hex(),
//! Some("09b6890b23e32e607f0e5f670ab224e36af8f6599cbe88b468f4b0f761802dd6".to_string())
//! );
//!
//! // Rolling back to the previous state
//! merkle_tree.rollback();
//! assert_eq!(
//! merkle_tree.root_hex(),
//! Some("e2a80e0e872a6c6eaed37b4c1f220e1935004805585b5f99617e48e9c8fe4034".to_string())
//! );
//!
//! // We can rollback multiple times as well
//! merkle_tree.rollback();
//! assert_eq!(
//! merkle_tree.root_hex(),
//! Some("1f7379539707bcaea00564168d1d4d626b09b73f8a2a365234c62d763f854da2".to_string())
//! );
//! # Ok(())
//! # }
//! ```
pub use error::Error;
pub use error::ErrorKind;
pub use hasher::Hasher;
pub use merkle_proof::MerkleProof;
pub use merkle_tree::MerkleTree;
pub use partial_tree::PartialTree;

mod error;
mod hasher;
mod merkle_proof;
mod merkle_tree;
mod partial_tree;
#[doc(hidden)]
pub mod utils;

pub mod algorithms;
pub mod error;
mod utils;
Loading

0 comments on commit 93f93a9

Please sign in to comment.