use std::sync::LazyLock;
use super::{Blake2b, Digest};
pub const NODE_PREFIX: u8 = 0x00;
pub const LEAF_PREFIX: u8 = 0x01;
static EMPTY: LazyLock<Digest> = LazyLock::new(|| Blake2b::new_raw().as_digest());
#[derive(Debug, PartialEq, Eq)]
pub struct MerkleTree {
data: Vec<(Digest, u8)>,
}
impl MerkleTree {
pub fn new() -> Self {
Self { data: Vec::new() }
}
pub fn add_leaf(&mut self, digest: Digest) {
self.add_node(digest, 0);
}
pub fn root(self) -> Digest {
Self::collapse(self.data)
}
fn merge(left: &Digest, right: &Digest) -> Digest {
Blake2b::new_node()
.update(left.as_bytes())
.update(right.as_bytes())
.as_digest()
}
fn add_node(&mut self, digest: Digest, depth: u8) {
if let [.., (last_digest, last_depth)] = self.data.as_mut_slice()
&& *last_depth == depth
{
let new_digest = Self::merge(last_digest, &digest);
let new_depth = depth + 1;
self.data.pop();
self.add_node(new_digest, new_depth);
} else {
let new_leaf = (digest, depth);
self.data.push(new_leaf);
}
}
fn collapse(mut data: Vec<(Digest, u8)>) -> Digest {
match data.as_mut_slice() {
[] => *EMPTY,
[(root, _)] => *root,
[.., (digest_m, depth_m), (digest_n, depth_n)] if *depth_m == *depth_n => {
let new_digest = Self::merge(digest_m, digest_n);
let new_depth = *depth_m + 1;
let new_leaf = (new_digest, new_depth);
data.truncate(data.len() - 2);
data.push(new_leaf);
Self::collapse(data)
}
[.., (_, depth_m), (_, depth_n)] if *depth_m > *depth_n => {
*depth_n = *depth_m;
Self::collapse(data)
}
_ => unreachable!(),
}
}
}
impl Default for MerkleTree {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::super::HASH_SIZE;
use super::*;
use hex::FromHex;
use proptest::prelude::*;
#[rustfmt::skip]
const GOLDEN: [(usize, &str); 5] = [
(2, "d3af86eb3e383618180e9e5e67b458f351335462871682ee332e20f8"),
(3, "37c91e733d32604abaf8ddff2b274f60e9b66a0311860ceddb5d43f4"),
(4, "e291561d5aa6555c44991f28f70dccb86352a4cd505b894a7bf85c4c"),
(5, "4aa703d6add1a49f1603328aa89db822122f174282c1637606c59f7c"),
(7, "ff159b2b1d0c703bf65bb0ae36dac26420cab122cc09df37c28df3a5"),
];
#[test]
fn empty_merkle_tree() {
let merkle = MerkleTree::new();
assert_eq!(merkle.root(), *EMPTY);
}
#[test]
fn golden() {
let leaf = Blake2b::new_leaf().update(b"Test").as_digest();
for (leaves, golden_bytestring) in GOLDEN.iter() {
let computed_root = {
let mut merkle = MerkleTree::new();
for _ in 0..*leaves {
merkle.add_leaf(leaf);
}
merkle.root()
};
let golden_root = {
let golden_bytes = <[u8; HASH_SIZE]>::from_hex(golden_bytestring).unwrap();
Digest::new(golden_bytes)
};
assert_eq!(computed_root, golden_root);
}
}
prop_compose! {
fn range_of_leaves(min: usize, max: usize)
(leaf_bytes in prop::collection::vec(any::<[u8; HASH_SIZE]>(), min..=max))
-> Vec<Digest> {
leaf_bytes.into_iter().map(Digest::new).collect()
}
}
fn n_leaves(count: usize) -> impl Strategy<Value = Vec<Digest>> {
range_of_leaves(count, count)
}
proptest! {
#[test]
fn single_leaf_is_root(leaves in n_leaves(1)) {
let mut merkle = MerkleTree::new();
let leaf = leaves[0];
merkle.add_leaf(leaf);
prop_assert_eq!(merkle.root(), leaf);
}
#[test]
fn deterministic_root(leaves in range_of_leaves(1, 16)) {
let mut merkle_1 = MerkleTree::new();
let mut merkle_2 = MerkleTree::new();
for leaf in leaves {
merkle_1.add_leaf(leaf);
merkle_2.add_leaf(leaf);
}
prop_assert_eq!(merkle_1.root(), merkle_2.root());
}
#[test]
fn non_commutativity(leaves in n_leaves(2)) {
prop_assume!(leaves[0] != leaves[1]);
let mut merkle_1 = MerkleTree::new();
merkle_1.add_leaf(leaves[0]);
merkle_1.add_leaf(leaves[1]);
let mut merkle_2 = MerkleTree::new();
merkle_2.add_leaf(leaves[1]);
merkle_2.add_leaf(leaves[0]);
prop_assert_ne!(merkle_1.root(), merkle_2.root());
}
#[test]
fn adding_leaf_changes_root(leaves in n_leaves(2)) {
let before = {
let mut merkle = MerkleTree::new();
merkle.add_leaf(leaves[0]);
merkle.root()
};
let after = {
let mut merkle = MerkleTree::new();
merkle.add_leaf(leaves[0]);
merkle.add_leaf(leaves[1]);
merkle.root()
};
prop_assert_ne!(before, after);
}
}
}