mirror of
https://github.com/tlsnotary/rs-merkle.git
synced 2026-01-08 22:38:07 -05:00
feat: bring documentation to accordance with guidelines (#6)
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
name = "rs_merkle"
|
||||
version = "0.2.2"
|
||||
authors = ["Anton Suprunchuk <anton.suprunchuk@gmail.com>"]
|
||||
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"
|
||||
|
||||
@@ -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/).
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
|
||||
32
src/error.rs
32
src/error.rs
@@ -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,
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
@@ -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
|
||||
@@ -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();
|
||||
|
||||
|
||||
140
src/lib.rs
140
src/lib.rs
@@ -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;
|
||||
|
||||
@@ -4,24 +4,45 @@ use crate::error::Error;
|
||||
use crate::partial_tree::PartialTree;
|
||||
use crate::{utils, Hasher};
|
||||
|
||||
/// `MerkleProof` is used to parse, verify, calculate a root for merkle proofs.
|
||||
/// [`MerkleProof`] is used to parse, verify, calculate a root for Merkle proofs.
|
||||
///
|
||||
/// # Usage
|
||||
/// ## Usage
|
||||
///
|
||||
/// MerkleProof requires specifying hashing algorithm and hash size in order to work.
|
||||
/// Check out the `Hasher` trait for examples. rs_merkle provides some built in `Hasher`
|
||||
/// implementations, for example `rs_merkle::algorithms::Sha256`
|
||||
/// The hashing algorithm is set through the [`Hasher`] trait, which is supplied as a generic
|
||||
/// parameter to the [`MerkleProof`]. `rs_merkle` provides some built-in [`Hasher`] implementations,
|
||||
/// for example, [`algorithms::Sha256`]
|
||||
///
|
||||
/// # Example
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rs_merkle::{MerkleProof, algorithms::Sha256};
|
||||
/// # use rs_merkle::{MerkleTree, MerkleProof, algorithms::Sha256, Hasher, Error, utils};
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let proof_hashes: Vec<[u8; 32]> = 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_hashes_copy = proof_hashes.clone();
|
||||
///
|
||||
/// let proof = MerkleProof::<Sha256>::new(proof_hashes);
|
||||
///```
|
||||
/// let proof = MerkleProof::<Sha256>::new(proof_hashes_copy);
|
||||
/// assert_eq!(proof.proof_hashes(), &proof_hashes);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`Hasher`]: crate::Hasher
|
||||
/// [`algorithms::Sha256`]: crate::algorithms::Sha256
|
||||
pub struct MerkleProof<T: Hasher> {
|
||||
proof_hashes: Vec<T::Hash>,
|
||||
}
|
||||
@@ -31,35 +52,117 @@ impl<T: Hasher> MerkleProof<T> {
|
||||
MerkleProof { proof_hashes }
|
||||
}
|
||||
|
||||
/// Creates a proof from a slice of bytes. For more details and examples, please see
|
||||
/// [`try_from`](MerkleProof::try_from)
|
||||
/// Creates a proof from a slice of bytes
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # use rs_merkle::{MerkleProof, algorithms::Sha256};
|
||||
///
|
||||
/// 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.as_slice());
|
||||
/// ```
|
||||
///
|
||||
/// ## Errors
|
||||
///
|
||||
/// In case of a parsing error result will contain [`Error`]
|
||||
///
|
||||
/// ['Error`]: crate::Error
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
|
||||
Self::try_from(bytes)
|
||||
}
|
||||
|
||||
/// Returns all hashes from the proof, sorted from the left to right,
|
||||
/// bottom to top.
|
||||
pub fn proof_hashes(&self) -> &[T::Hash] {
|
||||
&self.proof_hashes
|
||||
/// Uses proof to verify that a given set of elements is contained in the original data
|
||||
/// set the proof was made for.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use rs_merkle::{MerkleTree, MerkleProof, algorithms::Sha256, Hasher, Error, utils};
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let leaves = [
|
||||
/// Sha256::hash("a".as_bytes()),
|
||||
/// Sha256::hash("b".as_bytes()),
|
||||
/// Sha256::hash("c".as_bytes()),
|
||||
/// ];
|
||||
///
|
||||
/// let merkle_tree = MerkleTree::<Sha256>::from_leaves(&leaves);
|
||||
///
|
||||
/// let indices_to_prove = vec![0, 1];
|
||||
/// let leaves_to_prove = leaves.get(0..2).ok_or("can't get leaves to prove")?;
|
||||
///
|
||||
/// let proof = merkle_tree.proof(&indices_to_prove);
|
||||
/// let root = merkle_tree.root().ok_or("couldn't get the merkle root")?;
|
||||
///
|
||||
/// assert!(proof.verify(root, &indices_to_prove, leaves_to_prove, leaves.len()));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn verify(
|
||||
&self,
|
||||
root: T::Hash,
|
||||
leaf_indices: &[usize],
|
||||
leaf_hashes: &[T::Hash],
|
||||
total_leaves_count: usize,
|
||||
) -> bool {
|
||||
match self.root(leaf_indices, leaf_hashes, total_leaves_count) {
|
||||
Ok(extracted_root) => extracted_root == root,
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns all hashes from the proof, sorted from the left to right,
|
||||
/// bottom to top, as a vector of lower hex strings.
|
||||
/// For a slice of `&[T::Hash]`, see [`proof_hashes`](MerkleProof::proof_hashes)
|
||||
pub fn proof_hashes_hex(&self) -> Vec<String> {
|
||||
self.proof_hashes
|
||||
.iter()
|
||||
.map(utils::collections::to_hex_string)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Calculates merkle root based on provided leaves and proof hashes
|
||||
/// Calculates Merkle root based on provided leaves and proof hashes. Used inside the
|
||||
/// [`MerkleProof::verify`] method, but sometimes can be used on its own.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use rs_merkle::{MerkleTree, MerkleProof, algorithms::Sha256, Hasher, Error, utils};
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let leaves = [
|
||||
/// Sha256::hash("a".as_bytes()),
|
||||
/// Sha256::hash("b".as_bytes()),
|
||||
/// Sha256::hash("c".as_bytes()),
|
||||
/// ];
|
||||
///
|
||||
/// let merkle_tree = MerkleTree::<Sha256>::from_leaves(&leaves);
|
||||
///
|
||||
/// let indices_to_prove = vec![0, 1];
|
||||
/// let leaves_to_prove = leaves.get(0..2).ok_or("can't get leaves to prove")?;
|
||||
///
|
||||
/// let proof = merkle_tree.proof(&indices_to_prove);
|
||||
/// let root = merkle_tree.root().ok_or("couldn't get the merkle root")?;
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// proof.root(&indices_to_prove, leaves_to_prove, leaves.len())?,
|
||||
/// root
|
||||
/// );
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn root(
|
||||
&self,
|
||||
leaf_indices: &[usize],
|
||||
leaf_hashes: &[T::Hash],
|
||||
total_leaves_count: usize,
|
||||
) -> Result<T::Hash, Error> {
|
||||
if leaf_indices.len() != leaf_hashes.len() {
|
||||
return Err(Error::leaves_indices_count_mismatch(
|
||||
leaf_indices.len(),
|
||||
leaf_hashes.len(),
|
||||
));
|
||||
}
|
||||
let tree_depth = utils::indices::tree_depth(total_leaves_count);
|
||||
|
||||
// Zipping indices and hashes into a vector of (original_index_in_tree, leaf_hash)
|
||||
@@ -98,7 +201,35 @@ impl<T: Hasher> MerkleProof<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the root and serializes it into a hex string
|
||||
/// Calculates the root and serializes it into a hex string.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use rs_merkle::{MerkleTree, MerkleProof, algorithms::Sha256, Hasher, Error, utils};
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let leaves = [
|
||||
/// Sha256::hash("a".as_bytes()),
|
||||
/// Sha256::hash("b".as_bytes()),
|
||||
/// Sha256::hash("c".as_bytes()),
|
||||
/// ];
|
||||
///
|
||||
/// let merkle_tree = MerkleTree::<Sha256>::from_leaves(&leaves);
|
||||
///
|
||||
/// let indices_to_prove = vec![0, 1];
|
||||
/// let leaves_to_prove = leaves.get(0..2).ok_or("can't get leaves to prove")?;
|
||||
///
|
||||
/// let proof = merkle_tree.proof(&indices_to_prove);
|
||||
/// let root_hex = merkle_tree.root_hex().ok_or("couldn't get the merkle root")?;
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// proof.root_hex(&indices_to_prove, leaves_to_prove, leaves.len())?,
|
||||
/// root_hex
|
||||
/// );
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn root_hex(
|
||||
&self,
|
||||
leaf_indices: &[usize],
|
||||
@@ -109,29 +240,129 @@ impl<T: Hasher> MerkleProof<T> {
|
||||
Ok(utils::collections::to_hex_string(&root))
|
||||
}
|
||||
|
||||
/// Verifies the proof for a given set of leaves
|
||||
pub fn verify(
|
||||
&self,
|
||||
root: T::Hash,
|
||||
leaf_indices: &[usize],
|
||||
leaf_hashes: &[T::Hash],
|
||||
total_leaves_count: usize,
|
||||
) -> bool {
|
||||
match self.root(leaf_indices, leaf_hashes, total_leaves_count) {
|
||||
Ok(extracted_root) => extracted_root == root,
|
||||
Err(_) => false,
|
||||
}
|
||||
/// Returns all hashes from the proof, sorted from the left to right,
|
||||
/// bottom to top.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use rs_merkle::{MerkleTree, MerkleProof, algorithms::Sha256, Hasher, Error, utils};
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let proof_hashes: Vec<[u8; 32]> = 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_hashes_copy = proof_hashes.clone();
|
||||
///
|
||||
/// let proof = MerkleProof::<Sha256>::new(proof_hashes_copy);
|
||||
/// assert_eq!(proof.proof_hashes(), &proof_hashes);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn proof_hashes(&self) -> &[T::Hash] {
|
||||
&self.proof_hashes
|
||||
}
|
||||
|
||||
/// Serializes proof hashes to a flat vector of bytes
|
||||
/// Returns all hashes from the proof, sorted from the left to right,
|
||||
/// bottom to top, as a vector of lower hex strings.
|
||||
/// For a slice of [`Hasher::Hash`], see [`MerkleProof::proof_hashes`]
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use rs_merkle::{MerkleTree, MerkleProof, algorithms::Sha256, Hasher, Error, utils};
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// 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 = MerkleProof::<Sha256>::from_bytes(proof_bytes.as_slice())?;
|
||||
/// assert_eq!(
|
||||
/// proof.proof_hashes_hex(),
|
||||
/// vec![
|
||||
/// "2e7d2c03a9507ae265ecf5b5356885a53393a2029d241394997265a1a25aefc6".to_string(),
|
||||
/// "252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111".to_string(),
|
||||
/// "e5a01fee14e0ed5c48714f22180f25ad8365b53f9779f79dc4a3d7e93963f94a".to_string()
|
||||
/// ]
|
||||
/// );
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn proof_hashes_hex(&self) -> Vec<String> {
|
||||
self.proof_hashes
|
||||
.iter()
|
||||
.map(utils::collections::to_hex_string)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Serializes proof hashes to a flat vector of bytes, from left to right, bottom to top.
|
||||
/// Usually used to pass the proof to the client after extracting it from the tree.
|
||||
///
|
||||
/// ## Important
|
||||
///
|
||||
/// Please note that some applications may serialize proof differently, for example in reverse
|
||||
/// order - from top to bottom, right to left. In that case, you'll need to do some
|
||||
/// manipulations on the proof data manually. Raw proof hashes are available through
|
||||
/// [`MerkleProof::proof_hashes`]
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// # 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 over the network
|
||||
/// let proof_bytes = merkle_proof.to_bytes();
|
||||
///
|
||||
/// assert_eq!(proof_bytes, 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,
|
||||
/// ]);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
let vectors: Vec<Vec<u8>> = self
|
||||
let mut vectors: Vec<Vec<u8>> = self
|
||||
.proof_hashes()
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|hash| hash.into())
|
||||
.collect();
|
||||
vectors.iter().cloned().flatten().collect()
|
||||
vectors.drain(..).flatten().collect()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +371,8 @@ impl<T: Hasher> TryFrom<Vec<u8>> for MerkleProof<T> {
|
||||
|
||||
/// Parses proof serialized to a collection of bytes. Consumes passed vector.
|
||||
///
|
||||
/// # Example
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::convert::TryFrom;
|
||||
/// use rs_merkle::{MerkleProof, algorithms::Sha256};
|
||||
@@ -166,7 +398,8 @@ impl<T: Hasher> TryFrom<&[u8]> for MerkleProof<T> {
|
||||
|
||||
/// Parses proof serialized to a collection of bytes
|
||||
///
|
||||
/// # Example
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::convert::TryFrom;
|
||||
/// use rs_merkle::{MerkleProof, algorithms::Sha256};
|
||||
|
||||
@@ -4,93 +4,12 @@ use crate::{utils, Hasher, MerkleProof};
|
||||
|
||||
/// [`MerkleTree`] is a Merkle Tree that is well suited for both basic and advanced usage.
|
||||
///
|
||||
/// Basic features include creation and verification of merkle proofs from a set of leaves.
|
||||
/// Basic features include the creation and verification of Merkle proofs from a set of leaves.
|
||||
/// This is often done in various cryptocurrencies.
|
||||
///
|
||||
/// Advanced features include being able to make transactional changes to a tree with
|
||||
/// being able to roll back to any previous committed state of tree. This scenario is similar
|
||||
/// to Git and can be found in databases and file systems.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ## Basic usage for cryptocurrency proofs:
|
||||
///
|
||||
/// ```
|
||||
/// use rs_merkle::{MerkleTree, algorithms::Sha256, Hasher};
|
||||
///
|
||||
/// let leaf_values = ["a", "b", "c", "d", "e", "f"];
|
||||
/// let expected_root_hex = "1f7379539707bcaea00564168d1d4d626b09b73f8a2a365234c62d763f854da2";
|
||||
/// let leaves: Vec<[u8; 32]> = leaf_values
|
||||
/// .iter()
|
||||
/// .map(|x| Sha256::hash(x.as_bytes().to_vec().as_ref()))
|
||||
/// .collect();
|
||||
///
|
||||
/// let merkle_tree = MerkleTree::<Sha256>::from_leaves(&leaves);
|
||||
/// let hex_root = merkle_tree.root_hex().unwrap();
|
||||
///
|
||||
/// assert_eq!(hex_root, expected_root_hex);
|
||||
///
|
||||
/// let indices_to_prove = vec![3, 4];
|
||||
/// let merkle_proof = merkle_tree.proof(&indices_to_prove);
|
||||
/// ```
|
||||
///
|
||||
/// ## Advanced usage with rolling several commits back
|
||||
///
|
||||
/// ```
|
||||
/// use rs_merkle::{MerkleTree, algorithms::Sha256, Hasher};
|
||||
///
|
||||
/// let leaf_values = ["a", "b", "c", "d", "e", "f"];
|
||||
/// let expected_root_hex = "1f7379539707bcaea00564168d1d4d626b09b73f8a2a365234c62d763f854da2";
|
||||
/// let leaves: Vec<[u8; 32]> = leaf_values
|
||||
/// .iter()
|
||||
/// .map(|x| Sha256::hash(x.as_bytes().to_vec().as_ref()))
|
||||
/// .collect();
|
||||
///
|
||||
/// let mut merkle_tree: MerkleTree<Sha256> = MerkleTree::new();
|
||||
/// merkle_tree.append(leaves.clone().as_mut());
|
||||
/// // No changes were committed just yet, tree is empty
|
||||
/// assert_eq!(merkle_tree.root(), None);
|
||||
///
|
||||
/// merkle_tree.commit();
|
||||
///
|
||||
/// assert_eq!(merkle_tree.uncommitted_root_hex().unwrap(), String::from("1f7379539707bcaea00564168d1d4d626b09b73f8a2a365234c62d763f854da2"));
|
||||
///
|
||||
/// // Adding a new leaf
|
||||
/// merkle_tree.insert(Sha256::hash("g".as_bytes().to_vec().as_ref()));
|
||||
///
|
||||
/// // Uncommitted root must reflect the insert
|
||||
/// assert_eq!(merkle_tree.uncommitted_root_hex().unwrap(), String::from("e2a80e0e872a6c6eaed37b4c1f220e1935004805585b5f99617e48e9c8fe4034"));
|
||||
///
|
||||
/// merkle_tree.commit();
|
||||
///
|
||||
/// // After calling commit, uncommitted root will become committed
|
||||
/// assert_eq!(merkle_tree.root_hex().unwrap(), String::from("e2a80e0e872a6c6eaed37b4c1f220e1935004805585b5f99617e48e9c8fe4034"));
|
||||
///
|
||||
/// // Adding some more leaves
|
||||
/// merkle_tree.append(vec![
|
||||
/// Sha256::hash("h".as_bytes().to_vec().as_ref()),
|
||||
/// Sha256::hash("k".as_bytes().to_vec().as_ref()),
|
||||
/// ].as_mut());
|
||||
///
|
||||
/// // Checking that the uncommitted root has changed, but the committed one hasn't
|
||||
/// assert_eq!(merkle_tree.uncommitted_root_hex().unwrap(), String::from("09b6890b23e32e607f0e5f670ab224e36af8f6599cbe88b468f4b0f761802dd6"));
|
||||
/// assert_eq!(merkle_tree.root_hex().unwrap(), String::from("e2a80e0e872a6c6eaed37b4c1f220e1935004805585b5f99617e48e9c8fe4034"));
|
||||
///
|
||||
/// merkle_tree.commit();
|
||||
///
|
||||
/// // Checking committed changes again
|
||||
/// assert_eq!(merkle_tree.root_hex().unwrap(), String::from("09b6890b23e32e607f0e5f670ab224e36af8f6599cbe88b468f4b0f761802dd6"));
|
||||
///
|
||||
/// merkle_tree.rollback();
|
||||
///
|
||||
/// // Check that we rolled one commit back
|
||||
/// assert_eq!(merkle_tree.root_hex().unwrap(), String::from("e2a80e0e872a6c6eaed37b4c1f220e1935004805585b5f99617e48e9c8fe4034"));
|
||||
///
|
||||
/// merkle_tree.rollback();
|
||||
///
|
||||
/// // Rolling back to the state after the very first commit
|
||||
/// assert_eq!(merkle_tree.root_hex().unwrap(), expected_root_hex);
|
||||
/// ```
|
||||
/// Advanced features include being able to make transactional changes to a tree with being able to
|
||||
/// roll back to any previously committed state of the tree. This scenario is similar to Git and
|
||||
/// can be found in databases and file systems.
|
||||
#[derive(Clone)]
|
||||
pub struct MerkleTree<T: Hasher> {
|
||||
current_working_tree: PartialTree<T>,
|
||||
@@ -105,7 +24,7 @@ impl<T: Hasher> Default for MerkleTree<T> {
|
||||
}
|
||||
|
||||
impl<T: Hasher> MerkleTree<T> {
|
||||
/// Creates a new instance of Merkle Tree. Requires specifying the hash algorithm.
|
||||
/// Creates a new instance of Merkle Tree. Requires a hash algorithm to be specified.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -124,12 +43,23 @@ impl<T: Hasher> MerkleTree<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns Merkle tree root
|
||||
pub fn root(&self) -> Option<T::Hash> {
|
||||
self.layers().last()?.first().cloned()
|
||||
}
|
||||
|
||||
/// Clones leave hashes and build the tree from them
|
||||
/// Clones the leaves and builds the tree from them
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use rs_merkle::{MerkleTree, MerkleProof, algorithms::Sha256, Hasher, Error, utils};
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let leaves = [
|
||||
/// Sha256::hash("a".as_bytes()),
|
||||
/// Sha256::hash("b".as_bytes()),
|
||||
/// Sha256::hash("c".as_bytes()),
|
||||
/// ];
|
||||
///
|
||||
/// let merkle_tree = MerkleTree::<Sha256>::from_leaves(&leaves);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
pub fn from_leaves(leaves: &[T::Hash]) -> Self {
|
||||
let mut tree = Self::new();
|
||||
|
||||
@@ -139,20 +69,68 @@ impl<T: Hasher> MerkleTree<T> {
|
||||
tree
|
||||
}
|
||||
|
||||
/// Returns Merkle tree root serialized as a hex string
|
||||
/// Returns the tree root - the top hash of the tree. Used in the inclusion proof verification.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use rs_merkle::{MerkleTree, MerkleProof, algorithms::Sha256, Hasher, Error, utils};
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let leaves = [
|
||||
/// Sha256::hash("a".as_bytes()),
|
||||
/// Sha256::hash("b".as_bytes()),
|
||||
/// Sha256::hash("c".as_bytes()),
|
||||
/// ];
|
||||
///
|
||||
/// let merkle_tree = MerkleTree::<Sha256>::from_leaves(&leaves);
|
||||
///
|
||||
/// let indices_to_prove = vec![0, 1];
|
||||
/// let leaves_to_prove = leaves.get(0..2).ok_or("can't get leaves to prove")?;
|
||||
///
|
||||
/// let proof = merkle_tree.proof(&indices_to_prove);
|
||||
/// let root = merkle_tree.root().ok_or("couldn't get the merkle root")?;
|
||||
///
|
||||
/// assert!(proof.verify(root, &indices_to_prove, leaves_to_prove, leaves.len()));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn root(&self) -> Option<T::Hash> {
|
||||
Some(self.layer_tuples().last()?.first()?.1)
|
||||
}
|
||||
|
||||
/// Similar to [`MerkleTree::root`], but returns a hex encoded string instead of
|
||||
/// [`Hasher::Hash`].
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use rs_merkle::{MerkleTree, MerkleProof, algorithms::Sha256, Hasher, Error, utils};
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let leaves = [
|
||||
/// Sha256::hash("a".as_bytes()),
|
||||
/// Sha256::hash("b".as_bytes()),
|
||||
/// Sha256::hash("c".as_bytes()),
|
||||
/// ];
|
||||
///
|
||||
/// let merkle_tree = MerkleTree::<Sha256>::from_leaves(&leaves);
|
||||
/// let root = merkle_tree.root_hex().ok_or("couldn't get the merkle root")?;
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// root,
|
||||
/// "7075152d03a5cd92104887b476862778ec0c87be5c2fa1c0a90f87c49fad6eff".to_string()
|
||||
/// );
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn root_hex(&self) -> Option<String> {
|
||||
let root = self.root()?;
|
||||
Some(utils::collections::to_hex_string(&root))
|
||||
}
|
||||
|
||||
/// Returns tree depth. Tree depth is how many layers there is between
|
||||
/// leaves and root
|
||||
pub fn depth(&self) -> usize {
|
||||
self.layers().len() - 1
|
||||
}
|
||||
|
||||
/// Returns helper nodes required to build a partial tree for the given indices
|
||||
/// to be able to extract a root from it. Useful in constructing merkle proofs
|
||||
/// to be able to extract a root from it. Useful in constructing Merkle proofs
|
||||
fn helper_nodes(&self, leaf_indices: &[usize]) -> Vec<T::Hash> {
|
||||
let mut helper_nodes = Vec::<T::Hash>::new();
|
||||
|
||||
@@ -171,20 +149,15 @@ impl<T: Hasher> MerkleTree<T> {
|
||||
let mut current_layer_indices = leaf_indices.to_vec();
|
||||
let mut helper_nodes: Vec<Vec<(usize, T::Hash)>> = Vec::new();
|
||||
|
||||
for tree_layer in self.layers() {
|
||||
for tree_layer in self.layer_tuples() {
|
||||
let mut helpers_layer = Vec::new();
|
||||
let siblings = utils::indices::sibling_indices(¤t_layer_indices);
|
||||
// Filter all nodes that do not require an additional hash to be calculated
|
||||
let helper_indices = utils::collections::difference(&siblings, ¤t_layer_indices);
|
||||
|
||||
for index in helper_indices {
|
||||
match tree_layer.get(index) {
|
||||
Some(hash) => {
|
||||
helpers_layer.push((index, *hash));
|
||||
}
|
||||
// This means that there's no right sibling to the current index, thus
|
||||
// we don't need to include anything in the proof for that index
|
||||
None => continue,
|
||||
if let Some(tuple) = tree_layer.get(index) {
|
||||
helpers_layer.push(*tuple);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,82 +168,188 @@ impl<T: Hasher> MerkleTree<T> {
|
||||
helper_nodes
|
||||
}
|
||||
|
||||
/// Returns merkle proof required to prove inclusion of items at given indices
|
||||
/// Returns the Merkle proof required to prove the inclusion of items in a data set.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use rs_merkle::{MerkleTree, MerkleProof, algorithms::Sha256, Hasher, Error, utils};
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let leaves: Vec<[u8; 32]> = ["a", "b", "c", "d", "e", "f"]
|
||||
/// .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(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn proof(&self, leaf_indices: &[usize]) -> MerkleProof<T> {
|
||||
MerkleProof::<T>::new(self.helper_nodes(leaf_indices))
|
||||
}
|
||||
|
||||
/// Returns tree leaves, i.e. the bottom level
|
||||
pub fn leaves(&self) -> Option<Vec<T::Hash>> {
|
||||
self.layers().first().cloned()
|
||||
}
|
||||
|
||||
/// Returns the whole tree, where the first layer is leaves and
|
||||
/// consequent layers are nodes.
|
||||
pub fn layers(&self) -> Vec<Vec<T::Hash>> {
|
||||
self.current_working_tree.layer_nodes()
|
||||
}
|
||||
|
||||
/// Same as [`layers`](MerkleTree::layers), but serializes each hash as a hex string
|
||||
pub fn layers_hex(&self) -> Vec<Vec<String>> {
|
||||
self.layers()
|
||||
.iter()
|
||||
.map(|layer| {
|
||||
layer
|
||||
.iter()
|
||||
.map(utils::collections::to_hex_string)
|
||||
.collect()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Inserts a new leaf. Please note it won't modify the root just yet; For the changes
|
||||
/// to be applied to the root, [`commit`](MerkleTree::commit) method should be called first. To get the root
|
||||
/// of the new tree without applying the changes, you can use [`uncommitted_root`](MerkleTree::uncommitted_root)
|
||||
/// to be applied to the root, [`MerkleTree::commit`] method should be called first. To get the
|
||||
/// root of the new tree without applying the changes, you can use
|
||||
/// [`MerkleTree::uncommitted_root`]
|
||||
///
|
||||
/// # Example
|
||||
/// // TODO
|
||||
pub fn insert(&mut self, leaf: T::Hash) {
|
||||
self.uncommitted_leaves.push(leaf)
|
||||
/// # Examples
|
||||
///
|
||||
/// Get the root after an insert:
|
||||
///
|
||||
/// ```
|
||||
/// # use rs_merkle::{MerkleTree, MerkleProof, algorithms::Sha256, Hasher, Error, utils};
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let mut merkle_tree = MerkleTree::<Sha256>::new();
|
||||
/// merkle_tree.insert(Sha256::hash("a".as_bytes()));
|
||||
///
|
||||
/// assert_eq!(merkle_tree.root(), None);
|
||||
///
|
||||
/// merkle_tree.commit();
|
||||
/// assert_eq!(
|
||||
/// merkle_tree.root_hex(),
|
||||
/// Some("ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb".to_string())
|
||||
/// );
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Inserts also can be chained with [`MerkleTree::commit`] for convenience:
|
||||
///
|
||||
/// ```
|
||||
/// # use rs_merkle::{MerkleTree, MerkleProof, algorithms::Sha256, Hasher, Error, utils};
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let mut merkle_tree = MerkleTree::<Sha256>::new();
|
||||
/// merkle_tree
|
||||
/// .insert(Sha256::hash("a".as_bytes()))
|
||||
/// .commit();
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// merkle_tree.root_hex(),
|
||||
/// Some("ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb".to_string())
|
||||
/// );
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn insert(&mut self, leaf: T::Hash) -> &mut Self {
|
||||
self.uncommitted_leaves.push(leaf);
|
||||
self
|
||||
}
|
||||
|
||||
/// Appends leaves to the tree. Behaves similarly to [`commit`](MerkleTree::commit), but for a list of items.
|
||||
/// Takes ownership of the elements of the [`std::vec::Vec<T>`], similarly to [`append`](std::vec::Vec::append) of a [`std::vec::Vec<T>`]
|
||||
pub fn append(&mut self, leaves: &mut Vec<T::Hash>) {
|
||||
self.uncommitted_leaves.append(leaves)
|
||||
/// Appends leaves to the tree. Behaves similarly to [`MerkleTree::insert`], but for a list of
|
||||
/// items. Takes ownership of the elements of the [`std::vec::Vec<T>`],
|
||||
/// similarly to [`std::vec::Vec::append`].
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use rs_merkle::{MerkleTree, MerkleProof, algorithms::Sha256, Hasher, Error, utils};
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let mut merkle_tree = MerkleTree::<Sha256>::new();
|
||||
/// let mut leaves = vec![
|
||||
/// Sha256::hash("a".as_bytes()),
|
||||
/// Sha256::hash("b".as_bytes()),
|
||||
/// ];
|
||||
/// merkle_tree
|
||||
/// .append(&mut leaves)
|
||||
/// .commit();
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// merkle_tree.root_hex(),
|
||||
/// Some("e5a01fee14e0ed5c48714f22180f25ad8365b53f9779f79dc4a3d7e93963f94a".to_string())
|
||||
/// );
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn append(&mut self, leaves: &mut Vec<T::Hash>) -> &mut Self {
|
||||
self.uncommitted_leaves.append(leaves);
|
||||
self
|
||||
}
|
||||
|
||||
/// Calculates the root of the uncommitted changes as if they were committed.
|
||||
/// Will return the same hash as [`root`](MerkleTree::root) after [`commit`](MerkleTree::commit)
|
||||
pub fn uncommitted_root(&self) -> Option<T::Hash> {
|
||||
let shadow_tree = self.uncommitted_diff()?;
|
||||
shadow_tree.root().cloned()
|
||||
}
|
||||
|
||||
/// Same as `uncommitted_root`, but serialized to a hex string
|
||||
pub fn uncommitted_root_hex(&self) -> Option<String> {
|
||||
let root = self.uncommitted_root()?;
|
||||
Some(utils::collections::to_hex_string(&root))
|
||||
}
|
||||
|
||||
/// Commits changes made by [`insert`](MerkleTree::insert) and [`append`](MerkleTree::append)
|
||||
/// Commits the changes made by [`MerkleTree::insert`] and [`MerkleTree::append`]
|
||||
/// and modifies the root.
|
||||
/// Commits changes to the history, so the tree can be rolled back to any previous commit.
|
||||
/// Commits are saved to the history, so the tree can be rolled back to any previous commit
|
||||
/// using [`MerkleTree::rollback`]
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use rs_merkle::{MerkleTree, MerkleProof, algorithms::Sha256, Hasher, Error, utils};
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let mut merkle_tree = MerkleTree::<Sha256>::new();
|
||||
/// let mut leaves = vec![
|
||||
/// Sha256::hash("a".as_bytes()),
|
||||
/// Sha256::hash("b".as_bytes()),
|
||||
/// ];
|
||||
/// merkle_tree.append(&mut leaves);
|
||||
/// assert_eq!(
|
||||
/// merkle_tree.root_hex(),
|
||||
/// None
|
||||
/// );
|
||||
///
|
||||
/// merkle_tree.commit();
|
||||
/// assert_eq!(
|
||||
/// merkle_tree.root_hex(),
|
||||
/// Some("e5a01fee14e0ed5c48714f22180f25ad8365b53f9779f79dc4a3d7e93963f94a".to_string())
|
||||
/// );
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn commit(&mut self) {
|
||||
if let Some(diff) = self.uncommitted_diff() {
|
||||
self.history.push(diff.clone());
|
||||
self.current_working_tree.merge_unverified(diff);
|
||||
self.uncommitted_leaves.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// Aborts all uncommitted [`insert`](MerkleTree::insert) and [`append`](MerkleTree::append)
|
||||
/// operations without applying them to the tree.
|
||||
pub fn abort_uncommitted(&mut self) {
|
||||
self.uncommitted_leaves.clear()
|
||||
}
|
||||
|
||||
/// Rolls back one commit and reverts tree to the previous state.
|
||||
/// Removes the latest commit from the changes history
|
||||
/// Rolls back one commit and reverts the tree to the previous state.
|
||||
/// Removes the most recent commit from the history.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use rs_merkle::{MerkleTree, MerkleProof, algorithms::Sha256, Hasher, Error, utils};
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let mut merkle_tree = MerkleTree::<Sha256>::new();
|
||||
///
|
||||
/// merkle_tree.insert(Sha256::hash("a".as_bytes())).commit();
|
||||
/// assert_eq!(
|
||||
/// merkle_tree.root_hex(),
|
||||
/// Some("ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb".to_string())
|
||||
/// );
|
||||
///
|
||||
/// merkle_tree.insert(Sha256::hash("b".as_bytes())).commit();
|
||||
/// assert_eq!(
|
||||
/// merkle_tree.root_hex(),
|
||||
/// Some("e5a01fee14e0ed5c48714f22180f25ad8365b53f9779f79dc4a3d7e93963f94a".to_string())
|
||||
/// );
|
||||
///
|
||||
/// // Rollback to the previous state
|
||||
/// merkle_tree.rollback();
|
||||
/// assert_eq!(
|
||||
/// merkle_tree.root_hex(),
|
||||
/// Some("ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb".to_string())
|
||||
/// );
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn rollback(&mut self) {
|
||||
// Remove the most recent commit
|
||||
self.history.pop();
|
||||
@@ -284,14 +363,189 @@ impl<T: Hasher> MerkleTree<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the root of the uncommitted changes as if they were committed.
|
||||
/// Will return the same hash as [`MerkleTree::root`] after [`MerkleTree::commit`]
|
||||
///
|
||||
/// For examples, please check [`MerkleTree::uncommitted_root_hex`]
|
||||
pub fn uncommitted_root(&self) -> Option<T::Hash> {
|
||||
let shadow_tree = self.uncommitted_diff()?;
|
||||
shadow_tree.root().cloned()
|
||||
}
|
||||
|
||||
/// Calculates the root of the uncommitted changes as if they were committed. Serializes
|
||||
/// the result as a hex string.
|
||||
/// Will return the same hash as [`MerkleTree::root_hex`] after [`MerkleTree::commit`]
|
||||
///
|
||||
/// ### Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use rs_merkle::{MerkleTree, MerkleProof, algorithms::Sha256, Hasher, Error, utils};
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let mut merkle_tree = MerkleTree::<Sha256>::new();
|
||||
/// let mut leaves = vec![
|
||||
/// Sha256::hash("a".as_bytes()),
|
||||
/// Sha256::hash("b".as_bytes()),
|
||||
/// ];
|
||||
/// merkle_tree.append(&mut leaves);
|
||||
/// assert_eq!(
|
||||
/// merkle_tree.root_hex(),
|
||||
/// None
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// merkle_tree.uncommitted_root_hex(),
|
||||
/// Some("e5a01fee14e0ed5c48714f22180f25ad8365b53f9779f79dc4a3d7e93963f94a".to_string())
|
||||
/// );
|
||||
///
|
||||
/// merkle_tree.commit();
|
||||
/// assert_eq!(
|
||||
/// merkle_tree.root_hex(),
|
||||
/// Some("e5a01fee14e0ed5c48714f22180f25ad8365b53f9779f79dc4a3d7e93963f94a".to_string())
|
||||
/// );
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn uncommitted_root_hex(&self) -> Option<String> {
|
||||
let root = self.uncommitted_root()?;
|
||||
Some(utils::collections::to_hex_string(&root))
|
||||
}
|
||||
|
||||
/// Clears all uncommitted changes made by [`MerkleTree::insert`] and [`MerkleTree::append`]
|
||||
/// operations without applying them to the tree.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use rs_merkle::{MerkleTree, MerkleProof, algorithms::Sha256, Hasher, Error, utils};
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let mut merkle_tree = MerkleTree::<Sha256>::new();
|
||||
/// let mut leaves = vec![
|
||||
/// Sha256::hash("a".as_bytes()),
|
||||
/// Sha256::hash("b".as_bytes()),
|
||||
/// ];
|
||||
/// assert_eq!(
|
||||
/// merkle_tree.root(),
|
||||
/// None
|
||||
/// );
|
||||
///
|
||||
/// merkle_tree.append(&mut leaves);
|
||||
/// merkle_tree.abort_uncommitted();
|
||||
/// merkle_tree.commit();
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// merkle_tree.root(),
|
||||
/// None
|
||||
/// );
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn abort_uncommitted(&mut self) {
|
||||
self.uncommitted_leaves.clear()
|
||||
}
|
||||
|
||||
/// Returns the tree depth. A tree depth is how many layers there is between the
|
||||
/// leaves and the root
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use rs_merkle::{MerkleTree, MerkleProof, algorithms::Sha256, Hasher, Error, utils};
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let leaves = [
|
||||
/// Sha256::hash("a".as_bytes()),
|
||||
/// Sha256::hash("b".as_bytes()),
|
||||
/// Sha256::hash("c".as_bytes()),
|
||||
/// ];
|
||||
///
|
||||
/// let merkle_tree = MerkleTree::<Sha256>::from_leaves(&leaves);
|
||||
/// assert_eq!(merkle_tree.depth(), 2);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn depth(&self) -> usize {
|
||||
self.layer_tuples().len() - 1
|
||||
}
|
||||
|
||||
/// Returns a copy of the tree leaves - the base level of the tree.
|
||||
///
|
||||
/// ### Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use rs_merkle::{MerkleTree, MerkleProof, algorithms::Sha256, Hasher, Error, utils};
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let leaves = [
|
||||
/// Sha256::hash("a".as_bytes()),
|
||||
/// Sha256::hash("b".as_bytes()),
|
||||
/// Sha256::hash("c".as_bytes()),
|
||||
/// ];
|
||||
///
|
||||
/// let merkle_tree = MerkleTree::<Sha256>::from_leaves(&leaves);
|
||||
/// assert_eq!(merkle_tree.leaves(), Some(leaves.to_vec()));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn leaves(&self) -> Option<Vec<T::Hash>> {
|
||||
Some(self.layers().first()?.to_vec())
|
||||
}
|
||||
|
||||
/// Returns the number of leaves in the tree.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use rs_merkle::{MerkleTree, MerkleProof, algorithms::Sha256, Hasher, Error, utils};
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let leaves = [
|
||||
/// Sha256::hash("a".as_bytes()),
|
||||
/// Sha256::hash("b".as_bytes()),
|
||||
/// Sha256::hash("c".as_bytes()),
|
||||
/// ];
|
||||
///
|
||||
/// let merkle_tree = MerkleTree::<Sha256>::from_leaves(&leaves);
|
||||
/// assert_eq!(merkle_tree.leaves_len(), 3);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn leaves_len(&self) -> usize {
|
||||
if let Some(leaves) = self.leaves_tuples() {
|
||||
return leaves.len();
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
fn leaves_tuples(&self) -> Option<&[(usize, T::Hash)]> {
|
||||
Some(self.layer_tuples().first()?.as_slice())
|
||||
}
|
||||
|
||||
/// Returns the whole tree, where the first layer is leaves and
|
||||
/// consequent layers are nodes.
|
||||
fn layers(&self) -> Vec<Vec<T::Hash>> {
|
||||
self.current_working_tree.layer_nodes()
|
||||
}
|
||||
|
||||
fn layer_tuples(&self) -> &[Vec<(usize, T::Hash)>] {
|
||||
self.current_working_tree.layers()
|
||||
}
|
||||
|
||||
/// Creates a diff from a changes that weren't committed to the main tree yet. Can be used
|
||||
/// to get uncommitted root or can be merged with the main tree
|
||||
fn uncommitted_diff(&self) -> Option<PartialTree<T>> {
|
||||
if self.uncommitted_leaves.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let committed_leaves_count = self.leaves_len();
|
||||
|
||||
let shadow_indices: Vec<usize> = self
|
||||
.uncommitted_leaves
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, _)| index)
|
||||
.map(|(index, _)| committed_leaves_count + index)
|
||||
.collect();
|
||||
// Tuples (index, hash) needed to construct a partial tree, since partial tree can't
|
||||
// maintain indices otherwise
|
||||
@@ -303,10 +557,7 @@ impl<T: Hasher> MerkleTree<T> {
|
||||
let mut partial_tree_tuples = self.helper_node_tuples(&shadow_indices);
|
||||
|
||||
// Figuring what tree height would be if we've committed the changes
|
||||
let mut leaves_in_new_tree = self.uncommitted_leaves.len();
|
||||
if let Some(committed_leaves) = self.leaves() {
|
||||
leaves_in_new_tree += committed_leaves.len();
|
||||
}
|
||||
let leaves_in_new_tree = self.leaves_len() + self.uncommitted_leaves.len();
|
||||
let uncommitted_tree_depth = utils::indices::tree_depth(leaves_in_new_tree);
|
||||
|
||||
match partial_tree_tuples.first_mut() {
|
||||
|
||||
@@ -5,7 +5,13 @@ type PartialTreeLayer<H> = Vec<(usize, H)>;
|
||||
|
||||
/// Partial tree represents a part of the original tree that is enough to calculate the root.
|
||||
/// Used in to extract the root in a merkle proof, to apply diff to a tree or to merge
|
||||
/// multiple trees into one
|
||||
/// multiple trees into one.
|
||||
///
|
||||
/// It is a rare case when you need to use this struct on it's own. It's mostly used inside
|
||||
/// [`MerkleTree`] and [`MerkleProof`]
|
||||
///
|
||||
/// [`MerkleTree`]: crate::MerkleTree
|
||||
/// [`MerkleProof`]: crate::MerkleProof
|
||||
#[derive(Clone)]
|
||||
pub struct PartialTree<T: Hasher> {
|
||||
layers: Vec<Vec<(usize, T::Hash)>>,
|
||||
|
||||
@@ -17,7 +17,10 @@ fn combine<T: Clone>(active: Vec<T>, rest: Vec<T>, mut combinations: Vec<Vec<T>>
|
||||
}
|
||||
} else {
|
||||
let mut next = active.clone();
|
||||
next.push(rest.get(0).unwrap().clone());
|
||||
|
||||
if let Some(first) = rest.get(0) {
|
||||
next.push(first.clone());
|
||||
}
|
||||
|
||||
combinations = combine(next, rest.clone().drain(1..).collect(), combinations);
|
||||
combinations = combine(active, rest.clone().drain(1..).collect(), combinations);
|
||||
@@ -35,7 +38,7 @@ pub fn setup() -> TestData {
|
||||
let expected_root_hex = "1f7379539707bcaea00564168d1d4d626b09b73f8a2a365234c62d763f854da2";
|
||||
let leaf_hashes = leaf_values
|
||||
.iter()
|
||||
.map(|x| Sha256::hash(x.as_bytes().to_vec().as_ref()))
|
||||
.map(|x| Sha256::hash(x.as_bytes()))
|
||||
.collect();
|
||||
|
||||
TestData {
|
||||
@@ -80,32 +83,24 @@ pub fn setup_proof_test_cases() -> Vec<ProofTestCases> {
|
||||
|
||||
let leaves: Vec<[u8; 32]> = tree_elements
|
||||
.iter()
|
||||
.map(|x| Sha256::hash(x.as_bytes().to_vec().as_ref()))
|
||||
.map(|x| Sha256::hash(x.as_bytes()))
|
||||
.collect();
|
||||
|
||||
let merkle_tree = MerkleTree::<Sha256>::from_leaves(&leaves);
|
||||
let tuples: Vec<(usize, [u8; 32])> = leaves.iter().cloned().enumerate().collect();
|
||||
|
||||
let indices = tree_elements
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, _)| index)
|
||||
.collect();
|
||||
let possible_proof_elements_combinations = combinations(tuples);
|
||||
|
||||
let possible_proof_index_combinations = combinations(indices);
|
||||
|
||||
let cases: Vec<MerkleProofTestCase> = possible_proof_index_combinations
|
||||
let cases: Vec<MerkleProofTestCase> = possible_proof_elements_combinations
|
||||
.par_iter()
|
||||
.cloned()
|
||||
.map(|index_combination| {
|
||||
MerkleProofTestCase::new(
|
||||
index_combination
|
||||
.iter()
|
||||
.map(|index| leaves.get(*index).unwrap().clone())
|
||||
.collect(),
|
||||
index_combination,
|
||||
)
|
||||
.map(|proof_elements| {
|
||||
let (indices, leaves2): (Vec<usize>, Vec<[u8; 32]>) =
|
||||
proof_elements.iter().cloned().unzip();
|
||||
MerkleProofTestCase::new(leaves2, indices)
|
||||
})
|
||||
.collect();
|
||||
let merkle_tree = MerkleTree::<Sha256>::from_leaves(&leaves);
|
||||
|
||||
let case = ProofTestCases { merkle_tree, cases };
|
||||
case
|
||||
})
|
||||
|
||||
@@ -3,32 +3,30 @@ mod common;
|
||||
pub mod root {
|
||||
use crate::common;
|
||||
use rayon::prelude::*;
|
||||
use rs_merkle::{algorithms::Sha256, Hasher, MerkleTree};
|
||||
use rs_merkle::{algorithms::Sha256, Error, Hasher, MerkleTree};
|
||||
use std::time::Instant;
|
||||
|
||||
#[test]
|
||||
pub fn should_return_a_correct_root() {
|
||||
pub fn should_return_a_correct_root() -> Result<(), Error> {
|
||||
let test_data = common::setup();
|
||||
let expected_root = test_data.expected_root_hex.clone();
|
||||
let leaf_hashes = &test_data.leaf_hashes;
|
||||
let indices_to_prove = vec![3, 4];
|
||||
|
||||
let leaves_to_prove: Vec<[u8; 32]> = indices_to_prove
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|i| leaf_hashes.get(i).unwrap().clone())
|
||||
.map(|i| leaf_hashes.get(*i).unwrap().clone())
|
||||
.collect();
|
||||
|
||||
let merkle_tree = MerkleTree::<Sha256>::from_leaves(&test_data.leaf_hashes);
|
||||
let proof = merkle_tree.proof(&indices_to_prove);
|
||||
let extracted_root = proof
|
||||
.root_hex(
|
||||
&indices_to_prove,
|
||||
&leaves_to_prove,
|
||||
test_data.leaf_values.len(),
|
||||
)
|
||||
.unwrap();
|
||||
let extracted_root = proof.root_hex(
|
||||
&indices_to_prove,
|
||||
&leaves_to_prove,
|
||||
test_data.leaf_values.len(),
|
||||
)?;
|
||||
|
||||
assert_eq!(extracted_root, expected_root);
|
||||
assert_eq!(extracted_root, expected_root.to_string());
|
||||
|
||||
let test_preparation_started = Instant::now();
|
||||
let test_cases = common::setup_proof_test_cases();
|
||||
@@ -60,26 +58,27 @@ pub mod root {
|
||||
let test_run_started = Instant::now();
|
||||
test_cases.par_iter().for_each(|test_case| {
|
||||
let merkle_tree = &test_case.merkle_tree;
|
||||
let root = merkle_tree.root().unwrap().clone();
|
||||
let root = merkle_tree.root();
|
||||
|
||||
test_case.cases.par_iter().for_each(|case| {
|
||||
let proof = merkle_tree.proof(&case.leaf_indices_to_prove);
|
||||
let extracted_root = proof
|
||||
.root(
|
||||
&case.leaf_indices_to_prove,
|
||||
&case.leaf_hashes_to_prove,
|
||||
merkle_tree.leaves().unwrap().len(),
|
||||
)
|
||||
.unwrap();
|
||||
let extracted_root = proof.root(
|
||||
&case.leaf_indices_to_prove,
|
||||
&case.leaf_hashes_to_prove,
|
||||
merkle_tree.leaves_len(),
|
||||
);
|
||||
|
||||
assert_eq!(extracted_root, root)
|
||||
assert_eq!(extracted_root.ok(), root)
|
||||
});
|
||||
});
|
||||
|
||||
println!(
|
||||
"{} test cases executed in {:.2}s",
|
||||
test_cases_count,
|
||||
test_run_started.elapsed().as_secs_f32()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,10 +110,10 @@ pub mod to_bytes {
|
||||
|
||||
pub mod from_bytes {
|
||||
use crate::common;
|
||||
use rs_merkle::{algorithms::Sha256, MerkleProof};
|
||||
use rs_merkle::{algorithms::Sha256, Error, MerkleProof};
|
||||
|
||||
#[test]
|
||||
pub fn should_return_result_with_proof() {
|
||||
pub fn should_return_result_with_proof() -> Result<(), Error> {
|
||||
let expected_proof_hashes = [
|
||||
"2e7d2c03a9507ae265ecf5b5356885a53393a2029d241394997265a1a25aefc6",
|
||||
"252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111",
|
||||
@@ -130,10 +129,12 @@ pub mod from_bytes {
|
||||
249, 74,
|
||||
];
|
||||
|
||||
let proof = MerkleProof::<Sha256>::from_bytes(&bytes).unwrap();
|
||||
let proof = MerkleProof::<Sha256>::from_bytes(&bytes)?;
|
||||
let hex_hashes = proof.proof_hashes_hex();
|
||||
|
||||
assert_eq!(hex_hashes, expected_proof_hashes);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -9,9 +9,11 @@ pub mod root {
|
||||
let test_data = common::setup();
|
||||
|
||||
let merkle_tree = MerkleTree::<Sha256>::from_leaves(&test_data.leaf_hashes);
|
||||
let hex_root = merkle_tree.root_hex().unwrap();
|
||||
|
||||
assert_eq!(hex_root, test_data.expected_root_hex);
|
||||
assert_eq!(
|
||||
merkle_tree.root_hex(),
|
||||
Some(test_data.expected_root_hex.to_string())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,15 +56,13 @@ pub mod proof {
|
||||
|
||||
pub mod commit {
|
||||
use crate::common;
|
||||
use rs_merkle::{algorithms::Sha256, Hasher, MerkleTree};
|
||||
use rs_merkle::{algorithms::Sha256, Error, Hasher, MerkleTree};
|
||||
|
||||
#[test]
|
||||
pub fn should_give_correct_root_after_commit() {
|
||||
let test_data = common::setup();
|
||||
let expected_root = test_data.expected_root_hex.clone();
|
||||
let leaf_hashes = &test_data.leaf_hashes;
|
||||
// let indices_to_prove = vec![3, 4];
|
||||
// let leaves_to_prove = indices_to_prove.iter().cloned().map(|i| leaf_hashes.get(i).unwrap().clone()).collect();
|
||||
let vec = Vec::<[u8; 32]>::new();
|
||||
|
||||
// Passing empty vec to create an empty tree
|
||||
@@ -70,18 +70,18 @@ pub mod commit {
|
||||
let merkle_tree2 = MerkleTree::<Sha256>::from_leaves(&leaf_hashes);
|
||||
// Adding leaves
|
||||
merkle_tree.append(leaf_hashes.clone().as_mut());
|
||||
let root = merkle_tree.uncommitted_root_hex().unwrap();
|
||||
let root = merkle_tree.uncommitted_root_hex();
|
||||
|
||||
assert_eq!(merkle_tree2.root_hex().unwrap(), expected_root);
|
||||
assert_eq!(root, expected_root);
|
||||
assert_eq!(merkle_tree2.root_hex(), Some(expected_root.to_string()));
|
||||
assert_eq!(root, Some(expected_root.to_string()));
|
||||
|
||||
let expected_root = "e2a80e0e872a6c6eaed37b4c1f220e1935004805585b5f99617e48e9c8fe4034";
|
||||
let leaf = Sha256::hash("g".as_bytes().to_vec().as_ref());
|
||||
let leaf = Sha256::hash("g".as_bytes());
|
||||
merkle_tree.insert(leaf);
|
||||
|
||||
assert_eq!(
|
||||
merkle_tree.uncommitted_root_hex().unwrap(),
|
||||
String::from(expected_root)
|
||||
merkle_tree.uncommitted_root_hex(),
|
||||
Some(expected_root.to_string())
|
||||
);
|
||||
|
||||
// No changes were committed just yet, tree is empty
|
||||
@@ -89,31 +89,99 @@ pub mod commit {
|
||||
|
||||
merkle_tree.commit();
|
||||
|
||||
let mut new_leaves = vec![
|
||||
Sha256::hash("h".as_bytes().to_vec().as_ref()),
|
||||
Sha256::hash("k".as_bytes().to_vec().as_ref()),
|
||||
];
|
||||
let mut new_leaves = vec![Sha256::hash("h".as_bytes()), Sha256::hash("k".as_bytes())];
|
||||
merkle_tree.append(&mut new_leaves);
|
||||
|
||||
assert_eq!(
|
||||
merkle_tree.root_hex().unwrap(),
|
||||
String::from("e2a80e0e872a6c6eaed37b4c1f220e1935004805585b5f99617e48e9c8fe4034")
|
||||
merkle_tree.root_hex(),
|
||||
Some("e2a80e0e872a6c6eaed37b4c1f220e1935004805585b5f99617e48e9c8fe4034".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
merkle_tree.uncommitted_root_hex().unwrap(),
|
||||
String::from("09b6890b23e32e607f0e5f670ab224e36af8f6599cbe88b468f4b0f761802dd6")
|
||||
merkle_tree.uncommitted_root_hex(),
|
||||
Some("09b6890b23e32e607f0e5f670ab224e36af8f6599cbe88b468f4b0f761802dd6".to_string())
|
||||
);
|
||||
|
||||
merkle_tree.commit();
|
||||
let leaves = merkle_tree.leaves().unwrap();
|
||||
let leaves = merkle_tree
|
||||
.leaves()
|
||||
.expect("expect the tree to have some leaves");
|
||||
let reconstructed_tree = MerkleTree::<Sha256>::from_leaves(&leaves);
|
||||
|
||||
// Check that the commit is applied correctly
|
||||
assert_eq!(
|
||||
reconstructed_tree.root_hex().unwrap(),
|
||||
String::from("09b6890b23e32e607f0e5f670ab224e36af8f6599cbe88b468f4b0f761802dd6")
|
||||
reconstructed_tree.root_hex(),
|
||||
Some("09b6890b23e32e607f0e5f670ab224e36af8f6599cbe88b468f4b0f761802dd6".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn should_not_change_the_result_when_called_twice() {
|
||||
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 commit, and since there's no new staged changes
|
||||
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()));
|
||||
assert_eq!(
|
||||
merkle_tree.uncommitted_root_hex(),
|
||||
Some("e2a80e0e872a6c6eaed37b4c1f220e1935004805585b5f99617e48e9c8fe4034".to_string())
|
||||
);
|
||||
merkle_tree.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());
|
||||
merkle_tree.commit();
|
||||
merkle_tree.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())
|
||||
);
|
||||
assert_eq!(reconstructed_tree.layers(), merkle_tree.layers());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,12 +189,12 @@ pub mod rollback {
|
||||
use crate::common;
|
||||
use rs_merkle::{algorithms::Sha256, Hasher, MerkleTree};
|
||||
|
||||
#[test]
|
||||
pub fn should_rollback_previous_commit() {
|
||||
let leaf_values = ["a", "b", "c", "d", "e", "f"];
|
||||
let expected_root_hex = "1f7379539707bcaea00564168d1d4d626b09b73f8a2a365234c62d763f854da2";
|
||||
let leaves: Vec<[u8; 32]> = leaf_values
|
||||
.iter()
|
||||
.map(|x| Sha256::hash(x.as_bytes().to_vec().as_ref()))
|
||||
.map(|x| Sha256::hash(x.as_bytes()))
|
||||
.collect();
|
||||
|
||||
let mut merkle_tree: MerkleTree<Sha256> = MerkleTree::new();
|
||||
@@ -137,65 +205,63 @@ pub mod rollback {
|
||||
merkle_tree.commit();
|
||||
|
||||
assert_eq!(
|
||||
merkle_tree.uncommitted_root_hex().unwrap(),
|
||||
String::from("1f7379539707bcaea00564168d1d4d626b09b73f8a2a365234c62d763f854da2")
|
||||
merkle_tree.root_hex(),
|
||||
Some("1f7379539707bcaea00564168d1d4d626b09b73f8a2a365234c62d763f854da2".to_string())
|
||||
);
|
||||
|
||||
// Adding a new leaf
|
||||
merkle_tree.insert(Sha256::hash("g".as_bytes().to_vec().as_ref()));
|
||||
merkle_tree.insert(Sha256::hash("g".as_bytes()));
|
||||
|
||||
// Uncommitted root must reflect the insert
|
||||
assert_eq!(
|
||||
merkle_tree.uncommitted_root_hex().unwrap(),
|
||||
String::from("e2a80e0e872a6c6eaed37b4c1f220e1935004805585b5f99617e48e9c8fe4034")
|
||||
merkle_tree.uncommitted_root_hex(),
|
||||
Some("e2a80e0e872a6c6eaed37b4c1f220e1935004805585b5f99617e48e9c8fe4034".to_string())
|
||||
);
|
||||
|
||||
merkle_tree.commit();
|
||||
|
||||
// After calling commit, uncommitted root will become committed
|
||||
assert_eq!(
|
||||
merkle_tree.root_hex().unwrap(),
|
||||
String::from("e2a80e0e872a6c6eaed37b4c1f220e1935004805585b5f99617e48e9c8fe4034")
|
||||
merkle_tree.root_hex(),
|
||||
Some("e2a80e0e872a6c6eaed37b4c1f220e1935004805585b5f99617e48e9c8fe4034".to_string())
|
||||
);
|
||||
|
||||
// Adding some more leaves
|
||||
merkle_tree.append(
|
||||
vec![
|
||||
Sha256::hash("h".as_bytes().to_vec().as_ref()),
|
||||
Sha256::hash("k".as_bytes().to_vec().as_ref()),
|
||||
]
|
||||
.as_mut(),
|
||||
);
|
||||
merkle_tree
|
||||
.append(vec![Sha256::hash("h".as_bytes()), Sha256::hash("k".as_bytes())].as_mut());
|
||||
|
||||
// Checking that the uncommitted root has changed, but the committed one hasn't
|
||||
assert_eq!(
|
||||
merkle_tree.uncommitted_root_hex().unwrap(),
|
||||
String::from("09b6890b23e32e607f0e5f670ab224e36af8f6599cbe88b468f4b0f761802dd6")
|
||||
merkle_tree.uncommitted_root_hex(),
|
||||
Some("09b6890b23e32e607f0e5f670ab224e36af8f6599cbe88b468f4b0f761802dd6".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
merkle_tree.root_hex().unwrap(),
|
||||
String::from("e2a80e0e872a6c6eaed37b4c1f220e1935004805585b5f99617e48e9c8fe4034")
|
||||
merkle_tree.root_hex(),
|
||||
Some("e2a80e0e872a6c6eaed37b4c1f220e1935004805585b5f99617e48e9c8fe4034".to_string())
|
||||
);
|
||||
|
||||
merkle_tree.commit();
|
||||
|
||||
// Checking committed changes again
|
||||
assert_eq!(
|
||||
merkle_tree.root_hex().unwrap(),
|
||||
String::from("09b6890b23e32e607f0e5f670ab224e36af8f6599cbe88b468f4b0f761802dd6")
|
||||
merkle_tree.root_hex(),
|
||||
Some("09b6890b23e32e607f0e5f670ab224e36af8f6599cbe88b468f4b0f761802dd6".to_string())
|
||||
);
|
||||
|
||||
merkle_tree.rollback();
|
||||
|
||||
// Check that we rolled one commit back
|
||||
assert_eq!(
|
||||
merkle_tree.root_hex().unwrap(),
|
||||
String::from("e2a80e0e872a6c6eaed37b4c1f220e1935004805585b5f99617e48e9c8fe4034")
|
||||
merkle_tree.root_hex(),
|
||||
Some("e2a80e0e872a6c6eaed37b4c1f220e1935004805585b5f99617e48e9c8fe4034".to_string())
|
||||
);
|
||||
|
||||
merkle_tree.rollback();
|
||||
|
||||
// Rolling back to the state after the very first commit
|
||||
assert_eq!(merkle_tree.root_hex().unwrap(), expected_root_hex);
|
||||
assert_eq!(
|
||||
merkle_tree.root_hex(),
|
||||
Some("1f7379539707bcaea00564168d1d4d626b09b73f8a2a365234c62d763f854da2".to_string())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user