mirror of
https://github.com/tlsnotary/rs-merkle.git
synced 2026-01-08 22:38:07 -05:00
155 lines
5.1 KiB
Rust
155 lines
5.1 KiB
Rust
use std::convert::TryFrom;
|
|
|
|
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.
|
|
///
|
|
/// # 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>,
|
|
}
|
|
|
|
impl<T: Hasher> MerkleProof<T> {
|
|
pub fn new(proof_hashes: Vec<T::Hash>) -> Self {
|
|
MerkleProof { proof_hashes }
|
|
}
|
|
|
|
/// Parses proof serialized as bytes
|
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self, 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(Self::new(proof_hashes_slices))
|
|
}
|
|
|
|
/// Returns all hashes from the proof
|
|
pub fn proof_hashes(&self) -> &[T::Hash] {
|
|
&self.proof_hashes
|
|
}
|
|
|
|
pub fn hex_proof_hashes(&self) -> Vec<String> {
|
|
self.proof_hashes
|
|
.iter()
|
|
.map(utils::collections::to_hex_string)
|
|
.collect()
|
|
}
|
|
|
|
/// Calculates merkle root based on provided leaves and proof hashes
|
|
pub fn root(
|
|
&self,
|
|
leaf_indices: &[usize],
|
|
leaf_hashes: &[T::Hash],
|
|
total_leaves_count: usize,
|
|
) -> Result<T::Hash, Error> {
|
|
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
|
|
.iter()
|
|
.cloned()
|
|
.zip(leaf_hashes.iter().cloned())
|
|
.collect();
|
|
// Sorting leaves by indexes in case they weren't sorted already
|
|
leaf_tuples.sort_by(|(a, _), (b, _)| a.cmp(b));
|
|
// Getting back _sorted_ indices
|
|
let proof_indices_by_layers =
|
|
utils::indices::proof_indices_by_layers(leaf_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_copy = self.proof_hashes.clone();
|
|
for proof_indices in proof_indices_by_layers {
|
|
let proof_hashes = proof_copy.splice(0..proof_indices.len(), []);
|
|
proof_layers.push(proof_indices.iter().cloned().zip(proof_hashes).collect());
|
|
}
|
|
|
|
match proof_layers.first_mut() {
|
|
Some(first_layer) => {
|
|
first_layer.append(&mut leaf_tuples);
|
|
first_layer.sort_by(|(a, _), (b, _)| a.cmp(b));
|
|
}
|
|
None => proof_layers.push(leaf_tuples),
|
|
}
|
|
|
|
let partial_tree = PartialTree::<T>::build(proof_layers, tree_depth)?;
|
|
|
|
match partial_tree.root() {
|
|
Some(root) => Ok(*root),
|
|
None => Err(Error::not_enough_hashes_to_calculate_root()),
|
|
}
|
|
}
|
|
|
|
/// Calculates the root and serializes it into a hex string
|
|
pub fn hex_root(
|
|
&self,
|
|
leaf_indices: &[usize],
|
|
leaf_hashes: &[T::Hash],
|
|
total_leaves_count: usize,
|
|
) -> Result<String, Error> {
|
|
let root = self.root(leaf_indices, leaf_hashes, total_leaves_count)?;
|
|
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,
|
|
}
|
|
}
|
|
|
|
/// Serializes proof hashes to a flat vector of bytes
|
|
pub fn to_bytes(&self) -> Vec<u8> {
|
|
let vectors: Vec<Vec<u8>> = self
|
|
.proof_hashes()
|
|
.iter()
|
|
.cloned()
|
|
.map(|hash| hash.into())
|
|
.collect();
|
|
vectors.iter().cloned().flatten().collect()
|
|
}
|
|
}
|