diff --git a/Cargo.toml b/Cargo.toml index 3a27f6a..ce92e8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "rs_merkle" version = "0.2.2" authors = ["Anton Suprunchuk "] -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" diff --git a/README.md b/README.md index b582800..af37e19 100644 --- a/README.md +++ b/README.md @@ -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/). diff --git a/src/algorithms/mod.rs b/src/algorithms/mod.rs index 97bc05f..58055a2 100644 --- a/src/algorithms/mod.rs +++ b/src/algorithms/mod.rs @@ -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; diff --git a/src/algorithms/sha256.rs b/src/algorithms/sha256.rs index 4eb59ad..2b634d0 100644 --- a/src/algorithms/sha256.rs +++ b/src/algorithms/sha256.rs @@ -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> { +/// let tree = MerkleTree::::new(); +/// let other_tree: MerkleTree = MerkleTree::new(); +/// +/// let proof_bytes: Vec = 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::::from_bytes(&proof_bytes); +/// # Ok(()) +/// # } +/// ``` +/// +/// [`Hasher`]: crate::Hasher #[derive(Clone)] pub struct Sha256Algorithm {} diff --git a/src/error.rs b/src/error.rs index e440256..8b59ef4 100644 --- a/src/error.rs +++ b/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 } diff --git a/src/hasher.rs b/src/hasher.rs index 979c18a..9e4f1e5 100644 --- a/src/hasher.rs +++ b/src/hasher.rs @@ -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>` is required to parse hashes from a serialized proof type Hash: Copy + PartialEq + Into> + TryFrom>; - /// 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 = (*left).into(); diff --git a/src/lib.rs b/src/lib.rs index 3ba6424..53d4781 100644 --- a/src/lib.rs +++ b/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> { +//! 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::::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::::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> { +//! 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 = 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; diff --git a/src/merkle_proof.rs b/src/merkle_proof.rs index b31fc95..649ca06 100644 --- a/src/merkle_proof.rs +++ b/src/merkle_proof.rs @@ -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> { /// 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::::new(proof_hashes_copy); +/// assert_eq!(proof.proof_hashes(), &proof_hashes); +/// # Ok(()) +/// # } +/// ``` /// -/// let proof = MerkleProof::::new(proof_hashes); -///``` +/// [`Hasher`]: crate::Hasher +/// [`algorithms::Sha256`]: crate::algorithms::Sha256 pub struct MerkleProof { proof_hashes: Vec, } @@ -31,35 +52,117 @@ impl MerkleProof { 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 = 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::::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::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 - } - - /// 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 { - self.proof_hashes - .iter() - .map(utils::collections::to_hex_string) - .collect() + /// 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> { + /// let leaves = [ + /// Sha256::hash("a".as_bytes()), + /// Sha256::hash("b".as_bytes()), + /// Sha256::hash("c".as_bytes()), + /// ]; + /// + /// let merkle_tree = MerkleTree::::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, + } } - /// 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> { + /// let leaves = [ + /// Sha256::hash("a".as_bytes()), + /// Sha256::hash("b".as_bytes()), + /// Sha256::hash("c".as_bytes()), + /// ]; + /// + /// let merkle_tree = MerkleTree::::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 { + 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 MerkleProof { } } - /// 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> { + /// let leaves = [ + /// Sha256::hash("a".as_bytes()), + /// Sha256::hash("b".as_bytes()), + /// Sha256::hash("c".as_bytes()), + /// ]; + /// + /// let merkle_tree = MerkleTree::::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 MerkleProof { 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> { + /// 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::::new(proof_hashes_copy); + /// assert_eq!(proof.proof_hashes(), &proof_hashes); + /// # Ok(()) + /// # } + /// ``` + pub fn proof_hashes(&self) -> &[T::Hash] { + &self.proof_hashes + } + + /// 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> { + /// let proof_bytes: Vec = 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::::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 { + self.proof_hashes + .iter() + .map(utils::collections::to_hex_string) + .collect() } - /// Serializes proof hashes to a flat vector of bytes + /// 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> { + /// 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::::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 { - let vectors: Vec> = self + let mut vectors: Vec> = self .proof_hashes() .iter() .cloned() .map(|hash| hash.into()) .collect(); - vectors.iter().cloned().flatten().collect() + vectors.drain(..).flatten().collect() } } @@ -140,7 +371,8 @@ impl TryFrom> for MerkleProof { /// 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 TryFrom<&[u8]> for MerkleProof { /// Parses proof serialized to a collection of bytes /// - /// # Example + /// ## Examples + /// /// ``` /// use std::convert::TryFrom; /// use rs_merkle::{MerkleProof, algorithms::Sha256}; diff --git a/src/merkle_tree.rs b/src/merkle_tree.rs index f798e86..56d1ae3 100644 --- a/src/merkle_tree.rs +++ b/src/merkle_tree.rs @@ -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::::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 = 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 { current_working_tree: PartialTree, @@ -105,7 +24,7 @@ impl Default for MerkleTree { } impl MerkleTree { - /// 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 MerkleTree { } } - /// Returns Merkle tree root - pub fn root(&self) -> Option { - 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> { + /// let leaves = [ + /// Sha256::hash("a".as_bytes()), + /// Sha256::hash("b".as_bytes()), + /// Sha256::hash("c".as_bytes()), + /// ]; + /// + /// let merkle_tree = MerkleTree::::from_leaves(&leaves); + /// # Ok(()) + /// # } pub fn from_leaves(leaves: &[T::Hash]) -> Self { let mut tree = Self::new(); @@ -139,20 +69,68 @@ impl MerkleTree { 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> { + /// let leaves = [ + /// Sha256::hash("a".as_bytes()), + /// Sha256::hash("b".as_bytes()), + /// Sha256::hash("c".as_bytes()), + /// ]; + /// + /// let merkle_tree = MerkleTree::::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 { + 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> { + /// let leaves = [ + /// Sha256::hash("a".as_bytes()), + /// Sha256::hash("b".as_bytes()), + /// Sha256::hash("c".as_bytes()), + /// ]; + /// + /// let merkle_tree = MerkleTree::::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 { 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 { let mut helper_nodes = Vec::::new(); @@ -171,20 +149,15 @@ impl MerkleTree { let mut current_layer_indices = leaf_indices.to_vec(); let mut helper_nodes: Vec> = 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,103 +168,384 @@ impl MerkleTree { 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> { + /// let leaves: Vec<[u8; 32]> = ["a", "b", "c", "d", "e", "f"] + /// .iter() + /// .map(|x| Sha256::hash(x.as_bytes())) + /// .collect(); + /// + /// let merkle_tree = MerkleTree::::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::::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 { MerkleProof::::new(self.helper_nodes(leaf_indices)) } - /// Returns tree leaves, i.e. the bottom level - pub fn leaves(&self) -> Option> { - self.layers().first().cloned() - } - - /// Returns the whole tree, where the first layer is leaves and - /// consequent layers are nodes. - pub fn layers(&self) -> Vec> { - self.current_working_tree.layer_nodes() + /// Inserts a new leaf. Please note it won't modify the root just yet; For the changes + /// 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`] + /// + /// # 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> { + /// let mut merkle_tree = MerkleTree::::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> { + /// let mut merkle_tree = MerkleTree::::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 } - /// Same as [`layers`](MerkleTree::layers), but serializes each hash as a hex string - pub fn layers_hex(&self) -> Vec> { - self.layers() - .iter() - .map(|layer| { - layer - .iter() - .map(utils::collections::to_hex_string) - .collect() - }) - .collect() + /// 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`], + /// 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> { + /// let mut merkle_tree = MerkleTree::::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) -> &mut Self { + self.uncommitted_leaves.append(leaves); + self } - /// 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) + /// Commits the changes made by [`MerkleTree::insert`] and [`MerkleTree::append`] + /// and modifies the root. + /// 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> { + /// let mut merkle_tree = MerkleTree::::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 + /// ); /// - /// # Example - /// // TODO - pub fn insert(&mut self, leaf: T::Hash) { - self.uncommitted_leaves.push(leaf) + /// 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(); + } } - /// 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`], similarly to [`append`](std::vec::Vec::append) of a [`std::vec::Vec`] - pub fn append(&mut self, leaves: &mut Vec) { - self.uncommitted_leaves.append(leaves) + /// 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> { + /// let mut merkle_tree = MerkleTree::::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(); + // Clear working tree + self.current_working_tree.clear(); + // Applying all the commits up to the removed one. This is not an + // efficient way of doing things, but the diff subtraction is not implemented yet on + // PartialMerkleTree + for commit in &self.history { + self.current_working_tree.merge_unverified(commit.clone()); + } } /// 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) + /// 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 { let shadow_tree = self.uncommitted_diff()?; shadow_tree.root().cloned() } - /// Same as `uncommitted_root`, but serialized to a hex string + /// 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> { + /// let mut merkle_tree = MerkleTree::::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 { let root = self.uncommitted_root()?; Some(utils::collections::to_hex_string(&root)) } - /// Commits changes made by [`insert`](MerkleTree::insert) and [`append`](MerkleTree::append) - /// and modifies the root. - /// Commits changes to the history, so the tree can be rolled back to any previous commit. - pub fn commit(&mut self) { - if let Some(diff) = self.uncommitted_diff() { - self.history.push(diff.clone()); - self.current_working_tree.merge_unverified(diff); - } - } - - /// Aborts all uncommitted [`insert`](MerkleTree::insert) and [`append`](MerkleTree::append) + /// 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> { + /// let mut merkle_tree = MerkleTree::::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() } - /// Rolls back one commit and reverts tree to the previous state. - /// Removes the latest commit from the changes history - pub fn rollback(&mut self) { - // Remove the most recent commit - self.history.pop(); - // Clear working tree - self.current_working_tree.clear(); - // Applying all the commits up to the removed one. This is not an - // efficient way of doing things, but the diff subtraction is not implemented yet on - // PartialMerkleTree - for commit in &self.history { - self.current_working_tree.merge_unverified(commit.clone()); + /// 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> { + /// let leaves = [ + /// Sha256::hash("a".as_bytes()), + /// Sha256::hash("b".as_bytes()), + /// Sha256::hash("c".as_bytes()), + /// ]; + /// + /// let merkle_tree = MerkleTree::::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> { + /// let leaves = [ + /// Sha256::hash("a".as_bytes()), + /// Sha256::hash("b".as_bytes()), + /// Sha256::hash("c".as_bytes()), + /// ]; + /// + /// let merkle_tree = MerkleTree::::from_leaves(&leaves); + /// assert_eq!(merkle_tree.leaves(), Some(leaves.to_vec())); + /// # Ok(()) + /// # } + /// ``` + pub fn leaves(&self) -> Option> { + 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> { + /// let leaves = [ + /// Sha256::hash("a".as_bytes()), + /// Sha256::hash("b".as_bytes()), + /// Sha256::hash("c".as_bytes()), + /// ]; + /// + /// let merkle_tree = MerkleTree::::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> { + 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> { + if self.uncommitted_leaves.is_empty() { + return None; + } + + let committed_leaves_count = self.leaves_len(); + let shadow_indices: Vec = 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 MerkleTree { 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() { diff --git a/src/partial_tree.rs b/src/partial_tree.rs index df63e31..2bb1234 100644 --- a/src/partial_tree.rs +++ b/src/partial_tree.rs @@ -5,7 +5,13 @@ type PartialTreeLayer = 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 { layers: Vec>, diff --git a/tests/common.rs b/tests/common.rs index 1a31fb0..5c3d77c 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -17,7 +17,10 @@ fn combine(active: Vec, rest: Vec, mut combinations: Vec> } } 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 { 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::::from_leaves(&leaves); - - let indices = tree_elements - .iter() - .enumerate() - .map(|(index, _)| index) - .collect(); + let tuples: Vec<(usize, [u8; 32])> = leaves.iter().cloned().enumerate().collect(); - let possible_proof_index_combinations = combinations(indices); + let possible_proof_elements_combinations = combinations(tuples); - let cases: Vec = possible_proof_index_combinations + let cases: Vec = 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, Vec<[u8; 32]>) = + proof_elements.iter().cloned().unzip(); + MerkleProofTestCase::new(leaves2, indices) }) .collect(); + let merkle_tree = MerkleTree::::from_leaves(&leaves); + let case = ProofTestCases { merkle_tree, cases }; case }) diff --git a/tests/merkle_proof_test.rs b/tests/merkle_proof_test.rs index 4350962..d0bb380 100644 --- a/tests/merkle_proof_test.rs +++ b/tests/merkle_proof_test.rs @@ -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::::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(); - - assert_eq!(extracted_root, root) + let extracted_root = proof.root( + &case.leaf_indices_to_prove, + &case.leaf_hashes_to_prove, + merkle_tree.leaves_len(), + ); + + 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::::from_bytes(&bytes).unwrap(); + let proof = MerkleProof::::from_bytes(&bytes)?; let hex_hashes = proof.proof_hashes_hex(); assert_eq!(hex_hashes, expected_proof_hashes); + + Ok(()) } #[test] diff --git a/tests/merkle_tree_test.rs b/tests/merkle_tree_test.rs index f5bfb9e..039c7a5 100644 --- a/tests/merkle_tree_test.rs +++ b/tests/merkle_tree_test.rs @@ -9,9 +9,11 @@ pub mod root { let test_data = common::setup(); let merkle_tree = MerkleTree::::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::::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::::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 = 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 = 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()) + ); } }