use sha2::{Digest, Sha256};
const LEAF_PREFIX: u8 = 0x00;
const NODE_PREFIX: u8 = 0x01;
pub type Hash = [u8; 32];
pub fn hash_leaf(leaf: &[u8]) -> Hash {
let mut h = Sha256::new();
h.update([LEAF_PREFIX]);
h.update(leaf);
h.finalize().into()
}
pub fn hash_children(left: &Hash, right: &Hash) -> Hash {
#[cfg(test)]
tests::count_node_hash();
let mut h = Sha256::new();
h.update([NODE_PREFIX]);
h.update(left);
h.update(right);
h.finalize().into()
}
pub fn empty_root() -> Hash {
Sha256::digest([]).into()
}
fn split_point(n: u64) -> u64 {
debug_assert!(n >= 2);
1u64 << (63 - (n - 1).leading_zeros())
}
pub fn root(leaves: &[Hash]) -> Hash {
match leaves.len() {
0 => empty_root(),
1 => leaves[0],
n => {
let k = split_point(n as u64) as usize;
hash_children(&root(&leaves[..k]), &root(&leaves[k..]))
}
}
}
pub fn inclusion(index: u64, leaves: &[Hash]) -> Vec<Hash> {
fn path(m: u64, leaves: &[Hash]) -> Vec<Hash> {
let n = leaves.len() as u64;
if n <= 1 {
return Vec::new();
}
let k = split_point(n);
if m < k {
let mut proof = path(m, &leaves[..k as usize]);
proof.push(root(&leaves[k as usize..]));
proof
} else {
let mut proof = path(m - k, &leaves[k as usize..]);
proof.push(root(&leaves[..k as usize]));
proof
}
}
path(index, leaves)
}
pub fn verify_inclusion(
index: u64,
size: u64,
leaf_hash: &Hash,
proof: &[Hash],
want_root: &Hash,
) -> bool {
fn recompute(m: u64, n: u64, leaf_hash: &Hash, proof: &[Hash]) -> Option<Hash> {
if n <= 1 {
return if proof.is_empty() {
Some(*leaf_hash)
} else {
None
};
}
let k = split_point(n);
let (&sibling, rest) = proof.split_last()?;
if m < k {
let left = recompute(m, k, leaf_hash, rest)?;
Some(hash_children(&left, &sibling))
} else {
let right = recompute(m - k, n - k, leaf_hash, rest)?;
Some(hash_children(&sibling, &right))
}
}
index < size && recompute(index, size, leaf_hash, proof).as_ref() == Some(want_root)
}
pub fn consistency(first: u64, second: u64, leaves: &[Hash]) -> Vec<Hash> {
assert!(first <= second && second as usize <= leaves.len());
if first == 0 || first == second {
return Vec::new();
}
subproof(first, &leaves[..second as usize], true)
}
fn subproof(m: u64, leaves: &[Hash], b: bool) -> Vec<Hash> {
let n = leaves.len() as u64;
if m == n {
return if b { Vec::new() } else { vec![root(leaves)] };
}
let k = split_point(n);
if m <= k {
let mut proof = subproof(m, &leaves[..k as usize], b);
proof.push(root(&leaves[k as usize..]));
proof
} else {
let mut proof = subproof(m - k, &leaves[k as usize..], false);
proof.push(root(&leaves[..k as usize]));
proof
}
}
pub fn verify_consistency(
first: u64,
first_root: &Hash,
second: u64,
second_root: &Hash,
proof: &[Hash],
) -> bool {
if first == 0 || first > second {
return false;
}
if first == second {
return proof.is_empty() && first_root == second_root;
}
let mut cursor = proof.iter();
let Some((old, new)) = reconstruct(first, second, true, first_root, &mut cursor) else {
return false;
};
cursor.next().is_none() && &old == first_root && &new == second_root
}
fn reconstruct(
m: u64,
n: u64,
b: bool,
first_root: &Hash,
proof: &mut std::slice::Iter<'_, Hash>,
) -> Option<(Hash, Hash)> {
if m == n {
let node = if b { *first_root } else { *proof.next()? };
return Some((node, node));
}
let k = split_point(n);
if m <= k {
let (old, new_left) = reconstruct(m, k, b, first_root, proof)?;
Some((old, hash_children(&new_left, proof.next()?)))
} else {
let (old_right, new_right) = reconstruct(m - k, n - k, false, first_root, proof)?;
let left = proof.next()?;
Some((
hash_children(left, &old_right),
hash_children(left, &new_right),
))
}
}
#[derive(Debug, Clone, Default)]
pub struct Tree {
levels: Vec<Vec<Hash>>,
}
impl Tree {
pub fn new() -> Self {
Self::default()
}
pub fn rebuild(leaf_hashes: impl IntoIterator<Item = Hash>) -> Self {
let mut tree = Self::new();
for h in leaf_hashes {
tree.append(h);
}
tree
}
pub fn size(&self) -> u64 {
self.levels.first().map_or(0, |leaves| leaves.len() as u64)
}
pub fn append(&mut self, leaf_hash: Hash) {
if self.levels.is_empty() {
self.levels.push(Vec::new());
}
self.levels[0].push(leaf_hash);
let mut level = 0;
while self.levels[level].len().is_multiple_of(2) {
let nodes = &self.levels[level];
let parent = hash_children(&nodes[nodes.len() - 2], &nodes[nodes.len() - 1]);
if self.levels.len() == level + 1 {
self.levels.push(Vec::new());
}
self.levels[level + 1].push(parent);
level += 1;
}
}
pub fn root(&self) -> Hash {
match self.size() {
0 => empty_root(),
n => self.subtree(0, n),
}
}
pub fn root_at(&self, size: u64) -> Hash {
assert!(size <= self.size());
match size {
0 => empty_root(),
n => self.subtree(0, n),
}
}
pub fn consistency(&self, first: u64, second: u64) -> Vec<Hash> {
assert!(first <= second && second <= self.size());
let mut proof = Vec::new();
if first != 0 && first != second {
self.subproof(first, 0, second, true, &mut proof);
}
proof
}
fn subproof(&self, m: u64, start: u64, n: u64, b: bool, proof: &mut Vec<Hash>) {
if m == n {
if !b {
proof.push(self.subtree(start, n));
}
return;
}
let k = split_point(n);
if m <= k {
self.subproof(m, start, k, b, proof);
proof.push(self.subtree(start + k, n - k));
} else {
self.subproof(m - k, start + k, n - k, false, proof);
proof.push(self.subtree(start, k));
}
}
fn subtree(&self, start: u64, n: u64) -> Hash {
if n.is_power_of_two() {
let level = n.trailing_zeros() as usize;
debug_assert_eq!(start % n, 0, "a complete subtree is aligned");
return self.levels[level][(start >> level) as usize];
}
let k = split_point(n);
hash_children(&self.subtree(start, k), &self.subtree(start + k, n - k))
}
}
#[cfg(test)]
#[path = "merkle_tests.rs"]
mod tests;