@@ -731,7 +731,7 @@ impl HierarchyModuli {
731
731
)
732
732
}
733
733
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`.
735
735
pub fn closest_layer_points ( & self , slot : Slot , start_slot : Slot ) -> Vec < Slot > {
736
736
self . moduli
737
737
. iter ( )
@@ -991,4 +991,80 @@ mod tests {
991
991
]
992
992
) ;
993
993
}
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
+ }
994
1070
}
0 commit comments