use std::ops::{Deref, DerefMut};
use celestia_proto::celestia::core::v1::proof::NmtProof as RawNmtProof;
use celestia_proto::proof::pb::Proof as RawProof;
use nmt_rs::simple_merkle::proof::Proof as NmtProof;
use serde::{Deserialize, Serialize};
use tendermint_proto::Protobuf;
use crate::nmt::{NS_SIZE, NamespacedHash, NamespacedHashExt, NamespacedSha2Hasher};
use crate::{Error, Result};
type NmtNamespaceProof = nmt_rs::nmt_proof::NamespaceProof<NamespacedSha2Hasher, NS_SIZE>;
pub const EMPTY_LEAVES: &[&[u8]] = &[];
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(try_from = "RawProof", into = "RawProof")]
pub struct NamespaceProof(NmtNamespaceProof);
impl NamespaceProof {
pub fn into_inner(self) -> NmtNamespaceProof {
self.0
}
pub fn leaf(&self) -> Option<&NamespacedHash> {
match &self.0 {
NmtNamespaceProof::AbsenceProof { leaf, .. } => leaf.as_ref(),
_ => None,
}
}
pub fn max_ns_ignored(&self) -> bool {
match &self.0 {
NmtNamespaceProof::AbsenceProof { ignore_max_ns, .. }
| NmtNamespaceProof::PresenceProof { ignore_max_ns, .. } => *ignore_max_ns,
}
}
pub(crate) fn total_leaves(&self) -> Option<usize> {
if self.end_idx().saturating_sub(self.start_idx()) == 1 {
Some(1 << self.siblings().len())
} else {
None
}
}
}
impl Deref for NamespaceProof {
type Target = NmtNamespaceProof;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for NamespaceProof {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<NamespaceProof> for NmtNamespaceProof {
fn from(value: NamespaceProof) -> NmtNamespaceProof {
value.0
}
}
impl From<NmtNamespaceProof> for NamespaceProof {
fn from(value: NmtNamespaceProof) -> NamespaceProof {
NamespaceProof(value)
}
}
impl Protobuf<RawProof> for NamespaceProof {}
impl TryFrom<RawProof> for NamespaceProof {
type Error = Error;
fn try_from(value: RawProof) -> Result<Self, Self::Error> {
let siblings = value
.nodes
.iter()
.map(|bytes| NamespacedHash::from_raw(bytes))
.collect::<Result<Vec<_>>>()?;
let mut proof = NmtNamespaceProof::PresenceProof {
proof: NmtProof {
siblings,
range: value.start as u32..value.end as u32,
},
ignore_max_ns: value.is_max_namespace_ignored,
};
if !value.leaf_hash.is_empty() {
proof.convert_to_absence_proof(NamespacedHash::from_raw(&value.leaf_hash)?);
}
Ok(NamespaceProof(proof))
}
}
impl From<NamespaceProof> for RawProof {
fn from(value: NamespaceProof) -> Self {
RawProof {
start: value.start_idx() as i64,
end: value.end_idx() as i64,
nodes: value.siblings().iter().map(|hash| hash.to_vec()).collect(),
leaf_hash: value.leaf().map(|hash| hash.to_vec()).unwrap_or_default(),
is_max_namespace_ignored: value.max_ns_ignored(),
}
}
}
impl TryFrom<RawNmtProof> for NamespaceProof {
type Error = Error;
fn try_from(value: RawNmtProof) -> Result<Self, Self::Error> {
let raw_proof = RawProof {
start: value.start as i64,
end: value.end as i64,
nodes: value.nodes,
leaf_hash: value.leaf_hash,
is_max_namespace_ignored: true,
};
raw_proof.try_into()
}
}
impl From<NamespaceProof> for RawNmtProof {
fn from(value: NamespaceProof) -> Self {
let raw_proof = RawProof::from(value);
RawNmtProof {
start: raw_proof.start as i32,
end: raw_proof.end as i32,
nodes: raw_proof.nodes,
leaf_hash: raw_proof.leaf_hash,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_serialize_namespace_proof_binary() {
let nmt_proof = NmtNamespaceProof::PresenceProof {
proof: NmtProof {
siblings: vec![],
range: 0..1,
},
ignore_max_ns: false,
};
let proof = NamespaceProof::from(nmt_proof);
let serialized = postcard::to_allocvec(&proof).unwrap();
let deserialized: NamespaceProof = postcard::from_bytes(&serialized).unwrap();
assert_eq!(proof, deserialized);
}
}