From 8a2405218fa1604f7bc424fbafd453a65804a4a6 Mon Sep 17 00:00:00 2001 From: sinui0 <65924192+sinui0@users.noreply.github.com> Date: Wed, 31 Jul 2024 17:54:51 +0900 Subject: [PATCH 1/8] add Hash trait --- src/hasher.rs | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/hasher.rs b/src/hasher.rs index 95a0ded..4282a72 100644 --- a/src/hasher.rs +++ b/src/hasher.rs @@ -1,6 +1,22 @@ use crate::prelude::*; -use core::convert::TryFrom; -use core::mem; +use core::{convert::TryFrom, mem}; + +/// This type is used as a hash type in the library. +/// +/// 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 the 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>` is required to be able to serialize proof +/// `TryFrom>` is required to parse hashes from a serialized proof +pub trait Hash: Copy + PartialEq + Into> + TryFrom> {} + +impl Hash for T where T: Copy + PartialEq + Into> + TryFrom> {} /// Hasher is a trait used to provide a hashing algorithm for the library. /// @@ -27,19 +43,7 @@ use core::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 a hash type. For example, - /// for sha256 the type would be `[u8; 32]`, representing 32 bytes, - /// 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 - /// `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>` is required to be able to serialize proof - /// `TryFrom>` is required to parse hashes from a serialized proof - type Hash: Copy + PartialEq + Into> + TryFrom>; + type Hash: Hash; /// 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 From 5cbb5a2568f229f57c03eea9a6f9ea5ce27963d4 Mon Sep 17 00:00:00 2001 From: sinui0 <65924192+sinui0@users.noreply.github.com> Date: Wed, 31 Jul 2024 18:05:11 +0900 Subject: [PATCH 2/8] receive &self in Hasher --- src/algorithms/sha256.rs | 4 ++-- src/algorithms/sha384.rs | 4 ++-- src/hasher.rs | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/algorithms/sha256.rs b/src/algorithms/sha256.rs index 2ec2a62..06af025 100644 --- a/src/algorithms/sha256.rs +++ b/src/algorithms/sha256.rs @@ -28,13 +28,13 @@ use sha2::{digest::FixedOutput, Digest, Sha256}; /// ``` /// /// [`Hasher`]: crate::Hasher -#[derive(Clone)] +#[derive(Default, Clone)] pub struct Sha256Algorithm {} impl Hasher for Sha256Algorithm { type Hash = [u8; 32]; - fn hash(data: &[u8]) -> [u8; 32] { + fn hash(&self, data: &[u8]) -> [u8; 32] { let mut hasher = Sha256::new(); hasher.update(data); diff --git a/src/algorithms/sha384.rs b/src/algorithms/sha384.rs index 150024d..a017849 100644 --- a/src/algorithms/sha384.rs +++ b/src/algorithms/sha384.rs @@ -32,13 +32,13 @@ use sha2::{digest::FixedOutput, Digest, Sha384}; /// ``` /// /// [`Hasher`]: crate::Hasher -#[derive(Clone)] +#[derive(Default, Clone)] pub struct Sha384Algorithm {} impl Hasher for Sha384Algorithm { type Hash = [u8; 48]; - fn hash(data: &[u8]) -> [u8; 48] { + fn hash(&self, data: &[u8]) -> [u8; 48] { let mut hasher = Sha384::new(); hasher.update(data); <[u8; 48]>::from(hasher.finalize_fixed()) diff --git a/src/hasher.rs b/src/hasher.rs index 4282a72..52620c3 100644 --- a/src/hasher.rs +++ b/src/hasher.rs @@ -47,7 +47,7 @@ pub trait Hasher: Clone { /// 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; + fn hash(&self, data: &[u8]) -> Self::Hash; /// Used by [`MerkleTree`] and [`PartialTree`] when calculating the root. /// The provided default implementation propagates the left node if it doesn't @@ -60,14 +60,14 @@ pub trait Hasher: Clone { /// /// [`MerkleTree`]: crate::MerkleTree /// [`PartialTree`]: crate::PartialTree - fn concat_and_hash(left: &Self::Hash, right: Option<&Self::Hash>) -> Self::Hash { + fn concat_and_hash(&self, left: &Self::Hash, right: Option<&Self::Hash>) -> Self::Hash { let mut concatenated: Vec = (*left).into(); match right { Some(right_node) => { let mut right_node_clone: Vec = (*right_node).into(); concatenated.append(&mut right_node_clone); - Self::hash(&concatenated) + self.hash(&concatenated) } None => *left, } From afdd56ed8fc3bef52682e7f4c16047924780c88d Mon Sep 17 00:00:00 2001 From: sinui0 <65924192+sinui0@users.noreply.github.com> Date: Wed, 31 Jul 2024 18:07:58 +0900 Subject: [PATCH 3/8] refactor PartialTree, remove bound on struct and receive hasher in methods --- src/partial_tree.rs | 70 ++++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/src/partial_tree.rs b/src/partial_tree.rs index b2d98b8..0687407 100644 --- a/src/partial_tree.rs +++ b/src/partial_tree.rs @@ -1,5 +1,4 @@ -use crate::prelude::*; -use crate::{error::Error, utils, Hasher}; +use crate::{error::Error, hasher::Hash, prelude::*, utils, Hasher}; type PartialTreeLayer = Vec<(usize, H)>; @@ -13,17 +12,17 @@ type PartialTreeLayer = Vec<(usize, H)>; /// [`MerkleTree`]: crate::MerkleTree /// [`MerkleProof`]: crate::MerkleProof #[derive(Clone)] -pub struct PartialTree { - layers: Vec>, +pub struct PartialTree { + layers: Vec>, } -impl Default for PartialTree { +impl Default for PartialTree { fn default() -> Self { Self::new() } } -impl PartialTree { +impl PartialTree { /// Takes leaves (item hashes) as an argument and build a Merkle Tree from them. /// Since it's a partial tree, hashes must be accompanied by their index in the original tree. pub fn new() -> Self { @@ -32,30 +31,47 @@ impl PartialTree { /// This is a helper function to build a full tree from a full set of leaves without any /// helper indices - pub fn from_leaves(leaves: &[T::Hash]) -> Result { - let leaf_tuples: Vec<(usize, T::Hash)> = leaves.iter().cloned().enumerate().collect(); - - Self::build(vec![leaf_tuples], utils::indices::tree_depth(leaves.len())) + pub fn from_leaves(hasher: &T, leaves: &[H]) -> Result + where + T: Hasher, + { + let leaf_tuples: Vec<(usize, H)> = leaves.iter().cloned().enumerate().collect(); + + Self::build( + hasher, + vec![leaf_tuples], + utils::indices::tree_depth(leaves.len()), + ) } - pub fn build(partial_layers: Vec>, depth: usize) -> Result { - let layers = Self::build_tree(partial_layers, depth)?; + pub fn build( + hasher: &T, + partial_layers: Vec>, + depth: usize, + ) -> Result + where + T: Hasher, + { + let layers = Self::build_tree(hasher, partial_layers, depth)?; Ok(Self { layers }) } /// This is a general algorithm for building a partial tree. It can be used to extract root /// from merkle proof, or if a complete set of leaves provided as a first argument and no /// helper indices given, will construct the whole tree. - fn build_tree( - mut partial_layers: Vec>, + fn build_tree( + hasher: &T, + mut partial_layers: Vec>, full_tree_depth: usize, - ) -> Result>, Error> { - let mut partial_tree: Vec> = Vec::new(); + ) -> Result>, Error> + where + T: Hasher, + { + let mut partial_tree: Vec> = Vec::new(); let mut current_layer = Vec::new(); // Reversing helper nodes, so we can remove one layer starting from 0 each iteration - let mut reversed_layers: Vec> = - partial_layers.drain(..).rev().collect(); + let mut reversed_layers: Vec> = partial_layers.drain(..).rev().collect(); // This iterates to full_tree_depth and not to the partial_layers_len because // when constructing @@ -74,7 +90,7 @@ impl PartialTree { partial_tree.push(current_layer.clone()); // This empties `current` layer and prepares it to be reused for the next iteration - let (indices, nodes): (Vec, Vec) = current_layer.drain(..).unzip(); + let (indices, nodes): (Vec, Vec) = current_layer.drain(..).unzip(); let parent_layer_indices = utils::indices::parent_indices(&indices); for (i, parent_node_index) in parent_layer_indices.iter().enumerate() { @@ -82,7 +98,7 @@ impl PartialTree { // Populate `current_layer` back for the next iteration Some(left_node) => current_layer.push(( *parent_node_index, - T::concat_and_hash(left_node, nodes.get(i * 2 + 1)), + hasher.concat_and_hash(left_node, nodes.get(i * 2 + 1)), )), None => return Err(Error::not_enough_helper_nodes()), } @@ -100,7 +116,7 @@ impl PartialTree { } /// Return the root of the tree - pub fn root(&self) -> Option<&T::Hash> { + pub fn root(&self) -> Option<&H> { Some(&self.layers.last()?.first()?.1) } @@ -127,10 +143,10 @@ impl PartialTree { }; for layer_index in 0..combined_tree_size { - let mut combined_layer: Vec<(usize, T::Hash)> = Vec::new(); + let mut combined_layer: Vec<(usize, H)> = Vec::new(); if let Some(self_layer) = self.layers().get(layer_index) { - let mut filtered_layer: Vec<(usize, T::Hash)> = self_layer + let mut filtered_layer: Vec<(usize, H)> = self_layer .iter() .filter(|(node_index, _)| !other.contains(layer_index, *node_index)) .cloned() @@ -150,7 +166,7 @@ impl PartialTree { } /// Replace layer at a given index with a new layer. Used during tree merge - fn upsert_layer(&mut self, layer_index: usize, mut new_layer: Vec<(usize, T::Hash)>) { + fn upsert_layer(&mut self, layer_index: usize, mut new_layer: Vec<(usize, H)>) { match self.layers.get_mut(layer_index) { Some(layer) => { layer.clear(); @@ -160,8 +176,8 @@ impl PartialTree { } } - pub fn layer_nodes(&self) -> Vec> { - let hashes: Vec> = self + pub fn layer_nodes(&self) -> Vec> { + let hashes: Vec> = self .layers() .iter() .map(|layer| layer.iter().cloned().map(|(_, hash)| hash).collect()) @@ -171,7 +187,7 @@ impl PartialTree { } /// Returns partial tree layers - pub fn layers(&self) -> &[Vec<(usize, T::Hash)>] { + pub fn layers(&self) -> &[Vec<(usize, H)>] { &self.layers } From 57c77c3d8cac9ddb9f135bf0281b3c270ecefeb9 Mon Sep 17 00:00:00 2001 From: sinui0 <65924192+sinui0@users.noreply.github.com> Date: Wed, 31 Jul 2024 18:15:26 +0900 Subject: [PATCH 4/8] add associated const SIZE to Hash --- src/hasher.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/hasher.rs b/src/hasher.rs index 52620c3..a652798 100644 --- a/src/hasher.rs +++ b/src/hasher.rs @@ -1,5 +1,5 @@ use crate::prelude::*; -use core::{convert::TryFrom, mem}; +use core::convert::TryFrom; /// This type is used as a hash type in the library. /// @@ -14,9 +14,14 @@ use core::{convert::TryFrom, mem}; /// `PartialEq` is required to compare equality when verifying proof /// `Into>` is required to be able to serialize proof /// `TryFrom>` is required to parse hashes from a serialized proof -pub trait Hash: Copy + PartialEq + Into> + TryFrom> {} +pub trait Hash: Copy + PartialEq + Into> + TryFrom> { + /// The size of the hash in bytes. + const SIZE: usize; +} -impl Hash for T where T: Copy + PartialEq + Into> + TryFrom> {} +impl Hash for [u8; N] { + const SIZE: usize = N; +} /// Hasher is a trait used to provide a hashing algorithm for the library. /// @@ -72,11 +77,4 @@ pub trait Hasher: Clone { None => *left, } } - - /// Returns the byte size of `Self::Hash`. Default implementation returns - /// `mem::size_of::()`. 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::() - } } From 8289aef375e50478ff10c914f49497363e22fbc9 Mon Sep 17 00:00:00 2001 From: sinui0 <65924192+sinui0@users.noreply.github.com> Date: Wed, 31 Jul 2024 18:20:31 +0900 Subject: [PATCH 5/8] update serializers to use Hash trait --- src/proof_serializers/direct_hashes_order.rs | 23 ++++++++----------- .../merkle_proof_serializer.rs | 6 ++--- src/proof_serializers/reverse_hashes_order.rs | 23 ++++++++----------- 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/src/proof_serializers/direct_hashes_order.rs b/src/proof_serializers/direct_hashes_order.rs index cd1cdf4..149e38f 100644 --- a/src/proof_serializers/direct_hashes_order.rs +++ b/src/proof_serializers/direct_hashes_order.rs @@ -1,12 +1,11 @@ -use crate::{prelude::*, Error, Hasher, MerkleProof, MerkleProofSerializer}; -use core::convert::TryFrom; +use crate::{hasher::Hash, prelude::*, Error, MerkleProof, MerkleProofSerializer}; /// Serializes proof data to bytes with a direct hash order - hashes are concatenated from /// left to right, bottom to top. pub struct DirectHashesOrder {} impl MerkleProofSerializer for DirectHashesOrder { - fn serialize(proof: &MerkleProof) -> Vec { + fn serialize(proof: &MerkleProof) -> Vec { let mut vectors: Vec> = proof .proof_hashes() .iter() @@ -16,24 +15,22 @@ impl MerkleProofSerializer for DirectHashesOrder { vectors.drain(..).flatten().collect() } - fn deserialize(bytes: &[u8]) -> Result, Error> { - let hash_size = T::hash_size(); - - if bytes.len() % hash_size != 0 { - return Err(Error::wrong_proof_size(bytes.len(), hash_size)); + fn deserialize(bytes: &[u8]) -> Result, Error> { + if bytes.len() % H::SIZE != 0 { + return Err(Error::wrong_proof_size(bytes.len(), H::SIZE)); } - let hashes_count = bytes.len() / hash_size; - let mut proof_hashes_slices = Vec::::with_capacity(hashes_count); + let hashes_count = bytes.len() / H::SIZE; + let mut proof_hashes_slices = Vec::::with_capacity(hashes_count); for i in 0..hashes_count { - let slice_start = i * hash_size; - let slice_end = (i + 1) * hash_size; + let slice_start = i * H::SIZE; + let slice_end = (i + 1) * H::SIZE; let slice = bytes .get(slice_start..slice_end) .ok_or_else(Error::vec_to_hash_conversion_error)?; let vec = Vec::from(slice); - match T::Hash::try_from(vec) { + match H::try_from(vec) { Ok(val) => proof_hashes_slices.push(val), Err(_) => return Err(Error::vec_to_hash_conversion_error()), } diff --git a/src/proof_serializers/merkle_proof_serializer.rs b/src/proof_serializers/merkle_proof_serializer.rs index ff11a81..6e3d097 100644 --- a/src/proof_serializers/merkle_proof_serializer.rs +++ b/src/proof_serializers/merkle_proof_serializer.rs @@ -1,4 +1,4 @@ -use crate::{prelude::*, Error, Hasher, MerkleProof}; +use crate::{hasher::Hash, prelude::*, Error, MerkleProof}; /// Trait representing a Merkle proof serializer. Used in [`MerkleProof::serialize`] and /// [`MerkleProof::deserialize`]. @@ -9,8 +9,8 @@ use crate::{prelude::*, Error, Hasher, MerkleProof}; /// [`proof_serializers`]: crate::proof_serializers pub trait MerkleProofSerializer { /// Serialize data from [`MerkleProof`] into a binary - fn serialize(proof: &MerkleProof) -> Vec; + fn serialize(proof: &MerkleProof) -> Vec; /// Deserialize data produced by [`MerkleProofSerializer::serialize`] back into [`MerkleProof`] - fn deserialize(bytes: &[u8]) -> Result, Error>; + fn deserialize(bytes: &[u8]) -> Result, Error>; } diff --git a/src/proof_serializers/reverse_hashes_order.rs b/src/proof_serializers/reverse_hashes_order.rs index 5f8a4d1..bd6d92e 100644 --- a/src/proof_serializers/reverse_hashes_order.rs +++ b/src/proof_serializers/reverse_hashes_order.rs @@ -1,12 +1,11 @@ -use crate::{prelude::*, Error, Hasher, MerkleProof, MerkleProofSerializer}; -use core::convert::TryFrom; +use crate::{hasher::Hash, prelude::*, Error, MerkleProof, MerkleProofSerializer}; /// Serializes proof data to bytes with a reverse hash order - hashes are concatenated from /// top to bottom, right to left. pub struct ReverseHashesOrder {} impl MerkleProofSerializer for ReverseHashesOrder { - fn serialize(proof: &MerkleProof) -> Vec { + fn serialize(proof: &MerkleProof) -> Vec { let mut hashes: Vec> = proof .proof_hashes() .iter() @@ -18,24 +17,22 @@ impl MerkleProofSerializer for ReverseHashesOrder { hashes.drain(..).flatten().collect() } - fn deserialize(bytes: &[u8]) -> Result, Error> { - let hash_size = T::hash_size(); - - if bytes.len() % hash_size != 0 { - return Err(Error::wrong_proof_size(bytes.len(), hash_size)); + fn deserialize(bytes: &[u8]) -> Result, Error> { + if bytes.len() % H::SIZE != 0 { + return Err(Error::wrong_proof_size(bytes.len(), H::SIZE)); } - let hashes_count = bytes.len() / hash_size; - let mut proof_hashes_slices = Vec::::with_capacity(hashes_count); + let hashes_count = bytes.len() / H::SIZE; + let mut proof_hashes_slices = Vec::::with_capacity(hashes_count); for i in 0..hashes_count { - let slice_start = i * hash_size; - let slice_end = (i + 1) * hash_size; + let slice_start = i * H::SIZE; + let slice_end = (i + 1) * H::SIZE; let slice = bytes .get(slice_start..slice_end) .ok_or_else(Error::vec_to_hash_conversion_error)?; let vec = Vec::from(slice); - match T::Hash::try_from(vec) { + match H::try_from(vec) { Ok(val) => proof_hashes_slices.push(val), Err(_) => return Err(Error::vec_to_hash_conversion_error()), } From d920a37b58866df29029e39efc9ce990b4b9c6f8 Mon Sep 17 00:00:00 2001 From: sinui0 <65924192+sinui0@users.noreply.github.com> Date: Wed, 31 Jul 2024 18:27:35 +0900 Subject: [PATCH 6/8] refactor Merkle(Tree/Proof), inject hasher into methods --- src/merkle_proof.rs | 57 ++++++++++++++++++------------ src/merkle_tree.rs | 84 ++++++++++++++++++++++++++++----------------- 2 files changed, 87 insertions(+), 54 deletions(-) diff --git a/src/merkle_proof.rs b/src/merkle_proof.rs index 285fff0..153c670 100644 --- a/src/merkle_proof.rs +++ b/src/merkle_proof.rs @@ -1,5 +1,6 @@ use crate::{ error::Error, + hasher::Hash, partial_tree::PartialTree, prelude::*, proof_serializers::{DirectHashesOrder, MerkleProofSerializer}, @@ -46,12 +47,12 @@ use core::convert::TryFrom; /// /// [`Hasher`]: crate::Hasher /// [`algorithms::Sha256`]: crate::algorithms::Sha256 -pub struct MerkleProof { - proof_hashes: Vec, +pub struct MerkleProof { + proof_hashes: Vec, } -impl MerkleProof { - pub fn new(proof_hashes: Vec) -> Self { +impl MerkleProof { + pub fn new(proof_hashes: Vec) -> Self { MerkleProof { proof_hashes } } @@ -156,14 +157,18 @@ impl MerkleProof { /// # Ok(()) /// # } /// ``` - pub fn verify( + pub fn verify( &self, - root: T::Hash, + hasher: &T, + root: H, leaf_indices: &[usize], - leaf_hashes: &[T::Hash], + leaf_hashes: &[H], total_leaves_count: usize, - ) -> bool { - match self.root(leaf_indices, leaf_hashes, total_leaves_count) { + ) -> bool + where + T: Hasher, + { + match self.root(hasher, leaf_indices, leaf_hashes, total_leaves_count) { Ok(extracted_root) => extracted_root == root, Err(_) => false, } @@ -199,12 +204,16 @@ impl MerkleProof { /// # Ok(()) /// # } /// ``` - pub fn root( + pub fn root( &self, + hasher: &T, leaf_indices: &[usize], - leaf_hashes: &[T::Hash], + leaf_hashes: &[H], total_leaves_count: usize, - ) -> Result { + ) -> Result + where + T: Hasher, + { if leaf_indices.len() != leaf_hashes.len() { return Err(Error::leaves_indices_count_mismatch( leaf_indices.len(), @@ -214,7 +223,7 @@ impl MerkleProof { let tree_depth = utils::indices::tree_depth(total_leaves_count); // Zipping indices and hashes into a vector of (original_index_in_tree, leaf_hash) - let mut leaf_tuples: Vec<(usize, T::Hash)> = leaf_indices + let mut leaf_tuples: Vec<(usize, H)> = leaf_indices .iter() .cloned() .zip(leaf_hashes.iter().cloned()) @@ -228,7 +237,7 @@ impl MerkleProof { utils::indices::proof_indices_by_layers(&sorted_indices, total_leaves_count); // The next lines copy hashes from proof hashes and group them by layer index - let mut proof_layers: Vec> = Vec::with_capacity(tree_depth + 1); + let mut proof_layers: Vec> = Vec::with_capacity(tree_depth + 1); let mut proof_copy = self.proof_hashes.clone(); for proof_indices in proof_indices_by_layers { @@ -247,7 +256,7 @@ impl MerkleProof { None => proof_layers.push(leaf_tuples), } - let partial_tree = PartialTree::::build(proof_layers, tree_depth)?; + let partial_tree = PartialTree::::build(hasher, proof_layers, tree_depth)?; match partial_tree.root() { Some(root) => Ok(*root), @@ -284,13 +293,17 @@ impl MerkleProof { /// # Ok(()) /// # } /// ``` - pub fn root_hex( + pub fn root_hex( &self, + hasher: &T, leaf_indices: &[usize], - leaf_hashes: &[T::Hash], + leaf_hashes: &[H], total_leaves_count: usize, - ) -> Result { - let root = self.root(leaf_indices, leaf_hashes, total_leaves_count)?; + ) -> Result + where + T: Hasher, + { + let root = self.root(hasher, leaf_indices, leaf_hashes, total_leaves_count)?; Ok(utils::collections::to_hex_string(&root)) } @@ -324,7 +337,7 @@ impl MerkleProof { /// # Ok(()) /// # } /// ``` - pub fn proof_hashes(&self) -> &[T::Hash] { + pub fn proof_hashes(&self) -> &[H] { &self.proof_hashes } @@ -458,7 +471,7 @@ impl MerkleProof { } } -impl TryFrom> for MerkleProof { +impl TryFrom> for MerkleProof { type Error = Error; /// Parses proof serialized to a collection of bytes. Consumes passed vector. @@ -485,7 +498,7 @@ impl TryFrom> for MerkleProof { } } -impl TryFrom<&[u8]> for MerkleProof { +impl TryFrom<&[u8]> for MerkleProof { type Error = Error; /// Parses proof serialized to a collection of bytes diff --git a/src/merkle_tree.rs b/src/merkle_tree.rs index 82ab616..e166d4d 100644 --- a/src/merkle_tree.rs +++ b/src/merkle_tree.rs @@ -1,5 +1,10 @@ -use crate::prelude::*; -use crate::{partial_tree::PartialTree, utils, utils::indices, Hasher, MerkleProof}; +use crate::{ + hasher::Hash, + partial_tree::PartialTree, + prelude::*, + utils::{self, indices}, + Hasher, MerkleProof, +}; /// [`MerkleTree`] is a Merkle Tree that is well suited for both basic and advanced usage. /// @@ -10,19 +15,19 @@ use crate::{partial_tree::PartialTree, utils, utils::indices, Hasher, MerkleProo /// 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, - history: Vec>, - uncommitted_leaves: Vec, +pub struct MerkleTree { + current_working_tree: PartialTree, + history: Vec>, + uncommitted_leaves: Vec, } -impl Default for MerkleTree { +impl Default for MerkleTree { fn default() -> Self { Self::new() } } -impl MerkleTree { +impl MerkleTree { /// Creates a new instance of Merkle Tree. Requires a hash algorithm to be specified. /// /// # Examples @@ -59,10 +64,13 @@ impl MerkleTree { /// let merkle_tree = MerkleTree::::from_leaves(&leaves); /// # Ok(()) /// # } - pub fn from_leaves(leaves: &[T::Hash]) -> Self { + pub fn from_leaves(hasher: &T, leaves: &[H]) -> Self + where + T: Hasher, + { let mut tree = Self::new(); tree.append(leaves.to_vec().as_mut()); - tree.commit(); + tree.commit(hasher); tree } @@ -92,7 +100,7 @@ impl MerkleTree { /// # Ok(()) /// # } /// ``` - pub fn root(&self) -> Option { + pub fn root(&self) -> Option { Some(self.layer_tuples().last()?.first()?.1) } @@ -128,8 +136,8 @@ impl MerkleTree { /// 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 - fn helper_nodes(&self, leaf_indices: &[usize]) -> Vec { - let mut helper_nodes = Vec::::new(); + fn helper_nodes(&self, leaf_indices: &[usize]) -> Vec { + let mut helper_nodes = Vec::::new(); for layer in self.helper_node_tuples(leaf_indices) { for (_index, hash) in layer { @@ -142,9 +150,9 @@ impl MerkleTree { /// Gets all helper nodes required to build a partial merkle tree for the given indices, /// cloning all required hashes into the resulting vector. - fn helper_node_tuples(&self, leaf_indices: &[usize]) -> Vec> { + fn helper_node_tuples(&self, leaf_indices: &[usize]) -> Vec> { let mut current_layer_indices = leaf_indices.to_vec(); - let mut helper_nodes: Vec> = Vec::new(); + let mut helper_nodes: Vec> = Vec::new(); for tree_layer in self.layer_tuples() { let mut helpers_layer = Vec::new(); @@ -193,8 +201,8 @@ impl MerkleTree { /// # Ok(()) /// # } /// ``` - pub fn proof(&self, leaf_indices: &[usize]) -> MerkleProof { - MerkleProof::::new(self.helper_nodes(leaf_indices)) + pub fn proof(&self, leaf_indices: &[usize]) -> MerkleProof { + MerkleProof::::new(self.helper_nodes(leaf_indices)) } /// Inserts a new leaf. Please note it won't modify the root just yet; For the changes @@ -242,7 +250,7 @@ impl MerkleTree { /// # Ok(()) /// # } /// ``` - pub fn insert(&mut self, leaf: T::Hash) -> &mut Self { + pub fn insert(&mut self, leaf: H) -> &mut Self { self.uncommitted_leaves.push(leaf); self } @@ -273,7 +281,7 @@ impl MerkleTree { /// # Ok(()) /// # } /// ``` - pub fn append(&mut self, leaves: &mut Vec) -> &mut Self { + pub fn append(&mut self, leaves: &mut Vec) -> &mut Self { self.uncommitted_leaves.append(leaves); self } @@ -308,8 +316,11 @@ impl MerkleTree { /// # Ok(()) /// # } /// ``` - pub fn commit(&mut self) { - if let Some(diff) = self.uncommitted_diff() { + pub fn commit(&mut self, hasher: &T) + where + T: Hasher, + { + if let Some(diff) = self.uncommitted_diff(hasher) { self.history.push(diff.clone()); self.current_working_tree.merge_unverified(diff); self.uncommitted_leaves.clear(); @@ -364,8 +375,11 @@ impl MerkleTree { /// 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()?; + pub fn uncommitted_root(&self, hasher: &T) -> Option + where + T: Hasher, + { + let shadow_tree = self.uncommitted_diff(hasher)?; shadow_tree.root().cloned() } @@ -402,8 +416,11 @@ impl MerkleTree { /// # Ok(()) /// # } /// ``` - pub fn uncommitted_root_hex(&self) -> Option { - let root = self.uncommitted_root()?; + pub fn uncommitted_root_hex(&self, hasher: &T) -> Option + where + T: Hasher, + { + let root = self.uncommitted_root(hasher)?; Some(utils::collections::to_hex_string(&root)) } @@ -484,7 +501,7 @@ impl MerkleTree { /// # Ok(()) /// # } /// ``` - pub fn leaves(&self) -> Option> { + pub fn leaves(&self) -> Option> { Some(self.layers().first()?.to_vec()) } @@ -515,23 +532,26 @@ impl MerkleTree { 0 } - fn leaves_tuples(&self) -> Option<&[(usize, T::Hash)]> { + fn leaves_tuples(&self) -> Option<&[(usize, H)]> { 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> { + fn layers(&self) -> Vec> { self.current_working_tree.layer_nodes() } - fn layer_tuples(&self) -> &[Vec<(usize, T::Hash)>] { + fn layer_tuples(&self) -> &[Vec<(usize, H)>] { 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> { + fn uncommitted_diff(&self, hasher: &T) -> Option> + where + T: Hasher, + { if self.uncommitted_leaves.is_empty() { return None; } @@ -546,7 +566,7 @@ impl MerkleTree { .collect(); // Tuples (index, hash) needed to construct a partial tree, since partial tree can't // maintain indices otherwise - let mut shadow_node_tuples: Vec<(usize, T::Hash)> = shadow_indices + let mut shadow_node_tuples: Vec<(usize, H)> = shadow_indices .iter() .cloned() .zip(self.uncommitted_leaves.iter().cloned()) @@ -566,6 +586,6 @@ impl MerkleTree { } // Building a partial tree with the changes that would be needed to the working tree - PartialTree::::build(partial_tree_tuples, uncommitted_tree_depth).ok() + PartialTree::::build(hasher, partial_tree_tuples, uncommitted_tree_depth).ok() } } From b07210c01b126d25ca0903b7a254c8764e86fe5a Mon Sep 17 00:00:00 2001 From: sinui0 <65924192+sinui0@users.noreply.github.com> Date: Wed, 31 Jul 2024 19:00:58 +0900 Subject: [PATCH 7/8] refactor algorithms to be unit structs --- src/algorithms/sha256.rs | 2 +- src/algorithms/sha384.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/algorithms/sha256.rs b/src/algorithms/sha256.rs index 06af025..b1ddfb3 100644 --- a/src/algorithms/sha256.rs +++ b/src/algorithms/sha256.rs @@ -29,7 +29,7 @@ use sha2::{digest::FixedOutput, Digest, Sha256}; /// /// [`Hasher`]: crate::Hasher #[derive(Default, Clone)] -pub struct Sha256Algorithm {} +pub struct Sha256Algorithm; impl Hasher for Sha256Algorithm { type Hash = [u8; 32]; diff --git a/src/algorithms/sha384.rs b/src/algorithms/sha384.rs index a017849..3dde447 100644 --- a/src/algorithms/sha384.rs +++ b/src/algorithms/sha384.rs @@ -33,7 +33,7 @@ use sha2::{digest::FixedOutput, Digest, Sha384}; /// /// [`Hasher`]: crate::Hasher #[derive(Default, Clone)] -pub struct Sha384Algorithm {} +pub struct Sha384Algorithm; impl Hasher for Sha384Algorithm { type Hash = [u8; 48]; From 0e2b78bf168f8efa00f88308b3840f50986e7aa1 Mon Sep 17 00:00:00 2001 From: sinu <65924192+sinui0@users.noreply.github.com> Date: Sun, 11 Aug 2024 21:34:39 -0700 Subject: [PATCH 8/8] export Hash trait --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 466d259..3f91acc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -135,7 +135,7 @@ extern crate std; pub use error::Error; pub use error::ErrorKind; -pub use hasher::Hasher; +pub use hasher::{Hash, Hasher}; pub use merkle_proof::MerkleProof; pub use merkle_tree::MerkleTree; pub use partial_tree::PartialTree;