use super::{AttestationError, DOM_EMPTY, hash_parts, node_hash};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProofStep {
pub sibling: [u8; 32],
pub sibling_is_left: bool,
}
pub fn merkle_root(leaves: &[[u8; 32]], session_id: &[u8; 16]) -> [u8; 32] {
if leaves.is_empty() {
return hash_parts(&[DOM_EMPTY, session_id]);
}
let mut level: Vec<[u8; 32]> = leaves.to_vec();
while level.len() > 1 {
let mut next = Vec::with_capacity(level.len().div_ceil(2));
let mut i = 0;
while i < level.len() {
if i + 1 < level.len() {
next.push(node_hash(&level[i], &level[i + 1]));
i += 2;
} else {
next.push(level[i]);
i += 1;
}
}
level = next;
}
level[0]
}
pub fn merkle_proof(leaves: &[[u8; 32]], index: usize) -> Result<Vec<ProofStep>, AttestationError> {
if index >= leaves.len() {
return Err(AttestationError::IndexOutOfRange(index));
}
let mut proof = Vec::new();
let mut level: Vec<[u8; 32]> = leaves.to_vec();
let mut idx = index;
while level.len() > 1 {
let mut next = Vec::with_capacity(level.len().div_ceil(2));
let mut i = 0;
while i < level.len() {
if i + 1 < level.len() {
if idx == i {
proof.push(ProofStep {
sibling: level[i + 1],
sibling_is_left: false,
});
} else if idx == i + 1 {
proof.push(ProofStep {
sibling: level[i],
sibling_is_left: true,
});
}
next.push(node_hash(&level[i], &level[i + 1]));
i += 2;
} else {
next.push(level[i]);
i += 1;
}
}
idx /= 2;
level = next;
}
Ok(proof)
}
pub fn verify_merkle_proof(leaf: &[u8; 32], proof: &[ProofStep], root: &[u8; 32]) -> bool {
let mut acc = *leaf;
for step in proof {
acc = if step.sibling_is_left {
node_hash(&step.sibling, &acc)
} else {
node_hash(&acc, &step.sibling)
};
}
&acc == root
}