Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions crates/blockchain/src/block_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -801,19 +801,17 @@ fn extend_proofs_greedily(
selected: &mut Vec<(AggregatedAttestation, SingleMessageAggregate)>,
att_data: &AttestationData,
) {
if proofs.is_empty() {
return;
}

let mut covered: HashSet<u64> = HashSet::new();
let mut remaining_indices: Vec<usize> = (0..proofs.len()).collect();

while !remaining_indices.is_empty() {
loop {
// Pick proof covering the most uncovered validators (count only, no
// allocation). Coverage ties break to the lowest index (pool insertion
// order): a HashSet here would let hash-iteration order pick an
// arbitrary equal-coverage winner, making the built block's
// aggregation bits differ from run to run.
// aggregation bits differ from run to run. The candidates must
// therefore stay in ascending order, which is why the winner leaves
// via `retain` below and not `swap_remove`.
let best = remaining_indices
.iter()
.map(|&idx| {
Expand All @@ -825,12 +823,10 @@ fn extend_proofs_greedily(
})
.max_by_key(|&(idx, count)| (count, Reverse(idx)));

let Some((best_idx, best_count)) = best else {
// Stops on an empty pool, and once no candidate adds coverage.
let Some((best_idx, _)) = best.filter(|&(_, count)| count > 0) else {
break;
};
if best_count == 0 {
break;
}

let proof = &proofs[best_idx];

Expand Down Expand Up @@ -1989,4 +1985,14 @@ mod tests {
let pool_order: Vec<Vec<u64>> = (0..6).map(|g| vec![g * 2, g * 2 + 1]).collect();
assert_eq!(order, pool_order);
}

/// An empty pool selects nothing: the candidate loop finds no best proof
/// and stops on its first round, so no `proofs.is_empty()` guard is needed
/// ahead of it.
#[test]
fn extend_proofs_greedily_selects_nothing_from_an_empty_pool() {
let mut selected = Vec::new();
extend_proofs_greedily(&[], &mut selected, &make_att_data(1));
assert!(selected.is_empty());
}
}
Loading