Skip to content

Commit 34b1a5f

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

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,10 @@ 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 =
470+
checkpoints_to_delete.len() < remove_count && !self.to_retain.contains(cid);
459471

460472
if removing {
461473
checkpoints_to_delete.push(cid.clone());
@@ -1355,6 +1367,42 @@ mod tests {
13551367
),
13561368
Ok(()),
13571369
);
1370+
1371+
// Append a leaf we want to retain
1372+
assert_eq!(tree.append('e'.to_string(), Retention::Marked), Ok(()),);
1373+
1374+
// Now a few more leaves and then checkpoint
1375+
for c in 'f'..='i' {
1376+
tree.append(c.to_string(), Retention::Ephemeral).unwrap();
1377+
}
1378+
1379+
// Checkpoint the tree. We'll want to retain this checkpoint.
1380+
assert_eq!(tree.checkpoint(12), Ok(true));
1381+
tree.ensure_retained(12);
1382+
1383+
// Simulate adding yet another block
1384+
for c in 'j'..='m' {
1385+
tree.append(c.to_string(), Retention::Ephemeral).unwrap();
1386+
}
1387+
1388+
assert_eq!(tree.checkpoint(13), Ok(true));
1389+
1390+
// Witness `e` as of checkpoint 12
1391+
let e_witness_12 = tree
1392+
.witness_at_checkpoint_id(Position::from(4), &12)
1393+
.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)