use crate::tree::largest_pow2_lt;
use crate::TreeHasher;
pub(crate) fn mth<H: TreeHasher>(hasher: &H, leaves: &[H::Digest]) -> H::Digest {
match leaves.len() {
0 => hasher.empty(),
1 => leaves[0].clone(),
n => {
let k = largest_pow2_lt(n);
let left = mth(hasher, &leaves[..k]);
let right = mth(hasher, &leaves[k..]);
hasher.node(&left, &right)
}
}
}
pub(crate) fn gen_path<H: TreeHasher>(
hasher: &H,
m: usize,
leaves: &[H::Digest],
) -> Vec<H::Digest> {
let n = leaves.len();
if n == 1 {
return Vec::new();
}
let k = largest_pow2_lt(n);
if m < k {
let mut result = gen_path(hasher, m, &leaves[..k]);
result.push(mth(hasher, &leaves[k..]));
result
} else {
let mut result = gen_path(hasher, m - k, &leaves[k..]);
result.push(mth(hasher, &leaves[..k]));
result
}
}
pub(crate) fn gen_subproof<H: TreeHasher>(
hasher: &H,
m: usize,
leaves: &[H::Digest],
b: bool,
) -> Vec<H::Digest> {
let n = leaves.len();
if m == n {
if b {
return Vec::new();
} else {
return vec![mth(hasher, leaves)];
}
}
let k = largest_pow2_lt(n);
if m <= k {
let mut result = gen_subproof(hasher, m, &leaves[..k], b);
result.push(mth(hasher, &leaves[k..]));
result
} else {
let mut result = gen_subproof(hasher, m - k, &leaves[k..], false);
result.push(mth(hasher, &leaves[..k]));
result
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InclusionProof<D> {
pub index: u64,
pub tree_size: u64,
pub path: Vec<D>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ConsistencyProof<D> {
pub old_size: u64,
pub new_size: u64,
pub path: Vec<D>,
}
pub fn verify_inclusion<H: TreeHasher>(
hasher: &H,
leaf_hash: &H::Digest,
proof: &InclusionProof<H::Digest>,
root: &H::Digest,
) -> bool {
if proof.index >= proof.tree_size {
return false;
}
let mut fn_ = proof.index;
let mut sn = proof.tree_size - 1;
let mut r = leaf_hash.clone();
for p in &proof.path {
if sn == 0 {
return false;
}
if (fn_ & 1 == 1) || fn_ == sn {
r = hasher.node(p, &r);
while fn_ & 1 == 0 && fn_ != 0 {
fn_ >>= 1;
sn >>= 1;
}
} else {
r = hasher.node(&r, p);
}
fn_ >>= 1;
sn >>= 1;
}
sn == 0 && r == *root
}
pub fn verify_consistency<H: TreeHasher>(
hasher: &H,
proof: &ConsistencyProof<H::Digest>,
old_root: &H::Digest,
new_root: &H::Digest,
) -> bool {
if proof.old_size == 0 || proof.old_size >= proof.new_size {
return false;
}
let path: Vec<&H::Digest> = if proof.old_size.is_power_of_two() {
std::iter::once(old_root).chain(proof.path.iter()).collect()
} else {
proof.path.iter().collect()
};
if path.is_empty() {
return false;
}
let mut fn_ = proof.old_size - 1;
let mut sn = proof.new_size - 1;
while fn_ & 1 == 1 {
fn_ >>= 1;
sn >>= 1;
}
let mut fr = path[0].clone();
let mut sr = path[0].clone();
for c in &path[1..] {
if sn == 0 {
return false;
}
if (fn_ & 1 == 1) || fn_ == sn {
fr = hasher.node(c, &fr);
sr = hasher.node(c, &sr);
while fn_ & 1 == 0 && fn_ != 0 {
fn_ >>= 1;
sn >>= 1;
}
} else {
sr = hasher.node(&sr, c);
}
fn_ >>= 1;
sn >>= 1;
}
sn == 0 && fr == *old_root && sr == *new_root
}