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

feat: implementation of built-in Hasher and Serializer for Bitcoin trees and proofs #10

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
57 changes: 57 additions & 0 deletions src/algorithms/bitcoin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use crate::Hasher;
use sha2::{digest::FixedOutput, Digest, Sha256};

/// Implementation of the [`Hasher`] trait for Bitcoin trees and proofs.
///
/// # Examples
///
/// ```
/// # use rs_merkle::{MerkleTree, MerkleProof, algorithms::Bitcoin, Hasher, Error, utils};
/// # use std::convert::TryFrom;
/// #
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use rs_merkle::proof_serializers::BitcoinProofSerializer;
/// let tree = MerkleTree::<Bitcoin>::new();
/// let other_tree: MerkleTree<Bitcoin> = MerkleTree::new();
///
/// let proof_bytes: Vec<u8> = vec![
/// 46, 125, 44, 3, 169, 80, 122, 226, 101, 236, 245, 181, 53, 104, 133, 165, 51, 147, 162,
/// 2, 157, 36, 19, 148, 153, 114, 101, 161, 162, 90, 239, 198, 37, 47, 16, 200, 54, 16,
/// 235, 202, 26, 5, 156, 11, 174, 130, 85, 235, 162, 249, 91, 228, 209, 215, 188, 250,
/// 137, 215, 36, 138, 130, 217, 241, 17, 229, 160, 31, 238, 20, 224, 237, 92, 72, 113, 79,
/// 34, 24, 15, 37, 173, 131, 101, 181, 63, 151, 121, 247, 157, 196, 163, 215, 233, 57, 99,
/// 249, 74,
/// ];
///
/// let proof: MerkleProof<Bitcoin> = MerkleProof::deserialize::<BitcoinProofSerializer>(&proof_bytes)?;
/// # Ok(())
/// # }
/// ```
///
/// [`Hasher`]: crate::Hasher
#[derive(Clone)]
pub struct BitcoinHasher {}

impl Hasher for BitcoinHasher {
type Hash = [u8; 32];

fn hash(data: &[u8]) -> [u8; 32] {
let mut hasher = Sha256::new();

hasher.update(data);
<[u8; 32]>::from(hasher.finalize_fixed())
}

fn concat_and_hash(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)
}
None => *left,
}
}
}
2 changes: 2 additions & 0 deletions src/algorithms/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
//!
//! [`Hasher`]: crate::Hasher
mod sha256;
mod bitcoin;

pub use sha256::Sha256Algorithm as Sha256;
pub use bitcoin::BitcoinHasher as Bitcoin;
45 changes: 45 additions & 0 deletions src/proof_serializers/bitcoin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use crate::{Error, Hasher, MerkleProof, MerkleProofSerializer};
use std::convert::TryFrom;

/// Serializes proof data to bytes with a direct hash order - hashes are concatenated from
/// left to right, bottom to top.
pub struct BitcoinProofSerializer {}

impl MerkleProofSerializer for BitcoinProofSerializer {
fn serialize<T: Hasher>(proof: &MerkleProof<T>) -> Vec<u8> {
let mut vectors: Vec<Vec<u8>> = proof
.proof_hashes()
.iter()
.cloned()
.map(|hash| hash.into())
.collect();
vectors.drain(..).flatten().collect()
}

fn deserialize<T: Hasher>(bytes: &[u8]) -> Result<MerkleProof<T>, Error> {
let hash_size = T::hash_size();

if bytes.len() % hash_size != 0 {
return Err(Error::wrong_proof_size(bytes.len(), hash_size));
}

let hashes_count = bytes.len() / hash_size;
let mut proof_hashes_slices = Vec::<T::Hash>::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 = bytes
.get(slice_start..slice_end)
.ok_or_else(Error::vec_to_hash_conversion_error)?;
let vec =
Vec::<u8>::try_from(slice).map_err(|_| Error::vec_to_hash_conversion_error())?;
match T::Hash::try_from(vec) {
Ok(val) => proof_hashes_slices.push(val),
Err(_) => return Err(Error::vec_to_hash_conversion_error()),
}
}

Ok(MerkleProof::new(proof_hashes_slices))
}
}
2 changes: 2 additions & 0 deletions src/proof_serializers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
mod direct_hashes_order;
mod merkle_proof_serializer;
mod reverse_hashes_order;
mod bitcoin;

pub use direct_hashes_order::DirectHashesOrder;
pub use merkle_proof_serializer::MerkleProofSerializer;
pub use reverse_hashes_order::ReverseHashesOrder;
pub use bitcoin::BitcoinProofSerializer;