Skip to content

Commit 444c1cd

Browse files
committed
Add a test for states retained for hot diffs
1 parent 5d8ddb6 commit 444c1cd

File tree

1 file changed

+77
-1
lines changed

1 file changed

+77
-1
lines changed

beacon_node/store/src/hdiff.rs

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -731,7 +731,7 @@ impl HierarchyModuli {
731731
)
732732
}
733733

734-
/// For each layer, returns the closest diff less that or equal to `slot`.
734+
/// For each layer, returns the closest diff less than or equal to `slot`.
735735
pub fn closest_layer_points(&self, slot: Slot, start_slot: Slot) -> Vec<Slot> {
736736
self.moduli
737737
.iter()
@@ -991,4 +991,80 @@ mod tests {
991991
]
992992
);
993993
}
994+
995+
// Test that the diffs and snapshots required for storage of split states are retained in the
996+
// hot DB as the split slot advances, if we begin from an initial configuration where this
997+
// invariant holds.
998+
fn test_slots_retained_invariant(hierarchy: HierarchyModuli, start_slot: u64, epoch_jump: u64) {
999+
let start_slot = Slot::new(start_slot);
1000+
let mut finalized_slot = start_slot;
1001+
1002+
// Initially we have just one snapshot stored at the `start_slot`. This is what checkpoint
1003+
// sync sets up (or the V24 migration).
1004+
let mut retained_slots = vec![finalized_slot];
1005+
1006+
// Iterate until we've reached two snapshots in the future.
1007+
let stop_at = hierarchy
1008+
.next_snapshot_slot(hierarchy.next_snapshot_slot(start_slot).unwrap() + 1)
1009+
.unwrap();
1010+
1011+
while finalized_slot <= stop_at {
1012+
// Jump multiple epocsh at a time because inter-epoch states are not interesting and
1013+
// would take too long to iterate over.
1014+
let new_finalized_slot = finalized_slot + 32 * epoch_jump;
1015+
1016+
let new_retained_slots = hierarchy.closest_layer_points(new_finalized_slot, start_slot);
1017+
1018+
for slot in &new_retained_slots {
1019+
// All new retained slots must either be already stored prior to the old finalized
1020+
// slot, OR newer than the finalized slot (i.e. stored in the hot DB as part of
1021+
// regular state storage).
1022+
assert!(retained_slots.contains(slot) || *slot >= finalized_slot);
1023+
}
1024+
1025+
retained_slots = new_retained_slots;
1026+
finalized_slot = new_finalized_slot;
1027+
}
1028+
}
1029+
1030+
#[test]
1031+
fn slots_retained_invariant() {
1032+
let cases = [
1033+
// Default hierarchy with a start_slot between the 2^13 and 2^16 layers.
1034+
(
1035+
HierarchyConfig::default().to_moduli().unwrap(),
1036+
2 * (1 << 14) - 5 * 32,
1037+
1,
1038+
),
1039+
// Default hierarchy with a start_slot between the 2^13 and 2^16 layers, with 8 epochs
1040+
// finalizing at a time (should not make any difference).
1041+
(
1042+
HierarchyConfig::default().to_moduli().unwrap(),
1043+
2 * (1 << 14) - 5 * 32,
1044+
8,
1045+
),
1046+
// Very dense hierarchy config.
1047+
(
1048+
HierarchyConfig::from_str("5,7")
1049+
.unwrap()
1050+
.to_moduli()
1051+
.unwrap(),
1052+
32,
1053+
1,
1054+
),
1055+
// Very dense hierarchy config that skips a whole snapshot on its first finalization.
1056+
(
1057+
HierarchyConfig::from_str("5,7")
1058+
.unwrap()
1059+
.to_moduli()
1060+
.unwrap(),
1061+
32,
1062+
1 << 7,
1063+
),
1064+
];
1065+
1066+
for (hierarchy, start_slot, epoch_jump) in cases {
1067+
test_slots_retained_invariant(hierarchy, start_slot, epoch_jump);
1068+
}
1069+
}
9941070
}

0 commit comments

Comments
 (0)