use incrementalmerkletree::Hashable;
use pasta_curves::Fp;
use crate::anchor::Anchor;
use crate::hash::{MerkleHashVote, TREE_DEPTH};
pub const MERKLE_PATH_BYTES: usize = 4 + 32 * TREE_DEPTH;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MerklePath {
position: u32,
auth_path: [MerkleHashVote; TREE_DEPTH],
}
impl From<incrementalmerkletree::MerklePath<MerkleHashVote, { TREE_DEPTH as u8 }>> for MerklePath {
fn from(path: incrementalmerkletree::MerklePath<MerkleHashVote, { TREE_DEPTH as u8 }>) -> Self {
let position: u64 = path.position().into();
Self {
position: position as u32,
auth_path: path.path_elems().try_into().unwrap(),
}
}
}
impl MerklePath {
pub fn from_parts(position: u32, auth_path: [MerkleHashVote; TREE_DEPTH]) -> Self {
Self {
position,
auth_path,
}
}
pub fn root(&self, leaf: MerkleHashVote) -> Anchor {
self.auth_path
.iter()
.enumerate()
.fold(leaf, |node, (l, sibling)| {
let l = l as u8;
if self.position & (1 << l) == 0 {
MerkleHashVote::combine(l.into(), &node, sibling)
} else {
MerkleHashVote::combine(l.into(), sibling, &node)
}
})
.into()
}
pub fn verify(&self, leaf: Fp, root: Fp) -> bool {
let computed = self.root(MerkleHashVote::from_fp(leaf));
computed.inner() == root
}
pub fn position(&self) -> u32 {
self.position
}
pub fn auth_path(&self) -> &[MerkleHashVote; TREE_DEPTH] {
&self.auth_path
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(MERKLE_PATH_BYTES);
buf.extend_from_slice(&self.position.to_le_bytes());
for hash in &self.auth_path {
buf.extend_from_slice(&hash.to_bytes());
}
buf
}
pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
if bytes.len() < MERKLE_PATH_BYTES {
return None;
}
let position = u32::from_le_bytes(bytes[0..4].try_into().ok()?);
let mut auth_path = [MerkleHashVote::from_fp(Fp::zero()); TREE_DEPTH];
for (i, hash) in auth_path.iter_mut().enumerate() {
let start = 4 + i * 32;
let chunk: [u8; 32] = bytes[start..start + 32].try_into().ok()?;
*hash = MerkleHashVote::from_bytes(&chunk)?;
}
Some(Self {
position,
auth_path,
})
}
}