Commit db05d78
fix: tiered attestation scoring for block building (lambdaclass#382)
## Description / Motivation
Replaces the `target.slot` ASC sort + STF-advance fixed point in
`build_block` with a tiered greedy modeled on Prysm's
`sortByProfitability`. The previous loop committed attestations in
arbitrary chain order and re-ran the STF after each batch to detect
justification advance, which:
- Doesn't prioritize attestations that finalize or justify under the
`MAX_ATTESTATIONS_DATA = 16` cap.
- Pays for a full STF execution every outer iteration.
- Has no signal for "this entry adds marginal voters worth packing" —
it's all-or-nothing per data entry.
The new selection algorithm scores each candidate against a projected
post-state and picks the highest-tier entry per round.
## Scoring tiers
| Tier | Predicate | Notes |
|---|---|---|
| 1 | applying the entry finalizes its source | 3SF-mini: no slot in
`(source.slot, target.slot)` is still justifiable per
`slot_is_justifiable_after`, so source and target are consecutive
justified checkpoints in the projected post-state |
| 2 | applying the entry justifies its target | total voters (prior +
new) crosses ⅔ |
| 3 | adds marginal new voters | not enough to cross ⅔, but contributes
toward future rounds |
Within a tier: more `new_voters` wins, then smaller `target_slot` (older
chain progress first), then smaller `att_slot`, then `data_root` for
determinism.
## What Changed
Three commits on this branch:
| Commit | Subject |
|---|---|
| `6336ce7` | feat(blockchain): tiered attestation scoring for block
building |
| `5ccc938` | refactor(blockchain): simplify attestation scoring helpers
|
| `d59d41b` | refactor(blockchain): extract block_builder module |
Key code:
- New module `crates/blockchain/src/block_builder.rs` containing
`build_block`, `select_attestations`, `pick_best_candidate`,
`score_entry`, `entry_passes_filters`, the `EntryScore` / `ChainContext`
/ `ProjectedState` types, plus `compact_attestations` and
`extend_proofs_greedily` (moved out of `store.rs`).
- `build_running_votes(state)` deserializes the flattened
`state.justifications_validators` bitlist into `HashMap<H256,
HashSet<u64>>` — the analog of Prysm/Lighthouse's participation flags.
Selection seeds from this and updates it incrementally.
- Projection of `justified_slots` and `finalized_slot` happens in-loop
after each tier 1/2 selection so dependent attestations (whose source is
the just-justified slot) become eligible on the next round without
running the STF.
- The final STF still runs once on the assembled block to seal
`state_root` and validate that the proposed block is sound.
## Correctness / Behavior Guarantees
- **Final STF unchanged**: `process_slots` + `process_block` still run
on the selected attestations before `state_root` is set. Any divergence
between projection and the actual STF produces an error here.
- **Filter invariants preserved**: `entry_passes_filters` enforces the
same predicates the previous loop did (head known, source justified,
chain-match, target not already justified) plus the genesis self-vote
bypass.
- **3SF-mini finalize predicate**: tier 1 elevation requires
`(source.slot + 1..target.slot).all(|s| !slot_is_justifiable_after(s,
projected_finalized_slot))` — matches `try_finalize` exactly, including
the implicit "source and target must be consecutive justified
checkpoints" constraint (any already-justified slot between them is by
construction still justifiable).
- **Cap preserved**: still bounded by `MAX_ATTESTATIONS_DATA = 16`
distinct `AttestationData` entries.
- **Genesis self-vote handling preserved**: `(source.slot == 0 &&
target.slot == 0)` entries are scored as tier 3 (fork-choice only) and
bypass the "target already justified" filter.
## Tests Added / Run
- All existing tests pass: 20 unit tests in `ethlambda-blockchain`
(incl. `build_block_caps_attestation_data_entries` and
`build_block_absorbs_older_but_justified_source`), 84 fork-choice spec
tests, 11 signature spec tests. Full `make test` = 415 passed / 0 failed
/ 6 ignored.
- Tests moved alongside the code into `block_builder::tests`.
## Related Issues / PRs
- Modeled after Prysm's `proposerAtts.sortByProfitability` in
`beacon-chain/operations/attestations/`.
- 3SF-mini reference for justifiability deltas:
https://github.com/ethereum/research/blob/c003fe1c1a785797e7b53e3cbf9569b989be6e93/3sf-mini/consensus.py#L52-L54
## Verification Checklist
- [x] Ran `make fmt` — clean
- [x] Ran `make lint` (clippy with `-D warnings`) — clean
- [x] Ran `cargo test --workspace --release` — all passing
---------
Co-authored-by: Pablo Deymonnaz <pdeymon@fi.uba.ar>1 parent ae6ac42 commit db05d78
3 files changed
Lines changed: 1257 additions & 864 deletions
0 commit comments