use anyhow::{bail, Result};
use sha2::{Digest, Sha256};
pub type ProofStep = [u8; 32];
type TreeLevels = (Vec<Vec<[u8; 32]>>, Vec<([u8; 32], usize)>);
fn leaf_hash(path: &str, sha256_hex: &str) -> [u8; 32] {
let mut h = Sha256::new();
h.update([0x00]);
h.update(path.as_bytes());
h.update([0x00]);
h.update(sha256_hex.as_bytes());
h.finalize().into()
}
fn node_hash(left: &[u8; 32], right: &[u8; 32]) -> [u8; 32] {
let (lo, hi) = if left <= right {
(left, right)
} else {
(right, left)
};
let mut h = Sha256::new();
h.update([0x01]);
h.update(lo.as_slice());
h.update(hi.as_slice());
h.finalize().into()
}
fn build_levels(entries: &[(String, String, String)]) -> TreeLevels {
let mut leaves: Vec<([u8; 32], usize)> = entries
.iter()
.enumerate()
.map(|(i, (_, path, hash))| (leaf_hash(path, hash), i))
.collect();
leaves.sort_by_key(|(h, _)| *h);
let leaf_hashes: Vec<[u8; 32]> = leaves.iter().map(|(h, _)| *h).collect();
let mut levels: Vec<Vec<[u8; 32]>> = vec![leaf_hashes];
loop {
let current = levels.last().unwrap();
if current.len() == 1 {
break;
}
let mut next = Vec::new();
let mut i = 0;
while i < current.len() {
if i + 1 < current.len() {
next.push(node_hash(¤t[i], ¤t[i + 1]));
i += 2;
} else {
next.push(node_hash(¤t[i], ¤t[i]));
i += 1;
}
}
levels.push(next);
}
(levels, leaves)
}
pub fn merkle_root(entries: &[(String, String, String)]) -> Result<String> {
if entries.is_empty() {
bail!("cannot compute Merkle root of empty manifest");
}
let (levels, _) = build_levels(entries);
let root = levels.last().unwrap()[0];
Ok(hex::encode(root))
}
pub fn generate_proof(entries: &[(String, String, String)], path: &str) -> Result<Vec<String>> {
if entries.is_empty() {
bail!("empty manifest");
}
let (levels, sorted_leaves) = build_levels(entries);
let entry = entries
.iter()
.find(|(_, p, _)| p == path)
.ok_or_else(|| anyhow::anyhow!("path not found in manifest: {path}"))?;
let target_leaf = leaf_hash(path, &entry.2);
let pos = sorted_leaves
.iter()
.position(|(h, _)| *h == target_leaf)
.ok_or_else(|| anyhow::anyhow!("leaf not found after sort (impossible)"))?;
if levels[0].len() == 1 {
return Ok(vec![]);
}
let mut proof = Vec::new();
let mut current_pos = pos;
for level in &levels[..levels.len() - 1] {
let sibling_pos = if current_pos % 2 == 0 {
if current_pos + 1 < level.len() {
current_pos + 1
} else {
current_pos
}
} else {
current_pos - 1
};
proof.push(hex::encode(level[sibling_pos]));
current_pos /= 2;
}
Ok(proof)
}
pub fn verify_proof(
root_hex: &str,
path: &str,
sha256_hex: &str,
proof: &[String],
) -> Result<bool> {
let mut current = leaf_hash(path, sha256_hex);
for sibling_hex in proof {
let sibling_bytes = hex::decode(sibling_hex)
.map_err(|_| anyhow::anyhow!("invalid hex in proof"))?;
if sibling_bytes.len() != 32 {
bail!("proof step must be 32 bytes");
}
let sibling: [u8; 32] = sibling_bytes.try_into().unwrap();
current = node_hash(¤t, &sibling);
}
let computed = hex::encode(current);
Ok(computed == root_hex)
}
pub fn build_tree(entries: &[(String, String, String)]) -> Result<(String, usize)> {
let root = merkle_root(entries)?;
Ok((root, entries.len()))
}