Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: dynamic support for hash algs #40

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/algorithms/sha256.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ use sha2::{digest::FixedOutput, Digest, Sha256};
/// ```
///
/// [`Hasher`]: crate::Hasher
#[derive(Clone)]
pub struct Sha256Algorithm {}
#[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);
Expand Down
6 changes: 3 additions & 3 deletions src/algorithms/sha384.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ use sha2::{digest::FixedOutput, Digest, Sha384};
/// ```
///
/// [`Hasher`]: crate::Hasher
#[derive(Clone)]
pub struct Sha384Algorithm {}
#[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())
Expand Down
50 changes: 26 additions & 24 deletions src/hasher.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,27 @@
use crate::prelude::*;
use core::convert::TryFrom;
use core::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<Vec<u8>>` is required to be able to serialize proof
/// `TryFrom<Vec<u8>>` is required to parse hashes from a serialized proof
pub trait Hash: Copy + PartialEq + Into<Vec<u8>> + TryFrom<Vec<u8>> {
/// The size of the hash in bytes.
const SIZE: usize;
}

impl<const N: usize> Hash for [u8; N] {
const SIZE: usize = N;
}

/// Hasher is a trait used to provide a hashing algorithm for the library.
///
Expand All @@ -27,23 +48,11 @@ 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<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>>;
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
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
Expand All @@ -56,23 +65,16 @@ 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<u8> = (*left).into();

match right {
Some(right_node) => {
let mut right_node_clone: Vec<u8> = (*right_node).into();
concatenated.append(&mut right_node_clone);
Self::hash(&concatenated)
self.hash(&concatenated)
}
None => *left,
}
}

/// 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>()
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
57 changes: 35 additions & 22 deletions src/merkle_proof.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::{
error::Error,
hasher::Hash,
partial_tree::PartialTree,
prelude::*,
proof_serializers::{DirectHashesOrder, MerkleProofSerializer},
Expand Down Expand Up @@ -46,12 +47,12 @@ use core::convert::TryFrom;
///
/// [`Hasher`]: crate::Hasher
/// [`algorithms::Sha256`]: crate::algorithms::Sha256
pub struct MerkleProof<T: Hasher> {
proof_hashes: Vec<T::Hash>,
pub struct MerkleProof<H> {
proof_hashes: Vec<H>,
}

impl<T: Hasher> MerkleProof<T> {
pub fn new(proof_hashes: Vec<T::Hash>) -> Self {
impl<H: Hash> MerkleProof<H> {
pub fn new(proof_hashes: Vec<H>) -> Self {
MerkleProof { proof_hashes }
}

Expand Down Expand Up @@ -156,14 +157,18 @@ impl<T: Hasher> MerkleProof<T> {
/// # Ok(())
/// # }
/// ```
pub fn verify(
pub fn verify<T>(
&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<Hash = H>,
{
match self.root(hasher, leaf_indices, leaf_hashes, total_leaves_count) {
Ok(extracted_root) => extracted_root == root,
Err(_) => false,
}
Expand Down Expand Up @@ -199,12 +204,16 @@ impl<T: Hasher> MerkleProof<T> {
/// # Ok(())
/// # }
/// ```
pub fn root(
pub fn root<T>(
&self,
hasher: &T,
leaf_indices: &[usize],
leaf_hashes: &[T::Hash],
leaf_hashes: &[H],
total_leaves_count: usize,
) -> Result<T::Hash, Error> {
) -> Result<H, Error>
where
T: Hasher<Hash = H>,
{
if leaf_indices.len() != leaf_hashes.len() {
return Err(Error::leaves_indices_count_mismatch(
leaf_indices.len(),
Expand All @@ -214,7 +223,7 @@ impl<T: Hasher> MerkleProof<T> {
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())
Expand All @@ -228,7 +237,7 @@ impl<T: Hasher> MerkleProof<T> {
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<(usize, T::Hash)>> = Vec::with_capacity(tree_depth + 1);
let mut proof_layers: Vec<Vec<(usize, H)>> = Vec::with_capacity(tree_depth + 1);
let mut proof_copy = self.proof_hashes.clone();

for proof_indices in proof_indices_by_layers {
Expand All @@ -247,7 +256,7 @@ impl<T: Hasher> MerkleProof<T> {
None => proof_layers.push(leaf_tuples),
}

let partial_tree = PartialTree::<T>::build(proof_layers, tree_depth)?;
let partial_tree = PartialTree::<H>::build(hasher, proof_layers, tree_depth)?;

match partial_tree.root() {
Some(root) => Ok(*root),
Expand Down Expand Up @@ -284,13 +293,17 @@ impl<T: Hasher> MerkleProof<T> {
/// # Ok(())
/// # }
/// ```
pub fn root_hex(
pub fn root_hex<T>(
&self,
hasher: &T,
leaf_indices: &[usize],
leaf_hashes: &[T::Hash],
leaf_hashes: &[H],
total_leaves_count: usize,
) -> Result<String, Error> {
let root = self.root(leaf_indices, leaf_hashes, total_leaves_count)?;
) -> Result<String, Error>
where
T: Hasher<Hash = H>,
{
let root = self.root(hasher, leaf_indices, leaf_hashes, total_leaves_count)?;
Ok(utils::collections::to_hex_string(&root))
}

Expand Down Expand Up @@ -324,7 +337,7 @@ impl<T: Hasher> MerkleProof<T> {
/// # Ok(())
/// # }
/// ```
pub fn proof_hashes(&self) -> &[T::Hash] {
pub fn proof_hashes(&self) -> &[H] {
&self.proof_hashes
}

Expand Down Expand Up @@ -458,7 +471,7 @@ impl<T: Hasher> MerkleProof<T> {
}
}

impl<T: Hasher> TryFrom<Vec<u8>> for MerkleProof<T> {
impl<H: Hash> TryFrom<Vec<u8>> for MerkleProof<H> {
type Error = Error;

/// Parses proof serialized to a collection of bytes. Consumes passed vector.
Expand All @@ -485,7 +498,7 @@ impl<T: Hasher> TryFrom<Vec<u8>> for MerkleProof<T> {
}
}

impl<T: Hasher> TryFrom<&[u8]> for MerkleProof<T> {
impl<H: Hash> TryFrom<&[u8]> for MerkleProof<H> {
type Error = Error;

/// Parses proof serialized to a collection of bytes
Expand Down
Loading