Skip to content

Commit

Permalink
Merge pull request #72 from nuttycom/no_initial_checkpoint
Browse files Browse the repository at this point in the history
Remove initial checkpoints.
  • Loading branch information
nuttycom authored May 24, 2023
2 parents a26844a + 2b8e2d6 commit beb6ead
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 194 deletions.
63 changes: 24 additions & 39 deletions bridgetree/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -470,13 +470,13 @@ impl<H, C, const DEPTH: u8> BridgeTree<H, C, DEPTH> {
///
/// Panics if `max_checkpoints < 1` because mark/rewind logic depends upon the presence
/// of checkpoints to function.
pub fn new(max_checkpoints: usize, initial_checkpoint_id: C) -> Self {
pub fn new(max_checkpoints: usize) -> Self {
assert!(max_checkpoints >= 1);
Self {
prior_bridges: vec![],
current_bridge: None,
saved: BTreeMap::new(),
checkpoints: VecDeque::from(vec![Checkpoint::at_length(0, initial_checkpoint_id)]),
checkpoints: VecDeque::new(),
max_checkpoints,
}
}
Expand Down Expand Up @@ -535,12 +535,8 @@ impl<H, C, const DEPTH: u8> BridgeTree<H, C, DEPTH> {
impl<H: Hashable + Clone + Ord, C: Clone + Ord, const DEPTH: u8> BridgeTree<H, C, DEPTH> {
/// Construct a new BridgeTree that will start recording changes from the state of
/// the specified frontier.
pub fn from_frontier(
max_checkpoints: usize,
frontier: NonEmptyFrontier<H>,
checkpoint_id: C,
) -> Self {
let mut bridge = Self {
pub fn from_frontier(max_checkpoints: usize, frontier: NonEmptyFrontier<H>) -> Self {
Self {
prior_bridges: vec![],
current_bridge: Some(MerkleBridge::from_parts(
None,
Expand All @@ -551,9 +547,7 @@ impl<H: Hashable + Clone + Ord, C: Clone + Ord, const DEPTH: u8> BridgeTree<H, C
saved: BTreeMap::new(),
checkpoints: VecDeque::new(),
max_checkpoints,
};
bridge.checkpoint(checkpoint_id);
bridge
}
}

/// Construct a new BridgeTree from its constituent parts, checking for internal
Expand Down Expand Up @@ -717,11 +711,9 @@ impl<H: Hashable + Clone + Ord, C: Clone + Ord, const DEPTH: u8> BridgeTree<H, C

// mark the position as having been marked in the current checkpoint
if let std::collections::btree_map::Entry::Vacant(e) = self.saved.entry(pos) {
let c = self
.checkpoints
.back_mut()
.expect("Checkpoints deque must never be empty");
c.marked.insert(pos);
if let Some(c) = self.checkpoints.back_mut() {
c.marked.insert(pos);
}
e.insert(self.prior_bridges.len() - 1);
}

Expand Down Expand Up @@ -749,11 +741,11 @@ impl<H: Hashable + Clone + Ord, C: Clone + Ord, const DEPTH: u8> BridgeTree<H, C
/// false if we were already not maintaining a mark at this position.
pub fn remove_mark(&mut self, position: Position) -> bool {
if self.saved.contains_key(&position) {
let c = self
.checkpoints
.back_mut()
.expect("Checkpoints deque must never be empty.");
c.forgotten.insert(position);
if let Some(c) = self.checkpoints.back_mut() {
c.forgotten.insert(position);
} else {
self.saved.remove(&position);
}
true
} else {
false
Expand Down Expand Up @@ -805,12 +797,7 @@ impl<H: Hashable + Clone + Ord, C: Clone + Ord, const DEPTH: u8> BridgeTree<H, C
/// at that tree state have been removed using `rewind`. This function
/// return false and leave the tree unmodified if no checkpoints exist.
pub fn rewind(&mut self) -> bool {
if self.checkpoints.len() > 1 {
let c = self
.checkpoints
.pop_back()
.expect("Checkpoints deque is known to be non-empty.");

if let Some(c) = self.checkpoints.pop_back() {
// Remove marks for positions that were marked during the lifetime of this checkpoint.
for pos in c.marked {
self.saved.remove(&pos);
Expand Down Expand Up @@ -1063,7 +1050,7 @@ mod tests {

#[test]
fn tree_depth() {
let mut tree = BridgeTree::<String, usize, 3>::new(100, 0);
let mut tree = BridgeTree::<String, usize, 3>::new(100);
for c in 'a'..'i' {
assert!(tree.append(c.to_string()))
}
Expand Down Expand Up @@ -1095,7 +1082,7 @@ mod tests {
{
let pos_gen = (0..max_count).prop_map(|p| Position::try_from(p).unwrap());
proptest::collection::vec(arb_operation(item_gen, pos_gen), 0..max_count).prop_map(|ops| {
let mut tree: BridgeTree<G::Value, usize, 8> = BridgeTree::new(10, 0);
let mut tree: BridgeTree<G::Value, usize, 8> = BridgeTree::new(10);
for (i, op) in ops.into_iter().enumerate() {
apply_operation(&mut tree, op.map_checkpoint_id(|_| i));
}
Expand Down Expand Up @@ -1130,33 +1117,31 @@ mod tests {

#[test]
fn root_hashes() {
check_root_hashes(|max_checkpoints| {
BridgeTree::<String, usize, 4>::new(max_checkpoints, 0)
});
check_root_hashes(BridgeTree::<String, usize, 4>::new);
}

#[test]
fn witness() {
check_witnesses(|max_checkpoints| BridgeTree::<String, usize, 4>::new(max_checkpoints, 0));
check_witnesses(BridgeTree::<String, usize, 4>::new);
}

#[test]
fn checkpoint_rewind() {
check_checkpoint_rewind(|max_checkpoints| {
BridgeTree::<String, usize, 4>::new(max_checkpoints, 0)
BridgeTree::<String, usize, 4>::new(max_checkpoints)
});
}

#[test]
fn rewind_remove_mark() {
check_rewind_remove_mark(|max_checkpoints| {
BridgeTree::<String, usize, 4>::new(max_checkpoints, 0)
BridgeTree::<String, usize, 4>::new(max_checkpoints)
});
}

#[test]
fn garbage_collect() {
let mut tree: BridgeTree<String, usize, 7> = BridgeTree::new(1000, 0);
let mut tree: BridgeTree<String, usize, 7> = BridgeTree::new(1000);
let empty_root = tree.root(0);
tree.append("a".to_string());
for i in 0..100 {
Expand All @@ -1167,7 +1152,7 @@ mod tests {
tree.rewind();
assert!(tree.root(0) != empty_root);

let mut t = BridgeTree::<String, usize, 7>::new(10, 0);
let mut t = BridgeTree::<String, usize, 7>::new(10);
let mut to_unmark = vec![];
let mut has_witness = vec![];
for i in 0u64..100 {
Expand Down Expand Up @@ -1213,8 +1198,8 @@ mod tests {
max_checkpoints: usize,
) -> CombinedTree<H, usize, CompleteTree<H, usize, 4>, BridgeTree<H, usize, 4>> {
CombinedTree::new(
CompleteTree::<H, usize, 4>::new(max_checkpoints, 0),
BridgeTree::<H, usize, 4>::new(max_checkpoints, 0),
CompleteTree::<H, usize, 4>::new(max_checkpoints),
BridgeTree::<H, usize, 4>::new(max_checkpoints),
)
}

Expand Down
66 changes: 27 additions & 39 deletions incrementalmerkletree/src/testing/complete_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ pub struct CompleteTree<H, C: Ord, const DEPTH: u8> {

impl<H: Hashable, C: Clone + Ord + core::fmt::Debug, const DEPTH: u8> CompleteTree<H, C, DEPTH> {
/// Creates a new, empty binary tree
pub fn new(max_checkpoints: usize, initial_checkpoint_id: C) -> Self {
pub fn new(max_checkpoints: usize) -> Self {
Self {
leaves: vec![],
marks: BTreeSet::new(),
checkpoints: BTreeMap::from([(initial_checkpoint_id, Checkpoint::at_length(0))]),
checkpoints: BTreeMap::new(),
max_checkpoints,
}
}
Expand Down Expand Up @@ -140,22 +140,18 @@ impl<H: Hashable, C: Clone + Ord + core::fmt::Debug, const DEPTH: u8> CompleteTr
/// Marks the current tree state leaf as a value that we're interested in
/// marking. Returns the current position if the tree is non-empty.
fn mark(&mut self) -> Option<Position> {
match self.current_position() {
Some(pos) => {
if !self.marks.contains(&pos) {
self.marks.insert(pos);
self.checkpoints
.iter_mut()
.rev()
.next()
.unwrap()
.1
.marked
.insert(pos);
if let Some(pos) = self.current_position() {
if !self.marks.contains(&pos) {
self.marks.insert(pos);

if let Some(checkpoint) = self.checkpoints.values_mut().rev().next() {
checkpoint.marked.insert(pos);
}
Some(pos)
}
None => None,

Some(pos)
} else {
None
}
}

Expand Down Expand Up @@ -282,22 +278,19 @@ impl<H: Hashable + PartialEq + Clone, C: Ord + Clone + core::fmt::Debug, const D

fn remove_mark(&mut self, position: Position) -> bool {
if self.marks.contains(&position) {
self.checkpoints
.iter_mut()
.rev()
.next()
.unwrap()
.1
.forgotten
.insert(position);
if let Some(c) = self.checkpoints.values_mut().rev().next() {
c.forgotten.insert(position);
} else {
self.marks.remove(&position);
}
true
} else {
false
}
}

fn checkpoint(&mut self, id: C) -> bool {
if Some(&id) > self.checkpoints.iter().rev().next().map(|(id, _)| id) {
if Some(&id) > self.checkpoints.keys().rev().next() {
Self::checkpoint(self, id, self.current_position());
true
} else {
Expand All @@ -306,8 +299,7 @@ impl<H: Hashable + PartialEq + Clone, C: Ord + Clone + core::fmt::Debug, const D
}

fn rewind(&mut self) -> bool {
if self.checkpoints.len() > 1 {
let (id, c) = self.checkpoints.iter().rev().next().unwrap();
if let Some((id, c)) = self.checkpoints.iter().rev().next() {
self.leaves.truncate(c.leaves_len);
for pos in c.marked.iter() {
self.marks.remove(pos);
Expand Down Expand Up @@ -342,7 +334,7 @@ mod tests {
expected = SipHashable::combine(lvl.into(), &expected, &expected);
}

let tree = CompleteTree::<SipHashable, (), DEPTH>::new(100, ());
let tree = CompleteTree::<SipHashable, (), DEPTH>::new(100);
assert_eq!(tree.root(0).unwrap(), expected);
}

Expand All @@ -351,7 +343,7 @@ mod tests {
const DEPTH: u8 = 3;
let values = (0..(1 << DEPTH)).map(SipHashable);

let mut tree = CompleteTree::<SipHashable, (), DEPTH>::new(100, ());
let mut tree = CompleteTree::<SipHashable, (), DEPTH>::new(100);
for value in values {
assert!(tree.append(value, Retention::Ephemeral).is_ok());
}
Expand All @@ -376,21 +368,17 @@ mod tests {

#[test]
fn append() {
check_append(|max_checkpoints| CompleteTree::<String, usize, 4>::new(max_checkpoints, 0));
check_append(CompleteTree::<String, usize, 4>::new);
}

#[test]
fn root_hashes() {
check_root_hashes(|max_checkpoints| {
CompleteTree::<String, usize, 4>::new(max_checkpoints, 0)
});
check_root_hashes(CompleteTree::<String, usize, 4>::new);
}

#[test]
fn witnesses() {
check_witnesses(|max_checkpoints| {
CompleteTree::<String, usize, 4>::new(max_checkpoints, 0)
});
check_witnesses(CompleteTree::<String, usize, 4>::new);
}

#[test]
Expand All @@ -400,7 +388,7 @@ mod tests {
const DEPTH: u8 = 3;
let values = (0..(1 << DEPTH)).map(SipHashable);

let mut tree = CompleteTree::<SipHashable, (), DEPTH>::new(100, ());
let mut tree = CompleteTree::<SipHashable, (), DEPTH>::new(100);
for value in values {
assert!(Tree::append(&mut tree, value, Retention::Marked));
}
Expand Down Expand Up @@ -435,14 +423,14 @@ mod tests {
#[test]
fn checkpoint_rewind() {
check_checkpoint_rewind(|max_checkpoints| {
CompleteTree::<String, usize, 4>::new(max_checkpoints, 0)
CompleteTree::<String, usize, 4>::new(max_checkpoints)
});
}

#[test]
fn rewind_remove_mark() {
check_rewind_remove_mark(|max_checkpoints| {
CompleteTree::<String, usize, 4>::new(max_checkpoints, 0)
CompleteTree::<String, usize, 4>::new(max_checkpoints)
});
}
}
4 changes: 4 additions & 0 deletions shardtree/proptest-regressions/lib.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ cc cf1a33ef6df58bbf7cc199b8b1879c3a078e7784aa3edc0aba9ca03772bea5f2 # shrinks to
cc 544e027d994eaf7f97b1c8d9ee7b35522a64a610b1430d56d74ec947018b759d # shrinks to ops = [Append("a", Marked), Append("a", Ephemeral), Append("a", Ephemeral), Append("a", Ephemeral), Append("a", Ephemeral), Append("a", Ephemeral), Append("a", Ephemeral), Checkpoint(()), Append("a", Marked), Checkpoint(()), Checkpoint(()), Append("a", Checkpoint { id: (), is_marked: false }), Rewind, Rewind, Witness(Position(7), 2)]
cc 55d00b68a0f0a02f83ab53f18a29d16d0233153b69a01414a1622104e0eead31 # shrinks to ops = [Append("a", Marked), Append("a", Checkpoint { id: (), is_marked: false }), Append("a", Marked), Checkpoint(()), Checkpoint(()), Checkpoint(()), Append("a", Checkpoint { id: (), is_marked: false }), Append("a", Checkpoint { id: (), is_marked: false }), Witness(Position(0), 7)]
cc 9dd966ff1ab66965c5b84153ae13f684258560cdd5e84c7deb24f724cb12aba7 # shrinks to ops = [Append("a", Marked), Append("a", Ephemeral), Append("a", Checkpoint { id: (), is_marked: true }), Checkpoint(()), Append("a", Checkpoint { id: (), is_marked: false }), Rewind, Rewind, Append("a", Checkpoint { id: (), is_marked: false }), Append("a", Checkpoint { id: (), is_marked: false }), Checkpoint(()), Witness(Position(2), 4)]
cc d53a73021238de143764ee1d48b944abb93bd4bc54f35d16e514261220d3eb78 # shrinks to ops = [Append(SipHashable(0), Marked), Unmark(Position(0))]
cc d9460b8acbc5b4d112cae5d9e2296fcd793999b2b2e1d5405722f2bd8d176c31 # shrinks to ops = [Append("a", Checkpoint { id: (), is_marked: true }), Rewind, Append("a", Ephemeral), Rewind, Unmark(Position(0))]
cc 644c7763bc7bdc65bd9e6eb156b3b1a9b0632571a571c462bd44f3e04a389ca0 # shrinks to ops = [Append("a", Ephemeral), Append("a", Checkpoint { id: (), is_marked: true }), Append("a", Ephemeral), Append("a", Ephemeral), Unmark(Position(1)), Witness(Position(1), 0)]
cc 12790169d3df4280dd155d9cdfa76719318b8ec97a80bd562b7cb182d4f9bc79 # shrinks to ops = [CurrentPosition, CurrentPosition, Append(SipHashable(0), Ephemeral), Append(SipHashable(0), Marked), Append(SipHashable(0), Ephemeral), Checkpoint(()), Checkpoint(()), Checkpoint(()), Unmark(Position(1)), Checkpoint(()), Witness(Position(1), 0)]
Loading

0 comments on commit beb6ead

Please sign in to comment.