2021-01-05 12:40:05 -08:00
|
|
|
//! # `incrementalmerkletree`
|
|
|
|
//!
|
|
|
|
//! Incremental Merkle Trees are fixed-depth Merkle trees with two primary
|
|
|
|
//! capabilities: appending (assigning a value to the next unused leaf and
|
|
|
|
//! advancing the tree) and obtaining the root of the tree. Importantly the tree
|
|
|
|
//! structure attempts to store the least amount of information necessary to
|
|
|
|
//! continue to function; other information should be pruned eagerly to avoid
|
|
|
|
//! waste when the tree state is encoded.
|
|
|
|
//!
|
2022-07-21 17:12:39 -07:00
|
|
|
//! ## Marking
|
2021-01-05 12:40:05 -08:00
|
|
|
//!
|
|
|
|
//! Merkle trees are typically used to show that a value exists in the tree via
|
2022-07-21 17:17:36 -07:00
|
|
|
//! a witness. We need an API that allows us to identify the
|
|
|
|
//! current leaf as a value we wish to compute witnesss for even as
|
2021-01-05 12:40:05 -08:00
|
|
|
//! the tree continues to be appended to in the future; this is called
|
|
|
|
//! maintaining a witness. When we're later uninterested in such a leaf, we can
|
|
|
|
//! prune a witness and remove all unnecessary information from the structure as
|
|
|
|
//! a consequence.
|
|
|
|
//!
|
|
|
|
//! ## Checkpoints and Rollbacks
|
|
|
|
//!
|
|
|
|
//! The structure is not append-only in the strict sense. It is possible to
|
|
|
|
//! identify the current state of the tree as a "checkpoint" and to remove older
|
|
|
|
//! checkpoints that we're no longer interested in. It should be possible to
|
|
|
|
//! roll back to any previous checkpoint.
|
|
|
|
|
2021-06-17 18:31:26 -07:00
|
|
|
pub mod bridgetree;
|
2021-06-24 13:12:52 -07:00
|
|
|
mod sample;
|
2021-06-17 18:17:47 -07:00
|
|
|
|
2021-06-17 18:32:50 -07:00
|
|
|
use serde::{Deserialize, Serialize};
|
2022-04-05 11:59:36 -07:00
|
|
|
use std::collections::BTreeSet;
|
2022-07-14 12:45:51 -07:00
|
|
|
use std::convert::{TryFrom, TryInto};
|
2022-02-22 20:34:38 -08:00
|
|
|
use std::num::TryFromIntError;
|
2022-07-21 12:20:41 -07:00
|
|
|
use std::ops::{Add, AddAssign, Range};
|
2021-01-05 12:40:05 -08:00
|
|
|
|
2021-07-08 09:16:55 -07:00
|
|
|
/// A type-safe wrapper for indexing into "levels" of a binary tree, such that
|
2022-07-21 12:20:41 -07:00
|
|
|
/// nodes at level `0` are leaves, nodes at level `1` are parents of nodes at
|
|
|
|
/// level `0`, and so forth. This type is capable of representing levels in
|
|
|
|
/// trees containing up to 2^255 leaves.
|
2022-07-14 12:45:51 -07:00
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
2021-06-17 18:24:18 -07:00
|
|
|
#[repr(transparent)]
|
2022-07-21 12:20:41 -07:00
|
|
|
pub struct Level(u8);
|
2021-06-17 18:24:18 -07:00
|
|
|
|
2022-07-21 12:20:41 -07:00
|
|
|
impl Level {
|
|
|
|
// TODO: replace with an instance for `Step<Level>` once `step_trait`
|
|
|
|
// is stabilized
|
|
|
|
pub fn iter_to(self, other: Level) -> impl Iterator<Item = Self> {
|
|
|
|
(self.0..other.0).into_iter().map(Level)
|
2021-06-17 18:24:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-21 12:20:41 -07:00
|
|
|
impl Add<u8> for Level {
|
2022-01-23 11:16:30 -08:00
|
|
|
type Output = Self;
|
2021-06-23 13:14:57 -07:00
|
|
|
fn add(self, value: u8) -> Self {
|
2022-01-23 11:16:30 -08:00
|
|
|
Self(self.0 + value)
|
2021-06-17 18:24:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-21 12:20:41 -07:00
|
|
|
impl From<u8> for Level {
|
2021-06-23 13:14:57 -07:00
|
|
|
fn from(value: u8) -> Self {
|
2022-01-23 11:16:30 -08:00
|
|
|
Self(value)
|
2021-06-17 18:24:18 -07:00
|
|
|
}
|
2015-12-19 17:37:23 -08:00
|
|
|
}
|
|
|
|
|
2022-07-21 12:20:41 -07:00
|
|
|
impl From<Level> for u8 {
|
|
|
|
fn from(level: Level) -> u8 {
|
2021-06-23 13:14:57 -07:00
|
|
|
level.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-21 12:20:41 -07:00
|
|
|
impl From<Level> for usize {
|
|
|
|
fn from(level: Level) -> usize {
|
2021-06-17 18:24:18 -07:00
|
|
|
level.0 as usize
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-22 10:30:22 -07:00
|
|
|
/// A type representing the position of a leaf in a Merkle tree.
|
2022-02-16 15:37:19 -08:00
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
2021-07-22 10:30:22 -07:00
|
|
|
#[repr(transparent)]
|
2022-02-22 20:30:14 -08:00
|
|
|
pub struct Position(usize);
|
2021-07-22 10:30:22 -07:00
|
|
|
|
|
|
|
impl Position {
|
2022-07-21 12:20:41 -07:00
|
|
|
/// Return whether the position is odd-valued.
|
|
|
|
pub fn is_odd(&self) -> bool {
|
|
|
|
self.0 & 0x1 == 1
|
2021-07-22 10:30:22 -07:00
|
|
|
}
|
|
|
|
|
2022-07-21 12:20:41 -07:00
|
|
|
/// Returns the minimum possible level of the root of a binary tree containing at least
|
|
|
|
/// `self + 1` nodes.
|
|
|
|
pub fn root_level(&self) -> Level {
|
|
|
|
Level(64 - self.0.leading_zeros() as u8)
|
2021-07-22 10:30:22 -07:00
|
|
|
}
|
|
|
|
|
2022-07-21 12:20:41 -07:00
|
|
|
/// Returns the number of cousins and/or ommers required to construct an authentication
|
|
|
|
/// path to the root of a merkle tree that has `self + 1` nodes.
|
|
|
|
pub fn past_ommer_count(&self) -> usize {
|
|
|
|
(0..self.root_level().0)
|
|
|
|
.filter(|i| (self.0 >> i) & 0x1 == 1)
|
2022-07-14 12:45:51 -07:00
|
|
|
.count()
|
2021-07-22 10:30:22 -07:00
|
|
|
}
|
|
|
|
|
2022-07-21 12:20:41 -07:00
|
|
|
/// Returns whether the binary tree having `self` as the position of the rightmost leaf
|
|
|
|
/// contains a perfect balanced tree with a root at level `root_level` that contains the
|
|
|
|
/// aforesaid leaf.
|
|
|
|
pub fn is_complete_subtree(&self, root_level: Level) -> bool {
|
|
|
|
!(0..(root_level.0)).any(|l| self.0 & (1 << l) == 0)
|
2022-07-14 12:45:51 -07:00
|
|
|
}
|
|
|
|
|
2022-07-21 12:20:41 -07:00
|
|
|
/// Returns an iterator over the addresses of nodes required to create a witness for this
|
|
|
|
/// position, beginning with the sibling of the leaf at this position and ending with the
|
|
|
|
/// sibling of the ancestor of the leaf at this position that is required to compute a root at
|
|
|
|
/// the specified level.
|
|
|
|
pub(crate) fn witness_addrs(
|
|
|
|
&self,
|
|
|
|
root_level: Level,
|
|
|
|
) -> impl Iterator<Item = (Address, Source)> {
|
|
|
|
WitnessAddrsIter {
|
|
|
|
root_level,
|
|
|
|
current: Address::from(self),
|
|
|
|
ommer_count: 0,
|
2021-07-22 10:30:22 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-22 20:30:14 -08:00
|
|
|
impl From<Position> for usize {
|
|
|
|
fn from(p: Position) -> usize {
|
|
|
|
p.0
|
2021-07-22 10:30:22 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<Position> for u64 {
|
|
|
|
fn from(p: Position) -> Self {
|
2022-02-22 20:30:14 -08:00
|
|
|
p.0 as u64
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Add<usize> for Position {
|
|
|
|
type Output = Position;
|
|
|
|
fn add(self, other: usize) -> Self {
|
|
|
|
Position(self.0 + other)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AddAssign<usize> for Position {
|
|
|
|
fn add_assign(&mut self, other: usize) {
|
|
|
|
self.0 += other
|
2021-07-22 10:30:22 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<usize> for Position {
|
|
|
|
fn from(sz: usize) -> Self {
|
2022-02-22 20:30:14 -08:00
|
|
|
Self(sz)
|
2021-07-22 10:30:22 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-22 20:34:38 -08:00
|
|
|
impl TryFrom<u64> for Position {
|
|
|
|
type Error = TryFromIntError;
|
|
|
|
fn try_from(sz: u64) -> Result<Self, Self::Error> {
|
|
|
|
<usize>::try_from(sz).map(Self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-14 12:45:51 -07:00
|
|
|
/// The address of an internal node of the Merkle tree.
|
|
|
|
/// When `level == 0`, the index has the same value as the
|
|
|
|
/// position.
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
|
|
|
pub struct Address {
|
2022-07-21 12:20:41 -07:00
|
|
|
level: Level,
|
2022-07-14 12:45:51 -07:00
|
|
|
index: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
|
|
pub(crate) enum Source {
|
2022-07-21 12:20:41 -07:00
|
|
|
/// The sibling to the address can be derived from the incremental frontier
|
|
|
|
/// at the contained ommer index
|
|
|
|
Past(usize),
|
|
|
|
/// The sibling to the address must be obtained from values discovered by
|
|
|
|
/// the addition of more nodes to the tree
|
2022-07-14 12:45:51 -07:00
|
|
|
Future,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Address {
|
2022-07-21 12:20:41 -07:00
|
|
|
pub fn from_parts(level: Level, index: usize) -> Self {
|
2022-07-14 12:45:51 -07:00
|
|
|
Address { level, index }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn position_range(&self) -> Range<Position> {
|
|
|
|
Range {
|
|
|
|
start: (self.index << self.level.0).try_into().unwrap(),
|
|
|
|
end: ((self.index + 1) << self.level.0).try_into().unwrap(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-21 12:20:41 -07:00
|
|
|
pub fn level(&self) -> Level {
|
2022-07-14 12:45:51 -07:00
|
|
|
self.level
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn index(&self) -> usize {
|
|
|
|
self.index
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn parent(&self) -> Address {
|
|
|
|
Address {
|
|
|
|
level: self.level + 1,
|
|
|
|
index: self.index >> 1,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn sibling(&self) -> Address {
|
|
|
|
Address {
|
|
|
|
level: self.level,
|
|
|
|
index: if self.index & 0x1 == 0 {
|
|
|
|
self.index + 1
|
|
|
|
} else {
|
|
|
|
self.index - 1
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_complete_node(&self) -> bool {
|
|
|
|
self.index & 0x1 == 1
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn current_incomplete(&self) -> Address {
|
|
|
|
// find the first zero bit in the index, searching from the least significant bit
|
|
|
|
let mut index = self.index;
|
|
|
|
for level in self.level.0.. {
|
|
|
|
if index & 0x1 == 1 {
|
|
|
|
index >>= 1;
|
|
|
|
} else {
|
|
|
|
return Address {
|
2022-07-21 12:20:41 -07:00
|
|
|
level: Level(level),
|
2022-07-14 12:45:51 -07:00
|
|
|
index,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unreachable!("The loop will always terminate via return in at most 64 iterations.")
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn next_incomplete_parent(&self) -> Address {
|
|
|
|
if self.is_complete_node() {
|
|
|
|
self.current_incomplete()
|
|
|
|
} else {
|
|
|
|
let complete = Address {
|
|
|
|
level: self.level,
|
|
|
|
index: self.index + 1,
|
|
|
|
};
|
|
|
|
complete.current_incomplete()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<Position> for Address {
|
|
|
|
fn from(p: Position) -> Self {
|
|
|
|
Address {
|
2022-07-21 12:20:41 -07:00
|
|
|
level: 0.into(),
|
2022-07-14 12:45:51 -07:00
|
|
|
index: p.into(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> From<&'a Position> for Address {
|
|
|
|
fn from(p: &'a Position) -> Self {
|
|
|
|
Address {
|
2022-07-21 12:20:41 -07:00
|
|
|
level: 0.into(),
|
2022-07-14 12:45:51 -07:00
|
|
|
index: (*p).into(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<Address> for Option<Position> {
|
|
|
|
fn from(addr: Address) -> Self {
|
2022-07-21 12:20:41 -07:00
|
|
|
if addr.level == 0.into() {
|
2022-07-14 12:45:51 -07:00
|
|
|
Some(addr.index.into())
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> From<&'a Address> for Option<Position> {
|
|
|
|
fn from(addr: &'a Address) -> Self {
|
2022-07-21 12:20:41 -07:00
|
|
|
if addr.level == 0.into() {
|
2022-07-14 12:45:51 -07:00
|
|
|
Some(addr.index.into())
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[must_use = "iterators are lazy and do nothing unless consumed"]
|
2022-07-21 12:20:41 -07:00
|
|
|
pub(crate) struct WitnessAddrsIter {
|
|
|
|
root_level: Level,
|
2022-07-14 12:45:51 -07:00
|
|
|
current: Address,
|
2022-07-21 12:20:41 -07:00
|
|
|
ommer_count: usize,
|
2022-07-14 12:45:51 -07:00
|
|
|
}
|
|
|
|
|
2022-07-21 12:20:41 -07:00
|
|
|
impl Iterator for WitnessAddrsIter {
|
2022-07-14 12:45:51 -07:00
|
|
|
type Item = (Address, Source);
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<(Address, Source)> {
|
2022-07-21 12:20:41 -07:00
|
|
|
if self.current.level() < self.root_level {
|
2022-07-14 12:45:51 -07:00
|
|
|
let current = self.current;
|
2022-07-21 12:20:41 -07:00
|
|
|
let source = if current.is_complete_node() {
|
|
|
|
Source::Past(self.ommer_count)
|
|
|
|
} else {
|
|
|
|
Source::Future
|
|
|
|
};
|
|
|
|
|
2022-07-14 12:45:51 -07:00
|
|
|
self.current = current.parent();
|
2022-07-21 12:20:41 -07:00
|
|
|
if matches!(source, Source::Past(_)) {
|
|
|
|
self.ommer_count += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
Some((current.sibling(), source))
|
2022-07-14 12:45:51 -07:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-18 13:21:03 -07:00
|
|
|
/// A trait describing the operations that make a value suitable for inclusion in
|
|
|
|
/// an incremental merkle tree.
|
2022-02-24 07:56:56 -08:00
|
|
|
pub trait Hashable: Sized {
|
2021-06-17 18:24:18 -07:00
|
|
|
fn empty_leaf() -> Self;
|
|
|
|
|
2022-07-21 12:20:41 -07:00
|
|
|
fn combine(level: Level, a: &Self, b: &Self) -> Self;
|
2021-06-17 18:24:18 -07:00
|
|
|
|
2022-07-21 12:20:41 -07:00
|
|
|
fn empty_root(level: Level) -> Self {
|
|
|
|
Level(0)
|
2021-06-17 18:24:18 -07:00
|
|
|
.iter_to(level)
|
|
|
|
.fold(Self::empty_leaf(), |v, lvl| Self::combine(lvl, &v, &v))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-18 13:21:03 -07:00
|
|
|
/// 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.
|
2021-06-17 18:24:18 -07:00
|
|
|
fn append(&mut self, value: &H) -> bool;
|
2021-01-05 14:11:17 -08:00
|
|
|
|
2021-06-18 13:21:03 -07: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.
|
2021-06-17 18:24:18 -07:00
|
|
|
fn root(&self) -> H;
|
2021-06-18 13:21:03 -07:00
|
|
|
}
|
|
|
|
|
2022-07-21 17:12:39 -07:00
|
|
|
/// A Merkle tree that supports incremental appends, marking of
|
|
|
|
/// leaf nodes for construction of witnesses, checkpoints and rollbacks.
|
2022-04-18 15:01:53 -07:00
|
|
|
pub trait Tree<H> {
|
|
|
|
/// 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.
|
|
|
|
fn append(&mut self, value: &H) -> bool;
|
|
|
|
|
2022-02-18 14:30:47 -08:00
|
|
|
/// Returns the most recently appended leaf value.
|
2022-02-22 20:30:14 -08:00
|
|
|
fn current_position(&self) -> Option<Position>;
|
|
|
|
|
|
|
|
/// Returns the most recently appended leaf value.
|
2022-03-30 20:31:47 -07:00
|
|
|
fn current_leaf(&self) -> Option<&H>;
|
2022-02-18 14:30:47 -08:00
|
|
|
|
2022-03-31 06:06:35 -07:00
|
|
|
/// Returns the leaf at the specified position if the tree can produce
|
2022-07-21 17:17:36 -07:00
|
|
|
/// a witness for it.
|
2022-07-21 17:12:39 -07:00
|
|
|
fn get_marked_leaf(&self, position: Position) -> Option<&H>;
|
2022-02-18 14:30:47 -08:00
|
|
|
|
|
|
|
/// Marks the current leaf as one for which we're interested in producing
|
2022-07-21 17:17:36 -07:00
|
|
|
/// a witness. Returns an optional value containing the
|
2022-03-31 06:06:35 -07:00
|
|
|
/// current position if successful or if the current value was already
|
|
|
|
/// marked, or None if the tree is empty.
|
2022-07-21 17:12:39 -07:00
|
|
|
fn mark(&mut self) -> Option<Position>;
|
2021-01-05 14:11:17 -08:00
|
|
|
|
2022-07-21 17:12:39 -07:00
|
|
|
/// Return a set of all the positions for which we have marked.
|
|
|
|
fn marked_positions(&self) -> BTreeSet<Position>;
|
2022-04-05 11:59:36 -07:00
|
|
|
|
2022-04-18 15:01:53 -07:00
|
|
|
/// 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>;
|
|
|
|
|
2022-07-21 17:17:36 -07:00
|
|
|
/// Obtains a witness to the value at the specified position,
|
2022-04-18 15:01:53 -07:00
|
|
|
/// as of the tree state corresponding to the given root.
|
2022-07-21 17:17:36 -07:00
|
|
|
/// Returns `None` if there is no available witness to that
|
2022-04-18 15:01:53 -07:00
|
|
|
/// position or if the root does not correspond to a checkpointed
|
|
|
|
/// root of the tree.
|
2022-07-21 17:17:36 -07:00
|
|
|
fn witness(&self, position: Position, as_of_root: &H) -> Option<Vec<H>>;
|
2021-01-05 14:11:17 -08:00
|
|
|
|
2022-03-31 06:06:35 -07:00
|
|
|
/// Marks the value at the specified position as a value we're no longer
|
2022-07-21 17:12:39 -07:00
|
|
|
/// 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;
|
2021-06-17 18:24:18 -07:00
|
|
|
|
2021-12-01 14:41:33 -08:00
|
|
|
/// Creates a new checkpoint for the current tree state. It is valid to
|
|
|
|
/// have multiple checkpoints for the same tree state, and each `rewind`
|
|
|
|
/// call will remove a single checkpoint.
|
2021-06-17 18:17:47 -07:00
|
|
|
fn checkpoint(&mut self);
|
2021-01-05 14:11:17 -08:00
|
|
|
|
2021-12-01 14:41:33 -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
|
2022-03-02 09:04:11 -08:00
|
|
|
/// return false and leave the tree unmodified if no checkpoints exist.
|
2021-06-17 18:17:47 -07:00
|
|
|
fn rewind(&mut self) -> bool;
|
2022-04-05 11:59:36 -07:00
|
|
|
|
|
|
|
/// Remove state from the tree that no longer needs to be maintained
|
2022-07-21 17:12:39 -07:00
|
|
|
/// because it is associated with checkpoints or marks that
|
2022-04-05 11:59:36 -07:00
|
|
|
/// have been removed from the tree at positions deeper than those
|
|
|
|
/// reachable by calls to `rewind`. It is always safe to implement
|
|
|
|
/// this as a no-op operation
|
|
|
|
fn garbage_collect(&mut self);
|
2021-01-05 14:11:17 -08:00
|
|
|
}
|
|
|
|
|
2022-07-21 14:07:01 -07:00
|
|
|
#[cfg(any(bench, test, feature = "test-dependencies"))]
|
|
|
|
pub mod testing {
|
|
|
|
#![allow(deprecated)]
|
|
|
|
|
|
|
|
use proptest::prelude::*;
|
|
|
|
use std::hash::{Hasher, SipHasher};
|
|
|
|
|
|
|
|
use super::{Hashable, Level, Position, Tree};
|
|
|
|
|
|
|
|
//
|
|
|
|
// Types and utilities for shared example tests.
|
|
|
|
//
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
|
|
pub(crate) struct SipHashable(pub(crate) u64);
|
|
|
|
|
|
|
|
impl Hashable for SipHashable {
|
|
|
|
fn empty_leaf() -> Self {
|
|
|
|
SipHashable(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn combine(_level: Level, a: &Self, b: &Self) -> Self {
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Operations
|
|
|
|
//
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub enum Operation<A> {
|
|
|
|
Append(A),
|
|
|
|
CurrentPosition,
|
|
|
|
CurrentLeaf,
|
|
|
|
Mark,
|
|
|
|
MarkedLeaf(Position),
|
|
|
|
MarkedPositions,
|
|
|
|
Unmark(Position),
|
|
|
|
Checkpoint,
|
|
|
|
Rewind,
|
|
|
|
Authpath(Position, usize),
|
|
|
|
GarbageCollect,
|
|
|
|
}
|
|
|
|
|
|
|
|
use Operation::*;
|
|
|
|
|
|
|
|
impl<H: Hashable> Operation<H> {
|
|
|
|
pub fn apply<T: Tree<H>>(&self, tree: &mut T) -> Option<(Position, Vec<H>)> {
|
|
|
|
match self {
|
|
|
|
Append(a) => {
|
|
|
|
assert!(tree.append(a), "append failed");
|
|
|
|
None
|
|
|
|
}
|
|
|
|
CurrentPosition => None,
|
|
|
|
CurrentLeaf => None,
|
|
|
|
Mark => {
|
|
|
|
assert!(tree.mark().is_some(), "mark failed");
|
|
|
|
None
|
|
|
|
}
|
|
|
|
MarkedLeaf(_) => None,
|
|
|
|
MarkedPositions => None,
|
|
|
|
Unmark(p) => {
|
|
|
|
assert!(tree.remove_mark(*p), "remove mark failed");
|
|
|
|
None
|
|
|
|
}
|
|
|
|
Checkpoint => {
|
|
|
|
tree.checkpoint();
|
|
|
|
None
|
|
|
|
}
|
|
|
|
Rewind => {
|
|
|
|
assert!(tree.rewind(), "rewind failed");
|
|
|
|
None
|
|
|
|
}
|
|
|
|
Authpath(p, d) => tree
|
|
|
|
.root(*d)
|
|
|
|
.and_then(|root| tree.witness(*p, &root))
|
|
|
|
.map(|xs| (*p, xs)),
|
|
|
|
GarbageCollect => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn apply_all<T: Tree<H>>(
|
|
|
|
ops: &[Operation<H>],
|
|
|
|
tree: &mut T,
|
|
|
|
) -> Option<(Position, Vec<H>)> {
|
|
|
|
let mut result = None;
|
|
|
|
for op in ops {
|
|
|
|
result = op.apply(tree);
|
|
|
|
}
|
|
|
|
result
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn arb_operation<G: Strategy + Clone>(
|
|
|
|
item_gen: G,
|
|
|
|
pos_gen: impl Strategy<Value = usize> + Clone,
|
|
|
|
) -> impl Strategy<Value = Operation<G::Value>>
|
|
|
|
where
|
|
|
|
G::Value: Clone + 'static,
|
|
|
|
{
|
|
|
|
prop_oneof![
|
|
|
|
item_gen.prop_map(Operation::Append),
|
|
|
|
Just(Operation::Mark),
|
|
|
|
prop_oneof![
|
|
|
|
Just(Operation::CurrentLeaf),
|
|
|
|
Just(Operation::CurrentPosition),
|
|
|
|
Just(Operation::MarkedPositions),
|
|
|
|
],
|
|
|
|
Just(Operation::GarbageCollect),
|
|
|
|
pos_gen
|
|
|
|
.clone()
|
|
|
|
.prop_map(|i| Operation::MarkedLeaf(Position(i))),
|
|
|
|
pos_gen.clone().prop_map(|i| Operation::Unmark(Position(i))),
|
|
|
|
Just(Operation::Checkpoint),
|
|
|
|
Just(Operation::Rewind),
|
|
|
|
pos_gen.prop_flat_map(
|
|
|
|
|i| (0usize..10).prop_map(move |depth| Operation::Authpath(Position(i), depth))
|
|
|
|
),
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn apply_operation<H, T: Tree<H>>(tree: &mut T, op: Operation<H>) {
|
|
|
|
match op {
|
|
|
|
Append(value) => {
|
|
|
|
tree.append(&value);
|
|
|
|
}
|
|
|
|
Mark => {
|
|
|
|
tree.mark();
|
|
|
|
}
|
|
|
|
Unmark(position) => {
|
|
|
|
tree.remove_mark(position);
|
|
|
|
}
|
|
|
|
Checkpoint => {
|
|
|
|
tree.checkpoint();
|
|
|
|
}
|
|
|
|
Rewind => {
|
|
|
|
tree.rewind();
|
|
|
|
}
|
|
|
|
CurrentPosition => {}
|
|
|
|
CurrentLeaf => {}
|
|
|
|
Authpath(_, _) => {}
|
|
|
|
MarkedLeaf(_) => {}
|
|
|
|
MarkedPositions => {}
|
|
|
|
GarbageCollect => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-17 18:17:47 -07:00
|
|
|
#[cfg(test)]
|
2021-06-17 18:24:18 -07:00
|
|
|
pub(crate) mod tests {
|
2022-07-21 14:07:01 -07:00
|
|
|
use proptest::prelude::*;
|
2022-04-05 11:59:36 -07:00
|
|
|
use std::collections::BTreeSet;
|
2022-02-18 13:02:35 -08:00
|
|
|
use std::fmt::Debug;
|
2021-06-17 18:24:18 -07:00
|
|
|
|
2022-07-21 14:07:01 -07:00
|
|
|
use super::{
|
|
|
|
bridgetree::BridgeTree,
|
|
|
|
sample::{lazy_root, CompleteTree},
|
|
|
|
testing::{arb_operation, Operation, Operation::*, SipHashable},
|
|
|
|
Address, Hashable, Level, Position, Source, Tree,
|
|
|
|
};
|
2022-07-14 12:45:51 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn position_is_complete_subtree() {
|
2022-07-21 12:20:41 -07:00
|
|
|
assert!(Position(0).is_complete_subtree(Level(0)));
|
|
|
|
assert!(Position(1).is_complete_subtree(Level(1)));
|
|
|
|
assert!(!Position(2).is_complete_subtree(Level(1)));
|
|
|
|
assert!(!Position(2).is_complete_subtree(Level(2)));
|
|
|
|
assert!(Position(3).is_complete_subtree(Level(2)));
|
|
|
|
assert!(!Position(4).is_complete_subtree(Level(2)));
|
|
|
|
assert!(Position(7).is_complete_subtree(Level(3)));
|
|
|
|
assert!(Position(u32::MAX as usize).is_complete_subtree(Level(32)));
|
2022-07-14 12:45:51 -07:00
|
|
|
}
|
2021-01-05 14:11:17 -08:00
|
|
|
|
2022-02-20 10:40:12 -08:00
|
|
|
#[test]
|
2022-07-21 12:20:41 -07:00
|
|
|
fn position_past_ommer_count() {
|
|
|
|
assert_eq!(0, Position(0).past_ommer_count());
|
|
|
|
assert_eq!(1, Position(1).past_ommer_count());
|
|
|
|
assert_eq!(1, Position(2).past_ommer_count());
|
|
|
|
assert_eq!(2, Position(3).past_ommer_count());
|
|
|
|
assert_eq!(1, Position(4).past_ommer_count());
|
|
|
|
assert_eq!(3, Position(7).past_ommer_count());
|
|
|
|
assert_eq!(1, Position(8).past_ommer_count());
|
2022-02-20 10:40:12 -08:00
|
|
|
}
|
|
|
|
|
2022-07-14 12:45:51 -07:00
|
|
|
#[test]
|
2022-07-21 12:20:41 -07:00
|
|
|
fn position_root_level() {
|
|
|
|
assert_eq!(Level(0), Position(0).root_level());
|
|
|
|
assert_eq!(Level(1), Position(1).root_level());
|
|
|
|
assert_eq!(Level(2), Position(2).root_level());
|
|
|
|
assert_eq!(Level(2), Position(3).root_level());
|
|
|
|
assert_eq!(Level(3), Position(4).root_level());
|
|
|
|
assert_eq!(Level(3), Position(7).root_level());
|
|
|
|
assert_eq!(Level(4), Position(8).root_level());
|
2022-07-14 12:45:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn current_incomplete() {
|
2022-07-21 12:20:41 -07:00
|
|
|
let addr = |l, i| Address::from_parts(Level(l), i);
|
2022-07-14 12:45:51 -07:00
|
|
|
assert_eq!(addr(0, 0), addr(0, 0).current_incomplete());
|
|
|
|
assert_eq!(addr(1, 0), addr(0, 1).current_incomplete());
|
|
|
|
assert_eq!(addr(0, 2), addr(0, 2).current_incomplete());
|
|
|
|
assert_eq!(addr(2, 0), addr(0, 3).current_incomplete());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn next_incomplete_parent() {
|
2022-07-21 12:20:41 -07:00
|
|
|
let addr = |l, i| Address::from_parts(Level(l), i);
|
2022-07-14 12:45:51 -07:00
|
|
|
assert_eq!(addr(1, 0), addr(0, 0).next_incomplete_parent());
|
|
|
|
assert_eq!(addr(1, 0), addr(0, 1).next_incomplete_parent());
|
|
|
|
assert_eq!(addr(2, 0), addr(0, 2).next_incomplete_parent());
|
|
|
|
assert_eq!(addr(2, 0), addr(0, 3).next_incomplete_parent());
|
|
|
|
assert_eq!(addr(3, 0), addr(2, 0).next_incomplete_parent());
|
|
|
|
assert_eq!(addr(1, 2), addr(0, 4).next_incomplete_parent());
|
|
|
|
assert_eq!(addr(3, 0), addr(1, 2).next_incomplete_parent());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-07-21 12:20:41 -07:00
|
|
|
fn position_witness_addrs() {
|
2022-07-14 12:45:51 -07:00
|
|
|
use Source::*;
|
2022-07-21 12:20:41 -07:00
|
|
|
let path_elem = |l, i, s| (Address::from_parts(Level(l), i), s);
|
2022-07-14 12:45:51 -07:00
|
|
|
assert_eq!(
|
|
|
|
vec![path_elem(0, 1, Future), path_elem(1, 1, Future)],
|
2022-07-21 12:20:41 -07:00
|
|
|
Position(0).witness_addrs(Level(2)).collect::<Vec<_>>()
|
2022-07-14 12:45:51 -07:00
|
|
|
);
|
|
|
|
assert_eq!(
|
2022-07-21 12:20:41 -07:00
|
|
|
vec![path_elem(0, 3, Future), path_elem(1, 0, Past(0))],
|
|
|
|
Position(2).witness_addrs(Level(2)).collect::<Vec<_>>()
|
2022-07-14 12:45:51 -07:00
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
vec![
|
2022-07-21 12:20:41 -07:00
|
|
|
path_elem(0, 2, Past(0)),
|
|
|
|
path_elem(1, 0, Past(1)),
|
2022-07-14 12:45:51 -07:00
|
|
|
path_elem(2, 1, Future)
|
|
|
|
],
|
2022-07-21 12:20:41 -07:00
|
|
|
Position(3).witness_addrs(Level(3)).collect::<Vec<_>>()
|
2022-07-14 12:45:51 -07:00
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
vec![
|
|
|
|
path_elem(0, 5, Future),
|
|
|
|
path_elem(1, 3, Future),
|
2022-07-21 12:20:41 -07:00
|
|
|
path_elem(2, 0, Past(0)),
|
2022-07-14 12:45:51 -07:00
|
|
|
path_elem(3, 1, Future)
|
|
|
|
],
|
2022-07-21 12:20:41 -07:00
|
|
|
Position(4).witness_addrs(Level(4)).collect::<Vec<_>>()
|
2022-07-14 12:45:51 -07:00
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
vec![
|
|
|
|
path_elem(0, 7, Future),
|
2022-07-21 12:20:41 -07:00
|
|
|
path_elem(1, 2, Past(0)),
|
|
|
|
path_elem(2, 0, Past(1)),
|
2022-07-14 12:45:51 -07:00
|
|
|
path_elem(3, 1, Future)
|
|
|
|
],
|
2022-07-21 12:20:41 -07:00
|
|
|
Position(6).witness_addrs(Level(4)).collect::<Vec<_>>()
|
2022-07-14 12:45:51 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-02-20 10:40:12 -08:00
|
|
|
//
|
|
|
|
// Shared example tests
|
|
|
|
//
|
|
|
|
|
|
|
|
pub(crate) fn check_root_hashes<T: Tree<String>, F: Fn(usize) -> T>(new_tree: F) {
|
|
|
|
let mut tree = new_tree(100);
|
2022-04-18 15:01:53 -07:00
|
|
|
assert_eq!(tree.root(0).unwrap(), "________________");
|
2022-02-20 10:40:12 -08:00
|
|
|
|
|
|
|
tree.append(&"a".to_string());
|
2022-04-18 15:01:53 -07:00
|
|
|
assert_eq!(tree.root(0).unwrap().len(), 16);
|
|
|
|
assert_eq!(tree.root(0).unwrap(), "a_______________");
|
2022-02-20 10:40:12 -08:00
|
|
|
|
|
|
|
tree.append(&"b".to_string());
|
2022-04-18 15:01:53 -07:00
|
|
|
assert_eq!(tree.root(0).unwrap(), "ab______________");
|
2022-02-20 10:40:12 -08:00
|
|
|
|
|
|
|
tree.append(&"c".to_string());
|
2022-04-18 15:01:53 -07:00
|
|
|
assert_eq!(tree.root(0).unwrap(), "abc_____________");
|
2022-02-20 16:40:07 -08:00
|
|
|
|
|
|
|
let mut t = new_tree(100);
|
|
|
|
t.append(&"a".to_string());
|
|
|
|
t.checkpoint();
|
2022-07-21 17:12:39 -07:00
|
|
|
t.mark();
|
2022-02-20 16:40:07 -08:00
|
|
|
t.append(&"a".to_string());
|
|
|
|
t.append(&"a".to_string());
|
|
|
|
t.append(&"a".to_string());
|
2022-04-18 15:01:53 -07:00
|
|
|
assert_eq!(t.root(0).unwrap(), "aaaa____________");
|
2022-02-20 10:40:12 -08:00
|
|
|
}
|
|
|
|
|
2022-07-21 17:17:36 -07:00
|
|
|
pub(crate) fn check_witnesss<T: Tree<String> + std::fmt::Debug, F: Fn(usize) -> T>(
|
2022-02-20 16:40:07 -08:00
|
|
|
new_tree: F,
|
|
|
|
) {
|
2022-02-20 10:40:12 -08:00
|
|
|
let mut tree = new_tree(100);
|
|
|
|
tree.append(&"a".to_string());
|
2022-07-21 17:12:39 -07:00
|
|
|
tree.mark();
|
2022-02-20 10:40:12 -08:00
|
|
|
assert_eq!(
|
2022-07-21 17:17:36 -07:00
|
|
|
tree.witness(Position(0), &tree.root(0).unwrap()),
|
2022-02-22 20:34:38 -08:00
|
|
|
Some(vec![
|
|
|
|
"_".to_string(),
|
|
|
|
"__".to_string(),
|
|
|
|
"____".to_string(),
|
|
|
|
"________".to_string()
|
|
|
|
])
|
2022-02-20 10:40:12 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
tree.append(&"b".to_string());
|
|
|
|
assert_eq!(
|
2022-07-21 17:17:36 -07:00
|
|
|
tree.witness(0.into(), &tree.root(0).unwrap()),
|
2022-02-22 20:34:38 -08:00
|
|
|
Some(vec![
|
|
|
|
"b".to_string(),
|
|
|
|
"__".to_string(),
|
|
|
|
"____".to_string(),
|
|
|
|
"________".to_string()
|
|
|
|
])
|
2022-02-20 10:40:12 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
tree.append(&"c".to_string());
|
2022-07-21 17:12:39 -07:00
|
|
|
tree.mark();
|
2022-02-20 10:40:12 -08:00
|
|
|
assert_eq!(
|
2022-07-21 17:17:36 -07:00
|
|
|
tree.witness(Position(2), &tree.root(0).unwrap()),
|
2022-02-22 20:34:38 -08:00
|
|
|
Some(vec![
|
|
|
|
"_".to_string(),
|
|
|
|
"ab".to_string(),
|
|
|
|
"____".to_string(),
|
|
|
|
"________".to_string()
|
|
|
|
])
|
2022-02-20 10:40:12 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
tree.append(&"d".to_string());
|
|
|
|
assert_eq!(
|
2022-07-21 17:17:36 -07:00
|
|
|
tree.witness(Position(2), &tree.root(0).unwrap()),
|
2022-02-22 20:34:38 -08:00
|
|
|
Some(vec![
|
|
|
|
"d".to_string(),
|
|
|
|
"ab".to_string(),
|
|
|
|
"____".to_string(),
|
|
|
|
"________".to_string()
|
|
|
|
])
|
2022-02-20 10:40:12 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
tree.append(&"e".to_string());
|
|
|
|
assert_eq!(
|
2022-07-21 17:17:36 -07:00
|
|
|
tree.witness(Position(2), &tree.root(0).unwrap()),
|
2022-02-22 20:34:38 -08:00
|
|
|
Some(vec![
|
|
|
|
"d".to_string(),
|
|
|
|
"ab".to_string(),
|
|
|
|
"e___".to_string(),
|
|
|
|
"________".to_string()
|
|
|
|
])
|
2022-02-20 10:40:12 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
let mut tree = new_tree(100);
|
|
|
|
tree.append(&"a".to_string());
|
2022-07-21 17:12:39 -07:00
|
|
|
tree.mark();
|
2022-02-20 10:40:12 -08:00
|
|
|
for c in 'b'..'h' {
|
|
|
|
tree.append(&c.to_string());
|
|
|
|
}
|
2022-07-21 17:12:39 -07:00
|
|
|
tree.mark();
|
2022-02-20 10:40:12 -08:00
|
|
|
tree.append(&"h".to_string());
|
|
|
|
|
|
|
|
assert_eq!(
|
2022-07-21 17:17:36 -07:00
|
|
|
tree.witness(0.into(), &tree.root(0).unwrap()),
|
2022-02-22 20:34:38 -08:00
|
|
|
Some(vec![
|
|
|
|
"b".to_string(),
|
|
|
|
"cd".to_string(),
|
|
|
|
"efgh".to_string(),
|
|
|
|
"________".to_string()
|
|
|
|
])
|
2022-02-20 10:40:12 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
let mut tree = new_tree(100);
|
|
|
|
tree.append(&"a".to_string());
|
2022-07-21 17:12:39 -07:00
|
|
|
tree.mark();
|
2022-02-20 10:40:12 -08:00
|
|
|
tree.append(&"b".to_string());
|
|
|
|
tree.append(&"c".to_string());
|
|
|
|
tree.append(&"d".to_string());
|
2022-07-21 17:12:39 -07:00
|
|
|
tree.mark();
|
2022-02-20 10:40:12 -08:00
|
|
|
tree.append(&"e".to_string());
|
2022-07-21 17:12:39 -07:00
|
|
|
tree.mark();
|
2022-02-20 10:40:12 -08:00
|
|
|
tree.append(&"f".to_string());
|
2022-07-21 17:12:39 -07:00
|
|
|
tree.mark();
|
2022-02-20 10:40:12 -08:00
|
|
|
tree.append(&"g".to_string());
|
|
|
|
|
|
|
|
assert_eq!(
|
2022-07-21 17:17:36 -07:00
|
|
|
tree.witness(Position(5), &tree.root(0).unwrap()),
|
2022-02-22 20:34:38 -08:00
|
|
|
Some(vec![
|
|
|
|
"e".to_string(),
|
|
|
|
"g_".to_string(),
|
|
|
|
"abcd".to_string(),
|
|
|
|
"________".to_string()
|
|
|
|
])
|
2022-02-20 10:40:12 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
let mut tree = new_tree(100);
|
|
|
|
for c in 'a'..'l' {
|
|
|
|
tree.append(&c.to_string());
|
|
|
|
}
|
2022-07-21 17:12:39 -07:00
|
|
|
tree.mark();
|
2022-02-20 10:40:12 -08:00
|
|
|
tree.append(&'l'.to_string());
|
|
|
|
|
|
|
|
assert_eq!(
|
2022-07-21 17:17:36 -07:00
|
|
|
tree.witness(Position(10), &tree.root(0).unwrap()),
|
2022-02-22 20:34:38 -08:00
|
|
|
Some(vec![
|
|
|
|
"l".to_string(),
|
|
|
|
"ij".to_string(),
|
|
|
|
"____".to_string(),
|
|
|
|
"abcdefgh".to_string()
|
|
|
|
])
|
2022-02-20 10:40:12 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
let mut tree = new_tree(100);
|
|
|
|
tree.append(&'a'.to_string());
|
2022-07-21 17:12:39 -07:00
|
|
|
tree.mark();
|
2022-02-20 10:40:12 -08:00
|
|
|
tree.checkpoint();
|
2022-02-23 20:41:19 -08:00
|
|
|
assert!(tree.rewind());
|
2022-02-20 10:40:12 -08:00
|
|
|
for c in 'b'..'f' {
|
|
|
|
tree.append(&c.to_string());
|
|
|
|
}
|
2022-07-21 17:12:39 -07:00
|
|
|
tree.mark();
|
2022-02-20 10:40:12 -08:00
|
|
|
for c in 'f'..'i' {
|
|
|
|
tree.append(&c.to_string());
|
|
|
|
}
|
|
|
|
|
|
|
|
assert_eq!(
|
2022-07-21 17:17:36 -07:00
|
|
|
tree.witness(0.into(), &tree.root(0).unwrap()),
|
2022-02-22 20:34:38 -08:00
|
|
|
Some(vec![
|
|
|
|
"b".to_string(),
|
|
|
|
"cd".to_string(),
|
|
|
|
"efgh".to_string(),
|
|
|
|
"________".to_string()
|
|
|
|
])
|
2022-02-20 10:40:12 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
let mut tree = new_tree(100);
|
|
|
|
tree.append(&'a'.to_string());
|
|
|
|
tree.append(&'b'.to_string());
|
|
|
|
tree.append(&'c'.to_string());
|
2022-07-21 17:12:39 -07:00
|
|
|
tree.mark();
|
2022-02-20 10:40:12 -08:00
|
|
|
tree.append(&'d'.to_string());
|
|
|
|
tree.append(&'e'.to_string());
|
|
|
|
tree.append(&'f'.to_string());
|
|
|
|
tree.append(&'g'.to_string());
|
2022-07-21 17:12:39 -07:00
|
|
|
tree.mark();
|
2022-02-20 10:40:12 -08:00
|
|
|
tree.checkpoint();
|
|
|
|
tree.append(&'h'.to_string());
|
2022-02-23 20:41:19 -08:00
|
|
|
assert!(tree.rewind());
|
2022-02-20 10:40:12 -08:00
|
|
|
|
|
|
|
assert_eq!(
|
2022-07-21 17:17:36 -07:00
|
|
|
tree.witness(Position(2), &tree.root(0).unwrap()),
|
2022-02-22 20:34:38 -08:00
|
|
|
Some(vec![
|
|
|
|
"d".to_string(),
|
|
|
|
"ab".to_string(),
|
|
|
|
"efg_".to_string(),
|
|
|
|
"________".to_string()
|
|
|
|
])
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut tree = new_tree(100);
|
|
|
|
tree.append(&'a'.to_string());
|
|
|
|
tree.append(&'b'.to_string());
|
2022-07-21 17:12:39 -07:00
|
|
|
tree.mark();
|
2022-07-21 17:17:36 -07:00
|
|
|
assert_eq!(tree.witness(Position(0), &tree.root(0).unwrap()), None);
|
2022-02-20 10:40:12 -08:00
|
|
|
|
|
|
|
let mut tree = new_tree(100);
|
|
|
|
for c in 'a'..'n' {
|
|
|
|
tree.append(&c.to_string());
|
|
|
|
}
|
2022-07-21 17:12:39 -07:00
|
|
|
tree.mark();
|
2022-02-20 10:40:12 -08:00
|
|
|
tree.append(&'n'.to_string());
|
2022-07-21 17:12:39 -07:00
|
|
|
tree.mark();
|
2022-02-20 10:40:12 -08:00
|
|
|
tree.append(&'o'.to_string());
|
|
|
|
tree.append(&'p'.to_string());
|
|
|
|
|
|
|
|
assert_eq!(
|
2022-07-21 17:17:36 -07:00
|
|
|
tree.witness(Position(12), &tree.root(0).unwrap()),
|
2022-02-22 20:34:38 -08:00
|
|
|
Some(vec![
|
|
|
|
"n".to_string(),
|
|
|
|
"op".to_string(),
|
|
|
|
"ijkl".to_string(),
|
|
|
|
"abcdefgh".to_string()
|
|
|
|
])
|
2022-02-20 10:40:12 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
let ops = ('a'..='l')
|
|
|
|
.into_iter()
|
|
|
|
.map(|c| Append(c.to_string()))
|
2022-07-21 17:12:39 -07:00
|
|
|
.chain(Some(Mark))
|
2022-02-20 10:40:12 -08:00
|
|
|
.chain(Some(Append('m'.to_string())))
|
|
|
|
.chain(Some(Append('n'.to_string())))
|
2022-04-18 15:01:53 -07:00
|
|
|
.chain(Some(Authpath(11usize.into(), 0)))
|
2022-02-20 10:40:12 -08:00
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
let mut tree = new_tree(100);
|
|
|
|
assert_eq!(
|
|
|
|
Operation::apply_all(&ops, &mut tree),
|
|
|
|
Some((
|
2022-07-21 12:20:41 -07:00
|
|
|
Position(11),
|
2022-02-20 10:40:12 -08:00
|
|
|
vec![
|
|
|
|
"k".to_string(),
|
|
|
|
"ij".to_string(),
|
|
|
|
"mn__".to_string(),
|
|
|
|
"abcdefgh".to_string()
|
|
|
|
]
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn check_checkpoint_rewind<T: Tree<String>, F: Fn(usize) -> T>(new_tree: F) {
|
|
|
|
let mut t = new_tree(100);
|
2022-02-23 20:41:19 -08:00
|
|
|
assert!(!t.rewind());
|
2022-02-20 10:40:12 -08:00
|
|
|
|
2022-03-02 09:04:11 -08:00
|
|
|
let mut t = new_tree(100);
|
|
|
|
t.checkpoint();
|
|
|
|
assert!(t.rewind());
|
|
|
|
|
2022-02-20 10:40:12 -08:00
|
|
|
let mut t = new_tree(100);
|
|
|
|
t.append(&"a".to_string());
|
|
|
|
t.checkpoint();
|
2022-03-02 09:04:11 -08:00
|
|
|
t.append(&"b".to_string());
|
2022-07-21 17:12:39 -07:00
|
|
|
t.mark();
|
2022-03-02 09:04:11 -08:00
|
|
|
assert!(t.rewind());
|
2022-07-21 12:20:41 -07:00
|
|
|
assert_eq!(Some(Position(0)), t.current_position());
|
2022-02-20 10:40:12 -08:00
|
|
|
|
|
|
|
let mut t = new_tree(100);
|
|
|
|
t.append(&"a".to_string());
|
2022-07-21 17:12:39 -07:00
|
|
|
t.mark();
|
2022-02-20 10:40:12 -08:00
|
|
|
t.checkpoint();
|
2022-02-23 20:41:19 -08:00
|
|
|
assert!(t.rewind());
|
2022-02-20 16:40:07 -08:00
|
|
|
|
|
|
|
let mut t = new_tree(100);
|
|
|
|
t.append(&"a".to_string());
|
|
|
|
t.checkpoint();
|
2022-07-21 17:12:39 -07:00
|
|
|
t.mark();
|
2022-02-20 16:40:07 -08:00
|
|
|
t.append(&"a".to_string());
|
2022-03-02 09:04:11 -08:00
|
|
|
assert!(t.rewind());
|
2022-07-21 12:20:41 -07:00
|
|
|
assert_eq!(Some(Position(0)), t.current_position());
|
2022-02-20 16:40:07 -08:00
|
|
|
|
|
|
|
let mut t = new_tree(100);
|
|
|
|
t.append(&"a".to_string());
|
|
|
|
t.checkpoint();
|
|
|
|
t.checkpoint();
|
2022-02-23 20:41:19 -08:00
|
|
|
assert!(t.rewind());
|
2022-02-20 16:40:07 -08:00
|
|
|
t.append(&"b".to_string());
|
2022-02-23 20:41:19 -08:00
|
|
|
assert!(t.rewind());
|
2022-02-20 16:40:07 -08:00
|
|
|
t.append(&"b".to_string());
|
2022-04-18 15:01:53 -07:00
|
|
|
assert_eq!(t.root(0).unwrap(), "ab______________");
|
2022-02-20 10:40:12 -08:00
|
|
|
}
|
|
|
|
|
2022-07-21 14:07:01 -07:00
|
|
|
fn append(x: &str) -> Operation<String> {
|
|
|
|
Operation::Append(x.to_string())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn unmark(pos: usize) -> Operation<String> {
|
|
|
|
Operation::Unmark(Position(pos))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn witness(pos: usize, depth: usize) -> Operation<String> {
|
|
|
|
Operation::Authpath(Position(pos), depth)
|
|
|
|
}
|
|
|
|
|
2022-07-21 17:12:39 -07:00
|
|
|
pub(crate) fn check_rewind_remove_mark<T: Tree<String>, F: Fn(usize) -> T>(new_tree: F) {
|
2022-03-02 09:04:11 -08:00
|
|
|
let mut tree = new_tree(100);
|
|
|
|
tree.append(&"e".to_string());
|
2022-07-21 17:12:39 -07:00
|
|
|
tree.mark();
|
2022-03-02 09:04:11 -08:00
|
|
|
tree.checkpoint();
|
|
|
|
assert!(tree.rewind());
|
2022-07-21 17:12:39 -07:00
|
|
|
assert!(tree.remove_mark(0usize.into()));
|
2022-03-02 09:04:11 -08:00
|
|
|
|
|
|
|
let mut tree = new_tree(100);
|
|
|
|
tree.append(&"e".to_string());
|
|
|
|
tree.checkpoint();
|
2022-07-21 17:12:39 -07:00
|
|
|
tree.mark();
|
2022-03-02 09:04:11 -08:00
|
|
|
assert!(tree.rewind());
|
2022-07-21 17:12:39 -07:00
|
|
|
assert!(!tree.remove_mark(0usize.into()));
|
2022-03-02 09:04:11 -08:00
|
|
|
|
2022-02-20 10:40:12 -08:00
|
|
|
let mut tree = new_tree(100);
|
|
|
|
tree.append(&"e".to_string());
|
2022-07-21 17:12:39 -07:00
|
|
|
tree.mark();
|
2022-02-20 10:40:12 -08:00
|
|
|
tree.checkpoint();
|
2022-07-21 17:12:39 -07:00
|
|
|
assert!(tree.remove_mark(0usize.into()));
|
2022-02-23 20:41:19 -08:00
|
|
|
assert!(tree.rewind());
|
2022-07-21 17:12:39 -07:00
|
|
|
assert!(tree.remove_mark(0usize.into()));
|
2022-02-20 10:40:12 -08:00
|
|
|
|
|
|
|
let mut tree = new_tree(100);
|
|
|
|
tree.append(&"e".to_string());
|
2022-07-21 17:12:39 -07:00
|
|
|
tree.mark();
|
|
|
|
assert!(tree.remove_mark(0usize.into()));
|
2022-02-20 10:40:12 -08:00
|
|
|
tree.checkpoint();
|
2022-02-23 20:41:19 -08:00
|
|
|
assert!(tree.rewind());
|
2022-07-21 17:12:39 -07:00
|
|
|
assert!(!tree.remove_mark(0usize.into()));
|
2022-02-20 16:40:07 -08:00
|
|
|
|
|
|
|
let mut tree = new_tree(100);
|
|
|
|
tree.append(&"a".to_string());
|
2022-07-21 17:12:39 -07:00
|
|
|
assert!(!tree.remove_mark(0usize.into()));
|
2022-02-20 16:40:07 -08:00
|
|
|
tree.checkpoint();
|
2022-07-21 17:12:39 -07:00
|
|
|
assert!(tree.mark().is_some());
|
2022-03-02 09:04:11 -08:00
|
|
|
assert!(tree.rewind());
|
2022-02-20 16:40:07 -08:00
|
|
|
|
|
|
|
let mut tree = new_tree(100);
|
|
|
|
tree.append(&"a".to_string());
|
|
|
|
tree.checkpoint();
|
2022-07-21 17:12:39 -07:00
|
|
|
assert!(tree.mark().is_some());
|
|
|
|
assert!(tree.remove_mark(0usize.into()));
|
2022-02-23 20:41:19 -08:00
|
|
|
assert!(tree.rewind());
|
2022-07-21 17:12:39 -07:00
|
|
|
assert!(!tree.remove_mark(0usize.into()));
|
2022-02-20 16:40:07 -08:00
|
|
|
|
|
|
|
// The following check_operations tests cover errors where the
|
|
|
|
// test framework itself previously did not correctly handle
|
|
|
|
// chain state restoration.
|
|
|
|
|
2022-04-18 15:01:53 -07:00
|
|
|
let samples = vec![
|
2022-07-21 17:12:39 -07:00
|
|
|
vec![append("x"), Checkpoint, Mark, Rewind, unmark(0)],
|
2022-07-21 17:17:36 -07:00
|
|
|
vec![append("d"), Checkpoint, Mark, unmark(0), Rewind, unmark(0)],
|
2022-04-18 15:01:53 -07:00
|
|
|
vec![
|
|
|
|
append("o"),
|
|
|
|
Checkpoint,
|
2022-07-21 17:12:39 -07:00
|
|
|
Mark,
|
2022-04-18 15:01:53 -07:00
|
|
|
Checkpoint,
|
2022-07-21 17:12:39 -07:00
|
|
|
unmark(0),
|
2022-04-18 15:01:53 -07:00
|
|
|
Rewind,
|
|
|
|
Rewind,
|
|
|
|
],
|
|
|
|
vec![
|
|
|
|
append("s"),
|
2022-07-21 17:12:39 -07:00
|
|
|
Mark,
|
2022-04-18 15:01:53 -07:00
|
|
|
append("m"),
|
|
|
|
Checkpoint,
|
2022-07-21 17:12:39 -07:00
|
|
|
unmark(0),
|
2022-04-18 15:01:53 -07:00
|
|
|
Rewind,
|
2022-07-21 17:12:39 -07:00
|
|
|
unmark(0),
|
|
|
|
unmark(0),
|
2022-04-18 15:01:53 -07:00
|
|
|
],
|
2022-02-20 16:40:07 -08:00
|
|
|
];
|
|
|
|
|
2022-04-18 15:01:53 -07:00
|
|
|
for (i, sample) in samples.iter().enumerate() {
|
|
|
|
let result = check_operations(sample);
|
|
|
|
assert!(
|
|
|
|
matches!(result, Ok(())),
|
|
|
|
"Reference/Test mismatch at index {}: {:?}",
|
|
|
|
i,
|
|
|
|
result
|
|
|
|
);
|
|
|
|
}
|
2022-02-20 10:40:12 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Types and utilities for cross-verification property tests
|
|
|
|
//
|
|
|
|
|
2021-06-17 18:17:47 -07:00
|
|
|
#[derive(Clone)]
|
2022-02-24 07:56:56 -08:00
|
|
|
pub struct CombinedTree<H: Hashable + Ord, const DEPTH: u8> {
|
2021-06-17 18:17:47 -07:00
|
|
|
inefficient: CompleteTree<H>,
|
2021-06-23 13:14:57 -07:00
|
|
|
efficient: BridgeTree<H, DEPTH>,
|
2021-01-05 14:11:17 -08:00
|
|
|
}
|
2015-12-19 17:37:23 -08:00
|
|
|
|
2022-02-24 07:56:56 -08:00
|
|
|
impl<H: Hashable + Ord + Clone, const DEPTH: u8> CombinedTree<H, DEPTH> {
|
2021-06-23 13:14:57 -07:00
|
|
|
pub fn new() -> Self {
|
2021-06-17 18:17:47 -07:00
|
|
|
CombinedTree {
|
2021-06-23 13:14:57 -07:00
|
|
|
inefficient: CompleteTree::new(DEPTH.into(), 100),
|
|
|
|
efficient: BridgeTree::new(100),
|
2021-06-17 18:17:47 -07:00
|
|
|
}
|
2021-01-05 12:40:05 -08:00
|
|
|
}
|
2021-06-17 18:24:18 -07:00
|
|
|
}
|
|
|
|
|
2022-04-18 15:01:53 -07:00
|
|
|
impl<H: Hashable + Ord + Clone + Debug, const DEPTH: u8> Tree<H> for CombinedTree<H, DEPTH> {
|
2021-06-17 18:24:18 -07:00
|
|
|
fn append(&mut self, value: &H) -> bool {
|
2021-06-17 18:17:47 -07:00
|
|
|
let a = self.inefficient.append(value);
|
2021-06-17 18:32:50 -07:00
|
|
|
let b = self.efficient.append(value);
|
2021-06-17 18:17:47 -07:00
|
|
|
assert_eq!(a, b);
|
|
|
|
a
|
2015-12-19 17:37:23 -08:00
|
|
|
}
|
|
|
|
|
2022-04-18 15:01:53 -07:00
|
|
|
fn root(&self, checkpoint_depth: usize) -> Option<H> {
|
|
|
|
let a = self.inefficient.root(checkpoint_depth);
|
|
|
|
let b = self.efficient.root(checkpoint_depth);
|
2021-06-17 18:17:47 -07:00
|
|
|
assert_eq!(a, b);
|
|
|
|
a
|
2015-12-19 17:37:23 -08:00
|
|
|
}
|
2021-06-18 13:21:03 -07:00
|
|
|
|
2022-02-22 20:34:38 -08:00
|
|
|
fn current_position(&self) -> Option<Position> {
|
|
|
|
let a = self.inefficient.current_position();
|
|
|
|
let b = self.efficient.current_position();
|
|
|
|
assert_eq!(a, b);
|
|
|
|
a
|
|
|
|
}
|
|
|
|
|
2022-03-30 20:31:47 -07:00
|
|
|
fn current_leaf(&self) -> Option<&H> {
|
2022-02-18 14:30:47 -08:00
|
|
|
let a = self.inefficient.current_leaf();
|
|
|
|
let b = self.efficient.current_leaf();
|
|
|
|
assert_eq!(a, b);
|
|
|
|
a
|
|
|
|
}
|
|
|
|
|
2022-07-21 17:12:39 -07:00
|
|
|
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);
|
2022-02-18 14:30:47 -08:00
|
|
|
assert_eq!(a, b);
|
|
|
|
a
|
|
|
|
}
|
|
|
|
|
2022-07-21 17:12:39 -07:00
|
|
|
fn mark(&mut self) -> Option<Position> {
|
|
|
|
let a = self.inefficient.mark();
|
|
|
|
let b = self.efficient.mark();
|
2021-06-17 18:17:47 -07:00
|
|
|
assert_eq!(a, b);
|
2022-07-21 17:12:39 -07:00
|
|
|
let apos = self.inefficient.marked_positions();
|
|
|
|
let bpos = self.efficient.marked_positions();
|
2022-04-05 11:59:36 -07:00
|
|
|
assert_eq!(apos, bpos);
|
|
|
|
a
|
|
|
|
}
|
|
|
|
|
2022-07-21 17:12:39 -07:00
|
|
|
fn marked_positions(&self) -> BTreeSet<Position> {
|
|
|
|
let a = self.inefficient.marked_positions();
|
|
|
|
let b = self.efficient.marked_positions();
|
2022-04-05 11:59:36 -07:00
|
|
|
assert_eq!(a, b);
|
2021-06-17 18:17:47 -07:00
|
|
|
a
|
2015-12-19 17:37:23 -08:00
|
|
|
}
|
|
|
|
|
2022-07-21 17:17:36 -07:00
|
|
|
fn witness(&self, position: Position, as_of_root: &H) -> Option<Vec<H>> {
|
|
|
|
let a = self.inefficient.witness(position, as_of_root);
|
|
|
|
let b = self.efficient.witness(position, as_of_root);
|
2021-06-17 18:17:47 -07:00
|
|
|
assert_eq!(a, b);
|
|
|
|
a
|
2015-12-19 17:37:23 -08:00
|
|
|
}
|
|
|
|
|
2022-07-21 17:12:39 -07:00
|
|
|
fn remove_mark(&mut self, position: Position) -> bool {
|
|
|
|
let a = self.inefficient.remove_mark(position);
|
|
|
|
let b = self.efficient.remove_mark(position);
|
2021-06-17 18:17:47 -07:00
|
|
|
assert_eq!(a, b);
|
|
|
|
a
|
2021-01-05 12:40:05 -08:00
|
|
|
}
|
2015-12-19 17:37:23 -08:00
|
|
|
|
2021-06-17 18:24:18 -07:00
|
|
|
fn checkpoint(&mut self) {
|
2021-06-17 18:17:47 -07:00
|
|
|
self.inefficient.checkpoint();
|
2021-06-17 18:32:50 -07:00
|
|
|
self.efficient.checkpoint();
|
2021-01-05 12:40:05 -08:00
|
|
|
}
|
2015-12-19 17:37:23 -08:00
|
|
|
|
2021-06-17 18:24:18 -07:00
|
|
|
fn rewind(&mut self) -> bool {
|
2021-06-17 18:17:47 -07:00
|
|
|
let a = self.inefficient.rewind();
|
2021-06-17 18:32:50 -07:00
|
|
|
let b = self.efficient.rewind();
|
2021-06-17 18:17:47 -07:00
|
|
|
assert_eq!(a, b);
|
|
|
|
a
|
2021-01-05 12:40:05 -08:00
|
|
|
}
|
2022-04-05 11:59:36 -07:00
|
|
|
|
|
|
|
fn garbage_collect(&mut self) {
|
|
|
|
self.inefficient.garbage_collect();
|
|
|
|
self.efficient.garbage_collect();
|
|
|
|
}
|
2021-06-17 18:17:47 -07:00
|
|
|
}
|
2021-01-05 12:40:05 -08:00
|
|
|
|
2022-07-21 17:17:36 -07:00
|
|
|
pub(crate) fn compute_root_from_witness<H: Hashable>(
|
2021-06-17 18:24:18 -07:00
|
|
|
value: H,
|
2021-07-22 10:30:22 -07:00
|
|
|
position: Position,
|
2021-06-17 18:24:18 -07:00
|
|
|
path: &[H],
|
|
|
|
) -> H {
|
2021-01-05 12:40:05 -08:00
|
|
|
let mut cur = value;
|
2022-07-21 12:20:41 -07:00
|
|
|
let mut lvl = 0.into();
|
2021-01-05 12:40:05 -08:00
|
|
|
for (i, v) in path
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
2022-02-22 20:34:38 -08:00
|
|
|
.map(|(i, v)| (((<usize>::from(position) >> i) & 1) == 1, v))
|
2021-01-05 12:40:05 -08:00
|
|
|
{
|
|
|
|
if i {
|
2021-06-17 18:24:18 -07:00
|
|
|
cur = H::combine(lvl, v, &cur);
|
2021-01-05 12:40:05 -08:00
|
|
|
} else {
|
2021-06-17 18:24:18 -07:00
|
|
|
cur = H::combine(lvl, &cur, v);
|
2015-12-19 17:37:23 -08:00
|
|
|
}
|
2021-06-17 18:24:18 -07:00
|
|
|
lvl = lvl + 1;
|
2015-12-19 17:37:23 -08:00
|
|
|
}
|
2021-01-05 12:40:05 -08:00
|
|
|
cur
|
2015-12-19 17:37:23 -08:00
|
|
|
}
|
|
|
|
|
2021-01-05 12:40:05 -08:00
|
|
|
#[test]
|
2022-07-21 17:17:36 -07:00
|
|
|
fn test_compute_root_from_witness() {
|
2021-06-17 18:24:18 -07:00
|
|
|
let expected = SipHashable::combine(
|
2022-07-21 12:20:41 -07:00
|
|
|
<Level>::from(2),
|
2021-06-17 18:24:18 -07:00
|
|
|
&SipHashable::combine(
|
2022-07-21 12:20:41 -07:00
|
|
|
Level(1),
|
|
|
|
&SipHashable::combine(0.into(), &SipHashable(0), &SipHashable(1)),
|
|
|
|
&SipHashable::combine(0.into(), &SipHashable(2), &SipHashable(3)),
|
2021-06-17 18:24:18 -07:00
|
|
|
),
|
|
|
|
&SipHashable::combine(
|
2022-07-21 12:20:41 -07:00
|
|
|
Level(1),
|
|
|
|
&SipHashable::combine(0.into(), &SipHashable(4), &SipHashable(5)),
|
|
|
|
&SipHashable::combine(0.into(), &SipHashable(6), &SipHashable(7)),
|
2021-06-17 18:24:18 -07:00
|
|
|
),
|
2021-01-05 12:40:05 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
2022-07-21 17:17:36 -07:00
|
|
|
compute_root_from_witness::<SipHashable>(
|
2021-06-17 18:24:18 -07:00
|
|
|
SipHashable(0),
|
2022-07-21 12:20:41 -07:00
|
|
|
0.into(),
|
2021-01-05 12:40:05 -08:00
|
|
|
&[
|
2021-06-17 18:24:18 -07:00
|
|
|
SipHashable(1),
|
2022-07-21 12:20:41 -07:00
|
|
|
SipHashable::combine(0.into(), &SipHashable(2), &SipHashable(3)),
|
2021-06-17 18:24:18 -07:00
|
|
|
SipHashable::combine(
|
2022-07-21 12:20:41 -07:00
|
|
|
Level(1),
|
|
|
|
&SipHashable::combine(0.into(), &SipHashable(4), &SipHashable(5)),
|
|
|
|
&SipHashable::combine(0.into(), &SipHashable(6), &SipHashable(7))
|
2021-06-17 18:24:18 -07:00
|
|
|
)
|
2021-01-05 12:40:05 -08:00
|
|
|
]
|
|
|
|
),
|
|
|
|
expected
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
2022-07-21 17:17:36 -07:00
|
|
|
compute_root_from_witness(
|
2021-06-17 18:24:18 -07:00
|
|
|
SipHashable(4),
|
2021-07-22 10:30:22 -07:00
|
|
|
<Position>::from(4),
|
2021-01-05 12:40:05 -08:00
|
|
|
&[
|
2021-06-17 18:24:18 -07:00
|
|
|
SipHashable(5),
|
2022-07-21 12:20:41 -07:00
|
|
|
SipHashable::combine(0.into(), &SipHashable(6), &SipHashable(7)),
|
2021-06-17 18:24:18 -07:00
|
|
|
SipHashable::combine(
|
2022-07-21 12:20:41 -07:00
|
|
|
Level(1),
|
|
|
|
&SipHashable::combine(0.into(), &SipHashable(0), &SipHashable(1)),
|
|
|
|
&SipHashable::combine(0.into(), &SipHashable(2), &SipHashable(3))
|
2021-06-17 18:24:18 -07:00
|
|
|
)
|
2021-01-05 12:40:05 -08:00
|
|
|
]
|
|
|
|
),
|
|
|
|
expected
|
|
|
|
);
|
2015-12-19 17:37:23 -08:00
|
|
|
}
|
|
|
|
|
2022-04-18 15:01:53 -07:00
|
|
|
#[test]
|
2022-07-21 17:17:36 -07:00
|
|
|
fn test_witness_consistency() {
|
2022-04-18 15:01:53 -07:00
|
|
|
let samples = vec![
|
|
|
|
// Reduced examples
|
2022-07-21 14:07:01 -07:00
|
|
|
vec![append("a"), append("b"), Checkpoint, Mark, witness(0, 1)],
|
|
|
|
vec![append("c"), append("d"), Mark, Checkpoint, witness(1, 1)],
|
|
|
|
vec![append("e"), Checkpoint, Mark, append("f"), witness(0, 1)],
|
2022-04-18 15:01:53 -07:00
|
|
|
vec![
|
|
|
|
append("g"),
|
2022-07-21 17:12:39 -07:00
|
|
|
Mark,
|
2022-04-18 15:01:53 -07:00
|
|
|
Checkpoint,
|
2022-07-21 17:12:39 -07:00
|
|
|
unmark(0),
|
2022-04-18 15:01:53 -07:00
|
|
|
append("h"),
|
2022-07-21 14:07:01 -07:00
|
|
|
witness(0, 0),
|
2022-04-18 15:01:53 -07:00
|
|
|
],
|
|
|
|
vec![
|
|
|
|
append("i"),
|
|
|
|
Checkpoint,
|
2022-07-21 17:12:39 -07:00
|
|
|
Mark,
|
|
|
|
unmark(0),
|
2022-04-18 15:01:53 -07:00
|
|
|
append("j"),
|
2022-07-21 14:07:01 -07:00
|
|
|
witness(0, 0),
|
2022-04-18 15:01:53 -07:00
|
|
|
],
|
|
|
|
vec![
|
|
|
|
append("i"),
|
2022-07-21 17:12:39 -07:00
|
|
|
Mark,
|
2022-04-18 15:01:53 -07:00
|
|
|
append("j"),
|
|
|
|
Checkpoint,
|
|
|
|
append("k"),
|
2022-07-21 14:07:01 -07:00
|
|
|
witness(0, 1),
|
2022-04-18 15:01:53 -07:00
|
|
|
],
|
|
|
|
vec![
|
|
|
|
append("l"),
|
|
|
|
Checkpoint,
|
2022-07-21 17:12:39 -07:00
|
|
|
Mark,
|
2022-04-18 15:01:53 -07:00
|
|
|
Checkpoint,
|
|
|
|
append("m"),
|
|
|
|
Checkpoint,
|
2022-07-21 14:07:01 -07:00
|
|
|
witness(0, 2),
|
2022-04-18 15:01:53 -07:00
|
|
|
],
|
2022-07-21 14:07:01 -07:00
|
|
|
vec![Checkpoint, append("n"), Mark, witness(0, 1)],
|
2022-04-18 15:01:53 -07:00
|
|
|
vec![
|
|
|
|
append("a"),
|
2022-07-21 17:12:39 -07:00
|
|
|
Mark,
|
2022-04-18 15:01:53 -07:00
|
|
|
Checkpoint,
|
2022-07-21 17:12:39 -07:00
|
|
|
unmark(0),
|
2022-04-18 15:01:53 -07:00
|
|
|
Checkpoint,
|
|
|
|
append("b"),
|
2022-07-21 14:07:01 -07:00
|
|
|
witness(0, 1),
|
2022-04-18 15:01:53 -07:00
|
|
|
],
|
|
|
|
vec![
|
|
|
|
append("a"),
|
2022-07-21 17:12:39 -07:00
|
|
|
Mark,
|
2022-04-18 15:01:53 -07:00
|
|
|
append("b"),
|
2022-07-21 17:12:39 -07:00
|
|
|
unmark(0),
|
2022-04-18 15:01:53 -07:00
|
|
|
Checkpoint,
|
2022-07-21 14:07:01 -07:00
|
|
|
witness(0, 0),
|
2022-04-18 15:01:53 -07:00
|
|
|
],
|
|
|
|
vec![
|
|
|
|
append("a"),
|
2022-07-21 17:12:39 -07:00
|
|
|
Mark,
|
2022-04-18 15:01:53 -07:00
|
|
|
Checkpoint,
|
2022-07-21 17:12:39 -07:00
|
|
|
unmark(0),
|
2022-04-18 15:01:53 -07:00
|
|
|
Checkpoint,
|
|
|
|
Rewind,
|
|
|
|
append("b"),
|
2022-07-21 14:07:01 -07:00
|
|
|
witness(0, 0),
|
2022-04-18 15:01:53 -07:00
|
|
|
],
|
|
|
|
vec![
|
|
|
|
append("a"),
|
2022-07-21 17:12:39 -07:00
|
|
|
Mark,
|
2022-04-18 15:01:53 -07:00
|
|
|
Checkpoint,
|
|
|
|
Checkpoint,
|
|
|
|
Rewind,
|
|
|
|
append("a"),
|
2022-07-21 17:12:39 -07:00
|
|
|
unmark(0),
|
2022-07-21 14:07:01 -07:00
|
|
|
witness(0, 1),
|
2022-04-18 15:01:53 -07:00
|
|
|
],
|
|
|
|
// Unreduced examples
|
|
|
|
vec![
|
|
|
|
append("o"),
|
|
|
|
append("p"),
|
2022-07-21 17:12:39 -07:00
|
|
|
Mark,
|
2022-04-18 15:01:53 -07:00
|
|
|
append("q"),
|
|
|
|
Checkpoint,
|
2022-07-21 17:12:39 -07:00
|
|
|
unmark(1),
|
2022-07-21 14:07:01 -07:00
|
|
|
witness(1, 1),
|
2022-04-18 15:01:53 -07:00
|
|
|
],
|
|
|
|
vec![
|
|
|
|
append("r"),
|
|
|
|
append("s"),
|
|
|
|
append("t"),
|
2022-07-21 17:12:39 -07:00
|
|
|
Mark,
|
2022-04-18 15:01:53 -07:00
|
|
|
Checkpoint,
|
2022-07-21 17:12:39 -07:00
|
|
|
unmark(2),
|
2022-04-18 15:01:53 -07:00
|
|
|
Checkpoint,
|
2022-07-21 14:07:01 -07:00
|
|
|
witness(2, 2),
|
2022-04-18 15:01:53 -07:00
|
|
|
],
|
|
|
|
vec![
|
|
|
|
append("u"),
|
2022-07-21 17:12:39 -07:00
|
|
|
Mark,
|
2022-04-18 15:01:53 -07:00
|
|
|
append("v"),
|
|
|
|
append("w"),
|
|
|
|
Checkpoint,
|
2022-07-21 17:12:39 -07:00
|
|
|
unmark(0),
|
2022-04-18 15:01:53 -07:00
|
|
|
append("x"),
|
|
|
|
Checkpoint,
|
|
|
|
Checkpoint,
|
2022-07-21 14:07:01 -07:00
|
|
|
witness(0, 3),
|
2022-04-18 15:01:53 -07:00
|
|
|
],
|
|
|
|
];
|
|
|
|
|
|
|
|
for (i, sample) in samples.iter().enumerate() {
|
|
|
|
let result = check_operations(sample);
|
|
|
|
assert!(
|
|
|
|
matches!(result, Ok(())),
|
|
|
|
"Reference/Test mismatch at index {}: {:?}",
|
|
|
|
i,
|
|
|
|
result
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// These check_operations tests cover errors where the test framework itself previously did not
|
|
|
|
// correctly handle chain state restoration.
|
|
|
|
#[test]
|
2022-07-21 17:12:39 -07:00
|
|
|
fn test_rewind_remove_mark_consistency() {
|
2022-04-18 15:01:53 -07:00
|
|
|
let samples = vec![
|
2022-07-21 17:12:39 -07:00
|
|
|
vec![append("x"), Checkpoint, Mark, Rewind, unmark(0)],
|
2022-07-21 17:17:36 -07:00
|
|
|
vec![append("d"), Checkpoint, Mark, unmark(0), Rewind, unmark(0)],
|
2022-04-18 15:01:53 -07:00
|
|
|
vec![
|
|
|
|
append("o"),
|
|
|
|
Checkpoint,
|
2022-07-21 17:12:39 -07:00
|
|
|
Mark,
|
2022-04-18 15:01:53 -07:00
|
|
|
Checkpoint,
|
2022-07-21 17:12:39 -07:00
|
|
|
unmark(0),
|
2022-04-18 15:01:53 -07:00
|
|
|
Rewind,
|
|
|
|
Rewind,
|
|
|
|
],
|
|
|
|
vec![
|
|
|
|
append("s"),
|
2022-07-21 17:12:39 -07:00
|
|
|
Mark,
|
2022-04-18 15:01:53 -07:00
|
|
|
append("m"),
|
|
|
|
Checkpoint,
|
2022-07-21 17:12:39 -07:00
|
|
|
unmark(0),
|
2022-04-18 15:01:53 -07:00
|
|
|
Rewind,
|
2022-07-21 17:12:39 -07:00
|
|
|
unmark(0),
|
|
|
|
unmark(0),
|
2022-04-18 15:01:53 -07:00
|
|
|
],
|
|
|
|
];
|
|
|
|
for (i, sample) in samples.iter().enumerate() {
|
|
|
|
let result = check_operations(sample);
|
|
|
|
assert!(
|
|
|
|
matches!(result, Ok(())),
|
|
|
|
"Reference/Test mismatch at index {}: {:?}",
|
|
|
|
i,
|
|
|
|
result
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-24 07:56:56 -08:00
|
|
|
fn check_operations<H: Hashable + Ord + Clone + Debug>(
|
2022-04-18 15:01:53 -07:00
|
|
|
ops: &[Operation<H>],
|
2021-06-17 18:24:18 -07:00
|
|
|
) -> Result<(), TestCaseError> {
|
2021-06-23 13:14:57 -07:00
|
|
|
const DEPTH: u8 = 4;
|
|
|
|
let mut tree = CombinedTree::<H, DEPTH>::new();
|
2021-06-17 18:24:18 -07:00
|
|
|
|
|
|
|
let mut tree_size = 0;
|
2022-03-30 20:31:47 -07:00
|
|
|
let mut tree_values: Vec<H> = vec![];
|
2022-02-20 16:40:07 -08:00
|
|
|
// the number of leaves in the tree at the time that a checkpoint is made
|
|
|
|
let mut tree_checkpoints: Vec<usize> = vec![];
|
2021-06-17 18:24:18 -07:00
|
|
|
|
|
|
|
for op in ops {
|
|
|
|
prop_assert_eq!(tree_size, tree_values.len());
|
|
|
|
match op {
|
|
|
|
Append(value) => {
|
2022-04-18 15:01:53 -07:00
|
|
|
if tree.append(value) {
|
2021-06-17 18:24:18 -07:00
|
|
|
prop_assert!(tree_size < (1 << DEPTH));
|
|
|
|
tree_size += 1;
|
|
|
|
tree_values.push(value.clone());
|
|
|
|
} else {
|
|
|
|
prop_assert_eq!(tree_size, 1 << DEPTH);
|
2021-01-05 12:40:05 -08:00
|
|
|
}
|
2021-06-17 18:24:18 -07:00
|
|
|
}
|
2022-03-30 20:31:47 -07:00
|
|
|
CurrentPosition => {
|
|
|
|
if let Some(pos) = tree.current_position() {
|
|
|
|
prop_assert!(tree_size > 0);
|
|
|
|
prop_assert_eq!(tree_size - 1, pos.into());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CurrentLeaf => {
|
|
|
|
prop_assert_eq!(tree_values.last(), tree.current_leaf());
|
|
|
|
}
|
2022-07-21 17:12:39 -07:00
|
|
|
Mark => {
|
|
|
|
if tree.mark().is_some() {
|
2021-06-17 18:24:18 -07:00
|
|
|
prop_assert!(tree_size != 0);
|
|
|
|
} else {
|
|
|
|
prop_assert_eq!(tree_size, 0);
|
2021-01-05 12:40:05 -08:00
|
|
|
}
|
2021-06-17 18:24:18 -07:00
|
|
|
}
|
2022-07-21 17:12:39 -07:00
|
|
|
MarkedLeaf(position) => {
|
|
|
|
if tree.get_marked_leaf(*position).is_some() {
|
2022-04-18 15:01:53 -07:00
|
|
|
prop_assert!(<usize>::from(*position) < tree_size);
|
2022-03-30 20:31:47 -07:00
|
|
|
}
|
|
|
|
}
|
2022-07-21 17:12:39 -07:00
|
|
|
Unmark(position) => {
|
|
|
|
tree.remove_mark(*position);
|
2021-06-17 18:24:18 -07:00
|
|
|
}
|
2022-07-21 17:12:39 -07:00
|
|
|
MarkedPositions => {}
|
2021-06-17 18:24:18 -07:00
|
|
|
Checkpoint => {
|
|
|
|
tree_checkpoints.push(tree_size);
|
|
|
|
tree.checkpoint();
|
|
|
|
}
|
|
|
|
Rewind => {
|
|
|
|
if tree.rewind() {
|
|
|
|
prop_assert!(!tree_checkpoints.is_empty());
|
2022-02-20 16:40:07 -08:00
|
|
|
let checkpointed_tree_size = tree_checkpoints.pop().unwrap();
|
|
|
|
tree_values.truncate(checkpointed_tree_size);
|
|
|
|
tree_size = checkpointed_tree_size;
|
2021-01-05 12:40:05 -08:00
|
|
|
}
|
2021-06-17 18:24:18 -07:00
|
|
|
}
|
2022-04-18 15:01:53 -07:00
|
|
|
Authpath(position, depth) => {
|
2022-07-21 17:17:36 -07:00
|
|
|
if let Some(path) = tree.root(*depth).and_then(|r| tree.witness(*position, &r))
|
2022-04-18 15:01:53 -07: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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// extend the tree with empty leaves until it is full
|
|
|
|
extended_tree_values.resize(1 << DEPTH, H::empty_leaf());
|
|
|
|
|
|
|
|
// compute the root
|
|
|
|
let expected_root = lazy_root::<H>(extended_tree_values);
|
|
|
|
prop_assert_eq!(&tree_root.unwrap(), &expected_root);
|
|
|
|
|
|
|
|
prop_assert_eq!(
|
2022-07-21 17:17:36 -07:00
|
|
|
&compute_root_from_witness(value, *position, &path),
|
2022-04-18 15:01:53 -07:00
|
|
|
&expected_root
|
|
|
|
);
|
|
|
|
}
|
2021-01-05 12:40:05 -08:00
|
|
|
}
|
2015-12-19 17:37:23 -08:00
|
|
|
}
|
2022-04-05 15:10:09 -07:00
|
|
|
GarbageCollect => {}
|
2015-12-19 17:37:23 -08:00
|
|
|
}
|
2021-06-17 18:24:18 -07:00
|
|
|
}
|
2021-01-05 12:56:23 -08:00
|
|
|
|
2021-06-17 18:24:18 -07:00
|
|
|
Ok(())
|
2015-12-19 17:37:23 -08:00
|
|
|
}
|
2022-02-20 10:40:12 -08:00
|
|
|
|
|
|
|
proptest! {
|
|
|
|
#![proptest_config(ProptestConfig::with_cases(100000))]
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn check_randomized_u64_ops(
|
2022-03-30 20:31:47 -07:00
|
|
|
ops in proptest::collection::vec(
|
|
|
|
arb_operation((0..32u64).prop_map(SipHashable), 0usize..100),
|
|
|
|
1..100
|
|
|
|
)
|
2022-02-20 10:40:12 -08:00
|
|
|
) {
|
2022-04-18 15:01:53 -07:00
|
|
|
check_operations(&ops)?;
|
2022-02-20 10:40:12 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn check_randomized_str_ops(
|
2022-03-30 20:31:47 -07:00
|
|
|
ops in proptest::collection::vec(
|
|
|
|
arb_operation((97u8..123).prop_map(|c| char::from(c).to_string()), 0usize..100),
|
|
|
|
1..100
|
|
|
|
)
|
2022-02-20 10:40:12 -08:00
|
|
|
) {
|
2022-04-18 15:01:53 -07:00
|
|
|
check_operations::<String>(&ops)?;
|
2022-02-20 10:40:12 -08:00
|
|
|
}
|
|
|
|
}
|
2021-01-05 12:40:05 -08:00
|
|
|
}
|