@@ -187,8 +187,8 @@ impl<S: Scheme, V: Variant> Message<S, V> {
187187 ..
188188 }
189189 | Self :: GetFinalization { height, .. } => Some ( * height) < current,
190- // Hints only inform the actor about heights strictly above the floor
191- Self :: HintFinalized { height, .. } => Some ( * height) <= current,
190+ // Hints can fetch the current floor if the block is missing locally.
191+ Self :: HintFinalized { height, .. } => Some ( * height) < current,
192192 // Durability acks cannot be dropped: callers depend on them
193193 Self :: Proposed { .. } | Self :: Verified { .. } | Self :: Certified { .. } => false ,
194194 // Digest and latest lookups are not bound to a specific height
@@ -270,7 +270,7 @@ impl<S: Scheme, V: Variant> Pending<S, V> {
270270
271271 fn retain ( & mut self ) {
272272 let current = self . height ( ) ;
273- self . hints . retain ( |height, _| Some ( * height) > current) ;
273+ self . hints . retain ( |height, _| Some ( * height) >= current) ;
274274
275275 let hints = & self . hints ;
276276 self . messages . retain ( |message| match message {
@@ -283,13 +283,23 @@ impl<S: Scheme, V: Variant> Pending<S, V> {
283283
284284 fn set_floor ( & mut self , height : Height , prune_archives : bool ) {
285285 let current = self . height ( ) ;
286- match & mut self . floor {
287- Some ( ( pending, pending_prune) ) if * pending > height => return ,
288- Some ( ( pending, pending_prune) ) if * pending == height => {
289- * pending_prune |= prune_archives;
286+ match self . floor . take ( ) {
287+ Some ( ( pending, pending_prune) ) if pending > height => {
288+ self . floor = Some ( ( pending, pending_prune) ) ;
289+ return ;
290+ }
291+ Some ( ( pending, pending_prune) ) if pending == height => {
292+ self . floor = Some ( ( height, pending_prune || prune_archives) ) ;
293+ }
294+ Some ( ( pending, true ) ) if !prune_archives => {
295+ self . prune = self . prune . max ( Some ( pending) ) ;
296+ self . floor = Some ( ( height, false ) ) ;
297+ }
298+ Some ( _) => {
299+ self . floor = Some ( ( height, prune_archives) ) ;
290300 }
291- floor => {
292- * floor = Some ( ( height, prune_archives) ) ;
301+ None => {
302+ self . floor = Some ( ( height, prune_archives) ) ;
293303 }
294304 }
295305
@@ -352,9 +362,9 @@ impl<S: Scheme, V: Variant> Pending<S, V> {
352362 }
353363
354364 fn hint_finalized ( & mut self , height : Height , targets : Recipients < S :: PublicKey > ) {
355- // Hint is already covered by floor/prune
365+ // Hints at the current floor may still need to fetch a missing block.
356366 let current = self . height ( ) ;
357- if current. is_some_and ( |current| height <= current) {
367+ if current. is_some_and ( |current| height < current) {
358368 return ;
359369 }
360370
@@ -890,9 +900,13 @@ mod tests {
890900 }
891901
892902 fn set_floor ( height : u64 ) -> TestMessage {
903+ set_floor_prune ( height, false )
904+ }
905+
906+ fn set_floor_prune ( height : u64 , prune_archives : bool ) -> TestMessage {
893907 TestMessage :: SetFloor {
894908 height : Height :: new ( height) ,
895- prune_archives : false ,
909+ prune_archives,
896910 }
897911 }
898912
@@ -1172,6 +1186,64 @@ mod tests {
11721186 ) ) ;
11731187 }
11741188
1189+ #[ test]
1190+ fn policy_retains_floor_height_hints ( ) {
1191+ let mut overflow = pending ( ) ;
1192+ let target = public_key ( 1 ) ;
1193+
1194+ <TestMessage as Policy >:: handle ( & mut overflow, set_floor ( 8 ) ) ;
1195+ <TestMessage as Policy >:: handle ( & mut overflow, hint_finalized ( 7 , public_key ( 2 ) ) ) ;
1196+ <TestMessage as Policy >:: handle ( & mut overflow, hint_finalized ( 8 , target. clone ( ) ) ) ;
1197+ <TestMessage as Policy >:: handle ( & mut overflow, hint_finalized ( 9 , public_key ( 3 ) ) ) ;
1198+
1199+ assert ! ( hint_targets( & overflow, 7 ) . is_none( ) ) ;
1200+ assert ! ( matches!(
1201+ hint_targets( & overflow, 8 ) ,
1202+ Some ( Recipients :: One ( found) ) if * found == target
1203+ ) ) ;
1204+ assert ! ( hint_targets( & overflow, 9 ) . is_some( ) ) ;
1205+
1206+ let drained = drain ( & mut overflow) ;
1207+ assert_eq ! ( drained. len( ) , 3 ) ;
1208+ assert ! ( matches!(
1209+ & drained[ 0 ] ,
1210+ TestMessage :: SetFloor { height, .. } if * height == Height :: new( 8 )
1211+ ) ) ;
1212+ assert ! ( matches!(
1213+ & drained[ 1 ] ,
1214+ TestMessage :: HintFinalized { height, .. } if * height == Height :: new( 8 )
1215+ ) ) ;
1216+ assert ! ( matches!(
1217+ & drained[ 2 ] ,
1218+ TestMessage :: HintFinalized { height, .. } if * height == Height :: new( 9 )
1219+ ) ) ;
1220+ }
1221+
1222+ #[ test]
1223+ fn policy_preserves_floor_prune_when_higher_floor_supersedes_without_over_pruning ( ) {
1224+ let mut overflow = pending ( ) ;
1225+
1226+ <TestMessage as Policy >:: handle ( & mut overflow, set_floor_prune ( 10 , true ) ) ;
1227+ <TestMessage as Policy >:: handle ( & mut overflow, set_floor_prune ( 15 , false ) ) ;
1228+
1229+ assert_eq ! ( overflow. floor, Some ( ( Height :: new( 15 ) , false ) ) ) ;
1230+ assert_eq ! ( overflow. prune, Some ( Height :: new( 10 ) ) ) ;
1231+
1232+ let drained = drain ( & mut overflow) ;
1233+ assert_eq ! ( drained. len( ) , 2 ) ;
1234+ assert ! ( matches!(
1235+ & drained[ 0 ] ,
1236+ TestMessage :: SetFloor {
1237+ height,
1238+ prune_archives: false ,
1239+ } if * height == Height :: new( 15 )
1240+ ) ) ;
1241+ assert ! ( matches!(
1242+ & drained[ 1 ] ,
1243+ TestMessage :: Prune { height } if * height == Height :: new( 10 )
1244+ ) ) ;
1245+ }
1246+
11751247 #[ test]
11761248 fn policy_replaces_floor_and_prune_and_drops_stale_pending_on_drain ( ) {
11771249 let mut overflow = pending ( ) ;
@@ -1192,19 +1264,23 @@ mod tests {
11921264 . push_back ( PendingMessage :: Message ( get_block_8) ) ;
11931265 <TestMessage as Policy >:: handle ( & mut overflow, set_floor ( 8 ) ) ;
11941266 assert_eq ! ( overflow. floor, Some ( ( Height :: new( 8 ) , false ) ) ) ;
1195- assert_eq ! ( overflow. messages. len( ) , 1 ) ;
1267+ assert_eq ! ( overflow. messages. len( ) , 2 ) ;
11961268 assert ! ( !has_get_info( & overflow, 4 ) ) ;
11971269 assert ! ( !has_get_block( & overflow, 7 ) ) ;
11981270 assert ! ( has_get_block( & overflow, 8 ) ) ;
1199- assert ! ( hint_targets( & overflow, 8 ) . is_none ( ) ) ;
1271+ assert ! ( hint_targets( & overflow, 8 ) . is_some ( ) ) ;
12001272 let drained = drain ( & mut overflow) ;
1201- assert_eq ! ( drained. len( ) , 2 ) ;
1273+ assert_eq ! ( drained. len( ) , 3 ) ;
12021274 assert ! ( matches!(
12031275 & drained[ 0 ] ,
12041276 TestMessage :: SetFloor { height, .. } if * height == Height :: new( 8 )
12051277 ) ) ;
12061278 assert ! ( matches!(
12071279 & drained[ 1 ] ,
1280+ TestMessage :: HintFinalized { height, .. } if * height == Height :: new( 8 )
1281+ ) ) ;
1282+ assert ! ( matches!(
1283+ & drained[ 2 ] ,
12081284 TestMessage :: GetBlock {
12091285 identifier: Identifier :: Height ( height) ,
12101286 ..
0 commit comments