use ct_merkle::{ConsistencyProof as CtConsistencyProof, InclusionProof as CtInclusionProof};
use serde::Serialize;
use sha2::Sha256;
use crate::error::{TrustError, TrustResult};
use crate::merkle::leaf::serialize_leaf;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RootHash {
inner: ct_merkle::RootHash<Sha256>,
}
impl RootHash {
pub(crate) fn from_inner(inner: ct_merkle::RootHash<Sha256>) -> Self {
Self { inner }
}
#[must_use]
pub fn from_parts(root_hash: [u8; 32], num_leaves: u64) -> Self {
let digest = sha2::digest::Output::<Sha256>::from(root_hash);
Self {
inner: ct_merkle::RootHash::new(digest, num_leaves),
}
}
#[must_use]
pub fn to_parts(&self) -> ([u8; 32], u64) {
((*self.inner.as_bytes()).into(), self.inner.num_leaves())
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
self.inner.as_bytes().as_slice()
}
#[must_use]
pub fn num_leaves(&self) -> u64 {
self.inner.num_leaves()
}
}
#[derive(Clone, Debug)]
pub struct InclusionProof {
inner: CtInclusionProof<Sha256>,
}
impl InclusionProof {
pub(crate) fn from_inner(inner: CtInclusionProof<Sha256>) -> Self {
Self { inner }
}
pub(crate) fn as_inner(&self) -> &CtInclusionProof<Sha256> {
&self.inner
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
self.inner.as_bytes()
}
pub fn try_from_bytes(bytes: Vec<u8>) -> TrustResult<Self> {
CtInclusionProof::<Sha256>::try_from_bytes(bytes)
.map(|inner| Self { inner })
.map_err(|e| TrustError::MerkleTree {
reason: format!("inclusion proof bytes are malformed: {e}"),
})
}
}
#[derive(Clone, Debug)]
pub struct ConsistencyProof {
inner: CtConsistencyProof<Sha256>,
}
impl ConsistencyProof {
pub(crate) fn from_inner(inner: CtConsistencyProof<Sha256>) -> Self {
Self { inner }
}
pub(crate) fn as_inner(&self) -> &CtConsistencyProof<Sha256> {
&self.inner
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
self.inner.as_bytes()
}
pub fn try_from_bytes(bytes: Vec<u8>) -> TrustResult<Self> {
CtConsistencyProof::<Sha256>::try_from_bytes(bytes)
.map(|inner| Self { inner })
.map_err(|e| TrustError::MerkleTree {
reason: format!("consistency proof bytes are malformed: {e}"),
})
}
}
pub fn verify_inclusion<L: Serialize>(
root_hash: &RootHash,
leaf: &L,
index: u64,
proof: &InclusionProof,
) -> TrustResult<()> {
let serialized = serialize_leaf(leaf)?;
root_hash
.inner
.verify_inclusion(&serialized, index, proof.as_inner())
.map_err(|e| TrustError::MerkleTree {
reason: format!("inclusion proof verification failed: {e}"),
})
}
pub fn verify_inclusion_raw(
root_hash: &RootHash,
leaf_bytes: &[u8],
index: u64,
proof: &InclusionProof,
) -> TrustResult<()> {
root_hash
.inner
.verify_inclusion(&leaf_bytes, index, proof.as_inner())
.map_err(|e| TrustError::MerkleTree {
reason: format!("inclusion proof verification failed: {e}"),
})
}
pub fn verify_consistency(
old_root: &RootHash,
new_root: &RootHash,
proof: &ConsistencyProof,
) -> TrustResult<()> {
new_root
.inner
.verify_consistency(&old_root.inner, proof.as_inner())
.map_err(|e| TrustError::MerkleTree {
reason: format!("consistency proof verification failed: {e}"),
})
}
#[cfg(test)]
#[path = "proof_tests.rs"]
mod tests;