2022-03-02 09:04:11 -08:00
|
|
|
//! Sample implementation of the Tree interface.
|
2022-03-30 14:08:01 -07:00
|
|
|
use super::{Altitude, Frontier, Hashable, Position, Tree};
|
2021-06-17 18:17:47 -07:00
|
|
|
|
2022-02-20 16:40:07 -08:00
|
|
|
#[derive(Clone, Debug)]
|
2022-02-17 13:49:06 -08:00
|
|
|
pub struct TreeState<H: Hashable> {
|
2021-06-17 18:24:18 -07:00
|
|
|
leaves: Vec<H>,
|
2022-02-18 14:30:47 -08:00
|
|
|
current_offset: usize,
|
2022-02-22 20:30:14 -08:00
|
|
|
witnesses: Vec<(Position, H)>,
|
2021-06-17 18:17:47 -07:00
|
|
|
depth: usize,
|
|
|
|
}
|
|
|
|
|
2022-02-17 13:49:06 -08:00
|
|
|
impl<H: Hashable + Clone> TreeState<H> {
|
2021-06-17 18:17:47 -07:00
|
|
|
/// Creates a new, empty binary tree of specified depth.
|
2021-06-24 13:16:13 -07:00
|
|
|
#[cfg(test)]
|
2022-02-17 13:49:06 -08:00
|
|
|
pub fn new(depth: usize) -> Self {
|
2022-01-23 11:16:30 -08:00
|
|
|
Self {
|
2021-06-17 18:17:47 -07:00
|
|
|
leaves: vec![H::empty_leaf(); 1 << depth],
|
2022-02-18 14:30:47 -08:00
|
|
|
current_offset: 0,
|
2021-06-17 18:17:47 -07:00
|
|
|
witnesses: vec![],
|
|
|
|
depth,
|
2021-06-17 18:24:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-17 13:49:06 -08:00
|
|
|
impl<H: Hashable + Clone> Frontier<H> for TreeState<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 {
|
2022-02-18 14:30:47 -08:00
|
|
|
if self.current_offset == (1 << self.depth) {
|
2021-06-17 18:17:47 -07:00
|
|
|
false
|
|
|
|
} else {
|
2022-02-18 14:30:47 -08:00
|
|
|
self.leaves[self.current_offset] = value.clone();
|
|
|
|
self.current_offset += 1;
|
2021-06-17 18:17:47 -07:00
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Obtains the current root of this Merkle tree.
|
2021-06-17 18:24:18 -07:00
|
|
|
fn root(&self) -> H {
|
|
|
|
lazy_root(self.leaves.clone())
|
2021-06-17 18:17:47 -07:00
|
|
|
}
|
2021-06-18 13:21:03 -07:00
|
|
|
}
|
|
|
|
|
2022-02-17 13:49:06 -08:00
|
|
|
impl<H: Hashable + PartialEq + Clone> TreeState<H> {
|
2022-02-22 20:30:14 -08:00
|
|
|
fn current_position(&self) -> Option<Position> {
|
2022-02-18 14:30:47 -08:00
|
|
|
if self.current_offset == 0 {
|
2022-02-18 12:23:35 -08:00
|
|
|
None
|
|
|
|
} else {
|
2022-02-22 20:30:14 -08:00
|
|
|
Some((self.current_offset - 1).into())
|
2022-02-18 12:23:35 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-22 20:30:14 -08:00
|
|
|
/// Returns the leaf most recently appended to the tree
|
|
|
|
fn current_leaf(&self) -> Option<(Position, H)> {
|
|
|
|
self.current_position()
|
|
|
|
.map(|p| (p, self.leaves[<usize>::from(p)].clone()))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns whether a leaf with the specified position and value has been witnessed
|
|
|
|
fn is_witnessed(&self, position: Position, value: &H) -> bool {
|
|
|
|
self.witnesses
|
|
|
|
.iter()
|
|
|
|
.any(|(pos, v)| pos == &position && v == value)
|
2022-02-18 12:23:35 -08:00
|
|
|
}
|
|
|
|
|
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)> {
|
|
|
|
self.current_leaf().map(|(pos, value)| {
|
2022-02-22 20:30:14 -08:00
|
|
|
if !self.is_witnessed(pos, &value) {
|
2022-02-23 17:50:21 -08:00
|
|
|
self.witnesses.push((pos, value.clone()));
|
2021-06-17 18:24:18 -07:00
|
|
|
}
|
2022-02-23 17:50:21 -08:00
|
|
|
(pos, value)
|
|
|
|
})
|
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:30:14 -08:00
|
|
|
fn authentication_path(&self, position: Position, value: &H) -> Option<Vec<H>> {
|
2021-06-17 18:17:47 -07:00
|
|
|
self.witnesses
|
|
|
|
.iter()
|
2022-02-22 20:30:14 -08:00
|
|
|
.find(|(pos, v)| pos == &position && v == value)
|
|
|
|
.map(|_| {
|
2021-06-17 18:17:47 -07:00
|
|
|
let mut path = vec![];
|
|
|
|
|
2022-02-22 20:30:14 -08:00
|
|
|
let mut leaf_idx: usize = position.into();
|
2021-06-17 18:17:47 -07:00
|
|
|
for bit in 0..self.depth {
|
2022-02-22 20:30:14 -08:00
|
|
|
leaf_idx ^= 1 << bit;
|
|
|
|
path.push(lazy_root::<H>(
|
|
|
|
self.leaves[leaf_idx..][0..(1 << bit)].to_vec(),
|
|
|
|
));
|
|
|
|
leaf_idx &= usize::MAX << (bit + 1);
|
2021-06-17 18:17:47 -07:00
|
|
|
}
|
|
|
|
|
2022-02-22 20:30:14 -08:00
|
|
|
path
|
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:30:14 -08:00
|
|
|
fn remove_witness(&mut self, position: Position, value: &H) -> bool {
|
|
|
|
if let Some((witness_index, _)) = self
|
2021-06-17 18:17:47 -07:00
|
|
|
.witnesses
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
2022-02-22 20:30:14 -08:00
|
|
|
.find(|(_i, (pos, v))| pos == &position && v == value)
|
2021-06-17 18:17:47 -07:00
|
|
|
{
|
2022-02-22 20:30:14 -08:00
|
|
|
self.witnesses.remove(witness_index);
|
2021-06-17 18:17:47 -07:00
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
2022-02-17 13:49:06 -08:00
|
|
|
}
|
|
|
|
|
2022-02-20 16:40:07 -08:00
|
|
|
#[derive(Clone, Debug)]
|
2022-02-17 13:49:06 -08:00
|
|
|
pub struct CompleteTree<H: Hashable> {
|
|
|
|
tree_state: TreeState<H>,
|
2022-03-02 09:04:11 -08:00
|
|
|
checkpoints: Vec<TreeState<H>>,
|
2022-02-17 13:49:06 -08:00
|
|
|
max_checkpoints: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<H: Hashable + Clone> CompleteTree<H> {
|
|
|
|
/// Creates a new, empty binary tree of specified depth.
|
|
|
|
#[cfg(test)]
|
|
|
|
pub fn new(depth: usize, max_checkpoints: usize) -> Self {
|
|
|
|
CompleteTree {
|
|
|
|
tree_state: TreeState::new(depth),
|
|
|
|
checkpoints: vec![],
|
|
|
|
max_checkpoints,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<H: Hashable + Clone> Frontier<H> for CompleteTree<H> {
|
|
|
|
/// Appends a new value to the tree at the next available slot. Returns true
|
|
|
|
/// if successful and false if the tree is full.
|
|
|
|
fn append(&mut self, value: &H) -> bool {
|
|
|
|
self.tree_state.append(value)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Obtains the current root of this Merkle tree.
|
|
|
|
fn root(&self) -> H {
|
|
|
|
self.tree_state.root()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<H: Hashable + PartialEq + Clone> CompleteTree<H> {
|
|
|
|
/// Removes the oldest checkpoint. Returns true if successful and false if
|
|
|
|
/// there are no checkpoints.
|
|
|
|
fn drop_oldest_checkpoint(&mut self) -> bool {
|
|
|
|
if self.checkpoints.is_empty() {
|
|
|
|
false
|
|
|
|
} else {
|
|
|
|
self.checkpoints.remove(0);
|
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<H: Hashable + PartialEq + Clone> Tree<H> for CompleteTree<H> {
|
2022-02-22 20:30:14 -08:00
|
|
|
/// Returns the most recently appended leaf value.
|
|
|
|
fn current_position(&self) -> Option<Position> {
|
|
|
|
self.tree_state.current_position()
|
|
|
|
}
|
|
|
|
|
2022-02-18 14:30:47 -08:00
|
|
|
/// Returns the leaf most recently appended to the tree
|
2022-02-22 20:30:14 -08:00
|
|
|
fn current_leaf(&self) -> Option<(Position, H)> {
|
2022-02-18 14:30:47 -08:00
|
|
|
self.tree_state.current_leaf()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns whether a leaf with the specified value has been witnessed
|
2022-02-22 20:30:14 -08:00
|
|
|
fn is_witnessed(&self, position: Position, value: &H) -> bool {
|
|
|
|
self.tree_state.is_witnessed(position, value)
|
2022-02-18 14:30:47 -08:00
|
|
|
}
|
|
|
|
|
2022-02-17 13:49:06 -08: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)> {
|
2022-02-17 13:49:06 -08:00
|
|
|
self.tree_state.witness()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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>> {
|
|
|
|
self.tree_state.authentication_path(position, value)
|
2022-02-17 13:49:06 -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 {
|
|
|
|
self.tree_state.remove_witness(position, value)
|
2022-02-17 13:49:06 -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.
|
|
|
|
fn checkpoint(&mut self) {
|
2022-03-02 09:04:11 -08:00
|
|
|
self.checkpoints.push(self.tree_state.clone());
|
2021-06-17 18:24:18 -07:00
|
|
|
if self.checkpoints.len() > self.max_checkpoints {
|
|
|
|
self.drop_oldest_checkpoint();
|
|
|
|
}
|
2021-06-17 18:17:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Rewinds the tree state to the previous checkpoint. This function will
|
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-03-02 09:04:11 -08:00
|
|
|
if let Some(checkpointed_state) = self.checkpoints.pop() {
|
|
|
|
self.tree_state = checkpointed_state;
|
|
|
|
true
|
2021-06-17 18:17:47 -07:00
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-17 18:24:18 -07:00
|
|
|
pub(crate) fn lazy_root<H: Hashable + Clone>(mut leaves: Vec<H>) -> H {
|
|
|
|
//leaves are always at level zero, so we start there.
|
2021-06-19 10:24:33 -07:00
|
|
|
let mut level = Altitude::zero();
|
2021-06-17 18:17:47 -07:00
|
|
|
while leaves.len() != 1 {
|
|
|
|
leaves = leaves
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.filter(|(i, _)| (i % 2) == 0)
|
|
|
|
.map(|(_, a)| a)
|
|
|
|
.zip(
|
|
|
|
leaves
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.filter(|(i, _)| (i % 2) == 1)
|
|
|
|
.map(|(_, b)| b),
|
|
|
|
)
|
2021-06-17 18:24:18 -07:00
|
|
|
.map(|(a, b)| H::combine(level, a, b))
|
2021-06-17 18:17:47 -07:00
|
|
|
.collect();
|
2021-06-17 18:24:18 -07:00
|
|
|
level = level + 1;
|
2021-06-17 18:17:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
leaves[0].clone()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2021-06-17 18:24:18 -07:00
|
|
|
use crate::tests::{compute_root_from_auth_path, SipHashable};
|
2022-02-22 20:34:38 -08:00
|
|
|
use crate::{Altitude, Frontier, Hashable, Position, Tree};
|
|
|
|
use std::convert::TryFrom;
|
2021-06-17 18:17:47 -07:00
|
|
|
|
|
|
|
use super::CompleteTree;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn correct_empty_root() {
|
2021-06-23 13:14:57 -07:00
|
|
|
const DEPTH: u8 = 5;
|
2021-06-17 18:24:18 -07:00
|
|
|
let mut expected = SipHashable(0u64);
|
2021-06-23 13:14:57 -07:00
|
|
|
for lvl in 0u8..DEPTH {
|
2021-06-17 18:24:18 -07:00
|
|
|
expected = SipHashable::combine(lvl.into(), &expected, &expected);
|
2021-06-17 18:17:47 -07:00
|
|
|
}
|
|
|
|
|
2021-06-17 18:24:18 -07:00
|
|
|
let tree = CompleteTree::<SipHashable>::new(DEPTH as usize, 100);
|
2021-06-17 18:17:47 -07:00
|
|
|
assert_eq!(tree.root(), expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn correct_root() {
|
|
|
|
const DEPTH: usize = 3;
|
2021-06-17 18:24:18 -07:00
|
|
|
let values = (0..(1 << DEPTH)).into_iter().map(SipHashable);
|
2021-06-17 18:17:47 -07:00
|
|
|
|
2021-06-17 18:24:18 -07:00
|
|
|
let mut tree = CompleteTree::<SipHashable>::new(DEPTH, 100);
|
|
|
|
for value in values {
|
|
|
|
assert!(tree.append(&value));
|
2021-06-17 18:17:47 -07:00
|
|
|
}
|
2021-06-17 18:24:18 -07:00
|
|
|
assert!(!tree.append(&SipHashable(0)));
|
|
|
|
|
|
|
|
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-06-17 18:17:47 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(tree.root(), expected);
|
|
|
|
}
|
|
|
|
|
2022-02-20 10:40:12 -08:00
|
|
|
#[test]
|
|
|
|
fn root_hashes() {
|
|
|
|
crate::tests::check_root_hashes(|max_c| CompleteTree::<String>::new(4, max_c));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn auth_paths() {
|
|
|
|
crate::tests::check_auth_paths(|max_c| CompleteTree::<String>::new(4, max_c));
|
|
|
|
}
|
|
|
|
|
2021-06-17 18:17:47 -07:00
|
|
|
#[test]
|
|
|
|
fn correct_auth_path() {
|
|
|
|
const DEPTH: usize = 3;
|
2021-06-17 18:24:18 -07:00
|
|
|
let values = (0..(1 << DEPTH)).into_iter().map(SipHashable);
|
2021-06-17 18:17:47 -07:00
|
|
|
|
2021-06-17 18:24:18 -07:00
|
|
|
let mut tree = CompleteTree::<SipHashable>::new(DEPTH, 100);
|
|
|
|
for value in values {
|
|
|
|
assert!(tree.append(&value));
|
2021-06-17 18:17:47 -07:00
|
|
|
tree.witness();
|
|
|
|
}
|
2021-06-17 18:24:18 -07:00
|
|
|
assert!(!tree.append(&SipHashable(0)));
|
|
|
|
|
|
|
|
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-06-17 18:17:47 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(tree.root(), expected);
|
|
|
|
|
2022-02-22 20:34:38 -08:00
|
|
|
for i in 0u64..(1 << DEPTH) {
|
|
|
|
let position = Position::try_from(i).unwrap();
|
|
|
|
let path = tree.authentication_path(position, &SipHashable(i)).unwrap();
|
2021-06-17 18:17:47 -07:00
|
|
|
assert_eq!(
|
2021-06-17 18:24:18 -07:00
|
|
|
compute_root_from_auth_path(SipHashable(i), position, &path),
|
2021-06-17 18:17:47 -07:00
|
|
|
expected
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2022-02-17 13:46:27 -08:00
|
|
|
|
2022-02-20 10:40:12 -08:00
|
|
|
#[test]
|
|
|
|
fn checkpoint_rewind() {
|
|
|
|
crate::tests::check_checkpoint_rewind(|max_c| CompleteTree::<String>::new(4, max_c));
|
|
|
|
}
|
|
|
|
|
2022-02-17 13:46:27 -08:00
|
|
|
#[test]
|
|
|
|
fn rewind_remove_witness() {
|
2022-02-20 10:40:12 -08:00
|
|
|
crate::tests::check_rewind_remove_witness(|max_c| CompleteTree::<String>::new(4, max_c));
|
2022-02-17 13:46:27 -08:00
|
|
|
}
|
2021-06-17 18:17:47 -07:00
|
|
|
}
|