@@ -909,11 +909,11 @@ impl Store {
909909 . map_or ( finalized_slot, |header| {
910910 header. expect ( "Failed to get block header" ) . slot
911911 } ) ;
912- let pruned_signatures = self
912+ let pruned_below_slot = self
913913 . prune_old_block_signatures ( finalized_slot, tip_slot)
914914 . expect ( "prune old block signatures" ) ;
915- if pruned_signatures > 0 {
916- info ! ( pruned_signatures , "Pruned old finalized block signatures" ) ;
915+ if pruned_below_slot > 0 {
916+ info ! ( pruned_below_slot , "Pruned old finalized block signatures" ) ;
917917 }
918918 Ok ( ( ) )
919919 }
@@ -1078,41 +1078,37 @@ impl Store {
10781078 /// reverted, so their signatures are not needed for fork choice, re-org
10791079 /// safety, or re-aggregation once outside the window.
10801080 ///
1081- /// Returns the number of signatures pruned.
1081+ /// Returns the exclusive slot below which signatures were dropped, or 0 when
1082+ /// nothing was pruned. This is a range delete, so the count of removed keys
1083+ /// is not known without reading the table back.
10821084 pub fn prune_old_block_signatures (
10831085 & mut self ,
10841086 finalized_slot : u64 ,
10851087 tip_slot : u64 ,
1086- ) -> Result < usize , Error > {
1088+ ) -> Result < u64 , Error > {
10871089 let cutoff = tip_slot. saturating_sub ( SIGNATURE_PRUNING_RANGE ) ;
10881090 // Only prune when the whole window is finalized; never touch
1089- // non-finalized signatures.
1090- if cutoff > finalized_slot {
1091+ // non-finalized signatures. A zero cutoff covers nothing.
1092+ if cutoff > finalized_slot || cutoff == 0 {
10911093 return Ok ( 0 ) ;
10921094 }
10931095
1094- let view = self . backend . begin_read ( ) . expect ( "read view" ) ;
1095-
1096- // Keys are slot||root in big-endian slot order, so iteration ascends by
1097- // slot: take entries below the cutoff and stop at the first one past it.
1098- let keys_to_delete: Vec < Vec < u8 > > = view
1099- . prefix_iterator ( Table :: BlockSignatures , & [ ] )
1100- . expect ( "iterator" )
1101- . filter_map ( |res| res. ok ( ) )
1102- . map ( |( key, _) | key. to_vec ( ) )
1103- . take_while ( |key| decode_slot_root_key ( key) . 0 < cutoff)
1104- . collect ( ) ;
1105- drop ( view) ;
1096+ // Keys are slot||root in big-endian slot order, so the cutoff's bare
1097+ // slot prefix is an exact upper bound: keys below the cutoff sort
1098+ // before it, and keys at the cutoff sort after it (they extend it with
1099+ // a root). A single range delete drops them all without reading the
1100+ // table (and without walking the tombstones left by earlier prunes).
1101+ let mut batch = self . backend . begin_write ( ) . expect ( "write batch" ) ;
1102+ batch
1103+ . delete_range (
1104+ Table :: BlockSignatures ,
1105+ & 0u64 . to_be_bytes ( ) ,
1106+ & cutoff. to_be_bytes ( ) ,
1107+ )
1108+ . expect ( "delete finalized block signatures" ) ;
1109+ batch. commit ( ) . expect ( "commit" ) ;
11061110
1107- let count = keys_to_delete. len ( ) ;
1108- if count > 0 {
1109- let mut batch = self . backend . begin_write ( ) . expect ( "write batch" ) ;
1110- batch
1111- . delete_batch ( Table :: BlockSignatures , keys_to_delete)
1112- . expect ( "delete finalized block signatures" ) ;
1113- batch. commit ( ) . expect ( "commit" ) ;
1114- }
1115- Ok ( count)
1111+ Ok ( cutoff)
11161112 }
11171113
11181114 /// Get the block header by root.
@@ -1930,12 +1926,12 @@ mod tests {
19301926 // tip = range + 10, finalized = range + 5, so cutoff = tip - range = 10.
19311927 let tip_slot = SIGNATURE_PRUNING_RANGE + 10 ;
19321928 let finalized_slot = SIGNATURE_PRUNING_RANGE + 5 ;
1933- let pruned = store
1929+ let pruned_below_slot = store
19341930 . prune_old_block_signatures ( finalized_slot, tip_slot)
19351931 . expect ( "prune" ) ;
19361932
19371933 // cutoff = 10: slots 0..9 pruned, slots 10..12 kept (within the window).
1938- assert_eq ! ( pruned , 10 ) ;
1934+ assert_eq ! ( pruned_below_slot , 10 ) ;
19391935 assert_eq ! ( count_entries( backend. as_ref( ) , Table :: BlockSignatures ) , 3 ) ;
19401936
19411937 // Oldest signatures are gone, but headers, bodies, and roots stay queryable.
@@ -1965,10 +1961,10 @@ mod tests {
19651961 // cutoff = tip - range > finalized → prune nothing.
19661962 let tip_slot = SIGNATURE_PRUNING_RANGE + 100 ;
19671963 let finalized_slot = 5 ;
1968- let pruned = store
1964+ let pruned_below_slot = store
19691965 . prune_old_block_signatures ( finalized_slot, tip_slot)
19701966 . expect ( "prune" ) ;
1971- assert_eq ! ( pruned , 0 ) ;
1967+ assert_eq ! ( pruned_below_slot , 0 ) ;
19721968 assert_eq ! ( count_entries( backend. as_ref( ) , Table :: BlockSignatures ) , 10 ) ;
19731969 }
19741970
@@ -1983,8 +1979,8 @@ mod tests {
19831979
19841980 // Early chain: tip < SIGNATURE_PRUNING_RANGE → cutoff saturates to 0,
19851981 // so nothing is old enough to prune even though slots are finalized.
1986- let pruned = store. prune_old_block_signatures ( 9 , 9 ) . expect ( "prune" ) ;
1987- assert_eq ! ( pruned , 0 ) ;
1982+ let pruned_below_slot = store. prune_old_block_signatures ( 9 , 9 ) . expect ( "prune" ) ;
1983+ assert_eq ! ( pruned_below_slot , 0 ) ;
19881984 assert_eq ! ( count_entries( backend. as_ref( ) , Table :: BlockSignatures ) , 10 ) ;
19891985 }
19901986
0 commit comments