use std::fmt;
use std::marker::PhantomData;
use ct_merkle::mem_backed_tree::MemoryBackedTree;
use serde::Serialize;
use sha2::Sha256;
use crate::error::{TrustError, TrustResult};
use crate::merkle::leaf::{SerializedLeaf, serialize_leaf};
use crate::merkle::proof::{ConsistencyProof, InclusionProof, RootHash};
pub struct AppendOnlyTree<L> {
inner: MemoryBackedTree<Sha256, SerializedLeaf>,
_marker: PhantomData<fn(L)>,
}
impl<L> AppendOnlyTree<L> {
pub fn new() -> Self {
Self {
inner: MemoryBackedTree::new(),
_marker: PhantomData,
}
}
pub fn len(&self) -> u64 {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn root(&self) -> RootHash {
RootHash::from_inner(self.inner.root())
}
pub fn prove_inclusion(&self, leaf_index: u64) -> TrustResult<InclusionProof> {
let len = self.inner.len();
if leaf_index >= len {
return Err(TrustError::MerkleTree {
reason: format!(
"inclusion proof requested for leaf index {leaf_index} but tree has {len} leaves"
),
});
}
let idx = usize::try_from(leaf_index).map_err(|_err| TrustError::MerkleTree {
reason: format!("inclusion proof leaf index {leaf_index} does not fit in usize"),
})?;
Ok(InclusionProof::from_inner(self.inner.prove_inclusion(idx)))
}
pub fn prove_consistency(&self, old_size: u64, new_size: u64) -> TrustResult<ConsistencyProof> {
let len = self.inner.len();
if new_size != len {
return Err(TrustError::MerkleTree {
reason: format!(
"consistency proof requires new_size to equal current tree length: \
new_size={new_size}, current_len={len}"
),
});
}
if old_size == 0 {
return Err(TrustError::MerkleTree {
reason: "consistency proof requires old_size > 0; \
RFC 6962 has no consistency proof from an empty tree"
.to_string(),
});
}
if old_size > new_size {
return Err(TrustError::MerkleTree {
reason: format!(
"consistency proof old_size must be <= new_size: \
old_size={old_size}, new_size={new_size}"
),
});
}
let num_additions = new_size - old_size;
let num_additions_usize =
usize::try_from(num_additions).map_err(|_err| TrustError::MerkleTree {
reason: format!(
"consistency proof num_additions {num_additions} does not fit in usize"
),
})?;
Ok(ConsistencyProof::from_inner(
self.inner.prove_consistency(num_additions_usize),
))
}
}
pub enum RawLeaf {}
impl AppendOnlyTree<RawLeaf> {
pub fn append_raw(&mut self, leaf_bytes: &[u8]) -> u64 {
self.inner
.push(SerializedLeaf::from_raw_bytes(leaf_bytes.to_vec()));
self.inner.len()
}
pub fn reconstruct_from_raw_leaves<I>(leaves: I) -> Self
where
I: IntoIterator,
I::Item: AsRef<[u8]>,
{
let mut tree = Self::new();
for leaf in leaves {
tree.append_raw(leaf.as_ref());
}
tree
}
}
impl<L: Serialize> AppendOnlyTree<L> {
pub fn append(&mut self, leaf: L) -> TrustResult<u64> {
let serialized = serialize_leaf(&leaf)?;
self.inner.push(serialized);
Ok(self.inner.len())
}
pub fn reconstruct_from_leaves(leaves: Vec<L>) -> TrustResult<Self> {
let mut tree = Self::new();
for leaf in leaves {
tree.append(leaf)?;
}
Ok(tree)
}
}
impl<L> Default for AppendOnlyTree<L> {
fn default() -> Self {
Self::new()
}
}
impl<L> fmt::Debug for AppendOnlyTree<L> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AppendOnlyTree")
.field("num_leaves", &self.inner.len())
.finish()
}
}
#[cfg(test)]
#[path = "tree_tests.rs"]
mod tests;