2022-12-14 18:33:19 -08:00
|
|
|
//! Traits and types used to permit comparison testing between tree implementations.
|
|
|
|
|
2022-12-14 17:56:36 -08:00
|
|
|
use core::fmt::Debug;
|
2022-12-14 18:33:19 -08:00
|
|
|
use core::marker::PhantomData;
|
2022-12-14 16:25:34 -08:00
|
|
|
use proptest::prelude::*;
|
2022-12-14 16:17:04 -08:00
|
|
|
use std::collections::BTreeSet;
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
use crate::{Hashable, Level, Position, Retention};
|
2022-12-14 16:25:34 -08:00
|
|
|
|
2022-12-14 18:12:53 -08:00
|
|
|
pub mod complete_tree;
|
|
|
|
|
2022-12-14 16:17:04 -08:00
|
|
|
//
|
|
|
|
// Traits used to permit comparison testing between tree implementations.
|
|
|
|
//
|
|
|
|
|
|
|
|
/// A possibly-empty incremental Merkle frontier.
|
|
|
|
pub trait Frontier<H> {
|
|
|
|
/// Appends a new value to the frontier at the next available slot.
|
|
|
|
/// Returns true if successful and false if the frontier would exceed
|
|
|
|
/// the maximum allowed depth.
|
2023-01-04 13:08:11 -08:00
|
|
|
fn append(&mut self, value: H) -> bool;
|
2022-12-14 16:17:04 -08:00
|
|
|
|
|
|
|
/// Obtains the current root of this Merkle frontier by hashing
|
|
|
|
/// against empty nodes up to the maximum height of the pruned
|
|
|
|
/// tree that the frontier represents.
|
|
|
|
fn root(&self) -> H;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A Merkle tree that supports incremental appends, marking of
|
|
|
|
/// leaf nodes for construction of witnesses, checkpoints and rollbacks.
|
2022-12-15 14:02:44 -08:00
|
|
|
pub trait Tree<H, C> {
|
2022-12-22 10:44:30 -08:00
|
|
|
/// Returns the depth of the tree.
|
|
|
|
fn depth(&self) -> u8;
|
|
|
|
|
2022-12-14 16:17:04 -08:00
|
|
|
/// Appends a new value to the tree at the next available slot.
|
|
|
|
/// Returns true if successful and false if the tree would exceed
|
|
|
|
/// the maximum allowed depth.
|
2022-12-15 14:02:44 -08:00
|
|
|
fn append(&mut self, value: H, retention: Retention<C>) -> bool;
|
2022-12-14 16:17:04 -08:00
|
|
|
|
|
|
|
/// Returns the most recently appended leaf value.
|
|
|
|
fn current_position(&self) -> Option<Position>;
|
|
|
|
|
|
|
|
/// Returns the leaf at the specified position if the tree can produce
|
|
|
|
/// a witness for it.
|
|
|
|
fn get_marked_leaf(&self, position: Position) -> Option<&H>;
|
|
|
|
|
|
|
|
/// Return a set of all the positions for which we have marked.
|
|
|
|
fn marked_positions(&self) -> BTreeSet<Position>;
|
|
|
|
|
|
|
|
/// Obtains the root of the Merkle tree at the specified checkpoint depth
|
|
|
|
/// by hashing against empty nodes up to the maximum height of the tree.
|
|
|
|
/// Returns `None` if there are not enough checkpoints available to reach the
|
|
|
|
/// requested checkpoint depth.
|
|
|
|
fn root(&self, checkpoint_depth: usize) -> Option<H>;
|
|
|
|
|
2023-03-03 16:02:03 -08:00
|
|
|
/// Obtains a witness for the value at the specified leaf position, as of the tree state at the
|
|
|
|
/// given checkpoint depth. Returns `None` if there is no witness information for the requested
|
|
|
|
/// position or if no checkpoint is available at the specified depth.
|
2022-12-22 10:44:30 -08:00
|
|
|
fn witness(&self, position: Position, checkpoint_depth: usize) -> Option<Vec<H>>;
|
2022-12-14 16:17:04 -08:00
|
|
|
|
|
|
|
/// Marks the value at the specified position as a value we're no longer
|
|
|
|
/// interested in maintaining a mark for. Returns true if successful and
|
|
|
|
/// false if we were already not maintaining a mark at this position.
|
|
|
|
fn remove_mark(&mut self, position: Position) -> bool;
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
/// Creates a new checkpoint for the current tree state.
|
|
|
|
///
|
2023-01-13 07:40:57 -08:00
|
|
|
/// It is valid to have multiple checkpoints for the same tree state, and each `rewind` call
|
|
|
|
/// will remove a single checkpoint. Returns `false` if the checkpoint identifier provided is
|
|
|
|
/// less than or equal to the maximum checkpoint identifier observed.
|
2022-12-15 14:02:44 -08:00
|
|
|
fn checkpoint(&mut self, id: C) -> bool;
|
2022-12-14 16:17:04 -08:00
|
|
|
|
2023-01-13 07:40:57 -08:00
|
|
|
/// Rewinds the tree state to the previous checkpoint, and then removes that checkpoint record.
|
|
|
|
///
|
|
|
|
/// If there are multiple checkpoints at a given tree state, the tree state will not be altered
|
|
|
|
/// until all checkpoints at that tree state have been removed using `rewind`. This function
|
|
|
|
/// will return false and leave the tree unmodified if no checkpoints exist.
|
2022-12-14 16:17:04 -08:00
|
|
|
fn rewind(&mut self) -> bool;
|
|
|
|
}
|
2022-12-21 11:52:33 -08:00
|
|
|
|
2022-12-14 16:25:34 -08:00
|
|
|
//
|
|
|
|
// Types and utilities for shared example tests.
|
|
|
|
//
|
|
|
|
|
2022-12-14 17:56:36 -08:00
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
|
|
pub struct SipHashable(pub u64);
|
|
|
|
|
|
|
|
impl Hashable for SipHashable {
|
|
|
|
fn empty_leaf() -> Self {
|
|
|
|
SipHashable(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn combine(_level: Level, a: &Self, b: &Self) -> Self {
|
|
|
|
#![allow(deprecated)]
|
|
|
|
use std::hash::{Hasher, SipHasher};
|
|
|
|
|
|
|
|
let mut hasher = SipHasher::new();
|
|
|
|
hasher.write_u64(a.0);
|
|
|
|
hasher.write_u64(b.0);
|
|
|
|
SipHashable(hasher.finish())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Hashable for String {
|
|
|
|
fn empty_leaf() -> Self {
|
|
|
|
"_".to_string()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn combine(_: Level, a: &Self, b: &Self) -> Self {
|
|
|
|
a.to_string() + b
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-22 10:44:30 -08:00
|
|
|
impl<H: Hashable> Hashable for Option<H> {
|
|
|
|
fn empty_leaf() -> Self {
|
|
|
|
Some(H::empty_leaf())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn combine(l: Level, a: &Self, b: &Self) -> Self {
|
|
|
|
match (a, b) {
|
|
|
|
(Some(a), Some(b)) => Some(H::combine(l, a, b)),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-14 16:25:34 -08:00
|
|
|
//
|
|
|
|
// Operations
|
|
|
|
//
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
2022-12-15 14:02:44 -08:00
|
|
|
pub enum Operation<A, C> {
|
|
|
|
Append(A, Retention<C>),
|
2022-12-14 16:25:34 -08:00
|
|
|
CurrentPosition,
|
|
|
|
MarkedLeaf(Position),
|
|
|
|
MarkedPositions,
|
|
|
|
Unmark(Position),
|
2022-12-15 14:02:44 -08:00
|
|
|
Checkpoint(C),
|
2022-12-14 16:25:34 -08:00
|
|
|
Rewind,
|
2023-01-06 09:58:35 -08:00
|
|
|
Witness(Position, usize),
|
2022-12-14 16:25:34 -08:00
|
|
|
GarbageCollect,
|
|
|
|
}
|
|
|
|
|
|
|
|
use Operation::*;
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
pub fn append_str<C>(x: &str, retention: Retention<C>) -> Operation<String, C> {
|
|
|
|
Operation::Append(x.to_string(), retention)
|
2022-12-14 18:12:53 -08:00
|
|
|
}
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
pub fn unmark<H, C>(pos: usize) -> Operation<H, C> {
|
2022-12-14 18:12:53 -08:00
|
|
|
Operation::Unmark(Position::from(pos))
|
|
|
|
}
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
pub fn witness<H, C>(pos: usize, depth: usize) -> Operation<H, C> {
|
2023-01-06 09:58:35 -08:00
|
|
|
Operation::Witness(Position::from(pos), depth)
|
2022-12-14 18:12:53 -08:00
|
|
|
}
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
impl<H: Hashable + Clone, C: Clone> Operation<H, C> {
|
|
|
|
pub fn apply<T: Tree<H, C>>(&self, tree: &mut T) -> Option<(Position, Vec<H>)> {
|
2022-12-14 16:25:34 -08:00
|
|
|
match self {
|
2022-12-15 14:02:44 -08:00
|
|
|
Append(a, r) => {
|
|
|
|
assert!(tree.append(a.clone(), r.clone()), "append failed");
|
2022-12-14 16:25:34 -08:00
|
|
|
None
|
|
|
|
}
|
|
|
|
CurrentPosition => None,
|
|
|
|
MarkedLeaf(_) => None,
|
|
|
|
MarkedPositions => None,
|
|
|
|
Unmark(p) => {
|
|
|
|
assert!(tree.remove_mark(*p), "remove mark failed");
|
|
|
|
None
|
|
|
|
}
|
2022-12-15 14:02:44 -08:00
|
|
|
Checkpoint(id) => {
|
|
|
|
tree.checkpoint(id.clone());
|
2022-12-14 16:25:34 -08:00
|
|
|
None
|
|
|
|
}
|
|
|
|
Rewind => {
|
|
|
|
assert!(tree.rewind(), "rewind failed");
|
|
|
|
None
|
|
|
|
}
|
2023-01-06 09:58:35 -08:00
|
|
|
Witness(p, d) => tree.witness(*p, *d).map(|xs| (*p, xs)),
|
2022-12-14 16:25:34 -08:00
|
|
|
GarbageCollect => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
pub fn apply_all<T: Tree<H, C>>(
|
|
|
|
ops: &[Operation<H, C>],
|
|
|
|
tree: &mut T,
|
|
|
|
) -> Option<(Position, Vec<H>)> {
|
2022-12-14 16:25:34 -08:00
|
|
|
let mut result = None;
|
|
|
|
for op in ops {
|
|
|
|
result = op.apply(tree);
|
|
|
|
}
|
|
|
|
result
|
|
|
|
}
|
2022-12-15 14:02:44 -08:00
|
|
|
|
|
|
|
pub fn map_checkpoint_id<D, F: Fn(&C) -> D>(&self, f: F) -> Operation<H, D> {
|
|
|
|
match self {
|
|
|
|
Append(a, r) => Append(a.clone(), r.map(f)),
|
|
|
|
CurrentPosition => CurrentPosition,
|
|
|
|
MarkedLeaf(l) => MarkedLeaf(*l),
|
|
|
|
MarkedPositions => MarkedPositions,
|
|
|
|
Unmark(p) => Unmark(*p),
|
|
|
|
Checkpoint(id) => Checkpoint(f(id)),
|
|
|
|
Rewind => Rewind,
|
2023-01-06 09:58:35 -08:00
|
|
|
Witness(p, d) => Witness(*p, *d),
|
2022-12-15 14:02:44 -08:00
|
|
|
GarbageCollect => GarbageCollect,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn arb_retention() -> impl Strategy<Value = Retention<()>> {
|
|
|
|
prop_oneof![
|
|
|
|
Just(Retention::Ephemeral),
|
|
|
|
any::<bool>().prop_map(|is_marked| Retention::Checkpoint { id: (), is_marked }),
|
|
|
|
Just(Retention::Marked),
|
|
|
|
]
|
2022-12-14 16:25:34 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn arb_operation<G: Strategy + Clone>(
|
|
|
|
item_gen: G,
|
|
|
|
pos_gen: impl Strategy<Value = usize> + Clone,
|
2022-12-15 14:02:44 -08:00
|
|
|
) -> impl Strategy<Value = Operation<G::Value, ()>>
|
2022-12-14 16:25:34 -08:00
|
|
|
where
|
|
|
|
G::Value: Clone + 'static,
|
|
|
|
{
|
|
|
|
prop_oneof![
|
2022-12-15 14:02:44 -08:00
|
|
|
(item_gen, arb_retention()).prop_map(|(i, r)| Operation::Append(i, r)),
|
2022-12-14 16:25:34 -08:00
|
|
|
prop_oneof![
|
|
|
|
Just(Operation::CurrentPosition),
|
|
|
|
Just(Operation::MarkedPositions),
|
|
|
|
],
|
|
|
|
Just(Operation::GarbageCollect),
|
|
|
|
pos_gen
|
|
|
|
.clone()
|
|
|
|
.prop_map(|i| Operation::MarkedLeaf(Position::from(i))),
|
|
|
|
pos_gen
|
|
|
|
.clone()
|
|
|
|
.prop_map(|i| Operation::Unmark(Position::from(i))),
|
2022-12-15 14:02:44 -08:00
|
|
|
Just(Operation::Checkpoint(())),
|
2022-12-14 16:25:34 -08:00
|
|
|
Just(Operation::Rewind),
|
|
|
|
pos_gen
|
|
|
|
.prop_flat_map(|i| (0usize..10)
|
2023-01-06 09:58:35 -08:00
|
|
|
.prop_map(move |depth| Operation::Witness(Position::from(i), depth))),
|
2022-12-14 16:25:34 -08:00
|
|
|
]
|
|
|
|
}
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
pub fn apply_operation<H, C, T: Tree<H, C>>(tree: &mut T, op: Operation<H, C>) {
|
2022-12-14 16:25:34 -08:00
|
|
|
match op {
|
2022-12-15 14:02:44 -08:00
|
|
|
Append(value, r) => {
|
|
|
|
tree.append(value, r);
|
2022-12-14 16:25:34 -08:00
|
|
|
}
|
|
|
|
Unmark(position) => {
|
|
|
|
tree.remove_mark(position);
|
|
|
|
}
|
2022-12-15 14:02:44 -08:00
|
|
|
Checkpoint(id) => {
|
|
|
|
tree.checkpoint(id);
|
2022-12-14 16:25:34 -08:00
|
|
|
}
|
|
|
|
Rewind => {
|
|
|
|
tree.rewind();
|
|
|
|
}
|
|
|
|
CurrentPosition => {}
|
2023-01-06 09:58:35 -08:00
|
|
|
Witness(_, _) => {}
|
2022-12-14 16:25:34 -08:00
|
|
|
MarkedLeaf(_) => {}
|
|
|
|
MarkedPositions => {}
|
|
|
|
GarbageCollect => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
pub fn check_operations<H: Hashable + Ord + Clone, C: Clone, T: Tree<H, C>>(
|
2022-12-14 17:56:36 -08:00
|
|
|
mut tree: T,
|
2022-12-15 14:02:44 -08:00
|
|
|
ops: &[Operation<H, C>],
|
2022-12-14 17:56:36 -08:00
|
|
|
) -> Result<(), TestCaseError> {
|
|
|
|
let mut tree_size = 0;
|
|
|
|
let mut tree_values: Vec<H> = vec![];
|
|
|
|
// the number of leaves in the tree at the time that a checkpoint is made
|
|
|
|
let mut tree_checkpoints: Vec<usize> = vec![];
|
|
|
|
|
|
|
|
for op in ops {
|
|
|
|
prop_assert_eq!(tree_size, tree_values.len());
|
|
|
|
match op {
|
2022-12-15 14:02:44 -08:00
|
|
|
Append(value, r) => {
|
|
|
|
if tree.append(value.clone(), r.clone()) {
|
2022-12-22 10:44:30 -08:00
|
|
|
prop_assert!(tree_size < (1 << tree.depth()));
|
2022-12-14 17:56:36 -08:00
|
|
|
tree_size += 1;
|
|
|
|
tree_values.push(value.clone());
|
2022-12-15 14:02:44 -08:00
|
|
|
if r.is_checkpoint() {
|
|
|
|
tree_checkpoints.push(tree_size);
|
|
|
|
}
|
2022-12-14 17:56:36 -08:00
|
|
|
} else {
|
2023-01-13 07:40:57 -08:00
|
|
|
prop_assert_eq!(
|
|
|
|
tree_size,
|
|
|
|
tree.current_position().map_or(0, |p| usize::from(p) + 1)
|
|
|
|
);
|
2022-12-14 17:56:36 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
CurrentPosition => {
|
|
|
|
if let Some(pos) = tree.current_position() {
|
|
|
|
prop_assert!(tree_size > 0);
|
|
|
|
prop_assert_eq!(tree_size - 1, pos.into());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
MarkedLeaf(position) => {
|
|
|
|
if tree.get_marked_leaf(*position).is_some() {
|
|
|
|
prop_assert!(<usize>::from(*position) < tree_size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Unmark(position) => {
|
|
|
|
tree.remove_mark(*position);
|
|
|
|
}
|
|
|
|
MarkedPositions => {}
|
2022-12-15 14:02:44 -08:00
|
|
|
Checkpoint(id) => {
|
2022-12-14 17:56:36 -08:00
|
|
|
tree_checkpoints.push(tree_size);
|
2022-12-15 14:02:44 -08:00
|
|
|
tree.checkpoint(id.clone());
|
2022-12-14 17:56:36 -08:00
|
|
|
}
|
|
|
|
Rewind => {
|
|
|
|
if tree.rewind() {
|
|
|
|
prop_assert!(!tree_checkpoints.is_empty());
|
|
|
|
let checkpointed_tree_size = tree_checkpoints.pop().unwrap();
|
|
|
|
tree_values.truncate(checkpointed_tree_size);
|
|
|
|
tree_size = checkpointed_tree_size;
|
|
|
|
}
|
|
|
|
}
|
2023-01-06 09:58:35 -08:00
|
|
|
Witness(position, depth) => {
|
2022-12-22 10:44:30 -08:00
|
|
|
if let Some(path) = tree.witness(*position, *depth) {
|
2022-12-14 17:56:36 -08:00
|
|
|
let value: H = tree_values[<usize>::from(*position)].clone();
|
|
|
|
let tree_root = tree.root(*depth);
|
|
|
|
|
|
|
|
if tree_checkpoints.len() >= *depth {
|
|
|
|
let mut extended_tree_values = tree_values.clone();
|
|
|
|
if *depth > 0 {
|
|
|
|
// prune the tree back to the checkpointed size.
|
|
|
|
if let Some(checkpointed_tree_size) =
|
|
|
|
tree_checkpoints.get(tree_checkpoints.len() - depth)
|
|
|
|
{
|
|
|
|
extended_tree_values.truncate(*checkpointed_tree_size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// compute the root
|
2022-12-22 10:44:30 -08:00
|
|
|
let expected_root =
|
|
|
|
complete_tree::root::<H>(&extended_tree_values, tree.depth());
|
2022-12-14 17:56:36 -08:00
|
|
|
prop_assert_eq!(&tree_root.unwrap(), &expected_root);
|
|
|
|
|
|
|
|
prop_assert_eq!(
|
|
|
|
&compute_root_from_witness(value, *position, &path),
|
|
|
|
&expected_root
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
GarbageCollect => {}
|
|
|
|
}
|
2022-12-21 11:52:33 -08:00
|
|
|
}
|
|
|
|
|
2022-12-14 17:56:36 -08:00
|
|
|
Ok(())
|
2022-12-21 11:52:33 -08:00
|
|
|
}
|
|
|
|
|
2022-12-14 17:56:36 -08:00
|
|
|
pub fn compute_root_from_witness<H: Hashable>(value: H, position: Position, path: &[H]) -> H {
|
|
|
|
let mut cur = value;
|
|
|
|
let mut lvl = 0.into();
|
|
|
|
for (i, v) in path
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(i, v)| (((<usize>::from(position) >> i) & 1) == 1, v))
|
|
|
|
{
|
|
|
|
if i {
|
|
|
|
cur = H::combine(lvl, v, &cur);
|
|
|
|
} else {
|
|
|
|
cur = H::combine(lvl, &cur, v);
|
|
|
|
}
|
|
|
|
lvl = lvl + 1;
|
2022-12-21 11:52:33 -08:00
|
|
|
}
|
2022-12-14 17:56:36 -08:00
|
|
|
cur
|
2022-12-21 11:52:33 -08:00
|
|
|
}
|
2022-12-14 16:34:52 -08:00
|
|
|
|
2022-12-14 18:33:19 -08:00
|
|
|
//
|
|
|
|
// Types and utilities for cross-verification property tests
|
|
|
|
//
|
|
|
|
|
2023-01-13 07:40:57 -08:00
|
|
|
#[derive(Clone, Debug)]
|
2022-12-15 14:02:44 -08:00
|
|
|
pub struct CombinedTree<H, C, I: Tree<H, C>, E: Tree<H, C>> {
|
2022-12-14 18:33:19 -08:00
|
|
|
inefficient: I,
|
|
|
|
efficient: E,
|
2022-12-15 14:02:44 -08:00
|
|
|
_phantom_h: PhantomData<H>,
|
|
|
|
_phantom_c: PhantomData<C>,
|
2022-12-14 18:33:19 -08:00
|
|
|
}
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
impl<H: Hashable + Ord + Clone + Debug, C, I: Tree<H, C>, E: Tree<H, C>> CombinedTree<H, C, I, E> {
|
2022-12-14 18:33:19 -08:00
|
|
|
pub fn new(inefficient: I, efficient: E) -> Self {
|
2022-12-22 10:44:30 -08:00
|
|
|
assert_eq!(inefficient.depth(), efficient.depth());
|
2022-12-14 18:33:19 -08:00
|
|
|
CombinedTree {
|
|
|
|
inefficient,
|
|
|
|
efficient,
|
2022-12-15 14:02:44 -08:00
|
|
|
_phantom_h: PhantomData,
|
|
|
|
_phantom_c: PhantomData,
|
2022-12-14 18:33:19 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
impl<H: Hashable + Ord + Clone + Debug, C: Clone, I: Tree<H, C>, E: Tree<H, C>> Tree<H, C>
|
|
|
|
for CombinedTree<H, C, I, E>
|
|
|
|
{
|
2022-12-22 10:44:30 -08:00
|
|
|
fn depth(&self) -> u8 {
|
|
|
|
self.inefficient.depth()
|
|
|
|
}
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
fn append(&mut self, value: H, retention: Retention<C>) -> bool {
|
|
|
|
let a = self.inefficient.append(value.clone(), retention.clone());
|
|
|
|
let b = self.efficient.append(value, retention);
|
2022-12-14 18:33:19 -08:00
|
|
|
assert_eq!(a, b);
|
|
|
|
a
|
|
|
|
}
|
|
|
|
|
|
|
|
fn root(&self, checkpoint_depth: usize) -> Option<H> {
|
|
|
|
let a = self.inefficient.root(checkpoint_depth);
|
|
|
|
let b = self.efficient.root(checkpoint_depth);
|
|
|
|
assert_eq!(a, b);
|
|
|
|
a
|
|
|
|
}
|
|
|
|
|
|
|
|
fn current_position(&self) -> Option<Position> {
|
|
|
|
let a = self.inefficient.current_position();
|
|
|
|
let b = self.efficient.current_position();
|
|
|
|
assert_eq!(a, b);
|
|
|
|
a
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_marked_leaf(&self, position: Position) -> Option<&H> {
|
|
|
|
let a = self.inefficient.get_marked_leaf(position);
|
|
|
|
let b = self.efficient.get_marked_leaf(position);
|
|
|
|
assert_eq!(a, b);
|
|
|
|
a
|
|
|
|
}
|
|
|
|
|
|
|
|
fn marked_positions(&self) -> BTreeSet<Position> {
|
|
|
|
let a = self.inefficient.marked_positions();
|
|
|
|
let b = self.efficient.marked_positions();
|
|
|
|
assert_eq!(a, b);
|
|
|
|
a
|
|
|
|
}
|
|
|
|
|
2022-12-22 10:44:30 -08:00
|
|
|
fn witness(&self, position: Position, checkpoint_depth: usize) -> Option<Vec<H>> {
|
|
|
|
let a = self.inefficient.witness(position, checkpoint_depth);
|
|
|
|
let b = self.efficient.witness(position, checkpoint_depth);
|
2022-12-14 18:33:19 -08:00
|
|
|
assert_eq!(a, b);
|
|
|
|
a
|
|
|
|
}
|
|
|
|
|
|
|
|
fn remove_mark(&mut self, position: Position) -> bool {
|
|
|
|
let a = self.inefficient.remove_mark(position);
|
|
|
|
let b = self.efficient.remove_mark(position);
|
|
|
|
assert_eq!(a, b);
|
|
|
|
a
|
|
|
|
}
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
fn checkpoint(&mut self, id: C) -> bool {
|
|
|
|
let a = self.inefficient.checkpoint(id.clone());
|
|
|
|
let b = self.efficient.checkpoint(id);
|
|
|
|
assert_eq!(a, b);
|
|
|
|
a
|
2022-12-14 18:33:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
fn rewind(&mut self) -> bool {
|
|
|
|
let a = self.inefficient.rewind();
|
|
|
|
let b = self.efficient.rewind();
|
|
|
|
assert_eq!(a, b);
|
|
|
|
a
|
|
|
|
}
|
|
|
|
}
|
2022-12-15 14:02:44 -08:00
|
|
|
|
2022-12-14 16:34:52 -08:00
|
|
|
//
|
|
|
|
// Shared example tests
|
|
|
|
//
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
pub fn check_root_hashes<T: Tree<String, usize>, F: Fn(usize) -> T>(new_tree: F) {
|
2022-12-14 16:34:52 -08:00
|
|
|
let mut tree = new_tree(100);
|
|
|
|
assert_eq!(tree.root(0).unwrap(), "________________");
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
tree.append("a".to_string(), Retention::Ephemeral);
|
2022-12-14 16:34:52 -08:00
|
|
|
assert_eq!(tree.root(0).unwrap().len(), 16);
|
|
|
|
assert_eq!(tree.root(0).unwrap(), "a_______________");
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
tree.append("b".to_string(), Retention::Ephemeral);
|
2022-12-14 16:34:52 -08:00
|
|
|
assert_eq!(tree.root(0).unwrap(), "ab______________");
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
tree.append("c".to_string(), Retention::Ephemeral);
|
2022-12-14 16:34:52 -08:00
|
|
|
assert_eq!(tree.root(0).unwrap(), "abc_____________");
|
|
|
|
|
|
|
|
let mut t = new_tree(100);
|
2022-12-15 14:02:44 -08:00
|
|
|
t.append(
|
|
|
|
"a".to_string(),
|
|
|
|
Retention::Checkpoint {
|
|
|
|
id: 1,
|
|
|
|
is_marked: true,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
t.append("a".to_string(), Retention::Ephemeral);
|
|
|
|
t.append("a".to_string(), Retention::Ephemeral);
|
|
|
|
t.append("a".to_string(), Retention::Ephemeral);
|
2022-12-14 16:34:52 -08:00
|
|
|
assert_eq!(t.root(0).unwrap(), "aaaa____________");
|
|
|
|
}
|
|
|
|
|
2023-01-13 07:40:57 -08:00
|
|
|
/// This test expects a depth-4 tree and verifies that the tree reports itself as full after 2^4
|
|
|
|
/// appends.
|
|
|
|
pub fn check_append<T: Tree<String, usize> + std::fmt::Debug, F: Fn(usize) -> T>(new_tree: F) {
|
|
|
|
use Retention::*;
|
|
|
|
|
|
|
|
let mut tree = new_tree(100);
|
|
|
|
assert_eq!(tree.depth(), 4);
|
|
|
|
|
|
|
|
// 16 appends should succeed
|
|
|
|
for i in 0..16 {
|
|
|
|
assert!(tree.append(i.to_string(), Ephemeral));
|
|
|
|
assert_eq!(tree.current_position(), Some(Position::from(i)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// 17th append should fail
|
|
|
|
assert!(!tree.append("16".to_string(), Ephemeral));
|
|
|
|
|
|
|
|
// The following checks a condition on state restoration in the case that an append fails.
|
|
|
|
// We want to ensure that a failed append does not cause a loss of information.
|
|
|
|
let ops = (0..17)
|
|
|
|
.map(|i| Append(i.to_string(), Ephemeral))
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
let tree = new_tree(100);
|
|
|
|
check_operations(tree, &ops).unwrap();
|
|
|
|
}
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
pub fn check_witnesses<T: Tree<String, usize> + std::fmt::Debug, F: Fn(usize) -> T>(new_tree: F) {
|
2023-01-13 07:40:57 -08:00
|
|
|
use Retention::*;
|
|
|
|
|
2022-12-14 16:34:52 -08:00
|
|
|
let mut tree = new_tree(100);
|
2023-01-13 07:40:57 -08:00
|
|
|
tree.append("a".to_string(), Marked);
|
2022-12-14 16:34:52 -08:00
|
|
|
assert_eq!(
|
2022-12-22 10:44:30 -08:00
|
|
|
tree.witness(Position::from(0), 0),
|
2022-12-14 16:34:52 -08:00
|
|
|
Some(vec![
|
|
|
|
"_".to_string(),
|
|
|
|
"__".to_string(),
|
|
|
|
"____".to_string(),
|
|
|
|
"________".to_string()
|
|
|
|
])
|
|
|
|
);
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
tree.append("b".to_string(), Retention::Ephemeral);
|
2022-12-14 16:34:52 -08:00
|
|
|
assert_eq!(
|
2022-12-22 10:44:30 -08:00
|
|
|
tree.witness(0.into(), 0),
|
2022-12-14 16:34:52 -08:00
|
|
|
Some(vec![
|
|
|
|
"b".to_string(),
|
|
|
|
"__".to_string(),
|
|
|
|
"____".to_string(),
|
|
|
|
"________".to_string()
|
|
|
|
])
|
|
|
|
);
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
tree.append("c".to_string(), Retention::Marked);
|
2022-12-14 16:34:52 -08:00
|
|
|
assert_eq!(
|
2022-12-22 10:44:30 -08:00
|
|
|
tree.witness(Position::from(2), 0),
|
2022-12-14 16:34:52 -08:00
|
|
|
Some(vec![
|
|
|
|
"_".to_string(),
|
|
|
|
"ab".to_string(),
|
|
|
|
"____".to_string(),
|
|
|
|
"________".to_string()
|
|
|
|
])
|
|
|
|
);
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
tree.append("d".to_string(), Retention::Ephemeral);
|
2022-12-14 16:34:52 -08:00
|
|
|
assert_eq!(
|
2022-12-22 10:44:30 -08:00
|
|
|
tree.witness(Position::from(2), 0),
|
2022-12-14 16:34:52 -08:00
|
|
|
Some(vec![
|
|
|
|
"d".to_string(),
|
|
|
|
"ab".to_string(),
|
|
|
|
"____".to_string(),
|
|
|
|
"________".to_string()
|
|
|
|
])
|
|
|
|
);
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
tree.append("e".to_string(), Retention::Ephemeral);
|
2022-12-14 16:34:52 -08:00
|
|
|
assert_eq!(
|
2022-12-22 10:44:30 -08:00
|
|
|
tree.witness(Position::from(2), 0),
|
2022-12-14 16:34:52 -08:00
|
|
|
Some(vec![
|
|
|
|
"d".to_string(),
|
|
|
|
"ab".to_string(),
|
|
|
|
"e___".to_string(),
|
|
|
|
"________".to_string()
|
|
|
|
])
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut tree = new_tree(100);
|
2022-12-15 14:02:44 -08:00
|
|
|
tree.append("a".to_string(), Retention::Marked);
|
|
|
|
for c in 'b'..'g' {
|
|
|
|
tree.append(c.to_string(), Retention::Ephemeral);
|
2022-12-14 16:34:52 -08:00
|
|
|
}
|
2022-12-15 14:02:44 -08:00
|
|
|
tree.append("g".to_string(), Retention::Marked);
|
|
|
|
tree.append("h".to_string(), Retention::Ephemeral);
|
2022-12-14 16:34:52 -08:00
|
|
|
|
|
|
|
assert_eq!(
|
2022-12-22 10:44:30 -08:00
|
|
|
tree.witness(0.into(), 0),
|
2022-12-14 16:34:52 -08:00
|
|
|
Some(vec![
|
|
|
|
"b".to_string(),
|
|
|
|
"cd".to_string(),
|
|
|
|
"efgh".to_string(),
|
|
|
|
"________".to_string()
|
|
|
|
])
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut tree = new_tree(100);
|
2022-12-15 14:02:44 -08:00
|
|
|
tree.append("a".to_string(), Retention::Marked);
|
|
|
|
tree.append("b".to_string(), Retention::Ephemeral);
|
|
|
|
tree.append("c".to_string(), Retention::Ephemeral);
|
|
|
|
tree.append("d".to_string(), Retention::Marked);
|
|
|
|
tree.append("e".to_string(), Retention::Marked);
|
|
|
|
tree.append("f".to_string(), Retention::Marked);
|
|
|
|
tree.append("g".to_string(), Retention::Ephemeral);
|
2022-12-14 16:34:52 -08:00
|
|
|
|
|
|
|
assert_eq!(
|
2022-12-22 10:44:30 -08:00
|
|
|
tree.witness(Position::from(5), 0),
|
2022-12-14 16:34:52 -08:00
|
|
|
Some(vec![
|
|
|
|
"e".to_string(),
|
|
|
|
"g_".to_string(),
|
|
|
|
"abcd".to_string(),
|
|
|
|
"________".to_string()
|
|
|
|
])
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut tree = new_tree(100);
|
2022-12-15 14:02:44 -08:00
|
|
|
for c in 'a'..'k' {
|
|
|
|
tree.append(c.to_string(), Retention::Ephemeral);
|
2022-12-14 16:34:52 -08:00
|
|
|
}
|
2022-12-15 14:02:44 -08:00
|
|
|
tree.append('k'.to_string(), Retention::Marked);
|
|
|
|
tree.append('l'.to_string(), Retention::Ephemeral);
|
2022-12-14 16:34:52 -08:00
|
|
|
|
|
|
|
assert_eq!(
|
2022-12-22 10:44:30 -08:00
|
|
|
tree.witness(Position::from(10), 0),
|
2022-12-14 16:34:52 -08:00
|
|
|
Some(vec![
|
|
|
|
"l".to_string(),
|
|
|
|
"ij".to_string(),
|
|
|
|
"____".to_string(),
|
|
|
|
"abcdefgh".to_string()
|
|
|
|
])
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut tree = new_tree(100);
|
2022-12-15 14:02:44 -08:00
|
|
|
assert!(tree.append(
|
|
|
|
'a'.to_string(),
|
|
|
|
Retention::Checkpoint {
|
|
|
|
id: 1,
|
|
|
|
is_marked: true
|
|
|
|
}
|
|
|
|
));
|
2022-12-14 16:34:52 -08:00
|
|
|
assert!(tree.rewind());
|
2022-12-15 14:02:44 -08:00
|
|
|
for c in 'b'..'e' {
|
|
|
|
tree.append(c.to_string(), Retention::Ephemeral);
|
2022-12-14 16:34:52 -08:00
|
|
|
}
|
2022-12-15 14:02:44 -08:00
|
|
|
tree.append("e".to_string(), Retention::Marked);
|
2022-12-14 16:34:52 -08:00
|
|
|
for c in 'f'..'i' {
|
2022-12-15 14:02:44 -08:00
|
|
|
tree.append(c.to_string(), Retention::Ephemeral);
|
2022-12-14 16:34:52 -08:00
|
|
|
}
|
|
|
|
assert_eq!(
|
2022-12-22 10:44:30 -08:00
|
|
|
tree.witness(0.into(), 0),
|
2022-12-14 16:34:52 -08:00
|
|
|
Some(vec![
|
|
|
|
"b".to_string(),
|
|
|
|
"cd".to_string(),
|
|
|
|
"efgh".to_string(),
|
|
|
|
"________".to_string()
|
|
|
|
])
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut tree = new_tree(100);
|
2022-12-15 14:02:44 -08:00
|
|
|
tree.append('a'.to_string(), Retention::Ephemeral);
|
|
|
|
tree.append('b'.to_string(), Retention::Ephemeral);
|
|
|
|
tree.append('c'.to_string(), Retention::Marked);
|
|
|
|
tree.append('d'.to_string(), Retention::Ephemeral);
|
|
|
|
tree.append('e'.to_string(), Retention::Ephemeral);
|
|
|
|
tree.append('f'.to_string(), Retention::Ephemeral);
|
|
|
|
assert!(tree.append(
|
|
|
|
'g'.to_string(),
|
|
|
|
Retention::Checkpoint {
|
|
|
|
id: 1,
|
|
|
|
is_marked: true
|
|
|
|
}
|
|
|
|
));
|
|
|
|
tree.append('h'.to_string(), Retention::Ephemeral);
|
2022-12-14 16:34:52 -08:00
|
|
|
assert!(tree.rewind());
|
|
|
|
assert_eq!(
|
2022-12-22 10:44:30 -08:00
|
|
|
tree.witness(Position::from(2), 0),
|
2022-12-14 16:34:52 -08:00
|
|
|
Some(vec![
|
|
|
|
"d".to_string(),
|
|
|
|
"ab".to_string(),
|
|
|
|
"efg_".to_string(),
|
|
|
|
"________".to_string()
|
|
|
|
])
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut tree = new_tree(100);
|
2022-12-15 14:02:44 -08:00
|
|
|
tree.append('a'.to_string(), Retention::Ephemeral);
|
|
|
|
tree.append('b'.to_string(), Retention::Marked);
|
2022-12-22 10:44:30 -08:00
|
|
|
assert_eq!(tree.witness(Position::from(0), 0), None);
|
2022-12-14 16:34:52 -08:00
|
|
|
|
|
|
|
let mut tree = new_tree(100);
|
2022-12-15 14:02:44 -08:00
|
|
|
for c in 'a'..'m' {
|
|
|
|
tree.append(c.to_string(), Retention::Ephemeral);
|
2022-12-14 16:34:52 -08:00
|
|
|
}
|
2022-12-15 14:02:44 -08:00
|
|
|
tree.append('m'.to_string(), Retention::Marked);
|
|
|
|
tree.append('n'.to_string(), Retention::Marked);
|
|
|
|
tree.append('o'.to_string(), Retention::Ephemeral);
|
|
|
|
tree.append('p'.to_string(), Retention::Ephemeral);
|
2022-12-14 16:34:52 -08:00
|
|
|
|
|
|
|
assert_eq!(
|
2022-12-22 10:44:30 -08:00
|
|
|
tree.witness(Position::from(12), 0),
|
2022-12-14 16:34:52 -08:00
|
|
|
Some(vec![
|
|
|
|
"n".to_string(),
|
|
|
|
"op".to_string(),
|
|
|
|
"ijkl".to_string(),
|
|
|
|
"abcdefgh".to_string()
|
|
|
|
])
|
|
|
|
);
|
|
|
|
|
|
|
|
let ops = ('a'..='l')
|
|
|
|
.into_iter()
|
2022-12-15 14:02:44 -08:00
|
|
|
.map(|c| Append(c.to_string(), Retention::Marked))
|
|
|
|
.chain(Some(Append('m'.to_string(), Retention::Ephemeral)))
|
|
|
|
.chain(Some(Append('n'.to_string(), Retention::Ephemeral)))
|
2023-01-06 09:58:35 -08:00
|
|
|
.chain(Some(Witness(11usize.into(), 0)))
|
2022-12-14 16:34:52 -08:00
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
let mut tree = new_tree(100);
|
|
|
|
assert_eq!(
|
|
|
|
Operation::apply_all(&ops, &mut tree),
|
|
|
|
Some((
|
|
|
|
Position::from(11),
|
|
|
|
vec![
|
|
|
|
"k".to_string(),
|
|
|
|
"ij".to_string(),
|
|
|
|
"mn__".to_string(),
|
|
|
|
"abcdefgh".to_string()
|
|
|
|
]
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
pub fn check_checkpoint_rewind<T: Tree<String, usize>, F: Fn(usize) -> T>(new_tree: F) {
|
2022-12-14 16:34:52 -08:00
|
|
|
let mut t = new_tree(100);
|
|
|
|
assert!(!t.rewind());
|
|
|
|
|
|
|
|
let mut t = new_tree(100);
|
2022-12-15 14:02:44 -08:00
|
|
|
t.checkpoint(1);
|
2022-12-14 16:34:52 -08:00
|
|
|
assert!(t.rewind());
|
|
|
|
|
|
|
|
let mut t = new_tree(100);
|
2022-12-15 14:02:44 -08:00
|
|
|
t.append("a".to_string(), Retention::Ephemeral);
|
|
|
|
t.checkpoint(1);
|
|
|
|
t.append("b".to_string(), Retention::Marked);
|
2022-12-14 16:34:52 -08:00
|
|
|
assert!(t.rewind());
|
|
|
|
assert_eq!(Some(Position::from(0)), t.current_position());
|
|
|
|
|
|
|
|
let mut t = new_tree(100);
|
2022-12-15 14:02:44 -08:00
|
|
|
t.append("a".to_string(), Retention::Marked);
|
|
|
|
t.checkpoint(1);
|
2022-12-14 16:34:52 -08:00
|
|
|
assert!(t.rewind());
|
|
|
|
|
|
|
|
let mut t = new_tree(100);
|
2022-12-15 14:02:44 -08:00
|
|
|
t.append("a".to_string(), Retention::Marked);
|
|
|
|
t.checkpoint(1);
|
|
|
|
t.append("a".to_string(), Retention::Ephemeral);
|
2022-12-14 16:34:52 -08:00
|
|
|
assert!(t.rewind());
|
|
|
|
assert_eq!(Some(Position::from(0)), t.current_position());
|
|
|
|
|
|
|
|
let mut t = new_tree(100);
|
2022-12-15 14:02:44 -08:00
|
|
|
t.append("a".to_string(), Retention::Ephemeral);
|
|
|
|
t.checkpoint(1);
|
|
|
|
t.checkpoint(2);
|
2022-12-14 16:34:52 -08:00
|
|
|
assert!(t.rewind());
|
2022-12-15 14:02:44 -08:00
|
|
|
t.append("b".to_string(), Retention::Ephemeral);
|
2022-12-14 16:34:52 -08:00
|
|
|
assert!(t.rewind());
|
2022-12-15 14:02:44 -08:00
|
|
|
t.append("b".to_string(), Retention::Ephemeral);
|
2022-12-14 16:34:52 -08:00
|
|
|
assert_eq!(t.root(0).unwrap(), "ab______________");
|
|
|
|
}
|
2022-12-14 17:56:36 -08:00
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
pub fn check_remove_mark<T: Tree<String, usize>, F: Fn(usize) -> T>(new_tree: F) {
|
2022-12-22 10:44:30 -08:00
|
|
|
let samples = vec![
|
|
|
|
vec![
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("a", Retention::Ephemeral),
|
|
|
|
append_str(
|
|
|
|
"a",
|
|
|
|
Retention::Checkpoint {
|
|
|
|
id: 1,
|
|
|
|
is_marked: true,
|
|
|
|
},
|
|
|
|
),
|
2022-12-22 10:44:30 -08:00
|
|
|
witness(1, 1),
|
|
|
|
],
|
|
|
|
vec![
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("a", Retention::Ephemeral),
|
|
|
|
append_str("a", Retention::Ephemeral),
|
|
|
|
append_str("a", Retention::Ephemeral),
|
|
|
|
append_str("a", Retention::Marked),
|
|
|
|
Checkpoint(1),
|
2022-12-22 10:44:30 -08:00
|
|
|
unmark(3),
|
|
|
|
witness(3, 0),
|
|
|
|
],
|
|
|
|
];
|
|
|
|
|
|
|
|
for (i, sample) in samples.iter().enumerate() {
|
|
|
|
let result = check_operations(new_tree(100), sample);
|
|
|
|
assert!(
|
|
|
|
matches!(result, Ok(())),
|
|
|
|
"Reference/Test mismatch at index {}: {:?}",
|
|
|
|
i,
|
|
|
|
result
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
pub fn check_rewind_remove_mark<T: Tree<String, usize>, F: Fn(usize) -> T>(new_tree: F) {
|
2022-12-22 10:44:30 -08:00
|
|
|
// rewinding doesn't remove a mark
|
2022-12-14 18:12:53 -08:00
|
|
|
let mut tree = new_tree(100);
|
2022-12-15 14:02:44 -08:00
|
|
|
tree.append("e".to_string(), Retention::Marked);
|
|
|
|
tree.checkpoint(1);
|
2022-12-14 18:12:53 -08:00
|
|
|
assert!(tree.rewind());
|
|
|
|
assert!(tree.remove_mark(0usize.into()));
|
|
|
|
|
2022-12-22 10:44:30 -08:00
|
|
|
// use a maximum number of checkpoints of 1
|
|
|
|
let mut tree = new_tree(1);
|
2022-12-15 14:02:44 -08:00
|
|
|
tree.append("e".to_string(), Retention::Marked);
|
|
|
|
tree.checkpoint(1);
|
2022-12-22 10:44:30 -08:00
|
|
|
assert!(tree.marked_positions().contains(&0usize.into()));
|
2022-12-15 14:02:44 -08:00
|
|
|
tree.append("f".to_string(), Retention::Ephemeral);
|
2022-12-22 10:44:30 -08:00
|
|
|
// simulate a spend of `e` at `f`
|
2022-12-14 18:12:53 -08:00
|
|
|
assert!(tree.remove_mark(0usize.into()));
|
2022-12-22 10:44:30 -08:00
|
|
|
// even though the mark has been staged for removal, it's not gone yet
|
|
|
|
assert!(tree.marked_positions().contains(&0usize.into()));
|
2022-12-15 14:02:44 -08:00
|
|
|
tree.checkpoint(2);
|
2022-12-22 10:44:30 -08:00
|
|
|
// the newest checkpoint will have caused the oldest to roll off, and
|
|
|
|
// so the forgotten node will be unmarked
|
|
|
|
assert!(!tree.marked_positions().contains(&0usize.into()));
|
2022-12-14 18:12:53 -08:00
|
|
|
assert!(!tree.remove_mark(0usize.into()));
|
|
|
|
|
|
|
|
// The following check_operations tests cover errors where the
|
|
|
|
// test framework itself previously did not correctly handle
|
|
|
|
// chain state restoration.
|
|
|
|
|
|
|
|
let samples = vec![
|
|
|
|
vec![
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("x", Retention::Marked),
|
|
|
|
Checkpoint(1),
|
|
|
|
Rewind,
|
|
|
|
unmark(0),
|
|
|
|
],
|
|
|
|
vec![
|
|
|
|
append_str("d", Retention::Marked),
|
|
|
|
Checkpoint(1),
|
2022-12-14 18:12:53 -08:00
|
|
|
unmark(0),
|
|
|
|
Rewind,
|
|
|
|
unmark(0),
|
|
|
|
],
|
|
|
|
vec![
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("o", Retention::Marked),
|
|
|
|
Checkpoint(1),
|
|
|
|
Checkpoint(2),
|
2022-12-14 18:12:53 -08:00
|
|
|
unmark(0),
|
|
|
|
Rewind,
|
|
|
|
Rewind,
|
|
|
|
],
|
|
|
|
vec![
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("s", Retention::Marked),
|
|
|
|
append_str("m", Retention::Ephemeral),
|
|
|
|
Checkpoint(1),
|
2022-12-14 18:12:53 -08:00
|
|
|
unmark(0),
|
|
|
|
Rewind,
|
|
|
|
unmark(0),
|
|
|
|
unmark(0),
|
|
|
|
],
|
2022-12-22 10:44:30 -08:00
|
|
|
vec![
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("a", Retention::Marked),
|
|
|
|
Checkpoint(1),
|
2022-12-22 10:44:30 -08:00
|
|
|
Rewind,
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("a", Retention::Marked),
|
2022-12-22 10:44:30 -08:00
|
|
|
],
|
2022-12-14 18:12:53 -08:00
|
|
|
];
|
|
|
|
|
|
|
|
for (i, sample) in samples.iter().enumerate() {
|
2022-12-22 10:44:30 -08:00
|
|
|
let result = check_operations(new_tree(100), sample);
|
2022-12-14 18:12:53 -08:00
|
|
|
assert!(
|
|
|
|
matches!(result, Ok(())),
|
|
|
|
"Reference/Test mismatch at index {}: {:?}",
|
|
|
|
i,
|
|
|
|
result
|
|
|
|
);
|
|
|
|
}
|
2022-12-14 18:33:19 -08:00
|
|
|
}
|
|
|
|
|
2022-12-15 14:02:44 -08:00
|
|
|
pub fn check_witness_consistency<T: Tree<String, usize>, F: Fn(usize) -> T>(new_tree: F) {
|
2022-12-14 18:33:19 -08:00
|
|
|
let samples = vec![
|
|
|
|
// Reduced examples
|
|
|
|
vec![
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("a", Retention::Ephemeral),
|
|
|
|
append_str("b", Retention::Marked),
|
|
|
|
Checkpoint(1),
|
2022-12-14 18:33:19 -08:00
|
|
|
witness(0, 1),
|
|
|
|
],
|
|
|
|
vec![
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("c", Retention::Ephemeral),
|
|
|
|
append_str("d", Retention::Marked),
|
|
|
|
Checkpoint(1),
|
2022-12-14 18:33:19 -08:00
|
|
|
witness(1, 1),
|
|
|
|
],
|
|
|
|
vec![
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("e", Retention::Marked),
|
|
|
|
Checkpoint(1),
|
|
|
|
append_str("f", Retention::Ephemeral),
|
2022-12-14 18:33:19 -08:00
|
|
|
witness(0, 1),
|
|
|
|
],
|
|
|
|
vec![
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("g", Retention::Marked),
|
|
|
|
Checkpoint(1),
|
2022-12-14 18:33:19 -08:00
|
|
|
unmark(0),
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("h", Retention::Ephemeral),
|
2022-12-14 18:33:19 -08:00
|
|
|
witness(0, 0),
|
|
|
|
],
|
|
|
|
vec![
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("i", Retention::Marked),
|
|
|
|
Checkpoint(1),
|
2022-12-14 18:33:19 -08:00
|
|
|
unmark(0),
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("j", Retention::Ephemeral),
|
2022-12-14 18:33:19 -08:00
|
|
|
witness(0, 0),
|
|
|
|
],
|
|
|
|
vec![
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("i", Retention::Marked),
|
|
|
|
append_str("j", Retention::Ephemeral),
|
|
|
|
Checkpoint(1),
|
|
|
|
append_str("k", Retention::Ephemeral),
|
2022-12-14 18:33:19 -08:00
|
|
|
witness(0, 1),
|
|
|
|
],
|
|
|
|
vec![
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("l", Retention::Marked),
|
|
|
|
Checkpoint(1),
|
|
|
|
Checkpoint(2),
|
|
|
|
append_str("m", Retention::Ephemeral),
|
|
|
|
Checkpoint(3),
|
2022-12-14 18:33:19 -08:00
|
|
|
witness(0, 2),
|
|
|
|
],
|
|
|
|
vec![
|
2022-12-15 14:02:44 -08:00
|
|
|
Checkpoint(1),
|
|
|
|
append_str("n", Retention::Marked),
|
|
|
|
witness(0, 1),
|
|
|
|
],
|
|
|
|
vec![
|
|
|
|
append_str("a", Retention::Marked),
|
|
|
|
Checkpoint(1),
|
2022-12-14 18:33:19 -08:00
|
|
|
unmark(0),
|
2022-12-15 14:02:44 -08:00
|
|
|
Checkpoint(2),
|
|
|
|
append_str("b", Retention::Ephemeral),
|
2022-12-14 18:33:19 -08:00
|
|
|
witness(0, 1),
|
|
|
|
],
|
|
|
|
vec![
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("a", Retention::Marked),
|
|
|
|
append_str("b", Retention::Ephemeral),
|
2022-12-14 18:33:19 -08:00
|
|
|
unmark(0),
|
2022-12-15 14:02:44 -08:00
|
|
|
Checkpoint(1),
|
2022-12-14 18:33:19 -08:00
|
|
|
witness(0, 0),
|
|
|
|
],
|
|
|
|
vec![
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("a", Retention::Marked),
|
|
|
|
Checkpoint(1),
|
2022-12-14 18:33:19 -08:00
|
|
|
unmark(0),
|
2022-12-15 14:02:44 -08:00
|
|
|
Checkpoint(2),
|
2022-12-14 18:33:19 -08:00
|
|
|
Rewind,
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("b", Retention::Ephemeral),
|
2022-12-14 18:33:19 -08:00
|
|
|
witness(0, 0),
|
|
|
|
],
|
|
|
|
vec![
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("a", Retention::Marked),
|
|
|
|
Checkpoint(1),
|
|
|
|
Checkpoint(2),
|
2022-12-14 18:33:19 -08:00
|
|
|
Rewind,
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("a", Retention::Ephemeral),
|
2022-12-14 18:33:19 -08:00
|
|
|
unmark(0),
|
|
|
|
witness(0, 1),
|
|
|
|
],
|
|
|
|
// Unreduced examples
|
|
|
|
vec![
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("o", Retention::Ephemeral),
|
|
|
|
append_str("p", Retention::Marked),
|
|
|
|
append_str("q", Retention::Ephemeral),
|
|
|
|
Checkpoint(1),
|
2022-12-14 18:33:19 -08:00
|
|
|
unmark(1),
|
|
|
|
witness(1, 1),
|
|
|
|
],
|
|
|
|
vec![
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("r", Retention::Ephemeral),
|
|
|
|
append_str("s", Retention::Ephemeral),
|
|
|
|
append_str("t", Retention::Marked),
|
|
|
|
Checkpoint(1),
|
2022-12-14 18:33:19 -08:00
|
|
|
unmark(2),
|
2022-12-15 14:02:44 -08:00
|
|
|
Checkpoint(2),
|
2022-12-14 18:33:19 -08:00
|
|
|
witness(2, 2),
|
|
|
|
],
|
|
|
|
vec![
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("u", Retention::Marked),
|
|
|
|
append_str("v", Retention::Ephemeral),
|
|
|
|
append_str("w", Retention::Ephemeral),
|
|
|
|
Checkpoint(1),
|
2022-12-14 18:33:19 -08:00
|
|
|
unmark(0),
|
2022-12-15 14:02:44 -08:00
|
|
|
append_str("x", Retention::Ephemeral),
|
|
|
|
Checkpoint(2),
|
|
|
|
Checkpoint(3),
|
2022-12-14 18:33:19 -08:00
|
|
|
witness(0, 3),
|
|
|
|
],
|
|
|
|
];
|
|
|
|
|
|
|
|
for (i, sample) in samples.iter().enumerate() {
|
2022-12-22 10:44:30 -08:00
|
|
|
let result = check_operations(new_tree(100), sample);
|
2022-12-14 18:33:19 -08:00
|
|
|
assert!(
|
|
|
|
matches!(result, Ok(())),
|
|
|
|
"Reference/Test mismatch at index {}: {:?}",
|
|
|
|
i,
|
|
|
|
result
|
|
|
|
);
|
|
|
|
}
|
2022-12-14 18:12:53 -08:00
|
|
|
}
|
|
|
|
|
2022-12-14 17:56:36 -08:00
|
|
|
#[cfg(test)]
|
|
|
|
pub(crate) mod tests {
|
|
|
|
use crate::{
|
|
|
|
testing::{compute_root_from_witness, SipHashable},
|
|
|
|
Hashable, Level, Position,
|
|
|
|
};
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_compute_root_from_witness() {
|
|
|
|
let expected = SipHashable::combine(
|
|
|
|
<Level>::from(2),
|
|
|
|
&SipHashable::combine(
|
|
|
|
Level::from(1),
|
|
|
|
&SipHashable::combine(0.into(), &SipHashable(0), &SipHashable(1)),
|
|
|
|
&SipHashable::combine(0.into(), &SipHashable(2), &SipHashable(3)),
|
|
|
|
),
|
|
|
|
&SipHashable::combine(
|
|
|
|
Level::from(1),
|
|
|
|
&SipHashable::combine(0.into(), &SipHashable(4), &SipHashable(5)),
|
|
|
|
&SipHashable::combine(0.into(), &SipHashable(6), &SipHashable(7)),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
compute_root_from_witness::<SipHashable>(
|
|
|
|
SipHashable(0),
|
|
|
|
0.into(),
|
|
|
|
&[
|
|
|
|
SipHashable(1),
|
|
|
|
SipHashable::combine(0.into(), &SipHashable(2), &SipHashable(3)),
|
|
|
|
SipHashable::combine(
|
|
|
|
Level::from(1),
|
|
|
|
&SipHashable::combine(0.into(), &SipHashable(4), &SipHashable(5)),
|
|
|
|
&SipHashable::combine(0.into(), &SipHashable(6), &SipHashable(7))
|
|
|
|
)
|
|
|
|
]
|
|
|
|
),
|
|
|
|
expected
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
compute_root_from_witness(
|
|
|
|
SipHashable(4),
|
|
|
|
Position::from(4),
|
|
|
|
&[
|
|
|
|
SipHashable(5),
|
|
|
|
SipHashable::combine(0.into(), &SipHashable(6), &SipHashable(7)),
|
|
|
|
SipHashable::combine(
|
|
|
|
Level::from(1),
|
|
|
|
&SipHashable::combine(0.into(), &SipHashable(0), &SipHashable(1)),
|
|
|
|
&SipHashable::combine(0.into(), &SipHashable(2), &SipHashable(3))
|
|
|
|
)
|
|
|
|
]
|
|
|
|
),
|
|
|
|
expected
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|