use alloc::vec::Vec;
use bitcoin::consensus::Encodable;
use bitcoin::hashes::Hash;
use bitcoin::hash_types::TxMerkleNode;
pub struct IncrementalHasher {
stack: Vec<Option<TxMerkleNode>>,
}
fn hash(left: TxMerkleNode, right: TxMerkleNode) -> TxMerkleNode {
let mut encoder = TxMerkleNode::engine();
left.consensus_encode(&mut encoder)
.expect("in-memory writers don't error");
right
.consensus_encode(&mut encoder)
.expect("in-memory writers don't error");
TxMerkleNode::from_engine(encoder)
}
impl IncrementalHasher {
pub fn new() -> Self {
Self { stack: Vec::new() }
}
pub fn add(&mut self, mut node: TxMerkleNode) {
for height in 0.. {
if self.stack.len() <= height {
self.stack.push(Some(node));
break;
}
let left = self.stack[height].take();
if let Some(left) = left {
node = hash(left, node);
} else {
self.stack[height] = Some(node);
break;
}
}
}
pub fn finish(self) -> TxMerkleNode {
let height = self.stack.len();
let mut node = None;
for (h, left) in self.stack.into_iter().enumerate() {
let is_last = h == height - 1;
if let Some(left) = left {
if let Some(right) = node {
node = Some(hash(left, right));
} else {
node = Some(if is_last { left } else { hash(left, left) });
}
} else {
if let Some(single) = node {
node = Some(if is_last {
single
} else {
hash(single, single)
});
}
}
}
node.expect("empty merkle tree")
}
}
#[cfg(test)]
mod tests {
use super::*;
use bitcoin::hashes::Hash;
use bitcoin::hash_types::TxMerkleNode;
#[test]
fn test_merkle() {
for len in 1..63 {
run_one(len);
}
}
fn run_one(len: usize) {
let nodes = (0..len)
.map(|i| TxMerkleNode::from_slice(&[i as u8; 32]).unwrap())
.collect::<Vec<_>>();
let root = bitcoin::merkle_tree::calculate_root(nodes.clone().into_iter());
let mut incremental = IncrementalHasher::new();
for node in nodes.iter() {
incremental.add(*node);
}
let incremental_root = incremental.finish();
assert_eq!(root, Some(incremental_root), "mismatch for len={}", len);
}
}