use crate::merkle::{
hasher::Hasher, mem::Mem, path, proof::Proof, Error, Family, Location, Position, Readable,
};
use alloc::{
collections::BTreeMap,
sync::{Arc, Weak},
vec::Vec,
};
use commonware_cryptography::Digest;
use commonware_parallel::{Sequential, Strategy};
use core::ops::Range;
fn push_dirty<F: Family>(buckets: &mut Vec<Vec<Position<F>>>, height: u32, pos: Position<F>) {
let h = height as usize;
if buckets.len() <= h {
buckets.resize_with(h + 1, Vec::new);
}
buckets[h].push(pos);
}
pub struct UnmerkleizedBatch<F: Family, D: Digest, S: Strategy> {
parent: Arc<MerkleizedBatch<F, D, S>>,
appended: Vec<D>,
overwrites: BTreeMap<Position<F>, D>,
dirty_nodes: Vec<Vec<Position<F>>>,
}
impl<F: Family, D: Digest, S: Strategy> UnmerkleizedBatch<F, D, S> {
pub const fn new(parent: Arc<MerkleizedBatch<F, D, S>>) -> Self {
Self {
parent,
appended: Vec::new(),
overwrites: BTreeMap::new(),
dirty_nodes: Vec::new(),
}
}
pub fn strategy(&self) -> &S {
&self.parent.strategy
}
pub(crate) fn size(&self) -> Position<F> {
Position::new(*self.parent.size() + self.appended.len() as u64)
}
pub fn leaves(&self) -> Location<F> {
Location::try_from(self.size()).expect("invalid size")
}
fn get_node(&self, base: &Mem<F, D>, pos: Position<F>) -> Option<D> {
if pos >= self.size() {
return None;
}
if let Some(d) = self.overwrites.get(&pos) {
return Some(*d);
}
let parent_size = self.parent.size();
if pos >= parent_size {
let index = (*pos - *parent_size) as usize;
return self.appended.get(index).copied();
}
if let Some(d) = self.parent.get_node(pos) {
return Some(d);
}
base.get_node(pos)
}
fn store_node(&mut self, pos: Position<F>, digest: D) {
let parent_size = self.parent.size();
if pos >= parent_size {
let index = (*pos - *parent_size) as usize;
self.appended[index] = digest;
} else {
self.overwrites.insert(pos, digest);
}
}
fn mark_dirty(&mut self, loc: Location<F>) {
let mut first_leaf = Location::new(0);
for (peak_pos, height) in F::peaks(self.size()) {
let leaves_in_peak = 1u64 << height;
if loc >= first_leaf + leaves_in_peak {
first_leaf += leaves_in_peak;
continue;
}
let mut buf = [(Position::new(0), Position::new(0), 0u32); path::MAX_PATH_LEN];
let mut len = 0;
for item in path::Iterator::new(peak_pos, height, first_leaf, loc) {
buf[len] = item;
len += 1;
}
for &(parent_pos, _, h) in buf[..len].iter().rev() {
let h_idx = h as usize;
if self.dirty_nodes.get(h_idx).and_then(|b| b.last()) == Some(&parent_pos) {
break;
}
push_dirty(&mut self.dirty_nodes, h, parent_pos);
}
return;
}
panic!("leaf {loc} not found (size: {})", self.size());
}
pub fn add_leaf_digest(mut self, digest: D) -> Self {
let heights = F::parent_heights(self.leaves());
self.appended.push(digest);
for height in heights {
let pos = self.size();
self.appended.push(D::EMPTY);
push_dirty(&mut self.dirty_nodes, height, pos);
}
self
}
pub fn add(self, hasher: &impl Hasher<F, Digest = D>, element: &[u8]) -> Self {
let digest = hasher.leaf_digest(self.size(), element);
self.add_leaf_digest(digest)
}
fn validate_loc(&self, loc: Location<F>) -> Result<Position<F>, Error<F>> {
if loc >= self.leaves() {
return Err(Error::LeafOutOfBounds(loc));
}
if loc < self.parent.pruning_boundary() {
return Err(Error::ElementPruned(Position::try_from(loc)?));
}
Position::try_from(loc)
}
pub fn update_leaf(
mut self,
hasher: &impl Hasher<F, Digest = D>,
loc: Location<F>,
element: &[u8],
) -> Result<Self, Error<F>> {
let pos = self.validate_loc(loc)?;
let digest = hasher.leaf_digest(pos, element);
self.store_node(pos, digest);
self.mark_dirty(loc);
Ok(self)
}
#[cfg(any(feature = "std", test))]
pub fn update_leaf_digest(mut self, loc: Location<F>, digest: D) -> Result<Self, Error<F>> {
let pos = self.validate_loc(loc)?;
self.store_node(pos, digest);
self.mark_dirty(loc);
Ok(self)
}
#[cfg(any(feature = "std", test))]
pub fn update_leaf_batched(mut self, updates: &[(Location<F>, D)]) -> Result<Self, Error<F>> {
for (loc, _) in updates {
self.validate_loc(*loc)?;
}
for (loc, digest) in updates {
let pos = Position::try_from(*loc).expect("validated above");
self.store_node(pos, *digest);
self.mark_dirty(*loc);
}
Ok(self)
}
pub fn merkleize(
mut self,
base: &Mem<F, D>,
hasher: &impl Hasher<F, Digest = D>,
) -> Arc<MerkleizedBatch<F, D, S>> {
let mut buckets = core::mem::take(&mut self.dirty_nodes);
for bucket in &mut buckets {
bucket.sort();
bucket.dedup();
}
for (height, positions) in buckets.iter().enumerate() {
if positions.is_empty() {
continue;
}
self.merkleize_bucket(base, hasher, positions, height as u32);
}
let (ancestor_appended, ancestor_overwrites) = collect_ancestor_batches(&self.parent);
let parent_size = self.parent.size();
Arc::new(MerkleizedBatch {
parent: Some(Arc::downgrade(&self.parent)),
appended: Arc::new(self.appended),
overwrites: Arc::new(self.overwrites),
parent_size,
base_size: self.parent.base_size,
pruning_boundary: self.parent.pruning_boundary(),
ancestor_appended,
ancestor_overwrites,
strategy: self.parent.strategy.clone(),
})
}
fn merkleize_bucket(
&mut self,
base: &Mem<F, D>,
hasher: &impl Hasher<F, Digest = D>,
positions: &[Position<F>],
height: u32,
) {
let computed: Vec<(Position<F>, D)> = self.parent.strategy.map_init_collect_vec(
positions,
|| hasher.clone(),
|hasher, &pos| {
let (left, right) = F::children(pos, height);
let left_d = self.get_node(base, left).expect("left child missing");
let right_d = self.get_node(base, right).expect("right child missing");
let digest = hasher.node_digest(pos, &left_d, &right_d);
(pos, digest)
},
);
for (pos, digest) in computed {
self.store_node(pos, digest);
}
}
}
#[allow(clippy::type_complexity)]
fn collect_ancestor_batches<F: Family, D: Digest, S: Strategy>(
parent: &Arc<MerkleizedBatch<F, D, S>>,
) -> (Vec<Arc<Vec<D>>>, Vec<Arc<BTreeMap<Position<F>, D>>>) {
let mut appended = Vec::new();
let mut overwrites = Vec::new();
if !parent.appended.is_empty() || !parent.overwrites.is_empty() {
appended.push(Arc::clone(&parent.appended));
overwrites.push(Arc::clone(&parent.overwrites));
}
let mut current = parent.parent.as_ref().and_then(Weak::upgrade);
while let Some(batch) = current {
if !batch.appended.is_empty() || !batch.overwrites.is_empty() {
appended.push(Arc::clone(&batch.appended));
overwrites.push(Arc::clone(&batch.overwrites));
}
current = batch.parent.as_ref().and_then(Weak::upgrade);
}
appended.reverse();
overwrites.reverse();
(appended, overwrites)
}
#[derive(Debug)]
pub struct MerkleizedBatch<F: Family, D: Digest, S: Strategy> {
parent: Option<Weak<Self>>,
pub(crate) appended: Arc<Vec<D>>,
pub(crate) overwrites: Arc<BTreeMap<Position<F>, D>>,
pub(crate) parent_size: Position<F>,
pub(crate) base_size: Position<F>,
pruning_boundary: Location<F>,
pub(crate) ancestor_appended: Vec<Arc<Vec<D>>>,
pub(crate) ancestor_overwrites: Vec<Arc<BTreeMap<Position<F>, D>>>,
pub(crate) strategy: S,
}
impl<F: Family, D: Digest> MerkleizedBatch<F, D, Sequential> {
pub fn from_mem(mem: &Mem<F, D>) -> Arc<Self> {
Self::from_mem_with_strategy(mem, Sequential)
}
}
impl<F: Family, D: Digest, S: Strategy> MerkleizedBatch<F, D, S> {
pub fn from_mem_with_strategy(mem: &Mem<F, D>, strategy: S) -> Arc<Self> {
Arc::new(Self {
parent: None,
appended: Arc::new(Vec::new()),
overwrites: Arc::new(BTreeMap::new()),
parent_size: mem.size(),
base_size: mem.size(),
pruning_boundary: Readable::pruning_boundary(mem),
ancestor_appended: Vec::new(),
ancestor_overwrites: Vec::new(),
strategy,
})
}
pub fn size(&self) -> Position<F> {
Position::new(*self.parent_size + self.appended.len() as u64)
}
pub fn get_node(&self, pos: Position<F>) -> Option<D> {
if pos >= self.size() {
return None;
}
if let Some(d) = self.overwrites.get(&pos) {
return Some(*d);
}
if pos >= self.parent_size {
let i = (*pos - *self.parent_size) as usize;
return self.appended.get(i).copied();
}
let mut current = self.parent.as_ref().and_then(Weak::upgrade);
while let Some(batch) = current {
if let Some(d) = batch.overwrites.get(&pos) {
return Some(*d);
}
if pos >= batch.parent_size {
let i = (*pos - *batch.parent_size) as usize;
return batch.appended.get(i).copied();
}
current = batch.parent.as_ref().and_then(Weak::upgrade);
}
None
}
pub fn root(
&self,
base: &Mem<F, D>,
hasher: &impl Hasher<F, Digest = D>,
inactive_peaks: usize,
) -> Result<D, Error<F>> {
let leaves = self.leaves();
let peaks: Vec<D> = F::peaks(self.size())
.map(|(peak_pos, _)| {
self.get_node(peak_pos)
.or_else(|| base.get_node(peak_pos))
.expect("peak missing")
})
.collect();
hasher.root(leaves, inactive_peaks, peaks.iter())
}
pub fn proof(
&self,
hasher: &impl Hasher<F, Digest = D>,
loc: Location<F>,
inactive_peaks: usize,
) -> Result<Proof<F, D>, Error<F>> {
if !loc.is_valid_index() {
return Err(Error::LocationOverflow(loc));
}
self.range_proof(hasher, loc..loc + 1, inactive_peaks)
.map_err(|e| match e {
Error::RangeOutOfBounds(_) => Error::LeafOutOfBounds(loc),
_ => e,
})
}
pub fn range_proof(
&self,
hasher: &impl Hasher<F, Digest = D>,
range: Range<Location<F>>,
inactive_peaks: usize,
) -> Result<Proof<F, D>, Error<F>> {
crate::merkle::proof::build_range_proof(
hasher,
self.leaves(),
inactive_peaks,
range,
|pos| Self::get_node(self, pos),
Error::ElementPruned,
)
}
pub const fn pruning_boundary(&self) -> Location<F> {
self.pruning_boundary
}
pub fn leaves(&self) -> Location<F> {
Location::try_from(self.size()).expect("invalid size")
}
pub fn new_batch(self: &Arc<Self>) -> UnmerkleizedBatch<F, D, S> {
UnmerkleizedBatch::new(Arc::clone(self))
}
pub const fn base_size(&self) -> Position<F> {
self.base_size
}
pub const fn strategy(&self) -> &S {
&self.strategy
}
}
impl<F: Family, D: Digest, S: Strategy> Readable for MerkleizedBatch<F, D, S> {
type Family = F;
type Digest = D;
type Error = Error<F>;
fn size(&self) -> Position<F> {
Self::size(self)
}
fn get_node(&self, pos: Position<F>) -> Option<D> {
Self::get_node(self, pos)
}
fn pruning_boundary(&self) -> Location<F> {
Self::pruning_boundary(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::merkle::{hasher::Standard, mem::Mem, Bagging::ForwardFold};
use commonware_cryptography::{sha256, Sha256};
use commonware_runtime::{deterministic, Runner as _};
type D = sha256::Digest;
type H = Standard<Sha256>;
fn mem_root<F: Family>(mem: &Mem<F, D>, hasher: &H) -> D {
mem.root(hasher, 0).unwrap()
}
fn batch_root<F: Family>(
base: &Mem<F, D>,
batch: &MerkleizedBatch<F, D, commonware_parallel::Sequential>,
hasher: &H,
) -> D {
batch.root(base, hasher, 0).unwrap()
}
fn build_reference<F: Family>(hasher: &H, n: u64) -> Mem<F, D> {
let mut mem = Mem::new();
let batch = {
let mut batch = mem.new_batch();
for i in 0u64..n {
let element = hasher.digest(&i.to_be_bytes());
batch = batch.add(hasher, &element);
}
batch.merkleize(&mem, hasher)
};
mem.apply_batch(&batch).unwrap();
mem
}
fn consistency_with_reference<F: Family>() {
let executor = deterministic::Runner::default();
executor.start(|_| async move {
let hasher: H = Standard::new(ForwardFold);
for &n in &[1u64, 2, 10, 100, 199] {
let reference = build_reference::<F>(&hasher, n);
let base = Mem::<F, D>::new();
let mut batch = base.new_batch();
for i in 0..n {
let element = hasher.digest(&i.to_be_bytes());
batch = batch.add(&hasher, &element);
}
let merkleized = batch.merkleize(&base, &hasher);
let mut result = Mem::<F, D>::new();
result.apply_batch(&merkleized).unwrap();
assert_eq!(
mem_root(&result, &hasher),
mem_root(&reference, &hasher),
"root mismatch for n={n}"
);
}
});
}
fn lifecycle<F: Family>() {
let executor = deterministic::Runner::default();
executor.start(|_| async move {
let hasher: H = Standard::new(ForwardFold);
let base = build_reference::<F>(&hasher, 50);
let base_root = mem_root(&base, &hasher);
let mut batch = base.new_batch();
for i in 50u64..60 {
let element = hasher.digest(&i.to_be_bytes());
batch = batch.add(&hasher, &element);
}
let merkleized = batch.merkleize(&base, &hasher);
assert_ne!(batch_root(&base, &merkleized, &hasher), base_root);
assert_eq!(mem_root(&base, &hasher), base_root);
let mut applied = base;
applied.apply_batch(&merkleized).unwrap();
let loc = Location::<F>::new(55);
let element = hasher.digest(&55u64.to_be_bytes());
let proof = applied.proof(&hasher, loc, 0).unwrap();
assert!(proof.verify_element_inclusion(
&hasher,
&element,
loc,
&batch_root(&applied, &merkleized, &hasher)
));
});
}
fn apply_batch<F: Family>() {
let executor = deterministic::Runner::default();
executor.start(|_| async move {
let hasher: H = Standard::new(ForwardFold);
let mut base = build_reference::<F>(&hasher, 50);
let mut batch = base.new_batch();
for i in 50u64..75 {
let element = hasher.digest(&i.to_be_bytes());
batch = batch.add(&hasher, &element);
}
let merkleized = batch.merkleize(&base, &hasher);
let new_root = batch_root(&base, &merkleized, &hasher);
base.apply_batch(&merkleized).unwrap();
assert_eq!(mem_root(&base, &hasher), new_root);
let reference = build_reference::<F>(&hasher, 75);
assert_eq!(mem_root(&base, &hasher), mem_root(&reference, &hasher));
});
}
fn multiple_forks<F: Family>() {
let executor = deterministic::Runner::default();
executor.start(|_| async move {
let hasher: H = Standard::new(ForwardFold);
let base = build_reference::<F>(&hasher, 50);
let base_root = mem_root(&base, &hasher);
let mut ba = base.new_batch();
for i in 50u64..60 {
let element = hasher.digest(&i.to_be_bytes());
ba = ba.add(&hasher, &element);
}
let ma = ba.merkleize(&base, &hasher);
let mut bb = base.new_batch();
for i in 100u64..105 {
let element = hasher.digest(&i.to_be_bytes());
bb = bb.add(&hasher, &element);
}
let mb = bb.merkleize(&base, &hasher);
assert_ne!(
batch_root(&base, &ma, &hasher),
batch_root(&base, &mb, &hasher)
);
assert_ne!(batch_root(&base, &ma, &hasher), base_root);
assert_eq!(mem_root(&base, &hasher), base_root);
});
}
fn fork_of_fork_reads<F: Family>() {
let executor = deterministic::Runner::default();
executor.start(|_| async move {
let hasher: H = Standard::new(ForwardFold);
let base = build_reference::<F>(&hasher, 50);
let mut ba = base.new_batch();
for i in 50u64..60 {
let element = hasher.digest(&i.to_be_bytes());
ba = ba.add(&hasher, &element);
}
let ma = ba.merkleize(&base, &hasher);
let mut bb = ma.new_batch();
for i in 60u64..70 {
let element = hasher.digest(&i.to_be_bytes());
bb = bb.add(&hasher, &element);
}
let mb = bb.merkleize(&base, &hasher);
let reference = build_reference::<F>(&hasher, 70);
assert_eq!(
batch_root(&base, &mb, &hasher),
mem_root(&reference, &hasher)
);
let mut applied = base;
applied.apply_batch(&ma).unwrap();
applied.apply_batch(&mb).unwrap();
for i in [0u64, 25, 55, 65, 69] {
let loc = Location::<F>::new(i);
let element = hasher.digest(&i.to_be_bytes());
let proof = applied.proof(&hasher, loc, 0).unwrap();
assert!(proof.verify_element_inclusion(
&hasher,
&element,
loc,
&batch_root(&applied, &mb, &hasher)
));
}
});
}
fn update_leaf_digest_roundtrip<F: Family>() {
let executor = deterministic::Runner::default();
executor.start(|_| async move {
let hasher: H = Standard::new(ForwardFold);
let base = build_reference::<F>(&hasher, 100);
let base_root = mem_root(&base, &hasher);
let updated = Sha256::fill(0xFF);
let m = base
.new_batch()
.update_leaf_digest(Location::new(5), updated)
.unwrap()
.merkleize(&base, &hasher);
assert_ne!(batch_root(&base, &m, &hasher), base_root);
let pos5 = Position::<F>::try_from(Location::new(5)).unwrap();
let original = base.get_node(pos5).unwrap();
let m2 = base
.new_batch()
.update_leaf_digest(Location::new(5), original)
.unwrap()
.merkleize(&base, &hasher);
assert_eq!(batch_root(&base, &m2, &hasher), base_root);
});
}
fn update_and_add<F: Family>() {
let executor = deterministic::Runner::default();
executor.start(|_| async move {
let hasher: H = Standard::new(ForwardFold);
let base = build_reference::<F>(&hasher, 50);
let base_root = mem_root(&base, &hasher);
let updated = Sha256::fill(0xAA);
let mut batch = base
.new_batch()
.update_leaf_digest(Location::new(10), updated)
.unwrap();
for i in 50u64..55 {
let element = hasher.digest(&i.to_be_bytes());
batch = batch.add(&hasher, &element);
}
let m = batch.merkleize(&base, &hasher);
assert_ne!(batch_root(&base, &m, &hasher), base_root);
let pos10 = Position::<F>::try_from(Location::new(10)).unwrap();
assert_eq!(m.get_node(pos10), Some(updated));
});
}
fn update_leaf_batched_roundtrip<F: Family>() {
let executor = deterministic::Runner::default();
executor.start(|_| async move {
let hasher: H = Standard::new(ForwardFold);
let base = build_reference::<F>(&hasher, 100);
let base_root = mem_root(&base, &hasher);
let updated = Sha256::fill(0xBB);
let locs = [0u64, 10, 50, 99];
let updates: Vec<(Location<F>, D)> =
locs.iter().map(|&i| (Location::new(i), updated)).collect();
let m = base
.new_batch()
.update_leaf_batched(&updates)
.unwrap()
.merkleize(&base, &hasher);
assert_ne!(batch_root(&base, &m, &hasher), base_root);
let restore: Vec<(Location<F>, D)> = locs
.iter()
.map(|&l| {
let pos = Position::<F>::try_from(Location::new(l)).unwrap();
(Location::new(l), base.get_node(pos).unwrap())
})
.collect();
let m2 = base
.new_batch()
.update_leaf_batched(&restore)
.unwrap()
.merkleize(&base, &hasher);
assert_eq!(batch_root(&base, &m2, &hasher), base_root);
});
}
fn proof_verification<F: Family>() {
let executor = deterministic::Runner::default();
executor.start(|_| async move {
let hasher: H = Standard::new(ForwardFold);
let base = build_reference::<F>(&hasher, 50);
let mut batch = base.new_batch();
for i in 50u64..60 {
let element = hasher.digest(&i.to_be_bytes());
batch = batch.add(&hasher, &element);
}
let m = batch.merkleize(&base, &hasher);
let mut applied = base;
applied.apply_batch(&m).unwrap();
let loc = Location::<F>::new(55);
let element = hasher.digest(&55u64.to_be_bytes());
let proof = applied.proof(&hasher, loc, 0).unwrap();
assert!(proof.verify_element_inclusion(
&hasher,
&element,
loc,
&batch_root(&applied, &m, &hasher)
));
let range = Location::<F>::new(50)..Location::new(55);
let rp = applied.range_proof(&hasher, range.clone(), 0).unwrap();
let elements: Vec<D> = (50u64..55)
.map(|i| hasher.digest(&i.to_be_bytes()))
.collect();
assert!(rp.verify_range_inclusion(
&hasher,
&elements,
range.start,
&batch_root(&applied, &m, &hasher)
));
});
}
fn empty_batch<F: Family>() {
let executor = deterministic::Runner::default();
executor.start(|_| async move {
let hasher: H = Standard::new(ForwardFold);
let base = build_reference::<F>(&hasher, 50);
let base_root = mem_root(&base, &hasher);
let m = base.new_batch().merkleize(&base, &hasher);
assert_eq!(batch_root(&base, &m, &hasher), base_root);
});
}
fn batch_roundtrip<F: Family>() {
let executor = deterministic::Runner::default();
executor.start(|_| async move {
let hasher: H = Standard::new(ForwardFold);
let base = build_reference::<F>(&hasher, 50);
let mut batch = base.new_batch();
for i in 50u64..55 {
let element = hasher.digest(&i.to_be_bytes());
batch = batch.add(&hasher, &element);
}
let merkleized = batch.merkleize(&base, &hasher);
let mut batch_again = merkleized.new_batch();
for i in 55u64..60 {
let element = hasher.digest(&i.to_be_bytes());
batch_again = batch_again.add(&hasher, &element);
}
let reference = build_reference::<F>(&hasher, 60);
assert_eq!(
batch_root(&base, &batch_again.merkleize(&base, &hasher), &hasher),
mem_root(&reference, &hasher)
);
});
}
fn sequential_apply_batch<F: Family>() {
let executor = deterministic::Runner::default();
executor.start(|_| async move {
let hasher: H = Standard::new(ForwardFold);
let mut base = build_reference::<F>(&hasher, 50);
let mut b1 = base.new_batch();
for i in 50u64..60 {
let element = hasher.digest(&i.to_be_bytes());
b1 = b1.add(&hasher, &element);
}
let m1 = b1.merkleize(&base, &hasher);
base.apply_batch(&m1).unwrap();
let mut b2 = base.new_batch();
for i in 60u64..70 {
let element = hasher.digest(&i.to_be_bytes());
b2 = b2.add(&hasher, &element);
}
let m2 = b2.merkleize(&base, &hasher);
base.apply_batch(&m2).unwrap();
let reference = build_reference::<F>(&hasher, 70);
assert_eq!(mem_root(&base, &hasher), mem_root(&reference, &hasher));
});
}
fn batch_on_pruned_base<F: Family>() {
let executor = deterministic::Runner::default();
executor.start(|_| async move {
let hasher: H = Standard::new(ForwardFold);
let mut base = build_reference::<F>(&hasher, 100);
base.prune(Location::new(27)).unwrap();
let mut batch = base.new_batch();
for i in 100u64..110 {
let element = hasher.digest(&i.to_be_bytes());
batch = batch.add(&hasher, &element);
}
let m = batch.merkleize(&base, &hasher);
let expected_root = batch_root(&base, &m, &hasher);
let mut applied = base;
applied.apply_batch(&m).unwrap();
let loc = Location::<F>::new(80);
let element = hasher.digest(&80u64.to_be_bytes());
let proof = applied.proof(&hasher, loc, 0).unwrap();
assert!(proof.verify_element_inclusion(&hasher, &element, loc, &expected_root));
assert!(matches!(
applied.proof(&hasher, Location::new(0), 0),
Err(Error::ElementPruned(_))
));
});
}
fn three_deep_stacking<F: Family>() {
let executor = deterministic::Runner::default();
executor.start(|_| async move {
let hasher: H = Standard::new(ForwardFold);
let mut base = build_reference::<F>(&hasher, 100);
let da = Sha256::fill(0xDD);
let db = Sha256::fill(0xEE);
let ma = base
.new_batch()
.update_leaf_digest(Location::new(5), da)
.unwrap()
.merkleize(&base, &hasher);
let mb = ma
.new_batch()
.update_leaf_digest(Location::new(10), db)
.unwrap()
.merkleize(&base, &hasher);
let mut bc = mb.new_batch();
for i in 300u64..310 {
let element = hasher.digest(&i.to_be_bytes());
bc = bc.add(&hasher, &element);
}
let mc = bc.merkleize(&base, &hasher);
let c_root = batch_root(&base, &mc, &hasher);
base.apply_batch(&mc).unwrap();
assert_eq!(mem_root(&base, &hasher), c_root);
});
}
fn overwrite_collision<F: Family>() {
let executor = deterministic::Runner::default();
executor.start(|_| async move {
let hasher: H = Standard::new(ForwardFold);
let mut base = build_reference::<F>(&hasher, 100);
let dx = Sha256::fill(0xAA);
let dy = Sha256::fill(0xBB);
let ma = base
.new_batch()
.update_leaf_digest(Location::new(5), dx)
.unwrap()
.merkleize(&base, &hasher);
let mb = ma
.new_batch()
.update_leaf_digest(Location::new(5), dy)
.unwrap()
.merkleize(&base, &hasher);
let b_root = batch_root(&base, &mb, &hasher);
base.apply_batch(&mb).unwrap();
assert_eq!(mem_root(&base, &hasher), b_root);
let pos5 = Position::<F>::try_from(Location::new(5)).unwrap();
assert_eq!(base.get_node(pos5), Some(dy));
});
}
fn update_appended_leaf<F: Family>() {
let executor = deterministic::Runner::default();
executor.start(|_| async move {
let hasher: H = Standard::new(ForwardFold);
let base = build_reference::<F>(&hasher, 50);
let mut batch = base.new_batch();
for i in 50u64..60 {
let element = hasher.digest(&i.to_be_bytes());
batch = batch.add(&hasher, &element);
}
let updated = Sha256::fill(0xEE);
let m = batch
.update_leaf_digest(Location::new(52), updated)
.unwrap()
.merkleize(&base, &hasher);
let pos52 = Position::<F>::try_from(Location::new(52)).unwrap();
assert_eq!(m.get_node(pos52), Some(updated));
let mut reference = build_reference::<F>(&hasher, 60);
let batch = reference
.new_batch()
.update_leaf_digest(Location::new(52), updated)
.unwrap()
.merkleize(&reference, &hasher);
reference.apply_batch(&batch).unwrap();
assert_eq!(
batch_root(&base, &m, &hasher),
mem_root(&reference, &hasher)
);
});
}
fn update_leaf_element<F: Family>() {
let executor = deterministic::Runner::default();
executor.start(|_| async move {
let hasher: H = Standard::new(ForwardFold);
let base = build_reference::<F>(&hasher, 50);
let base_root = mem_root(&base, &hasher);
let element = b"updated-element";
let m = base
.new_batch()
.update_leaf(&hasher, Location::new(5), element)
.unwrap()
.merkleize(&base, &hasher);
assert_ne!(batch_root(&base, &m, &hasher), base_root);
let mut base = base;
let batch = base
.new_batch()
.update_leaf(&hasher, Location::new(5), element)
.unwrap()
.merkleize(&base, &hasher);
base.apply_batch(&batch).unwrap();
assert_eq!(batch_root(&base, &m, &hasher), mem_root(&base, &hasher));
});
}
fn update_out_of_bounds<F: Family>() {
let executor = deterministic::Runner::default();
executor.start(|_| async move {
let hasher: H = Standard::new(ForwardFold);
let base = build_reference::<F>(&hasher, 50);
let r1 = base
.new_batch()
.update_leaf_digest(Location::new(50), Sha256::fill(0xFF));
assert!(matches!(r1, Err(Error::LeafOutOfBounds(_))));
let updates = [(Location::<F>::new(50), Sha256::fill(0xFF))];
let r2 = base.new_batch().update_leaf_batched(&updates);
assert!(matches!(r2, Err(Error::LeafOutOfBounds(_))));
});
}
#[test]
fn mmr_consistency() {
consistency_with_reference::<crate::mmr::Family>();
}
#[test]
fn mmr_lifecycle() {
lifecycle::<crate::mmr::Family>();
}
#[test]
fn mmr_apply_batch() {
apply_batch::<crate::mmr::Family>();
}
#[test]
fn mmr_multiple_forks() {
multiple_forks::<crate::mmr::Family>();
}
#[test]
fn mmr_fork_of_fork_reads() {
fork_of_fork_reads::<crate::mmr::Family>();
}
#[test]
fn mmr_update_leaf_digest() {
update_leaf_digest_roundtrip::<crate::mmr::Family>();
}
#[test]
fn mmr_update_and_add() {
update_and_add::<crate::mmr::Family>();
}
#[test]
fn mmr_update_leaf_batched() {
update_leaf_batched_roundtrip::<crate::mmr::Family>();
}
#[test]
fn mmr_proof_verification() {
proof_verification::<crate::mmr::Family>();
}
#[test]
fn mmr_empty_batch() {
empty_batch::<crate::mmr::Family>();
}
#[test]
fn mmr_batch_roundtrip() {
batch_roundtrip::<crate::mmr::Family>();
}
#[test]
fn mmr_sequential_apply_batch() {
sequential_apply_batch::<crate::mmr::Family>();
}
#[test]
fn mmr_batch_on_pruned_base() {
batch_on_pruned_base::<crate::mmr::Family>();
}
#[test]
fn mmr_three_deep_stacking() {
three_deep_stacking::<crate::mmr::Family>();
}
#[test]
fn mmr_overwrite_collision() {
overwrite_collision::<crate::mmr::Family>();
}
#[test]
fn mmr_update_appended_leaf() {
update_appended_leaf::<crate::mmr::Family>();
}
#[test]
fn mmr_update_leaf_element() {
update_leaf_element::<crate::mmr::Family>();
}
#[test]
fn mmr_update_out_of_bounds() {
update_out_of_bounds::<crate::mmr::Family>();
}
#[test]
fn mmb_consistency() {
consistency_with_reference::<crate::mmb::Family>();
}
#[test]
fn mmb_lifecycle() {
lifecycle::<crate::mmb::Family>();
}
#[test]
fn mmb_apply_batch() {
apply_batch::<crate::mmb::Family>();
}
#[test]
fn mmb_multiple_forks() {
multiple_forks::<crate::mmb::Family>();
}
#[test]
fn mmb_fork_of_fork_reads() {
fork_of_fork_reads::<crate::mmb::Family>();
}
#[test]
fn mmb_update_leaf_digest() {
update_leaf_digest_roundtrip::<crate::mmb::Family>();
}
#[test]
fn mmb_update_and_add() {
update_and_add::<crate::mmb::Family>();
}
#[test]
fn mmb_update_leaf_batched() {
update_leaf_batched_roundtrip::<crate::mmb::Family>();
}
#[test]
fn mmb_proof_verification() {
proof_verification::<crate::mmb::Family>();
}
#[test]
fn mmb_empty_batch() {
empty_batch::<crate::mmb::Family>();
}
#[test]
fn mmb_batch_roundtrip() {
batch_roundtrip::<crate::mmb::Family>();
}
#[test]
fn mmb_sequential_apply_batch() {
sequential_apply_batch::<crate::mmb::Family>();
}
#[test]
fn mmb_batch_on_pruned_base() {
batch_on_pruned_base::<crate::mmb::Family>();
}
#[test]
fn mmb_three_deep_stacking() {
three_deep_stacking::<crate::mmb::Family>();
}
#[test]
fn mmb_overwrite_collision() {
overwrite_collision::<crate::mmb::Family>();
}
#[test]
fn mmb_update_appended_leaf() {
update_appended_leaf::<crate::mmb::Family>();
}
#[test]
fn mmb_update_leaf_element() {
update_leaf_element::<crate::mmb::Family>();
}
#[test]
fn mmb_update_out_of_bounds() {
update_out_of_bounds::<crate::mmb::Family>();
}
}