Skip to content

Commit f4d43b5

Browse files
committed
shardtree: Add the ability to avoid pruning specific checkpoints.
1 parent 4d797cc commit f4d43b5

File tree

1 file changed

+52
-4
lines changed

1 file changed

+52
-4
lines changed

shardtree/src/lib.rs

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ pub struct ShardTree<S: ShardStore, const DEPTH: u8, const SHARD_HEIGHT: u8> {
6868
store: S,
6969
/// The maximum number of checkpoints to retain before pruning.
7070
max_checkpoints: usize,
71+
/// The set of checkpoints to be explicitly retained in pruning operations.
72+
to_retain: BTreeSet<S::CheckpointId>
7173
}
7274

7375
impl<
@@ -83,6 +85,7 @@ impl<
8385
Self {
8486
store,
8587
max_checkpoints,
88+
to_retain: BTreeSet::new()
8689
}
8790
}
8891

@@ -111,6 +114,12 @@ impl<
111114
(0x1 << (DEPTH - SHARD_HEIGHT)) - 1
112115
}
113116

117+
/// Adds the provided checkpoint to the set of checkpoints to be retained
118+
/// across pruning operations.
119+
pub fn ensure_retained(&mut self, checkpoint_id: C) {
120+
self.to_retain.insert(checkpoint_id);
121+
}
122+
114123
/// Returns the leaf value at the specified position, if it is a marked leaf.
115124
pub fn get_marked_leaf(
116125
&self,
@@ -442,10 +451,11 @@ impl<
442451
checkpoint_count,
443452
self.max_checkpoints,
444453
);
445-
if checkpoint_count > self.max_checkpoints {
454+
let retain_count = self.max_checkpoints + self.to_retain.len();
455+
if checkpoint_count > retain_count {
446456
// Batch removals by subtree & create a list of the checkpoint identifiers that
447457
// will be removed from the checkpoints map.
448-
let remove_count = checkpoint_count - self.max_checkpoints;
458+
let remove_count = checkpoint_count - retain_count;
449459
let mut checkpoints_to_delete = vec![];
450460
let mut clear_positions: BTreeMap<Address, BTreeMap<Position, RetentionFlags>> =
451461
BTreeMap::new();
@@ -454,8 +464,9 @@ impl<
454464
// When removing is true, we are iterating through the range of
455465
// checkpoints being removed. When remove is false, we are
456466
// iterating through the range of checkpoints that are being
457-
// retained.
458-
let removing = checkpoints_to_delete.len() < remove_count;
467+
// retained, or skipping over a particular checkpoint that we
468+
// have been explicitly asked to retain.
469+
let removing = checkpoints_to_delete.len() < remove_count && !self.to_retain.contains(cid);
459470

460471
if removing {
461472
checkpoints_to_delete.push(cid.clone());
@@ -1355,6 +1366,43 @@ mod tests {
13551366
),
13561367
Ok(()),
13571368
);
1369+
1370+
// Append a leaf we want to retain
1371+
assert_eq!(
1372+
tree.append('e'.to_string(), Retention::Marked),
1373+
Ok(()),
1374+
);
1375+
1376+
// Now a few more leaves and then checkpoint
1377+
for c in 'f'..='i' {
1378+
tree.append(c.to_string(), Retention::Ephemeral).unwrap();
1379+
}
1380+
1381+
// Checkpoint the tree. We'll want to retain this checkpoint.
1382+
assert_eq!(tree.checkpoint(12), Ok(true));
1383+
tree.ensure_retained(12);
1384+
1385+
// Simulate adding yet another block
1386+
for c in 'j'..='m' {
1387+
tree.append(c.to_string(), Retention::Ephemeral).unwrap();
1388+
}
1389+
1390+
assert_eq!(tree.checkpoint(13), Ok(true));
1391+
1392+
// Witness `e` as of checkpoint 12
1393+
let e_witness_12 = tree.witness_at_checkpoint_id(Position::from(4), &12).unwrap();
1394+
1395+
// Now add some more checkpoints, which would ordinarily cause checkpoint 12
1396+
// to be pruned (but will not, because we explicitly retained it.)
1397+
for i in 14..24 {
1398+
assert_eq!(tree.checkpoint(i), Ok(true));
1399+
}
1400+
1401+
// Verify that we can still compute the same root
1402+
assert_matches!(
1403+
tree.witness_at_checkpoint_id(Position::from(4), &12),
1404+
Ok(w) if w == e_witness_12
1405+
);
13581406
}
13591407

13601408
// Combined tree tests

0 commit comments

Comments
 (0)