use crate::merkle::{
batch,
hasher::Hasher,
mem::{Config as MemConfig, Mem},
Error, Family, Location,
};
use commonware_cryptography::Digest;
use commonware_parallel::Strategy;
use commonware_utils::sync::RwLock;
use std::sync::Arc;
pub struct UnmerkleizedBatch<F: Family, D: Digest, S: Strategy> {
inner: batch::UnmerkleizedBatch<F, D, S>,
}
impl<F: Family, D: Digest, S: Strategy> UnmerkleizedBatch<F, D, S> {
pub(crate) const fn wrap(inner: batch::UnmerkleizedBatch<F, D, S>) -> Self {
Self { inner }
}
pub fn add(self, hasher: &impl Hasher<F, Digest = D>, element: &[u8]) -> Self {
Self {
inner: self.inner.add(hasher, element),
}
}
pub(crate) fn add_leaf_digests(self, digests: impl IntoIterator<Item = D>) -> Self {
Self {
inner: self.inner.add_leaf_digests(digests),
}
}
pub fn leaves(&self) -> Location<F> {
self.inner.leaves()
}
pub fn merkleize(
self,
base: &Mem<F, D>,
hasher: &impl Hasher<F, Digest = D>,
) -> Arc<batch::MerkleizedBatch<F, D, S>> {
self.inner.merkleize(base, hasher)
}
}
pub struct Merkle<F: Family, D: Digest, S: Strategy> {
inner: RwLock<Arc<Mem<F, D>>>,
strategy: S,
}
impl<F: Family, D: Digest, S: Strategy> Merkle<F, D, S> {
pub fn new(strategy: S) -> Self {
Self {
inner: RwLock::new(Arc::new(Mem::new())),
strategy,
}
}
pub(crate) fn from_compact_state(
strategy: S,
leaves: Location<F>,
pinned_nodes: Vec<D>,
) -> Result<Self, Error<F>> {
let mem = Self::mem_from_compact_state(leaves, pinned_nodes)?;
Ok(Self {
inner: RwLock::new(Arc::new(mem)),
strategy,
})
}
fn mem_from_compact_state(
leaves: Location<F>,
pinned_nodes: Vec<D>,
) -> Result<Mem<F, D>, Error<F>> {
if !leaves.is_valid() {
return Err(Error::LocationOverflow(leaves));
}
if pinned_nodes.len() != F::nodes_to_pin(leaves).count() {
return Err(Error::InvalidPinnedNodes);
}
if leaves == 0 {
Ok(Mem::new())
} else {
Mem::init(MemConfig {
nodes: vec![],
pruning_boundary: leaves,
pinned_nodes,
})
}
}
pub(crate) fn reset_to(
&self,
leaves: Location<F>,
pinned_nodes: Vec<D>,
) -> Result<(), Error<F>> {
let mem = Self::mem_from_compact_state(leaves, pinned_nodes)?;
*self.inner.write() = Arc::new(mem);
Ok(())
}
pub(crate) fn prune_to_frontier(&self) {
Arc::make_mut(&mut *self.inner.write()).prune_all();
}
pub fn root(
&self,
hasher: &impl Hasher<F, Digest = D>,
inactive_peaks: usize,
) -> Result<D, Error<F>> {
self.inner.read().root(hasher, inactive_peaks)
}
pub fn leaves(&self) -> Location<F> {
self.inner.read().leaves()
}
pub const fn strategy(&self) -> &S {
&self.strategy
}
pub fn with_mem<R>(&self, f: impl FnOnce(&Mem<F, D>) -> R) -> R {
let inner = self.inner.read();
f(&inner)
}
pub(crate) fn snapshot(&self) -> Arc<Mem<F, D>> {
Arc::clone(&self.inner.read())
}
pub fn new_batch(&self) -> UnmerkleizedBatch<F, D, S> {
let inner = self.inner.read();
UnmerkleizedBatch::wrap(inner.new_batch_with_strategy(self.strategy.clone()))
}
pub(crate) fn to_batch(&self) -> Arc<batch::MerkleizedBatch<F, D, S>> {
let inner = self.inner.read();
batch::MerkleizedBatch::from_mem_with_strategy(&inner, self.strategy.clone())
}
pub fn apply_batch(&mut self, batch: &batch::MerkleizedBatch<F, D, S>) -> Result<(), Error<F>> {
Arc::make_mut(self.inner.get_mut()).apply_batch(batch)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::merkle::{hasher::Standard as StandardHasher, mmb, mmr, Bagging::ForwardFold};
use commonware_cryptography::Sha256;
use commonware_parallel::Sequential;
type TestMerkle<F> = Merkle<F, <Sha256 as commonware_cryptography::Hasher>::Digest, Sequential>;
fn append<F: Family>(merkle: &mut TestMerkle<F>, values: &[&[u8]]) {
let hasher = StandardHasher::<Sha256>::new(ForwardFold);
let batch = {
let mut b = merkle.new_batch();
for v in values {
b = b.add(&hasher, v);
}
merkle.with_mem(|mem| b.merkleize(mem, &hasher))
};
merkle.apply_batch(&batch).unwrap();
}
fn pinned_nodes<F: Family>(
merkle: &TestMerkle<F>,
) -> Vec<<Sha256 as commonware_cryptography::Hasher>::Digest> {
merkle.with_mem(|mem| {
F::nodes_to_pin(mem.leaves())
.map(|pos| *mem.get_node_unchecked(pos))
.collect()
})
}
fn assert_reset_to_round_trip<F: Family>() {
let hasher = StandardHasher::<Sha256>::new(ForwardFold);
let mut merkle = TestMerkle::<F>::new(Sequential);
append(&mut merkle, &[b"a", b"b", b"c"]);
let root = merkle.root(&hasher, 0).unwrap();
let leaves = merkle.leaves();
let pins = pinned_nodes(&merkle);
merkle.prune_to_frontier();
assert_eq!(merkle.root(&hasher, 0).unwrap(), root);
let mut restored = TestMerkle::<F>::new(Sequential);
append(&mut restored, &[b"x"]);
restored.reset_to(leaves, pins.clone()).unwrap();
assert_eq!(restored.root(&hasher, 0).unwrap(), root);
assert_eq!(restored.leaves(), leaves);
append(&mut merkle, &[b"d"]);
append(&mut restored, &[b"d"]);
assert_eq!(
restored.root(&hasher, 0).unwrap(),
merkle.root(&hasher, 0).unwrap()
);
let from_state = TestMerkle::<F>::from_compact_state(Sequential, leaves, pins).unwrap();
assert_eq!(from_state.root(&hasher, 0).unwrap(), root);
}
#[test]
fn test_reset_to_round_trip_mmr() {
assert_reset_to_round_trip::<mmr::Family>();
}
#[test]
fn test_reset_to_round_trip_mmb() {
assert_reset_to_round_trip::<mmb::Family>();
}
#[test]
fn test_reset_to_rejects_invalid_snapshot() {
let mut merkle = TestMerkle::<mmr::Family>::new(Sequential);
append(&mut merkle, &[b"a", b"b"]);
let leaves = merkle.leaves();
assert!(matches!(
merkle.reset_to(leaves, vec![]),
Err(Error::InvalidPinnedNodes)
));
let too_many = Location::new(mmr::Family::MAX_LEAVES.as_u64() + 1);
assert!(matches!(
merkle.reset_to(too_many, vec![]),
Err(Error::LocationOverflow(loc)) if loc == too_many
));
}
}