Skip to content

Commit c48fb77

Browse files
committed
Fix marshal mailbox overflow coalescing
1 parent af9fe6d commit c48fb77

1 file changed

Lines changed: 91 additions & 15 deletions

File tree

consensus/src/marshal/core/mailbox.rs

Lines changed: 91 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)