use std::collections::BTreeMap;
use spine::ProofStep;
use crate::shape::{self, ShapeNode};
pub(crate) fn inclusion_path(
shape: &ShapeNode,
index: u64,
cache: &BTreeMap<(u64, u64), Vec<u8>>,
leaf_digest: &mut dyn FnMut(u64) -> Vec<u8>,
node: &mut dyn FnMut(&[&[u8]]) -> Vec<u8>,
) -> Vec<ProofStep> {
let mut path = Vec::new();
descend(shape, index, cache, leaf_digest, node, &mut path);
path
}
fn descend(
cur: &ShapeNode,
index: u64,
cache: &BTreeMap<(u64, u64), Vec<u8>>,
leaf_digest: &mut dyn FnMut(u64) -> Vec<u8>,
node: &mut dyn FnMut(&[&[u8]]) -> Vec<u8>,
path: &mut Vec<ProofStep>,
) -> Vec<u8> {
match cur {
ShapeNode::Leaf(pos) => leaf_digest(*pos),
ShapeNode::Inner(children) => {
let position = children
.iter()
.position(|c| shape::covers(c, index))
.expect("the path index is covered by exactly one child");
let mut child_digests: Vec<Vec<u8>> = Vec::with_capacity(children.len());
for (i, child) in children.iter().enumerate() {
if i == position {
child_digests.push(descend(child, index, cache, leaf_digest, node, path));
} else {
child_digests.push(eval(child, cache, leaf_digest, node, &mut 0));
}
}
let siblings: Vec<Vec<u8>> = child_digests
.iter()
.enumerate()
.filter(|(i, _)| *i != position)
.map(|(_, d)| d.clone())
.collect();
path.push(ProofStep { siblings, position });
let refs: Vec<&[u8]> = child_digests.iter().map(Vec::as_slice).collect();
node(&refs)
},
}
}
fn eval(
cur: &ShapeNode,
cache: &BTreeMap<(u64, u64), Vec<u8>>,
leaf_digest: &mut dyn FnMut(u64) -> Vec<u8>,
node: &mut dyn FnMut(&[&[u8]]) -> Vec<u8>,
miss_counter: &mut usize,
) -> Vec<u8> {
let key = (shape::leftmost(cur), shape::rightmost(cur));
if let Some(cached) = cache.get(&key) {
return cached.clone();
}
*miss_counter += 1;
match cur {
ShapeNode::Leaf(pos) => leaf_digest(*pos),
ShapeNode::Inner(children) => {
let digests: Vec<Vec<u8>> = children
.iter()
.map(|c| eval(c, cache, leaf_digest, node, miss_counter))
.collect();
let refs: Vec<&[u8]> = digests.iter().map(Vec::as_slice).collect();
node(&refs)
},
}
}
#[cfg(test)]
pub(crate) fn inclusion_path_with_miss_count(
shape: &ShapeNode,
index: u64,
cache: &BTreeMap<(u64, u64), Vec<u8>>,
leaf_digest: &mut dyn FnMut(u64) -> Vec<u8>,
node: &mut dyn FnMut(&[&[u8]]) -> Vec<u8>,
) -> (Vec<ProofStep>, usize) {
let mut path = Vec::new();
let mut misses = 0usize;
descend_counted(
shape,
index,
cache,
leaf_digest,
node,
&mut path,
&mut misses,
);
(path, misses)
}
#[cfg(test)]
fn descend_counted(
cur: &ShapeNode,
index: u64,
cache: &BTreeMap<(u64, u64), Vec<u8>>,
leaf_digest: &mut dyn FnMut(u64) -> Vec<u8>,
node: &mut dyn FnMut(&[&[u8]]) -> Vec<u8>,
path: &mut Vec<ProofStep>,
misses: &mut usize,
) -> Vec<u8> {
match cur {
ShapeNode::Leaf(pos) => leaf_digest(*pos),
ShapeNode::Inner(children) => {
let position = children
.iter()
.position(|c| shape::covers(c, index))
.expect("the path index is covered by exactly one child");
let mut child_digests: Vec<Vec<u8>> = Vec::with_capacity(children.len());
for (i, child) in children.iter().enumerate() {
if i == position {
child_digests.push(descend_counted(
child,
index,
cache,
leaf_digest,
node,
path,
misses,
));
} else {
child_digests.push(eval(child, cache, leaf_digest, node, misses));
}
}
let siblings: Vec<Vec<u8>> = child_digests
.iter()
.enumerate()
.filter(|(i, _)| *i != position)
.map(|(_, d)| d.clone())
.collect();
path.push(ProofStep { siblings, position });
let refs: Vec<&[u8]> = child_digests.iter().map(Vec::as_slice).collect();
node(&refs)
},
}
}