pub use incrementalmerkletree::{
frontier::{Frontier, NonEmptyFrontier},
Address, Hashable, Level, Position, Retention, Source,
};
use std::collections::{BTreeMap, BTreeSet, VecDeque};
use std::fmt::Debug;
use std::ops::Range;
use std::sync::OnceLock;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ContinuityError {
PriorPositionNotFound,
PositionMismatch(Position, Position),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum WitnessingError {
AuthBaseNotFound,
CheckpointInvalid,
CheckpointTooDeep(usize),
PositionNotMarked(Position),
BridgeFusionError(ContinuityError),
BridgeAddressInvalid(Address),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MerkleBridge<H> {
prior_position: Option<Position>,
tracking: BTreeSet<Address>,
ommers: BTreeMap<Address, H>,
frontier: NonEmptyFrontier<H>,
}
impl<H> MerkleBridge<H> {
pub fn new(value: H) -> Self {
Self {
prior_position: None,
tracking: BTreeSet::new(),
ommers: BTreeMap::new(),
frontier: NonEmptyFrontier::new(value),
}
}
pub fn from_parts(
prior_position: Option<Position>,
tracking: BTreeSet<Address>,
ommers: BTreeMap<Address, H>,
frontier: NonEmptyFrontier<H>,
) -> Self {
Self {
prior_position,
tracking,
ommers,
frontier,
}
}
pub fn prior_position(&self) -> Option<Position> {
self.prior_position
}
pub fn position(&self) -> Position {
self.frontier.position()
}
pub fn tracking(&self) -> &BTreeSet<Address> {
&self.tracking
}
pub fn ommers(&self) -> &BTreeMap<Address, H> {
&self.ommers
}
pub fn frontier(&self) -> &NonEmptyFrontier<H> {
&self.frontier
}
pub fn current_leaf(&self) -> &H {
self.frontier.leaf()
}
pub fn check_continuity(&self, next: &Self) -> Result<(), ContinuityError> {
if let Some(pos) = next.prior_position {
if pos == self.frontier.position() {
Ok(())
} else {
Err(ContinuityError::PositionMismatch(
self.frontier.position(),
pos,
))
}
} else {
Err(ContinuityError::PriorPositionNotFound)
}
}
pub fn position_range(&self) -> Range<Position> {
Range {
start: self.prior_position.unwrap_or_else(|| Position::from(0)),
end: self.position() + 1,
}
}
}
impl<'a, H: Hashable + Clone + Ord + 'a> MerkleBridge<H> {
#[must_use]
pub fn successor(&self, track_current_leaf: bool) -> Self {
let mut result = Self {
prior_position: Some(self.frontier.position()),
tracking: self.tracking.clone(),
ommers: BTreeMap::new(),
frontier: self.frontier.clone(),
};
if track_current_leaf {
result.track_current_leaf();
}
result
}
fn track_current_leaf(&mut self) {
self.tracking
.insert(Address::from(self.frontier.position()).current_incomplete());
}
pub fn append(&mut self, value: H) {
self.frontier.append(value);
let mut found = vec![];
for address in self.tracking.iter() {
if self
.frontier()
.position()
.is_complete_subtree(address.level())
{
let digest = self.frontier.root(Some(address.level()));
self.ommers.insert(address.sibling(), digest);
found.push(*address);
}
}
for address in found {
self.tracking.remove(&address);
let parent = address.next_incomplete_parent();
assert!(!self.ommers.contains_key(&parent));
self.tracking.insert(parent);
}
}
fn fuse(&self, next: &Self) -> Result<Self, ContinuityError> {
self.check_continuity(next)?;
Ok(Self {
prior_position: self.prior_position,
tracking: next.tracking.clone(),
ommers: self
.ommers
.iter()
.chain(next.ommers.iter())
.map(|(k, v)| (*k, v.clone()))
.collect(),
frontier: next.frontier.clone(),
})
}
fn fuse_all<T: Iterator<Item = &'a Self>>(
mut iter: T,
) -> Result<Option<Self>, ContinuityError> {
let mut fused = iter.next().cloned();
for next in iter {
fused = Some(fused.unwrap().fuse(next)?);
}
Ok(fused)
}
fn witness(
&self,
depth: u8,
prior_frontier: &NonEmptyFrontier<H>,
) -> Result<Vec<H>, WitnessingError> {
assert!(Some(prior_frontier.position()) == self.prior_position);
prior_frontier
.witness(depth, |addr| {
let r = addr.position_range();
if self.frontier.position() < r.start {
Some(H::empty_root(addr.level()))
} else if r.contains(&self.frontier.position()) {
Some(self.frontier.root(Some(addr.level())))
} else {
self.ommers.get(&addr).cloned()
}
})
.map_err(WitnessingError::BridgeAddressInvalid)
}
fn retain(&mut self, ommer_addrs: &BTreeSet<Address>) {
self.tracking
.retain(|addr| ommer_addrs.contains(&addr.sibling()));
self.ommers.retain(|addr, _| ommer_addrs.contains(addr));
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Checkpoint<C> {
id: C,
bridges_len: usize,
marked: BTreeSet<Position>,
forgotten: BTreeSet<Position>,
}
impl<C> Checkpoint<C> {
pub fn from_parts(
id: C,
bridges_len: usize,
marked: BTreeSet<Position>,
forgotten: BTreeSet<Position>,
) -> Self {
Self {
id,
bridges_len,
marked,
forgotten,
}
}
pub fn at_length(bridges_len: usize, id: C) -> Self {
Checkpoint {
id,
bridges_len,
marked: BTreeSet::new(),
forgotten: BTreeSet::new(),
}
}
pub fn id(&self) -> &C {
&self.id
}
pub fn bridges_len(&self) -> usize {
self.bridges_len
}
pub fn marked(&self) -> &BTreeSet<Position> {
&self.marked
}
pub fn forgotten(&self) -> &BTreeSet<Position> {
&self.forgotten
}
fn root<H>(&self, bridges: &[MerkleBridge<H>], level: Level) -> H
where
H: Hashable + Clone + Ord,
{
if self.bridges_len == 0 {
H::empty_root(level)
} else {
bridges[self.bridges_len - 1].frontier().root(Some(level))
}
}
fn position<H: Ord>(&self, bridges: &[MerkleBridge<H>]) -> Option<Position> {
if self.bridges_len == 0 {
None
} else {
Some(bridges[self.bridges_len - 1].position())
}
}
fn rewrite_indices<F: Fn(usize) -> usize>(&mut self, f: F) {
self.bridges_len = f(self.bridges_len);
}
}
#[derive(Clone)]
pub struct BridgeTree<H, C, const DEPTH: u8> {
prior_bridges: Vec<MerkleBridge<H>>,
current_bridge: Option<MerkleBridge<H>>,
saved: BTreeMap<Position, usize>,
checkpoints: VecDeque<Checkpoint<C>>,
max_checkpoints: usize,
current_root: OnceLock<H>,
}
impl<H: PartialEq, C: PartialEq, const DEPTH: u8> PartialEq for BridgeTree<H, C, DEPTH> {
fn eq(&self, other: &Self) -> bool {
self.prior_bridges == other.prior_bridges
&& self.current_bridge == other.current_bridge
&& self.saved == other.saved
&& self.checkpoints == other.checkpoints
&& self.max_checkpoints == other.max_checkpoints
}
}
impl<H: Eq, C: Eq, const DEPTH: u8> Eq for BridgeTree<H, C, DEPTH> {}
impl<H: Debug, C: Debug, const DEPTH: u8> Debug for BridgeTree<H, C, DEPTH> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
f,
"BridgeTree {{\n depth: {:?},\n prior_bridges: {:?},\n current_bridge: {:?},\n saved: {:?},\n checkpoints: {:?},\n max_checkpoints: {:?}\n}}",
DEPTH, self.prior_bridges, self.current_bridge, self.saved, self.checkpoints, self.max_checkpoints
)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BridgeTreeError {
IncorrectIncompleteIndex,
InvalidMarkIndex(usize),
PositionMismatch { expected: Position, found: Position },
InvalidSavePoints,
Discontinuity(ContinuityError),
CheckpointMismatch,
}
impl<H, C, const DEPTH: u8> BridgeTree<H, C, DEPTH> {
pub fn new(max_checkpoints: usize) -> Self {
assert!(max_checkpoints >= 1);
Self {
prior_bridges: vec![],
current_bridge: None,
saved: BTreeMap::new(),
checkpoints: VecDeque::new(),
max_checkpoints,
current_root: OnceLock::new(),
}
}
fn drop_oldest_checkpoint(&mut self) -> bool {
if self.checkpoints.len() > self.max_checkpoints {
let c = self
.checkpoints
.pop_front()
.expect("Checkpoints deque is known to be non-empty.");
for pos in c.forgotten.iter() {
self.saved.remove(pos);
}
true
} else {
false
}
}
pub fn prior_bridges(&self) -> &[MerkleBridge<H>] {
&self.prior_bridges
}
pub fn current_bridge(&self) -> &Option<MerkleBridge<H>> {
&self.current_bridge
}
pub fn marked_indices(&self) -> &BTreeMap<Position, usize> {
&self.saved
}
pub fn checkpoints(&self) -> &VecDeque<Checkpoint<C>> {
&self.checkpoints
}
pub fn max_checkpoints(&self) -> usize {
self.max_checkpoints
}
pub fn frontier(&self) -> Option<&NonEmptyFrontier<H>> {
self.current_bridge.as_ref().map(|b| b.frontier())
}
}
impl<H: Hashable + Clone + Ord, C: Clone + Ord, const DEPTH: u8> BridgeTree<H, C, DEPTH> {
pub fn from_frontier(max_checkpoints: usize, frontier: NonEmptyFrontier<H>) -> Self {
Self {
prior_bridges: vec![],
current_bridge: Some(MerkleBridge::from_parts(
None,
BTreeSet::new(),
BTreeMap::new(),
frontier,
)),
saved: BTreeMap::new(),
checkpoints: VecDeque::new(),
max_checkpoints,
current_root: OnceLock::new(),
}
}
pub fn from_parts(
prior_bridges: Vec<MerkleBridge<H>>,
current_bridge: Option<MerkleBridge<H>>,
saved: BTreeMap<Position, usize>,
checkpoints: VecDeque<Checkpoint<C>>,
max_checkpoints: usize,
) -> Result<Self, BridgeTreeError> {
Self::check_consistency_internal(
&prior_bridges,
¤t_bridge,
&saved,
&checkpoints,
max_checkpoints,
)?;
Ok(BridgeTree {
prior_bridges,
current_bridge,
saved,
checkpoints,
max_checkpoints,
current_root: OnceLock::new(),
})
}
fn check_consistency(&self) -> Result<(), BridgeTreeError> {
Self::check_consistency_internal(
&self.prior_bridges,
&self.current_bridge,
&self.saved,
&self.checkpoints,
self.max_checkpoints,
)
}
fn check_consistency_internal(
prior_bridges: &[MerkleBridge<H>],
current_bridge: &Option<MerkleBridge<H>>,
saved: &BTreeMap<Position, usize>,
checkpoints: &VecDeque<Checkpoint<C>>,
max_checkpoints: usize,
) -> Result<(), BridgeTreeError> {
for (pos, i) in saved {
if i >= &prior_bridges.len() {
return Err(BridgeTreeError::InvalidMarkIndex(*i));
}
let found = prior_bridges[*i].position();
if &found != pos {
return Err(BridgeTreeError::PositionMismatch {
expected: *pos,
found,
});
}
}
if checkpoints.len() > max_checkpoints
|| checkpoints
.iter()
.any(|c| c.bridges_len > prior_bridges.len())
{
return Err(BridgeTreeError::CheckpointMismatch);
}
for (prev, next) in prior_bridges.iter().zip(prior_bridges.iter().skip(1)) {
prev.check_continuity(next)
.map_err(BridgeTreeError::Discontinuity)?;
}
if let Some((prev, next)) = prior_bridges.last().zip(current_bridge.as_ref()) {
prev.check_continuity(next)
.map_err(BridgeTreeError::Discontinuity)?;
}
Ok(())
}
pub fn append(&mut self, value: H) -> bool {
if let Some(bridge) = self.current_bridge.as_mut() {
if bridge
.frontier
.position()
.is_complete_subtree(Level::from(DEPTH))
{
false
} else {
bridge.append(value);
self.current_root.take();
true
}
} else {
self.current_bridge = Some(MerkleBridge::new(value));
self.current_root.take();
true
}
}
pub fn root(&self, checkpoint_depth: usize) -> Option<H> {
let root_level = Level::from(DEPTH);
if checkpoint_depth == 0 {
let root = self.current_root.get_or_init(|| {
self.current_bridge
.as_ref()
.map_or(H::empty_root(root_level), |bridge| {
bridge.frontier().root(Some(root_level))
})
});
Some(root.clone())
} else if self.checkpoints.len() >= checkpoint_depth {
let checkpoint_idx = self.checkpoints.len() - checkpoint_depth;
self.checkpoints
.get(checkpoint_idx)
.map(|c| c.root(&self.prior_bridges, root_level))
} else {
None
}
}
pub fn current_position(&self) -> Option<Position> {
self.current_bridge.as_ref().map(|b| b.position())
}
pub fn current_leaf(&self) -> Option<&H> {
self.current_bridge.as_ref().map(|b| b.current_leaf())
}
pub fn mark(&mut self) -> Option<Position> {
match self.current_bridge.take() {
Some(mut cur_b) => {
let pos = cur_b.position();
if self
.prior_bridges
.last()
.is_some_and(|prior_b| prior_b.position() == cur_b.position())
{
cur_b.track_current_leaf();
self.current_bridge = Some(cur_b);
} else {
let successor = cur_b.successor(true);
self.prior_bridges.push(cur_b);
self.current_bridge = Some(successor);
}
if let std::collections::btree_map::Entry::Vacant(e) = self.saved.entry(pos) {
if let Some(c) = self.checkpoints.back_mut() {
c.marked.insert(pos);
}
e.insert(self.prior_bridges.len() - 1);
}
Some(pos)
}
None => None,
}
}
pub fn marked_positions(&self) -> BTreeSet<Position> {
self.saved.keys().cloned().collect()
}
pub fn get_marked_leaf(&self, position: Position) -> Option<&H> {
self.saved
.get(&position)
.and_then(|idx| self.prior_bridges.get(*idx).map(|b| b.current_leaf()))
}
pub fn remove_mark(&mut self, position: Position) -> bool {
if self.saved.contains_key(&position) {
if let Some(c) = self.checkpoints.back_mut() {
c.forgotten.insert(position);
} else {
self.saved.remove(&position);
}
true
} else {
false
}
}
pub fn checkpoint(&mut self, id: C) -> bool {
if Some(&id) > self.checkpoints.back().map(|c| &c.id) {
match self.current_bridge.take() {
Some(cur_b) => {
if self
.prior_bridges
.last()
.is_some_and(|pb| pb.position() == cur_b.position())
{
self.current_bridge = Some(cur_b);
} else {
self.current_bridge = Some(cur_b.successor(false));
self.prior_bridges.push(cur_b);
}
self.checkpoints
.push_back(Checkpoint::at_length(self.prior_bridges.len(), id));
}
None => {
self.checkpoints.push_back(Checkpoint::at_length(0, id));
}
}
if self.checkpoints.len() > self.max_checkpoints {
self.drop_oldest_checkpoint();
}
true
} else {
false
}
}
pub fn rewind(&mut self) -> bool {
if let Some(c) = self.checkpoints.pop_back() {
for pos in c.marked {
self.saved.remove(&pos);
}
self.prior_bridges.truncate(c.bridges_len);
self.current_bridge = self
.prior_bridges
.last()
.map(|b| b.successor(self.saved.contains_key(&b.position())));
self.current_root.take();
true
} else {
false
}
}
pub fn witness(
&self,
position: Position,
checkpoint_depth: usize,
) -> Result<Vec<H>, WitnessingError> {
let auth_base = if checkpoint_depth == 0 {
Ok(None)
} else if self.checkpoints.len() >= checkpoint_depth {
let c_idx = self.checkpoints.len() - checkpoint_depth;
if self
.checkpoints
.iter()
.skip(c_idx)
.take_while(|c| {
c.position(&self.prior_bridges)
.iter()
.any(|p| p <= &position)
})
.any(|c| c.marked.contains(&position))
{
Err(WitnessingError::PositionNotMarked(position))
} else {
Ok(Some(&self.checkpoints[c_idx]))
}
} else {
Err(WitnessingError::CheckpointInvalid)
}?;
let saved_idx = self
.saved
.get(&position)
.ok_or(WitnessingError::PositionNotMarked(position))?;
let prior_frontier = &self.prior_bridges[*saved_idx].frontier;
let fuse_from = saved_idx + 1;
let successor = match auth_base {
None => {
MerkleBridge::fuse_all(
self.prior_bridges[fuse_from..]
.iter()
.chain(&self.current_bridge),
)
.map(|fused| fused.unwrap()) .map_err(WitnessingError::BridgeFusionError)
}
Some(checkpoint) if fuse_from < checkpoint.bridges_len => {
MerkleBridge::fuse_all(self.prior_bridges[fuse_from..checkpoint.bridges_len].iter())
.map(|fused| fused.unwrap()) .map_err(WitnessingError::BridgeFusionError)
}
Some(checkpoint) if fuse_from == checkpoint.bridges_len => {
if checkpoint.bridges_len > 0 {
Ok(self.prior_bridges[checkpoint.bridges_len - 1].successor(false))
} else {
Err(WitnessingError::CheckpointInvalid)
}
}
Some(checkpoint) => {
Err(WitnessingError::CheckpointTooDeep(
fuse_from - checkpoint.bridges_len,
))
}
}?;
successor.witness(DEPTH, prior_frontier)
}
pub fn garbage_collect(&mut self) {
if self.checkpoints.len() == self.max_checkpoints {
let gc_len = self.checkpoints.front().unwrap().bridges_len;
let mut cur: Option<MerkleBridge<H>> = None;
let mut merged = 0;
let mut ommer_addrs: BTreeSet<Address> = BTreeSet::new();
for (i, next_bridge) in std::mem::take(&mut self.prior_bridges)
.into_iter()
.enumerate()
{
if let Some(cur_bridge) = cur {
let pos = cur_bridge.position();
let mut new_cur = if self.saved.contains_key(&pos) || i > gc_len {
if let Some(idx) = self.saved.get_mut(&pos) {
*idx -= merged;
}
for (addr, source) in cur_bridge
.frontier
.position()
.witness_addrs(Level::from(DEPTH))
{
if source == Source::Future {
ommer_addrs.insert(addr);
}
}
self.prior_bridges.push(cur_bridge);
next_bridge
} else {
merged += 1;
cur_bridge.fuse(&next_bridge).unwrap()
};
new_cur.retain(&ommer_addrs);
cur = Some(new_cur);
} else {
cur = Some(next_bridge);
}
}
if let Some(last_bridge) = cur {
if let Some(idx) = self.saved.get_mut(&last_bridge.position()) {
*idx -= merged;
}
self.prior_bridges.push(last_bridge);
}
for c in self.checkpoints.iter_mut() {
c.rewrite_indices(|idx| idx - merged);
}
}
if let Err(e) = self.check_consistency() {
panic!(
"Consistency check failed after garbage collection with {:?}",
e
);
}
}
}
#[cfg(test)]
mod tests {
use proptest::prelude::*;
use std::fmt::Debug;
use super::*;
use incrementalmerkletree::Hashable;
use incrementalmerkletree_testing::{
self as testing, apply_operation, arb_operation, check_checkpoint_rewind, check_operations,
check_remove_mark, check_rewind_remove_mark, check_root_hashes, check_witnesses,
complete_tree::CompleteTree, CombinedTree, SipHashable,
};
impl<H: Hashable + Clone + Ord, const DEPTH: u8> testing::Tree<H, usize>
for BridgeTree<H, usize, DEPTH>
{
fn append(&mut self, value: H, retention: Retention<usize>) -> bool {
let appended = BridgeTree::append(self, value);
if appended {
if retention.is_marked() {
BridgeTree::mark(self);
}
if let Retention::Checkpoint { id, .. } = retention {
BridgeTree::checkpoint(self, id);
}
}
appended
}
fn depth(&self) -> u8 {
DEPTH
}
fn current_position(&self) -> Option<Position> {
BridgeTree::current_position(self)
}
fn get_marked_leaf(&self, position: Position) -> Option<H> {
BridgeTree::get_marked_leaf(self, position).cloned()
}
fn marked_positions(&self) -> BTreeSet<Position> {
BridgeTree::marked_positions(self)
}
fn root(&self, checkpoint_depth: usize) -> Option<H> {
BridgeTree::root(self, checkpoint_depth)
}
fn witness(&self, position: Position, checkpoint_depth: usize) -> Option<Vec<H>> {
BridgeTree::witness(self, position, checkpoint_depth).ok()
}
fn remove_mark(&mut self, position: Position) -> bool {
BridgeTree::remove_mark(self, position)
}
fn checkpoint(&mut self, id: usize) -> bool {
BridgeTree::checkpoint(self, id)
}
fn rewind(&mut self) -> bool {
BridgeTree::rewind(self)
}
}
#[test]
fn tree_depth() {
let mut tree = BridgeTree::<String, usize, 3>::new(100);
for c in 'a'..'i' {
assert!(tree.append(c.to_string()))
}
assert!(!tree.append('i'.to_string()));
}
#[test]
fn bridgetree_is_send_and_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<BridgeTree<String, usize, 8>>();
}
fn check_garbage_collect<H: Hashable + Clone + Ord + Debug, const DEPTH: u8>(
mut tree: BridgeTree<H, usize, DEPTH>,
) {
for i in 0..tree.max_checkpoints {
tree.checkpoint(i + 1);
}
let mut tree_mut = tree.clone();
tree_mut.garbage_collect();
for pos in tree.saved.keys() {
assert_eq!(tree.witness(*pos, 0), tree_mut.witness(*pos, 0));
}
}
fn arb_bridgetree<G: Strategy + Clone>(
item_gen: G,
max_count: usize,
) -> impl Strategy<Value = BridgeTree<G::Value, usize, 8>>
where
G::Value: Hashable + Clone + Ord + Debug + 'static,
{
let pos_gen = (0..max_count).prop_map(|p| Position::try_from(p).unwrap());
proptest::collection::vec(arb_operation(item_gen, pos_gen), 0..max_count).prop_map(|ops| {
let mut tree: BridgeTree<G::Value, usize, 8> = BridgeTree::new(10);
for (i, op) in ops.into_iter().enumerate() {
apply_operation(&mut tree, op.map_checkpoint_id(|_| i));
}
tree
})
}
proptest! {
#[test]
fn bridgetree_from_parts(
tree in arb_bridgetree((97u8..123).prop_map(|c| char::from(c).to_string()), 100)
) {
assert_eq!(
BridgeTree::from_parts(
tree.prior_bridges.clone(),
tree.current_bridge.clone(),
tree.saved.clone(),
tree.checkpoints.clone(),
tree.max_checkpoints
),
Ok(tree),
);
}
#[test]
fn prop_garbage_collect(
tree in arb_bridgetree((97u8..123).prop_map(|c| char::from(c).to_string()), 100)
) {
check_garbage_collect(tree);
}
}
#[test]
fn root_hashes() {
check_root_hashes(BridgeTree::<String, usize, 4>::new);
}
#[test]
fn witness() {
check_witnesses(BridgeTree::<String, usize, 4>::new);
}
#[test]
fn checkpoint_rewind() {
check_checkpoint_rewind(|max_checkpoints| {
BridgeTree::<String, usize, 4>::new(max_checkpoints)
});
}
#[test]
fn rewind_remove_mark() {
check_rewind_remove_mark(|max_checkpoints| {
BridgeTree::<String, usize, 4>::new(max_checkpoints)
});
}
#[test]
fn garbage_collect() {
let mut tree: BridgeTree<String, usize, 7> = BridgeTree::new(1000);
let empty_root = tree.root(0);
tree.append("a".to_string());
for i in 0..100 {
tree.checkpoint(i + 1);
}
tree.garbage_collect();
assert!(tree.root(0) != empty_root);
tree.rewind();
assert!(tree.root(0) != empty_root);
let mut t = BridgeTree::<String, usize, 7>::new(10);
let mut to_unmark = vec![];
let mut has_witness = vec![];
for i in 0u64..100 {
let elem: String = format!("{},", i);
assert!(t.append(elem), "Append should succeed.");
if i % 5 == 0 {
t.checkpoint(usize::try_from(i).unwrap() + 1);
}
if i % 7 == 0 {
t.mark();
if i > 0 && i % 2 == 0 {
to_unmark.push(Position::from(i));
} else {
has_witness.push(Position::from(i));
}
}
if i % 11 == 0 && !to_unmark.is_empty() {
let pos = to_unmark.remove(0);
t.remove_mark(pos);
}
}
assert_eq!(t.prior_bridges().len(), 20 + 14 - 2);
let witness = has_witness
.iter()
.map(|pos| match t.witness(*pos, 0) {
Ok(path) => path,
Err(e) => panic!("Failed to get auth path: {:?}", e),
})
.collect::<Vec<_>>();
t.garbage_collect();
assert_eq!(t.prior_bridges().len(), 32 - 10 + 1 - 3);
let retained_witness = has_witness
.iter()
.map(|pos| t.witness(*pos, 0).expect("Must be able to get auth path"))
.collect::<Vec<_>>();
assert_eq!(witness, retained_witness);
}
fn new_combined_tree<H: Hashable + Clone + Ord + Debug>(
max_checkpoints: usize,
) -> CombinedTree<H, usize, CompleteTree<H, usize, 4>, BridgeTree<H, usize, 4>> {
CombinedTree::new(
CompleteTree::<H, usize, 4>::new(max_checkpoints),
BridgeTree::<H, usize, 4>::new(max_checkpoints),
)
}
#[test]
fn combined_remove_mark() {
check_remove_mark(new_combined_tree);
}
#[test]
fn combined_rewind_remove_mark() {
check_rewind_remove_mark(new_combined_tree);
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(100000))]
#[test]
fn check_randomized_u64_ops(
ops in proptest::collection::vec(
arb_operation(
(0..32u64).prop_map(SipHashable),
(0u64..100).prop_map(Position::from)
),
1..100
)
) {
let tree = new_combined_tree(100);
let indexed_ops = ops.iter().enumerate().map(|(i, op)| op.map_checkpoint_id(|_| i + 1)).collect::<Vec<_>>();
check_operations(tree, &indexed_ops)?;
}
#[test]
fn check_randomized_str_ops(
ops in proptest::collection::vec(
arb_operation(
(97u8..123).prop_map(|c| char::from(c).to_string()),
(0u64..100).prop_map(Position::from)
),
1..100
)
) {
let tree = new_combined_tree(100);
let indexed_ops = ops.iter().enumerate().map(|(i, op)| op.map_checkpoint_id(|_| i + 1)).collect::<Vec<_>>();
check_operations(tree, &indexed_ops)?;
}
}
}