feat: add documentation

This commit is contained in:
Anton Suprunchuk
2021-09-26 14:59:06 +03:00
parent bce4a64ae5
commit a6808df65b
13 changed files with 178 additions and 112 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
/target
Cargo.lock
.idea/
doc/

View File

@@ -1,5 +1,5 @@
[package]
name = "rs-merkle"
name = "rs_merkle"
version = "0.1.0"
edition = "2018"

4
src/algorithms/mod.rs Normal file
View File

@@ -0,0 +1,4 @@
//! This module contains built-in implementations of `rs_merkle::Hasher`
mod sha256;
pub use sha256::Sha256Algorithm as Sha256;

17
src/algorithms/sha256.rs Normal file
View File

@@ -0,0 +1,17 @@
use sha2::{Sha256, Digest, digest::FixedOutput};
use crate::Hasher;
/// Sha256 implementation of the `rs_merkle::Hasher` trait
#[derive(Clone)]
pub struct Sha256Algorithm {}
impl Hasher for Sha256Algorithm {
type Hash = [u8; 32];
fn hash(data: &Vec<u8>) -> [u8; 32] {
let mut hasher = Sha256::new();
hasher.update(data);
<[u8; 32]>::from(hasher.finalize_fixed())
}
}

View File

@@ -1,16 +1,57 @@
use std::convert::TryFrom;
use std::mem;
/// Hasher is a trait used to provide a hashing algorithm for the library.
///
/// # Example
///
/// This example shows how to implement sha256 algorithm
///
/// ```
/// use rs_merkle::{Hasher};
/// use sha2::{Sha256, Digest, digest::FixedOutput};
///
/// #[derive(Clone)]
/// pub struct Sha256Algorithm {}
///
/// impl Hasher for Sha256Algorithm {
/// type Hash = [u8; 32];
///
/// fn hash(data: &Vec<u8>) -> [u8; 32] {
/// let mut hasher = Sha256::new();
///
/// hasher.update(data);
/// <[u8; 32]>::from(hasher.finalize_fixed())
/// }
/// }
/// ```
pub trait Hasher {
type Hash: Copy + Into<Vec<u8>> + PartialEq + TryFrom<Vec<u8>>;
/// This type is used as a hash type in the library.
/// It is recommended to use fixed size u8 array as hash. 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`
/// by default satisfy all trait bounds required by this type.
///
/// # Trait bounds
/// `Copy` is required as the hash needs to be copied to be concatenated/propagated
/// when constructing nodes.
/// `PartialEq` is required to compare equality when verifying proof.
/// `Into<Vec<u8>>` is required to be able to serialize proof.
/// `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.
/// Used by `concat_and_hash` function to build a tree from concatenated hashes
fn hash(data: &Vec<u8>) -> Self::Hash;
fn hash_size() -> usize {
mem::size_of::<Self::Hash>()
}
// This is a default solidity implementation
/// Used by `MerkleTree` and `MerkleProof` when calculating the root.
/// The provided default implementation follows propagates left node if it doesn't
/// have a sibling.
///
/// 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.
fn concat_and_hash(left: Option<&Self::Hash>, right: Option<&Self::Hash>) -> Self::Hash {
let mut concatenated: Vec<u8> = left.expect("Left node should always be present, otherwise it's impossible to calculate hash").clone().into();
@@ -23,4 +64,11 @@ pub trait Hasher {
None => left.unwrap().clone()
}
}
/// Returns the byte size of `Self::Hash`. Default implementation returns
/// `mem::size_of::<Self::Hash>()`. Usually doesn't need to be overridden.
/// Used internally by `MerkleProof` to parse hashes from a serialized proof.
fn hash_size() -> usize {
mem::size_of::<Self::Hash>()
}
}

View File

@@ -1,3 +1,11 @@
//! 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 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.
pub use hasher::Hasher;
pub use merkle_proof::MerkleProof;
pub use merkle_tree::MerkleTree;
@@ -6,6 +14,7 @@ mod merkle_tree;
mod merkle_proof;
mod hasher;
pub mod utils;
mod error;
pub mod algorithms;
pub mod error;
mod utils;

View File

@@ -5,42 +5,37 @@ use crate::{Hasher, utils};
pub use crate::error::Error;
pub use crate::error::ErrorKind;
/// `MerkleProof` is used to parse, verify, calculate a root for merkle proofs.
///
/// # 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`
///
/// # Example
///
/// ```
/// use rs_merkle::{MerkleProof, algorithms::Sha256};
/// let proof_hashes: Vec<[u8; 32]> = vec![
///
/// ];
///
/// let proof = MerkleProof::<Sha256>::new(proof_hashes);
///```
pub struct MerkleProof<T: Hasher> {
proof_hashes: Vec<T::Hash>,
_hasher: PhantomData<T>,
}
impl<T: Hasher> MerkleProof<T> {
/// MerkleProof requires specifying hashing algorithm and hash size in order to work.
/// It uses Hasher trait from the crate to do that. An sha256 implementation of Hasher
/// could look like this:
/// ```
/// use rs_merkle::Hasher;
/// use sha2::{Sha256, Digest, digest::FixedOutput};
///
/// #[derive(Clone)]
/// pub struct Sha256Hasher {}
///
/// impl Hasher for Sha256Hasher {
/// // The size of sha256 is 32 bytes
/// type Hash = [u8; 32];
///
/// fn hash(data: &Vec<u8>) -> [u8; 32] {
/// let mut hasher = Sha256::new();
///
/// hasher.update(data);
/// <[u8; 32]>::from(hasher.finalize_fixed())
/// }
/// }
/// ```
pub fn new(proof_hashes: Vec<T::Hash>) -> Self {
MerkleProof {
proof_hashes,
_hasher: PhantomData
}
}
/// Parses proof serialized as bytes
///
pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, Error> {
let hash_size = T::hash_size();
@@ -68,6 +63,7 @@ impl<T: Hasher> MerkleProof<T> {
Ok(Self::new(proof_hashes_slices))
}
/// Returns
pub fn proof_hashes(&self) -> &Vec<T::Hash> {
&self.proof_hashes
}

View File

@@ -1,6 +1,7 @@
use crate::{utils, MerkleProof, Hasher};
use crate::utils::indices::parent_indices;
/// `MerkleTree`
#[derive(Clone)]
pub struct MerkleTree<T: Hasher> {
layers: Vec<Vec<T::Hash>>,
@@ -26,15 +27,18 @@ impl<T: Hasher> MerkleTree<T> {
tree
}
/// Takes leaves (item hashes) as an argument and build a Merkle Tree from them
pub fn new(leaves: &Vec<T::Hash>) -> Self {
let layers = Self::build_tree(leaves);
Self { layers }
}
/// Returns Merkle tree root
pub fn root(&self) -> Option<&T::Hash> {
self.layers.last()?.first()
}
/// Returns Merkle tree root serialized as a hex string
pub fn hex_root(&self) -> Option<String> {
let root = self.root()?;
Some(utils::collections::to_hex_string(root))
@@ -46,20 +50,14 @@ impl<T: Hasher> MerkleTree<T> {
self.layers.len() - 1
}
/// Proof consists of all siblings hashes that aren't in the set we're trying to prove
///
/// # Implementation
///
/// 1. Get all sibling indices. Those are the indices we need to get to the root
/// 2. Filter all nodes that doesn't require an additional hash
/// 3. Get all hashes for indices from step 2
/// 4. Remove empty spaces (the leftmost nodes that do not have anything to the right)
/// Returns merkle proof required to prove inclusion of items at given indices
pub fn proof(&self, leaf_indices: &Vec<usize>) -> MerkleProof<T> {
let mut current_layer_indices = leaf_indices.to_vec();
let mut proof_hashes: Vec<T::Hash> = Vec::new();
let mut proof_hashes = Vec::<T::Hash>::new();
for tree_layer in &self.layers {
let siblings = utils::indices::sibling_indices(&current_layer_indices);
// Filter all nodes that do not require an additional hash to be calculated
let proof_indices = utils::collections::difference(&siblings, &current_layer_indices);
for index in proof_indices {
@@ -67,6 +65,8 @@ impl<T: Hasher> MerkleTree<T> {
Some(hash) => {
proof_hashes.push(hash.clone());
},
// 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,
}
}
@@ -78,14 +78,18 @@ impl<T: Hasher> MerkleTree<T> {
proof
}
/// Returns tree leaves, i.e. the bottom level
pub fn leaves(&self) -> Option<&Vec<T::Hash>> {
self.layers().first()
}
/// Returns the whole tree, where the first layer is leaves and
/// consequent layers are nodes.
pub fn layers(&self) -> &Vec<Vec<T::Hash>> {
&self.layers
}
/// Same as `layers`, but serializes each hash as a hex string
pub fn hex_layers(&self) -> Vec<Vec<String>> {
self.layers()
.iter()

View File

@@ -2,9 +2,8 @@ fn byte_to_hex(byte: &u8) -> String {
format!("{:02x}", byte)
}
/// Serializes bytes into a hex string
pub fn to_hex_string<T: Clone + Into<Vec<u8>>>(bytes: &T) -> String {
// let keks: Vec<u8> = bytes.clone().into();
let hex_vec: Vec<String> = bytes.clone().into()
.iter()
.map(byte_to_hex)
@@ -13,30 +12,9 @@ pub fn to_hex_string<T: Clone + Into<Vec<u8>>>(bytes: &T) -> String {
hex_vec.join("")
}
// IMPORTANT! This function doesn't use HashSet because for the tree
// it is important to maintain original order
pub fn difference(a: &Vec<usize>, b: &Vec<usize>) -> Vec<usize> {
/// Find a difference between two vectors and return a third vector
/// containing the difference. This function preserves the first
/// vector order.
pub fn difference<T: Clone + PartialEq>(a: &Vec<T>, b: &Vec<T>) -> Vec<T> {
a.iter().cloned().filter(|x| !b.contains(x)).collect()
}
fn combine<T: Clone>(active: Vec<T>, rest: Vec<T>, mut combinations: Vec<Vec<T>>) -> Vec<Vec<T>> {
return if rest.is_empty() {
if active.is_empty() {
combinations
} else {
combinations.push(active);
combinations
}
} else {
let mut next = active.clone();
next.push(rest.get(0).unwrap().clone());
combinations = combine(next, rest.clone().drain(1..).collect(), combinations);
combinations = combine(active, rest.clone().drain(1..).collect(), combinations);
combinations
}
}
pub fn combinations<T: Clone>(vec: Vec<T>) -> Vec<Vec<T>> {
combine(Vec::new(), vec, Vec::new())
}

View File

@@ -1,2 +1,3 @@
//! Utilities used internally to manipulate tree indices
pub mod indices;
pub mod collections;

View File

@@ -1,20 +1,5 @@
use rayon::prelude::*;
use rs_merkle::{Hasher, MerkleTree, utils};
use sha2::{Sha256, Digest, digest::FixedOutput};
#[derive(Clone)]
pub struct Sha256Hasher {}
impl Hasher for Sha256Hasher {
type Hash = [u8; 32];
fn hash(data: &Vec<u8>) -> [u8; 32] {
let mut hasher = Sha256::new();
hasher.update(data);
<[u8; 32]>::from(hasher.finalize_fixed())
}
}
use rs_merkle::{Hasher, MerkleTree, algorithms::Sha256};
pub struct TestData {
pub leaf_values: Vec<String>,
@@ -22,12 +7,35 @@ pub struct TestData {
pub leaf_hashes: Vec<[u8; 32]>,
}
fn combine<T: Clone>(active: Vec<T>, rest: Vec<T>, mut combinations: Vec<Vec<T>>) -> Vec<Vec<T>> {
return if rest.is_empty() {
if active.is_empty() {
combinations
} else {
combinations.push(active);
combinations
}
} else {
let mut next = active.clone();
next.push(rest.get(0).unwrap().clone());
combinations = combine(next, rest.clone().drain(1..).collect(), combinations);
combinations = combine(active, rest.clone().drain(1..).collect(), combinations);
combinations
}
}
/// Create all possible combinations of elements inside a vector without duplicates
pub fn combinations<T: Clone>(vec: Vec<T>) -> Vec<Vec<T>> {
combine(Vec::new(), vec, Vec::new())
}
pub fn setup() -> TestData {
let leaf_values = ["a", "b", "c", "d", "e", "f"];
let expected_root_hex = "1f7379539707bcaea00564168d1d4d626b09b73f8a2a365234c62d763f854da2";
let leaf_hashes = leaf_values
.iter()
.map(|x| Sha256Hasher::hash(x.as_bytes().to_vec().as_ref()))
.map(|x| Sha256::hash(x.as_bytes().to_vec().as_ref()))
.collect();
TestData {
@@ -39,7 +47,7 @@ pub fn setup() -> TestData {
#[derive(Clone)]
pub struct ProofTestCases {
pub merkle_tree: MerkleTree<Sha256Hasher>,
pub merkle_tree: MerkleTree<Sha256>,
pub cases: Vec<MerkleProofTestCase>
}
@@ -70,10 +78,10 @@ pub fn setup_proof_test_cases() -> Vec<ProofTestCases> {
let leaves: Vec<[u8; 32]> = tree_elements
.iter()
.map(|x| Sha256Hasher::hash(x.as_bytes().to_vec().as_ref()))
.map(|x| Sha256::hash(x.as_bytes().to_vec().as_ref()))
.collect();
let merkle_tree = MerkleTree::<Sha256Hasher>::new(&leaves);
let merkle_tree = MerkleTree::<Sha256>::new(&leaves);
let indices = tree_elements
.iter()
@@ -81,7 +89,7 @@ pub fn setup_proof_test_cases() -> Vec<ProofTestCases> {
.map(|(index, _)| index)
.collect();
let possible_proof_index_combinations = utils::collections::combinations(indices);
let possible_proof_index_combinations = combinations(indices);
let cases: Vec<MerkleProofTestCase> = possible_proof_index_combinations
.par_iter()

View File

@@ -1,8 +1,8 @@
mod common;
pub mod root {
use crate::{common, common::Sha256Hasher};
use rs_merkle::{MerkleTree};
use crate::common;
use rs_merkle::{MerkleTree, algorithms::Sha256};
use std::time::Instant;
use rayon::prelude::*;
@@ -14,7 +14,7 @@ pub mod root {
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 merkle_tree = MerkleTree::<Sha256Hasher>::new(&test_data.leaf_hashes);
let merkle_tree = MerkleTree::<Sha256>::new(&test_data.leaf_hashes);
let proof = merkle_tree.proof(&indices_to_prove);
let extracted_root = proof.hex_root(&indices_to_prove, &leaves_to_prove, test_data.leaf_values.len());
@@ -27,12 +27,12 @@ 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 merkle_tree = &test_case.merkle_tree;
let root = merkle_tree.root().unwrap().clone();
test_case.cases.par_iter().for_each(|case| {
let proof = merkle_tree.proof(&case.leaf_indices_to_prove);
let root = merkle_tree.hex_root().unwrap();
let extracted_root = proof.hex_root(&case.leaf_indices_to_prove, &case.leaf_hashes_to_prove, merkle_tree.leaves().unwrap().len());
let extracted_root = proof.root(&case.leaf_indices_to_prove, &case.leaf_hashes_to_prove, merkle_tree.leaves().unwrap().len());
assert_eq!(extracted_root, root)
});
@@ -42,8 +42,8 @@ pub mod root {
}
pub mod to_bytes {
use crate::{common, common::Sha256Hasher};
use rs_merkle::MerkleTree;
use crate::common;
use rs_merkle::{MerkleTree, algorithms::Sha256};
#[test]
pub fn should_correctly_serialize_to_bytes() {
@@ -60,7 +60,7 @@ pub mod to_bytes {
let test_data = common::setup();
let indices_to_prove = vec![3, 4];
let merkle_tree = MerkleTree::<Sha256Hasher>::new(&test_data.leaf_hashes);
let merkle_tree = MerkleTree::<Sha256>::new(&test_data.leaf_hashes);
let proof = merkle_tree.proof(&indices_to_prove);
let bytes = proof.to_bytes();
@@ -70,8 +70,8 @@ pub mod to_bytes {
}
pub mod from_bytes {
use rs_merkle::MerkleProof;
use crate::common::Sha256Hasher;
use rs_merkle::{MerkleProof, algorithms::Sha256};
use crate::common;
#[test]
pub fn should_return_result_with_proof() {
@@ -92,7 +92,7 @@ pub mod from_bytes {
151, 121, 247, 157, 196, 163, 215, 233, 57, 99, 249, 74
];
let proof = MerkleProof::<Sha256Hasher>::from_bytes(bytes).unwrap();
let proof = MerkleProof::<Sha256>::from_bytes(bytes).unwrap();
let hex_hashes = proof.hex_proof_hashes();
assert_eq!(hex_hashes, expected_proof_hashes);
@@ -110,7 +110,7 @@ pub mod from_bytes {
72, 113, 79, 34, 24, 15, 37, 173, 131, 101, 181, 63,
];
let err = MerkleProof::<Sha256Hasher>::from_bytes(bytes).err().unwrap();
let err = MerkleProof::<Sha256>::from_bytes(bytes).err().unwrap();
assert_eq!(err.message(), "Proof of size 84 bytes can not be divided into chunks of 32 bytes");
}

View File

@@ -1,14 +1,14 @@
mod common;
pub mod root {
use crate::{common, common::Sha256Hasher};
use rs_merkle::{MerkleTree};
use crate::common;
use rs_merkle::{MerkleTree, algorithms::Sha256};
#[test]
pub fn should_return_a_correct_root() {
let test_data = common::setup();
let merkle_tree = MerkleTree::<Sha256Hasher>::new(&test_data.leaf_hashes);
let merkle_tree = MerkleTree::<Sha256>::new(&test_data.leaf_hashes);
let hex_root = merkle_tree.hex_root().unwrap();
assert_eq!(hex_root, test_data.expected_root_hex);
@@ -16,14 +16,14 @@ pub mod root {
}
pub mod tree_depth {
use crate::{common, common::Sha256Hasher};
use rs_merkle::MerkleTree;
use crate::common;
use rs_merkle::{MerkleTree, algorithms::Sha256};
#[test]
pub fn should_return_a_correct_tree_depth() {
let test_data = common::setup();
let merkle_tree = MerkleTree::<Sha256Hasher>::new(&test_data.leaf_hashes);
let merkle_tree = MerkleTree::<Sha256>::new(&test_data.leaf_hashes);
let depth = merkle_tree.depth();
assert_eq!(depth, 3)
@@ -31,8 +31,8 @@ pub mod tree_depth {
}
pub mod proof {
use crate::{common, common::Sha256Hasher};
use rs_merkle::MerkleTree;
use crate::common;
use rs_merkle::{MerkleTree, algorithms::Sha256};
#[test]
pub fn should_return_a_correct_proof() {
@@ -44,7 +44,7 @@ pub mod proof {
"e5a01fee14e0ed5c48714f22180f25ad8365b53f9779f79dc4a3d7e93963f94a",
];
let merkle_tree = MerkleTree::<Sha256Hasher>::new(&test_data.leaf_hashes);
let merkle_tree = MerkleTree::<Sha256>::new(&test_data.leaf_hashes);
let proof = merkle_tree.proof(&indices_to_prove);
let proof_hashes = proof.hex_proof_hashes();