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.
|
|
|
|
//!
|
|
|
|
//! ## Witnessing
|
|
|
|
//!
|
|
|
|
//! Merkle trees are typically used to show that a value exists in the tree via
|
|
|
|
//! an authentication path. We need an API that allows us to identify the
|
|
|
|
//! current leaf as a value we wish to compute authentication paths for even as
|
|
|
|
//! 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-02-22 20:34:38 -08:00
|
|
|
use std::convert::TryFrom;
|
|
|
|
use std::num::TryFromIntError;
|
2022-02-22 20:30:14 -08:00
|
|
|
use std::ops::{Add, AddAssign, Sub};
|
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
|
|
|
|
/// nodes at altitude `0` are leaves, nodes at altitude `1` are parents
|
2021-07-22 11:10:42 -07:00
|
|
|
/// of nodes at altitude `0`, and so forth. This type is capable of
|
2021-07-08 09:16:55 -07:00
|
|
|
/// representing altitudes in trees containing up to 2^256 leaves.
|
2021-06-17 18:32:50 -07:00
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
2021-06-17 18:24:18 -07:00
|
|
|
#[repr(transparent)]
|
2021-06-23 13:14:57 -07:00
|
|
|
pub struct Altitude(u8);
|
2021-06-17 18:24:18 -07:00
|
|
|
|
2021-06-19 10:24:33 -07:00
|
|
|
impl Altitude {
|
2021-07-20 08:45:36 -07:00
|
|
|
/// Convenience method for returning the zero altitude.
|
2021-06-17 18:24:18 -07:00
|
|
|
pub fn zero() -> Self {
|
2022-01-23 11:16:30 -08:00
|
|
|
Self(0)
|
2021-06-17 18:24:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn one() -> Self {
|
2022-01-23 11:16:30 -08:00
|
|
|
Self(1)
|
2021-06-17 18:24:18 -07:00
|
|
|
}
|
|
|
|
|
2022-01-23 11:16:30 -08:00
|
|
|
pub fn iter_to(self, other: Altitude) -> impl Iterator<Item = Self> {
|
2021-06-19 10:24:33 -07:00
|
|
|
(self.0..other.0).into_iter().map(Altitude)
|
2021-06-17 18:24:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-23 13:14:57 -07:00
|
|
|
impl Add<u8> for Altitude {
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-23 13:14:57 -07:00
|
|
|
impl Sub<u8> for Altitude {
|
2022-01-23 11:16:30 -08:00
|
|
|
type Output = Self;
|
2021-06-23 13:14:57 -07:00
|
|
|
fn sub(self, value: u8) -> Self {
|
2022-01-23 11:16:30 -08:00
|
|
|
Self(self.0 - value)
|
2021-06-17 18:24:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-23 13:14:57 -07:00
|
|
|
impl From<u8> for Altitude {
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2021-06-23 13:14:57 -07:00
|
|
|
impl From<Altitude> for u8 {
|
|
|
|
fn from(level: Altitude) -> u8 {
|
|
|
|
level.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-19 10:24:33 -07:00
|
|
|
impl From<Altitude> for usize {
|
|
|
|
fn from(level: Altitude) -> 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 {
|
|
|
|
/// Returns the position of the first leaf in the tree.
|
|
|
|
pub fn zero() -> Self {
|
2022-01-23 11:16:30 -08:00
|
|
|
Self(0)
|
2021-07-22 10:30:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the altitude of the top of a binary tree containing
|
|
|
|
/// a number of nodes equal to the next power of two greater than
|
|
|
|
/// or equal to `self + 1`.
|
|
|
|
fn max_altitude(&self) -> Altitude {
|
|
|
|
Altitude(if self.0 == 0 {
|
|
|
|
0
|
|
|
|
} else {
|
|
|
|
63 - self.0.leading_zeros() as u8
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the altitude of each populated ommer.
|
|
|
|
pub fn ommer_altitudes(&self) -> impl Iterator<Item = Altitude> + '_ {
|
|
|
|
(0..=self.max_altitude().0)
|
|
|
|
.into_iter()
|
|
|
|
.filter_map(move |i| {
|
|
|
|
if i != 0 && self.0 & (1 << i) != 0 {
|
|
|
|
Some(Altitude(i))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the altitude of each cousin and/or ommer required to construct
|
|
|
|
/// an authentication path to the root of a merkle tree that has `self + 1`
|
|
|
|
/// nodes.
|
|
|
|
pub fn altitudes_required(&self) -> impl Iterator<Item = Altitude> + '_ {
|
|
|
|
(0..=self.max_altitude().0)
|
|
|
|
.into_iter()
|
|
|
|
.filter_map(move |i| {
|
|
|
|
if self.0 == 0 || self.0 & (1 << i) == 0 {
|
|
|
|
Some(Altitude(i))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the altitude of each cousin and/or ommer required to construct
|
|
|
|
/// an authentication path to the root of a merkle tree containing 2^64
|
|
|
|
/// nodes.
|
|
|
|
pub fn all_altitudes_required(&self) -> impl Iterator<Item = Altitude> + '_ {
|
|
|
|
(0..64).into_iter().filter_map(move |i| {
|
|
|
|
if self.0 == 0 || self.0 & (1 << i) == 0 {
|
|
|
|
Some(Altitude(i))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns whether the binary tree having `self` as the position of the
|
|
|
|
/// rightmost leaf contains a perfect balanced tree of height
|
|
|
|
/// `to_altitude + 1` that contains the aforesaid leaf, without requiring
|
|
|
|
/// any empty leaves or internal nodes.
|
|
|
|
pub fn is_complete(&self, to_altitude: Altitude) -> bool {
|
|
|
|
for i in 0..(to_altitude.0) {
|
|
|
|
if self.0 & (1 << i) == 0 {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
2021-06-19 10:24:33 -07:00
|
|
|
fn combine(level: Altitude, a: &Self, b: &Self) -> Self;
|
2021-06-17 18:24:18 -07:00
|
|
|
|
2021-06-19 10:24:33 -07:00
|
|
|
fn empty_root(level: Altitude) -> Self {
|
|
|
|
Altitude::zero()
|
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
|
|
|
}
|
|
|
|
|
|
|
|
/// A Merkle tree that supports incremental appends, witnessing of
|
|
|
|
/// leaf nodes, checkpoints and rollbacks.
|
|
|
|
pub trait Tree<H>: Frontier<H> {
|
|
|
|
/// The type of recordings that can be made of the operations of this tree.
|
|
|
|
type Recording: Recording<H>;
|
2021-01-05 14:11:17 -08:00
|
|
|
|
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.
|
|
|
|
fn current_leaf(&self) -> Option<(Position, H)>;
|
2022-02-18 14:30:47 -08:00
|
|
|
|
|
|
|
/// Returns `true` if the tree can produce an authentication path for
|
2022-02-22 20:30:14 -08:00
|
|
|
/// the specified leaf value from the specified position in the tree.
|
|
|
|
fn is_witnessed(&self, position: Position, value: &H) -> bool;
|
2022-02-18 14:30:47 -08:00
|
|
|
|
|
|
|
/// Marks the current leaf as one for which we're interested in producing
|
2022-02-23 20:30:52 -08:00
|
|
|
/// an authentication path. Returns an optional value containing the
|
|
|
|
/// current position and leaf value if successful or if the current
|
2022-02-23 17:50:21 -08:00
|
|
|
/// value was already marked, or None if the tree is empty.
|
|
|
|
fn witness(&mut self) -> Option<(Position, H)>;
|
2021-01-05 14:11:17 -08:00
|
|
|
|
|
|
|
/// Obtains an authentication path to the value specified in the tree.
|
|
|
|
/// Returns `None` if there is no available authentication path to the
|
|
|
|
/// specified value.
|
2022-02-22 20:30:14 -08:00
|
|
|
fn authentication_path(&self, position: Position, value: &H) -> Option<Vec<H>>;
|
2021-01-05 14:11:17 -08:00
|
|
|
|
|
|
|
/// Marks the specified tree state value as a value we're no longer
|
|
|
|
/// interested in maintaining a witness for. Returns true if successful and
|
|
|
|
/// false if the value is not a known witness.
|
2022-02-22 20:30:14 -08:00
|
|
|
fn remove_witness(&mut self, position: Position, value: &H) -> 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;
|
2021-01-05 14:11:17 -08:00
|
|
|
|
|
|
|
/// Start a recording of append operations performed on a tree.
|
2021-06-17 18:17:47 -07:00
|
|
|
fn recording(&self) -> Self::Recording;
|
2021-01-05 14:11:17 -08:00
|
|
|
|
|
|
|
/// Plays a recording of append operations back. Returns true if successful
|
|
|
|
/// and false if the recording is incompatible with the current tree state.
|
2021-06-17 18:17:47 -07:00
|
|
|
fn play(&mut self, recording: &Self::Recording) -> bool;
|
2021-01-05 14:11:17 -08:00
|
|
|
}
|
|
|
|
|
2021-06-18 13:21:03 -07:00
|
|
|
pub trait Recording<H> {
|
2021-06-17 18:17:47 -07:00
|
|
|
/// Appends a new value to the tree at the next available slot. Returns true
|
|
|
|
/// if successful and false if the tree is full.
|
2021-06-17 18:24:18 -07:00
|
|
|
fn append(&mut self, value: &H) -> bool;
|
2021-01-05 14:11:17 -08:00
|
|
|
|
|
|
|
/// Plays a recording of append operations back. Returns true if successful
|
2021-06-17 18:17:47 -07:00
|
|
|
/// and false if the provided recording is incompatible with `Self`.
|
|
|
|
fn play(&mut self, recording: &Self) -> bool;
|
2021-01-05 14:11:17 -08:00
|
|
|
}
|
|
|
|
|
2021-06-17 18:17:47 -07:00
|
|
|
#[cfg(test)]
|
2021-06-17 18:24:18 -07:00
|
|
|
pub(crate) mod tests {
|
2021-06-17 18:17:47 -07:00
|
|
|
#![allow(deprecated)]
|
2022-02-18 13:02:35 -08:00
|
|
|
use std::fmt::Debug;
|
2021-06-17 18:17:47 -07:00
|
|
|
use std::hash::Hasher;
|
2021-06-17 18:24:18 -07:00
|
|
|
use std::hash::SipHasher;
|
|
|
|
|
2021-06-17 18:32:50 -07:00
|
|
|
use super::bridgetree::{BridgeRecording, BridgeTree};
|
2021-06-17 18:24:18 -07:00
|
|
|
use super::sample::{lazy_root, CompleteRecording, CompleteTree};
|
2021-07-22 10:30:22 -07:00
|
|
|
use super::{Altitude, Frontier, Hashable, Position, Recording, Tree};
|
2021-01-05 14:11:17 -08:00
|
|
|
|
2022-02-20 10:40:12 -08:00
|
|
|
#[test]
|
|
|
|
fn position_altitudes() {
|
|
|
|
assert_eq!(Position(0).max_altitude(), Altitude(0));
|
|
|
|
assert_eq!(Position(1).max_altitude(), Altitude(0));
|
|
|
|
assert_eq!(Position(2).max_altitude(), Altitude(1));
|
|
|
|
assert_eq!(Position(3).max_altitude(), Altitude(1));
|
|
|
|
assert_eq!(Position(4).max_altitude(), Altitude(2));
|
|
|
|
assert_eq!(Position(7).max_altitude(), Altitude(2));
|
|
|
|
assert_eq!(Position(8).max_altitude(), Altitude(3));
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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: Altitude, 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(_: Altitude, a: &Self, b: &Self) -> Self {
|
|
|
|
a.to_string() + b
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// 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);
|
|
|
|
assert_eq!(tree.root(), "________________");
|
|
|
|
|
|
|
|
tree.append(&"a".to_string());
|
|
|
|
assert_eq!(tree.root().len(), 16);
|
|
|
|
assert_eq!(tree.root(), "a_______________");
|
|
|
|
|
|
|
|
tree.append(&"b".to_string());
|
|
|
|
assert_eq!(tree.root(), "ab______________");
|
|
|
|
|
|
|
|
tree.append(&"c".to_string());
|
|
|
|
assert_eq!(tree.root(), "abc_____________");
|
2022-02-20 16:40:07 -08:00
|
|
|
|
|
|
|
let mut t = new_tree(100);
|
|
|
|
t.append(&"a".to_string());
|
|
|
|
t.checkpoint();
|
|
|
|
t.witness();
|
|
|
|
t.append(&"a".to_string());
|
|
|
|
t.append(&"a".to_string());
|
|
|
|
t.append(&"a".to_string());
|
|
|
|
assert_eq!(t.root(), "aaaa____________");
|
2022-02-20 10:40:12 -08:00
|
|
|
}
|
|
|
|
|
2022-02-20 16:40:07 -08:00
|
|
|
pub(crate) fn check_auth_paths<T: Tree<String> + std::fmt::Debug, F: Fn(usize) -> T>(
|
|
|
|
new_tree: F,
|
|
|
|
) {
|
2022-02-20 10:40:12 -08:00
|
|
|
let mut tree = new_tree(100);
|
|
|
|
tree.append(&"a".to_string());
|
|
|
|
tree.witness();
|
|
|
|
assert_eq!(
|
2022-02-22 20:34:38 -08:00
|
|
|
tree.authentication_path(Position::from(0), &"a".to_string()),
|
|
|
|
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-02-22 20:34:38 -08:00
|
|
|
tree.authentication_path(Position::zero(), &"a".to_string()),
|
|
|
|
Some(vec![
|
|
|
|
"b".to_string(),
|
|
|
|
"__".to_string(),
|
|
|
|
"____".to_string(),
|
|
|
|
"________".to_string()
|
|
|
|
])
|
2022-02-20 10:40:12 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
tree.append(&"c".to_string());
|
|
|
|
tree.witness();
|
|
|
|
assert_eq!(
|
2022-02-22 20:34:38 -08:00
|
|
|
tree.authentication_path(Position::from(2), &"c".to_string()),
|
|
|
|
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-02-22 20:34:38 -08:00
|
|
|
tree.authentication_path(Position::from(2), &"c".to_string()),
|
|
|
|
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-02-22 20:34:38 -08:00
|
|
|
tree.authentication_path(Position::from(2), &"c".to_string()),
|
|
|
|
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());
|
|
|
|
tree.witness();
|
|
|
|
for c in 'b'..'h' {
|
|
|
|
tree.append(&c.to_string());
|
|
|
|
}
|
|
|
|
tree.witness();
|
|
|
|
tree.append(&"h".to_string());
|
|
|
|
|
|
|
|
assert_eq!(
|
2022-02-22 20:34:38 -08:00
|
|
|
tree.authentication_path(Position::zero(), &"a".to_string()),
|
|
|
|
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.witness();
|
|
|
|
tree.append(&"b".to_string());
|
|
|
|
tree.append(&"c".to_string());
|
|
|
|
tree.append(&"d".to_string());
|
|
|
|
tree.witness();
|
|
|
|
tree.append(&"e".to_string());
|
|
|
|
tree.witness();
|
|
|
|
tree.append(&"f".to_string());
|
|
|
|
tree.witness();
|
|
|
|
tree.append(&"g".to_string());
|
|
|
|
|
|
|
|
assert_eq!(
|
2022-02-22 20:34:38 -08:00
|
|
|
tree.authentication_path(Position::from(5), &"f".to_string()),
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
tree.witness();
|
|
|
|
tree.append(&'l'.to_string());
|
|
|
|
|
|
|
|
assert_eq!(
|
2022-02-22 20:34:38 -08:00
|
|
|
tree.authentication_path(Position::from(10), &"k".to_string()),
|
|
|
|
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());
|
|
|
|
tree.witness();
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
tree.witness();
|
|
|
|
for c in 'f'..'i' {
|
|
|
|
tree.append(&c.to_string());
|
|
|
|
}
|
|
|
|
|
|
|
|
assert_eq!(
|
2022-02-22 20:34:38 -08:00
|
|
|
tree.authentication_path(Position::zero(), &"a".to_string()),
|
|
|
|
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());
|
|
|
|
tree.witness();
|
|
|
|
tree.append(&'d'.to_string());
|
|
|
|
tree.append(&'e'.to_string());
|
|
|
|
tree.append(&'f'.to_string());
|
|
|
|
tree.append(&'g'.to_string());
|
|
|
|
tree.witness();
|
|
|
|
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-02-22 20:34:38 -08:00
|
|
|
tree.authentication_path(Position::from(2), &"c".to_string()),
|
|
|
|
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());
|
|
|
|
tree.witness();
|
|
|
|
assert_eq!(
|
|
|
|
tree.authentication_path(Position::from(0), &"b".to_string()),
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
tree.witness();
|
|
|
|
tree.append(&'n'.to_string());
|
|
|
|
tree.witness();
|
|
|
|
tree.append(&'o'.to_string());
|
|
|
|
tree.append(&'p'.to_string());
|
|
|
|
|
|
|
|
assert_eq!(
|
2022-02-22 20:34:38 -08:00
|
|
|
tree.authentication_path(Position::from(12), &"m".to_string()),
|
|
|
|
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()))
|
|
|
|
.chain(Some(Witness))
|
|
|
|
.chain(Some(Append('m'.to_string())))
|
|
|
|
.chain(Some(Append('n'.to_string())))
|
2022-02-22 20:34:38 -08:00
|
|
|
.chain(Some(Authpath(11usize.into(), 'l'.to_string())))
|
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((
|
|
|
|
Position::from(11),
|
|
|
|
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-02-20 10:40:12 -08:00
|
|
|
t.witness();
|
2022-03-02 09:04:11 -08:00
|
|
|
assert!(t.rewind());
|
|
|
|
assert_eq!(Some((Position::from(0), "a".to_string())), t.current_leaf());
|
2022-02-20 10:40:12 -08:00
|
|
|
|
|
|
|
let mut t = new_tree(100);
|
|
|
|
t.append(&"a".to_string());
|
|
|
|
t.witness();
|
|
|
|
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();
|
|
|
|
t.witness();
|
|
|
|
t.append(&"a".to_string());
|
2022-03-02 09:04:11 -08:00
|
|
|
assert!(t.rewind());
|
|
|
|
assert_eq!(Some((Position::from(0), "a".to_string())), t.current_leaf());
|
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());
|
|
|
|
assert_eq!(t.root(), "ab______________");
|
2022-02-20 10:40:12 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn check_rewind_remove_witness<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());
|
|
|
|
tree.witness();
|
|
|
|
tree.checkpoint();
|
|
|
|
assert!(tree.rewind());
|
|
|
|
assert!(tree.remove_witness(0usize.into(), &"e".to_string()));
|
|
|
|
|
|
|
|
let mut tree = new_tree(100);
|
|
|
|
tree.append(&"e".to_string());
|
|
|
|
tree.checkpoint();
|
|
|
|
tree.witness();
|
|
|
|
assert!(tree.rewind());
|
|
|
|
assert!(!tree.remove_witness(0usize.into(), &"e".to_string()));
|
|
|
|
|
2022-02-20 10:40:12 -08:00
|
|
|
let mut tree = new_tree(100);
|
|
|
|
tree.append(&"e".to_string());
|
|
|
|
tree.witness();
|
|
|
|
tree.checkpoint();
|
2022-02-23 20:41:19 -08:00
|
|
|
assert!(tree.remove_witness(0usize.into(), &"e".to_string()));
|
|
|
|
assert!(tree.rewind());
|
|
|
|
assert!(tree.remove_witness(0usize.into(), &"e".to_string()));
|
2022-02-20 10:40:12 -08:00
|
|
|
|
|
|
|
let mut tree = new_tree(100);
|
|
|
|
tree.append(&"e".to_string());
|
|
|
|
tree.witness();
|
2022-02-23 20:41:19 -08:00
|
|
|
assert!(tree.remove_witness(0usize.into(), &"e".to_string()));
|
2022-02-20 10:40:12 -08:00
|
|
|
tree.checkpoint();
|
2022-02-23 20:41:19 -08:00
|
|
|
assert!(tree.rewind());
|
|
|
|
assert!(!tree.remove_witness(0usize.into(), &"e".to_string()));
|
2022-02-20 16:40:07 -08:00
|
|
|
|
|
|
|
let mut tree = new_tree(100);
|
|
|
|
tree.append(&"a".to_string());
|
2022-02-23 20:41:19 -08:00
|
|
|
assert!(!tree.remove_witness(0usize.into(), &"a".to_string()));
|
2022-02-20 16:40:07 -08:00
|
|
|
tree.checkpoint();
|
2022-02-23 20:41:19 -08:00
|
|
|
assert!(tree.witness().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-02-23 20:41:19 -08:00
|
|
|
assert!(tree.witness().is_some());
|
|
|
|
assert!(tree.remove_witness(0usize.into(), &"a".to_string()));
|
|
|
|
assert!(tree.rewind());
|
|
|
|
assert!(!tree.remove_witness(0usize.into(), &"a".to_string()));
|
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-03-02 09:04:11 -08:00
|
|
|
fn append(x: &str) -> Operation<String> {
|
|
|
|
Append(x.to_string())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn unwitness(pos: usize, x: &str) -> Operation<String> {
|
|
|
|
Unwitness(Position::from(pos), x.to_string())
|
|
|
|
}
|
|
|
|
|
|
|
|
let ops = vec![append("x"), Checkpoint, Witness, Rewind, unwitness(0, "x")];
|
2022-02-20 16:40:07 -08:00
|
|
|
let result = check_operations(ops);
|
2022-03-02 09:04:11 -08:00
|
|
|
assert!(
|
|
|
|
matches!(result, Ok(())),
|
|
|
|
"Reference/Test mismatch: {:?}",
|
|
|
|
result
|
|
|
|
);
|
2022-02-20 16:40:07 -08:00
|
|
|
|
|
|
|
let ops = vec![
|
2022-03-02 09:04:11 -08:00
|
|
|
append("d"),
|
2022-02-20 16:40:07 -08:00
|
|
|
Checkpoint,
|
2022-03-02 09:04:11 -08:00
|
|
|
Witness,
|
|
|
|
unwitness(0, "d"),
|
2022-02-20 16:40:07 -08:00
|
|
|
Rewind,
|
2022-03-02 09:04:11 -08:00
|
|
|
unwitness(0, "d"),
|
2022-02-20 16:40:07 -08:00
|
|
|
];
|
|
|
|
let result = check_operations(ops);
|
2022-03-02 09:04:11 -08:00
|
|
|
assert!(
|
|
|
|
matches!(result, Ok(())),
|
|
|
|
"Reference/Test mismatch: {:?}",
|
|
|
|
result
|
|
|
|
);
|
2022-02-20 16:40:07 -08:00
|
|
|
|
|
|
|
let ops = vec![
|
2022-03-02 09:04:11 -08:00
|
|
|
append("o"),
|
2022-02-20 16:40:07 -08:00
|
|
|
Checkpoint,
|
|
|
|
Witness,
|
2022-03-02 09:04:11 -08:00
|
|
|
Checkpoint,
|
|
|
|
unwitness(0, "o"),
|
|
|
|
Rewind,
|
2022-02-20 16:40:07 -08:00
|
|
|
Rewind,
|
|
|
|
];
|
|
|
|
let result = check_operations(ops);
|
2022-03-02 09:04:11 -08:00
|
|
|
assert!(
|
|
|
|
matches!(result, Ok(())),
|
|
|
|
"Reference/Test mismatch: {:?}",
|
|
|
|
result
|
|
|
|
);
|
2022-02-20 16:40:07 -08:00
|
|
|
|
|
|
|
let ops = vec![
|
2022-03-02 09:04:11 -08:00
|
|
|
append("s"),
|
2022-02-20 16:40:07 -08:00
|
|
|
Witness,
|
2022-03-02 09:04:11 -08:00
|
|
|
append("m"),
|
2022-02-20 16:40:07 -08:00
|
|
|
Checkpoint,
|
2022-03-02 09:04:11 -08:00
|
|
|
unwitness(0, "s"),
|
2022-02-20 16:40:07 -08:00
|
|
|
Rewind,
|
2022-03-02 09:04:11 -08:00
|
|
|
unwitness(0, "s"),
|
|
|
|
unwitness(0, "m"),
|
2022-02-20 16:40:07 -08:00
|
|
|
];
|
|
|
|
let result = check_operations(ops);
|
2022-03-02 09:04:11 -08:00
|
|
|
assert!(
|
|
|
|
matches!(result, Ok(())),
|
|
|
|
"Reference/Test mismatch: {:?}",
|
|
|
|
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-02-24 07:56:56 -08:00
|
|
|
impl<H: Hashable + Ord + Clone + Debug, const DEPTH: u8> Frontier<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
|
|
|
}
|
|
|
|
|
2021-06-17 18:17:47 -07:00
|
|
|
/// Obtains the current root of this Merkle tree.
|
2021-06-17 18:24:18 -07:00
|
|
|
fn root(&self) -> H {
|
2021-06-17 18:17:47 -07:00
|
|
|
let a = self.inefficient.root();
|
2021-06-17 18:32:50 -07:00
|
|
|
let b = self.efficient.root();
|
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-24 07:56:56 -08:00
|
|
|
impl<H: Hashable + Ord + Clone + Debug, const DEPTH: u8> Tree<H> for CombinedTree<H, DEPTH> {
|
2021-06-23 13:14:57 -07:00
|
|
|
type Recording = CombinedRecording<H, DEPTH>;
|
2015-12-19 17:37:23 -08:00
|
|
|
|
2022-02-18 14:30:47 -08:00
|
|
|
/// Returns the most recently appended leaf value.
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the most recently appended leaf value and its position in the tree.
|
|
|
|
fn current_leaf(&self) -> Option<(Position, 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
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns `true` if the tree can produce an authentication path for
|
2022-02-22 20:34:38 -08:00
|
|
|
/// the specified leaf value from the specified position in the tree.
|
|
|
|
fn is_witnessed(&self, position: Position, value: &H) -> bool {
|
|
|
|
let a = self.inefficient.is_witnessed(position, value);
|
|
|
|
let b = self.efficient.is_witnessed(position, value);
|
2022-02-18 14:30:47 -08:00
|
|
|
assert_eq!(a, b);
|
|
|
|
a
|
|
|
|
}
|
|
|
|
|
2021-06-17 18:17:47 -07:00
|
|
|
/// Marks the current tree state leaf as a value that we're interested in
|
2022-02-23 17:50:21 -08:00
|
|
|
/// witnessing. Returns the current position and leaf value if the tree
|
|
|
|
/// is non-empty.
|
|
|
|
fn witness(&mut self) -> Option<(Position, H)> {
|
2021-06-17 18:17:47 -07:00
|
|
|
let a = self.inefficient.witness();
|
2021-06-17 18:32:50 -07:00
|
|
|
let b = self.efficient.witness();
|
2021-06-17 18:17:47 -07:00
|
|
|
assert_eq!(a, b);
|
|
|
|
a
|
2015-12-19 17:37:23 -08:00
|
|
|
}
|
|
|
|
|
2021-06-17 18:17:47 -07:00
|
|
|
/// Obtains an authentication path to the value specified in the tree.
|
|
|
|
/// Returns `None` if there is no available authentication path to the
|
|
|
|
/// specified value.
|
2022-02-22 20:34:38 -08:00
|
|
|
fn authentication_path(&self, position: Position, value: &H) -> Option<Vec<H>> {
|
|
|
|
let a = self.inefficient.authentication_path(position, value);
|
|
|
|
let b = self.efficient.authentication_path(position, value);
|
2021-06-17 18:17:47 -07:00
|
|
|
assert_eq!(a, b);
|
|
|
|
a
|
2015-12-19 17:37:23 -08:00
|
|
|
}
|
|
|
|
|
2021-06-17 18:17:47 -07:00
|
|
|
/// Marks the specified tree state value as a value we're no longer
|
|
|
|
/// interested in maintaining a witness for. Returns true if successful and
|
|
|
|
/// false if the value is not a known witness.
|
2022-02-22 20:34:38 -08:00
|
|
|
fn remove_witness(&mut self, position: Position, value: &H) -> bool {
|
|
|
|
let a = self.inefficient.remove_witness(position, value);
|
|
|
|
let b = self.efficient.remove_witness(position, value);
|
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:17:47 -07:00
|
|
|
/// Marks the current tree state as a checkpoint if it is not already a
|
|
|
|
/// checkpoint.
|
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:17:47 -07:00
|
|
|
/// Rewinds the tree state to the previous checkpoint. This function will
|
|
|
|
/// fail and return false if there is no previous checkpoint or in the event
|
|
|
|
/// witness data would be destroyed in the process.
|
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
|
|
|
}
|
2015-12-19 17:37:23 -08:00
|
|
|
|
2021-06-17 18:17:47 -07:00
|
|
|
/// Start a recording of append operations performed on a tree.
|
2021-06-23 13:14:57 -07:00
|
|
|
fn recording(&self) -> CombinedRecording<H, DEPTH> {
|
2021-06-17 18:17:47 -07:00
|
|
|
CombinedRecording {
|
|
|
|
inefficient: self.inefficient.recording(),
|
2021-06-17 18:32:50 -07:00
|
|
|
efficient: self.efficient.recording(),
|
2021-06-17 18:17:47 -07:00
|
|
|
}
|
2015-12-19 17:37:23 -08:00
|
|
|
}
|
|
|
|
|
2021-06-17 18:17:47 -07:00
|
|
|
/// Plays a recording of append operations back. Returns true if successful
|
|
|
|
/// and false if the recording is incompatible with the current tree state.
|
2021-06-23 13:14:57 -07:00
|
|
|
fn play(&mut self, recording: &CombinedRecording<H, DEPTH>) -> bool {
|
2021-06-17 18:17:47 -07:00
|
|
|
let a = self.inefficient.play(&recording.inefficient);
|
2021-06-17 18:32:50 -07:00
|
|
|
let b = self.efficient.play(&recording.efficient);
|
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:17:47 -07:00
|
|
|
#[derive(Clone)]
|
2022-02-24 07:56:56 -08:00
|
|
|
pub struct CombinedRecording<H: Hashable + Ord, const DEPTH: u8> {
|
2021-06-17 18:17:47 -07:00
|
|
|
inefficient: CompleteRecording<H>,
|
2021-06-23 13:14:57 -07:00
|
|
|
efficient: BridgeRecording<H, DEPTH>,
|
2021-01-05 12:40:05 -08:00
|
|
|
}
|
2015-12-19 17:37:23 -08:00
|
|
|
|
2022-02-24 07:56:56 -08:00
|
|
|
impl<H: Hashable + Ord + Clone + Debug, const DEPTH: u8> Recording<H>
|
|
|
|
for CombinedRecording<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
|
|
|
|
2021-06-17 18:24:18 -07:00
|
|
|
fn play(&mut self, recording: &Self) -> bool {
|
2021-06-17 18:17:47 -07:00
|
|
|
let a = self.inefficient.play(&recording.inefficient);
|
2021-06-17 18:32:50 -07:00
|
|
|
let b = self.efficient.play(&recording.efficient);
|
2021-06-17 18:17:47 -07:00
|
|
|
assert_eq!(a, b);
|
|
|
|
a
|
|
|
|
}
|
|
|
|
}
|
2021-01-05 12:40:05 -08:00
|
|
|
|
2021-06-17 18:24:18 -07:00
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub enum Operation<A> {
|
|
|
|
Append(A),
|
|
|
|
Witness,
|
2022-02-22 20:34:38 -08:00
|
|
|
Unwitness(Position, A),
|
2021-06-17 18:24:18 -07:00
|
|
|
Checkpoint,
|
|
|
|
Rewind,
|
2022-02-22 20:34:38 -08:00
|
|
|
Authpath(Position, A),
|
2021-06-17 18:24:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
use Operation::*;
|
|
|
|
|
2022-02-18 13:02:35 -08:00
|
|
|
impl<H: Hashable> Operation<H> {
|
2021-07-22 10:30:22 -07:00
|
|
|
pub fn apply<T: Tree<H>>(&self, tree: &mut T) -> Option<(Position, Vec<H>)> {
|
2021-06-17 18:24:18 -07:00
|
|
|
match self {
|
|
|
|
Append(a) => {
|
|
|
|
assert!(tree.append(a), "append failed");
|
|
|
|
None
|
|
|
|
}
|
|
|
|
Witness => {
|
2022-02-23 17:50:21 -08:00
|
|
|
assert!(tree.witness().is_some(), "witness failed");
|
2021-06-17 18:24:18 -07:00
|
|
|
None
|
|
|
|
}
|
2022-02-22 20:34:38 -08:00
|
|
|
Unwitness(p, a) => {
|
|
|
|
assert!(tree.remove_witness(*p, a), "remove witness failed");
|
2021-06-17 18:24:18 -07:00
|
|
|
None
|
|
|
|
}
|
|
|
|
Checkpoint => {
|
|
|
|
tree.checkpoint();
|
|
|
|
None
|
|
|
|
}
|
|
|
|
Rewind => {
|
|
|
|
assert!(tree.rewind(), "rewind failed");
|
|
|
|
None
|
|
|
|
}
|
2022-02-22 20:34:38 -08:00
|
|
|
Authpath(p, a) => tree.authentication_path(*p, a).map(|xs| (*p, xs)),
|
2021-06-17 18:24:18 -07:00
|
|
|
}
|
2021-01-05 12:40:05 -08:00
|
|
|
}
|
2021-06-17 18:24:18 -07:00
|
|
|
|
|
|
|
pub fn apply_all<T: Tree<H>>(
|
|
|
|
ops: &[Operation<H>],
|
|
|
|
tree: &mut T,
|
2021-07-22 10:30:22 -07:00
|
|
|
) -> Option<(Position, Vec<H>)> {
|
2021-06-17 18:24:18 -07:00
|
|
|
let mut result = None;
|
|
|
|
for op in ops {
|
|
|
|
result = op.apply(tree);
|
|
|
|
}
|
|
|
|
result
|
2015-12-19 17:37:23 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-17 18:24:18 -07:00
|
|
|
pub(crate) fn compute_root_from_auth_path<H: Hashable>(
|
|
|
|
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;
|
2021-06-19 10:24:33 -07:00
|
|
|
let mut lvl = Altitude::zero();
|
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]
|
|
|
|
fn test_compute_root_from_auth_path() {
|
2021-06-17 18:24:18 -07:00
|
|
|
let expected = SipHashable::combine(
|
2021-06-19 10:24:33 -07:00
|
|
|
<Altitude>::from(2),
|
2021-06-17 18:24:18 -07:00
|
|
|
&SipHashable::combine(
|
2021-06-19 10:24:33 -07:00
|
|
|
Altitude::one(),
|
|
|
|
&SipHashable::combine(Altitude::zero(), &SipHashable(0), &SipHashable(1)),
|
|
|
|
&SipHashable::combine(Altitude::zero(), &SipHashable(2), &SipHashable(3)),
|
2021-06-17 18:24:18 -07:00
|
|
|
),
|
|
|
|
&SipHashable::combine(
|
2021-06-19 10:24:33 -07:00
|
|
|
Altitude::one(),
|
|
|
|
&SipHashable::combine(Altitude::zero(), &SipHashable(4), &SipHashable(5)),
|
|
|
|
&SipHashable::combine(Altitude::zero(), &SipHashable(6), &SipHashable(7)),
|
2021-06-17 18:24:18 -07:00
|
|
|
),
|
2021-01-05 12:40:05 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
2021-06-17 18:24:18 -07:00
|
|
|
compute_root_from_auth_path::<SipHashable>(
|
|
|
|
SipHashable(0),
|
2021-07-22 10:30:22 -07:00
|
|
|
Position::zero(),
|
2021-01-05 12:40:05 -08:00
|
|
|
&[
|
2021-06-17 18:24:18 -07:00
|
|
|
SipHashable(1),
|
2021-06-19 10:24:33 -07:00
|
|
|
SipHashable::combine(Altitude::zero(), &SipHashable(2), &SipHashable(3)),
|
2021-06-17 18:24:18 -07:00
|
|
|
SipHashable::combine(
|
2021-06-19 10:24:33 -07:00
|
|
|
Altitude::one(),
|
|
|
|
&SipHashable::combine(Altitude::zero(), &SipHashable(4), &SipHashable(5)),
|
|
|
|
&SipHashable::combine(Altitude::zero(), &SipHashable(6), &SipHashable(7))
|
2021-06-17 18:24:18 -07:00
|
|
|
)
|
2021-01-05 12:40:05 -08:00
|
|
|
]
|
|
|
|
),
|
|
|
|
expected
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
2021-06-17 18:24:18 -07:00
|
|
|
compute_root_from_auth_path(
|
|
|
|
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),
|
2021-06-19 10:24:33 -07:00
|
|
|
SipHashable::combine(Altitude::zero(), &SipHashable(6), &SipHashable(7)),
|
2021-06-17 18:24:18 -07:00
|
|
|
SipHashable::combine(
|
2021-06-19 10:24:33 -07:00
|
|
|
Altitude::one(),
|
|
|
|
&SipHashable::combine(Altitude::zero(), &SipHashable(0), &SipHashable(1)),
|
|
|
|
&SipHashable::combine(Altitude::zero(), &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
|
|
|
}
|
|
|
|
|
2021-01-05 12:40:05 -08:00
|
|
|
use proptest::prelude::*;
|
2022-02-22 20:34:38 -08:00
|
|
|
use proptest::sample::{select, SizeRange};
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
|
|
enum OpType {
|
|
|
|
Append,
|
|
|
|
Witness,
|
|
|
|
Unwitness,
|
|
|
|
Checkpoint,
|
|
|
|
Rewind,
|
|
|
|
Authpath,
|
|
|
|
}
|
2021-06-17 18:24:18 -07:00
|
|
|
|
2022-02-22 20:34:38 -08:00
|
|
|
fn arb_operations_0<H: Clone + Debug + 'static>(
|
|
|
|
optypes: Vec<OpType>,
|
|
|
|
items: Vec<H>,
|
|
|
|
) -> impl Strategy<Value = Vec<Operation<H>>> {
|
|
|
|
let mut idx = 0;
|
|
|
|
optypes
|
|
|
|
.iter()
|
|
|
|
.map(move |t| {
|
|
|
|
let items = items.clone();
|
|
|
|
match t {
|
|
|
|
OpType::Append => {
|
|
|
|
let item = items.get(idx).unwrap().clone();
|
|
|
|
idx += 1;
|
|
|
|
Just(Append(item)).boxed()
|
|
|
|
}
|
|
|
|
OpType::Witness => Just(Witness).boxed(),
|
|
|
|
OpType::Unwitness => (0..(idx + 1))
|
|
|
|
.prop_map(move |i| {
|
|
|
|
let item = items.get(i).unwrap().clone();
|
|
|
|
Unwitness(Position::from(i), item)
|
|
|
|
})
|
|
|
|
.boxed(),
|
|
|
|
OpType::Checkpoint => Just(Checkpoint).boxed(),
|
|
|
|
OpType::Rewind => Just(Rewind).boxed(),
|
|
|
|
OpType::Authpath => (0..(idx + 1))
|
|
|
|
.prop_map(move |i| {
|
|
|
|
let item = items.get(i).unwrap().clone();
|
|
|
|
Authpath(Position::from(i), item)
|
|
|
|
})
|
|
|
|
.boxed(),
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn arb_operations_1<G: Strategy>(
|
|
|
|
item_gen: G,
|
|
|
|
optypes: Vec<OpType>,
|
|
|
|
) -> impl Strategy<Value = Vec<Operation<G::Value>>>
|
2021-06-17 18:24:18 -07:00
|
|
|
where
|
|
|
|
G::Value: Clone + 'static,
|
|
|
|
{
|
2022-02-22 20:34:38 -08:00
|
|
|
let append_count = optypes
|
|
|
|
.iter()
|
|
|
|
.filter(|t| matches!(t, OpType::Append))
|
|
|
|
.count();
|
|
|
|
proptest::collection::vec(item_gen, append_count + 1)
|
|
|
|
.prop_flat_map(move |items: Vec<G::Value>| arb_operations_0(optypes.clone(), items))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn arb_operations<G: Strategy + Clone>(
|
|
|
|
item_gen: G,
|
|
|
|
count: impl Into<SizeRange>,
|
|
|
|
) -> impl Strategy<Value = Vec<Operation<G::Value>>>
|
|
|
|
where
|
|
|
|
G::Value: Clone + 'static,
|
|
|
|
{
|
|
|
|
proptest::collection::vec(
|
2021-06-17 18:24:18 -07:00
|
|
|
select(vec![
|
2022-02-22 20:34:38 -08:00
|
|
|
OpType::Append,
|
|
|
|
OpType::Witness,
|
|
|
|
OpType::Unwitness,
|
|
|
|
OpType::Checkpoint,
|
|
|
|
OpType::Rewind,
|
|
|
|
OpType::Authpath,
|
|
|
|
]),
|
|
|
|
count,
|
|
|
|
)
|
|
|
|
.prop_flat_map(move |optypes: Vec<OpType>| arb_operations_1(item_gen.clone(), optypes))
|
2015-12-19 17:37:23 -08:00
|
|
|
}
|
|
|
|
|
2022-02-24 07:56:56 -08:00
|
|
|
fn check_operations<H: Hashable + Ord + Clone + Debug>(
|
2021-06-17 18:24:18 -07:00
|
|
|
ops: Vec<Operation<H>>,
|
|
|
|
) -> 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 prevtrees = vec![];
|
|
|
|
|
|
|
|
let mut tree_size = 0;
|
|
|
|
let mut tree_values = 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) => {
|
|
|
|
prevtrees.push((tree.clone(), tree.recording()));
|
|
|
|
if tree.append(&value) {
|
|
|
|
prop_assert!(tree_size < (1 << DEPTH));
|
|
|
|
tree_size += 1;
|
|
|
|
tree_values.push(value.clone());
|
|
|
|
|
|
|
|
for &mut (_, ref mut recording) in &mut prevtrees {
|
|
|
|
prop_assert!(recording.append(&value));
|
2021-01-05 12:40:05 -08:00
|
|
|
}
|
2021-06-17 18:24:18 -07:00
|
|
|
} else {
|
|
|
|
prop_assert_eq!(tree_size, 1 << DEPTH);
|
2021-01-05 12:40:05 -08:00
|
|
|
}
|
2021-06-17 18:24:18 -07:00
|
|
|
}
|
|
|
|
Witness => {
|
2022-02-23 17:50:21 -08:00
|
|
|
if tree.witness().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-02-22 20:34:38 -08:00
|
|
|
Unwitness(position, value) => {
|
|
|
|
tree.remove_witness(position, &value);
|
2021-06-17 18:24:18 -07:00
|
|
|
}
|
|
|
|
Checkpoint => {
|
|
|
|
tree_checkpoints.push(tree_size);
|
|
|
|
tree.checkpoint();
|
|
|
|
}
|
|
|
|
Rewind => {
|
|
|
|
prevtrees.truncate(0);
|
|
|
|
|
|
|
|
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-02-22 20:34:38 -08:00
|
|
|
Authpath(position, value) => {
|
|
|
|
if let Some(path) = tree.authentication_path(position, &value) {
|
2021-06-17 18:24:18 -07:00
|
|
|
let mut extended_tree_values = tree_values.clone();
|
|
|
|
extended_tree_values.resize(1 << DEPTH, H::empty_leaf());
|
|
|
|
let expected_root = lazy_root::<H>(extended_tree_values);
|
|
|
|
|
|
|
|
let tree_root = tree.root();
|
|
|
|
prop_assert_eq!(&tree_root, &expected_root);
|
|
|
|
|
|
|
|
prop_assert_eq!(
|
|
|
|
&compute_root_from_auth_path(value, position, &path),
|
|
|
|
&expected_root
|
|
|
|
);
|
2021-01-05 12:40:05 -08:00
|
|
|
}
|
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
|
|
|
for (mut other_tree, other_recording) in prevtrees {
|
|
|
|
prop_assert!(other_tree.play(&other_recording));
|
|
|
|
prop_assert_eq!(tree.root(), other_tree.root());
|
2015-12-19 17:37: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-02-22 20:34:38 -08:00
|
|
|
ops in arb_operations((0..32u64).prop_map(SipHashable), 1..100)
|
2022-02-20 10:40:12 -08:00
|
|
|
) {
|
|
|
|
check_operations(ops)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn check_randomized_str_ops(
|
2022-02-22 20:34:38 -08:00
|
|
|
ops in arb_operations((97u8..123).prop_map(|c| char::from(c).to_string()), 1..100)
|
2022-02-20 10:40:12 -08:00
|
|
|
) {
|
|
|
|
check_operations::<String>(ops)?;
|
|
|
|
}
|
|
|
|
}
|
2021-01-05 12:40:05 -08:00
|
|
|
}
|