use std::collections::HashMap;
use crate::hash;
use crate::log;
use crate::log::MerkleLog;
use crate::log::subtree_sizes;
use crate::store::Store;
#[derive(Clone, Debug)]
pub struct NodeBlock {
pub children: Vec<[u8; 32]>,
}
impl NodeBlock {
pub fn hash(&self) -> [u8; 32] {
hash::hash_parent(&self.children)
}
pub fn to_bytes(&self) -> Vec<u8> {
log::serialize_branch(&self.children)
}
pub fn from_bytes(data: &[u8], b: usize) -> Option<Self> {
let children = log::deserialize_branch(data, b)?;
Some(NodeBlock { children })
}
}
pub fn collect_needed_nodes<S: Store, const B: usize>(
log: &MerkleLog<S, B>,
has_seq: impl Fn(u64) -> bool,
) -> Result<Vec<NodeBlock>, S::Error> {
let sizes = subtree_sizes::<B>(log.length());
let mut out = Vec::new();
let mut offset: u64 = 0;
for (i, &size) in sizes.iter().enumerate() {
collect_subtree::<S, B>(
log.store(),
log.roots()[i],
size,
offset,
&has_seq,
&mut out,
)?;
offset += size;
}
Ok(out)
}
fn collect_subtree<S: Store, const B: usize>(
store: &S,
node_hash: [u8; 32],
size: u64,
offset: u64,
has_seq: &impl Fn(u64) -> bool,
out: &mut Vec<NodeBlock>,
) -> Result<(), S::Error> {
if size <= 1 {
return Ok(());
}
if (offset..offset + size).all(|seq| has_seq(seq)) {
return Ok(());
}
let data = store.get(node_hash)?;
let children = log::deserialize_branch(&data, B)
.expect("valid branch node");
out.push(NodeBlock { children: children.clone() });
let child_size = size / B as u64;
for (i, &child_hash) in children.iter().enumerate() {
let child_offset = offset + i as u64 * child_size;
collect_subtree::<S, B>(
store,
child_hash,
child_size,
child_offset,
has_seq,
out,
)?;
}
Ok(())
}
pub fn verify_partial<const B: usize>(
signable: [u8; 32],
roots: &[[u8; 32]],
length: u64,
nodes: &[NodeBlock],
leaf_hash: impl Fn(u64) -> Option<[u8; 32]>,
) -> bool {
if hash::hash_root(roots, length) != signable {
return false;
}
let mut lookup: HashMap<[u8; 32], &[[u8; 32]]> = HashMap::new();
for node in nodes {
lookup.insert(node.hash(), &node.children);
}
let sizes = subtree_sizes::<B>(length);
let mut offset: u64 = 0;
for (i, &size) in sizes.iter().enumerate() {
if !verify_node::<B>(&lookup, roots[i], size, offset, &leaf_hash) {
return false;
}
offset += size;
}
true
}
fn verify_node<const B: usize>(
lookup: &HashMap<[u8; 32], &[[u8; 32]]>,
expected: [u8; 32],
size: u64,
offset: u64,
leaf_hash: &impl Fn(u64) -> Option<[u8; 32]>,
) -> bool {
if size == 1 {
return leaf_hash(offset) == Some(expected);
}
let child_size = size / B as u64;
if let Some(children) = lookup.get(&expected) {
for (i, &child_hash) in children.iter().enumerate() {
let child_offset = offset + i as u64 * child_size;
if !verify_node::<B>(lookup, child_hash, child_size, child_offset, leaf_hash) {
return false;
}
}
true
} else {
match compute_bottom_up::<B>(size, offset, leaf_hash) {
Some(computed) => computed == expected,
None => false,
}
}
}
fn compute_bottom_up<const B: usize>(
size: u64,
offset: u64,
leaf_hash: &impl Fn(u64) -> Option<[u8; 32]>,
) -> Option<[u8; 32]> {
if size == 1 {
return leaf_hash(offset);
}
let child_size = size / B as u64;
let mut children = Vec::with_capacity(B);
for i in 0..B {
let child_offset = offset + i as u64 * child_size;
children.push(compute_bottom_up::<B>(child_size, child_offset, leaf_hash)?);
}
Some(hash::hash_parent(&children))
}