fix(lstar): keep pending proofs that an aggregation round skips - #1208
Open
pucedoteth wants to merge 1 commit into
Open
pucedoteth wants to merge 1 commit into
pucedoteth wants to merge 1 commit into
Conversation
aggregate() rebuilds latest_new_aggregated_payloads from the proofs it built this round. A vote the loop skips builds no proof, so it is absent from the rebuilt pool and its existing proof is dropped. The loop skips a vote when it has no raw signatures and fewer than two child proofs, on the grounds that a lone child proof is already valid. That proof is pending, not yet promoted, and the round that produced it already dropped the raw signatures it absorbed, so nothing else holds those votes. Dropping it loses them. A gossiped aggregate that lands between the acceptance tick and the aggregation interval hits this: it sits alone in the pending pool with no raw signatures beside it, and the next round discards it. Carry skipped votes' proofs forward. Votes that did aggregate still keep only this round's proof, which subsumes the children it was built from. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🗒️ Description
aggregate()rebuilds the pending pool from the proofs it built this round:A vote the loop
continues over builds no proof, so it is absent from the rebuilt map and the proof it already had is dropped.The skip is the one just above:
"A lone child proof is already valid" is true, but the pool rebuild then throws it away. That proof is in
latest_new_aggregated_payloads— pending, not yet promoted — and the round that produced it already dropped the raw signatures it absorbed. Nothing else holds those votes, so they are gone for good.How a node hits this
A gossiped aggregate lands in the pending pool via
on_aggregated_attestation. If it arrives after the acceptance tick that last drained the pool and before the aggregator's interval-2 run, it sits there alone with no raw signatures beside it — exactly the skip condition. The next aggregation round discards it.The raw-signature bookkeeping below the loop does not save it; that filter keeps
attestation_signatures, a different pool.This is also the assumption #747 rests on when it proposes widening the skip:
That holds for raw signatures, not for pending payloads. The
0 raw + 1 childcase the skip already covers loses its proof today.Reproduction
Against
main, with one gossiped aggregate pending and no raw signatures for it:The change
Carry skipped votes' proofs forward:
Votes that did aggregate still keep only this round's proof, which subsumes the children it was built from. Proofs the greedy set cover left unselected are redundant by construction, so they are still dropped.
The raw-signature filter is deliberately keyed off
aggregated_payloads— the proofs built this round — so signature retention behaves exactly as before.Tests
tests/consensus/lstar/fork_choice/test_pending_pool_retention.pygossips one aggregate into the pending pool after the acceptance tick, runs the aggregation interval, and asserts the proof is still there and still reaches a block.Reverting only
aggregation.pyand keeping the test:The nearest existing vector,
test_fallback_pool_set_cover.py, does not catch this: it has two proofs in the pools, so it merges rather than skipping.Full suites with the fix:
ruff checkandruff format --checkare clean on both files.🔗 Related Issues or PRs
Related to #747 / #748, which propose widening this same skip. They are independent of this change, but the wider skip inherits the same drop unless this lands — worth a look together.
✅ Checklist
AI disclosure
Written with AI assistance (Claude Code): the investigation, the fix, the test, and this description.
🤖 Generated with Claude Code