[CL-111] Attestation aggregation: in-tile pool, admission gaps, and aggregate-topic verification shedding - #170
Open
Bronek wants to merge 15 commits into
Open
[CL-111] Attestation aggregation: in-tile pool, admission gaps, and aggregate-topic verification shedding#170Bronek wants to merge 15 commits into
Bronek wants to merge 15 commits into
Conversation
verify_single_attestation now yields the AttestationData root and the parsed, subgroup-checked signature instead of a bool, so the upcoming aggregation pool can reuse them without re-hashing or re-parsing. No behavior change. Assisted-by: Claude:claude-fable-5
Thread the subnet id from the gossip topic into handle_attestation and reject attestations arriving on the wrong subnet. Track (target_epoch, attester_index) in a two-lane epoch-parity bitset so a validator's second attestation for the same target epoch is ignored before any fork-choice or BLS work; marking happens only after full validation. Assisted-by: Claude:claude-fable-5
Each (slot, committee index, data root) entry accumulates participant bits and an incrementally-added BLS aggregate signature from gossip-verified singles, serialized on demand as a one-committee Electra/Fulu Attestation — the exact shape the future GET /eth/v2/validator/aggregate_attestation endpoint returns. Retention is the current and previous slot, pruned on the slot tick; the retention floor also gates inserts between ticks. Assisted-by: Claude:claude-fable-5
Split test_signing's aggregate builder into build_attestation + wrap_aggregate_and_proof so tests can wrap an arbitrary inner Attestation, and add the end-to-end oracle: two gossip-verified singles from distinct-key committee members, pooled, serialized, wrapped with a selection proof and outer signature, accepted by handle_aggregate_and_proof's full three-signature batch verification. Assisted-by: Claude:claude-fable-5
Use ATTESTATION_FIXED and SIGNED_AGG_PROOF_MIN - ATTESTATION_FIXED for the offsets where build_attestation and wrap_aggregate_and_proof meet the inner attestation bytes, and state the pool's no-group-check rationale via VerifiedSingleAttestation's contract rather than the gossip call site. Assisted-by: Claude:claude-fable-5
insert_verified (EC add + map probe per accepted attestation), aggregate_ssz (alloc + G2 compression), prune_before (per-slot retain) and verify_single_attestation (the gossip path's pairing verify) now carry #[timed], so surfer splits handle_attestation into its verify and pool phases. Bit-level helpers, pure arithmetic and the seen-set stay untimed, matching the granularity the codebase times elsewhere. Assisted-by: Claude:claude-fable-5
Review feedback: a stateful component with its own storage and rotation lifecycle belongs in its own file, matching attestation_pool and shuffling_cache, not inline in the gossip handler. The new direct tests cover what handler-level tests (all epoch 0) never exercised: lane clearing on epoch advance, re-markability two epochs later, out-of-window epochs reading empty, and growth past construction capacity. Assisted-by: Claude:claude-fable-5
Spec IGNORE rule: only the first valid aggregate per (aggregator index, target epoch) is processed; repeats now die on a bitset probe before any fork-choice or BLS work. SeenAttesters generalizes to SeenValidators with a second tile instance for aggregators, keeping its lifecycle tests; marking happens only on full acceptance so a forged aggregate cannot censor the aggregator's real one. Assisted-by: Claude:claude-fable-5
The spec's non-strict-superset IGNORE now gates the aggregate topic: a message whose aggregation_bits are covered by one already-verified aggregate for the same (slot, committee, data root) dies on a byte compare before the ~1 ms three-signature batch verify — the common case on a topic where 16 aggregators per committee publish near-identical views. Bits inside the union of seen patterns but no single one still verify and relay (union coverage must not gate forwarding, per mesh scoring) and skip only the redundant local vote fold. The verified data root now feeds the signature check instead of being recomputed, and a second data root appearing for one (slot, committee) logs the late-block/split-view diagnostic. Assisted-by: Claude:claude-fable-5
AttestationPoolFull and SeenAggregatesFull surface capacity pressure on the two bounded gossip-admission structures in surfer, where a debug log alone cannot show rates. Registers the tile's first counter enum in surfer's schema table. Assisted-by: Claude:claude-fable-5
Conflict in handle_aggregate_and_proof's tail: main replaced the apply_attestation call with a direct record_attester_votes fold (the committees are already resolved); this branch had wrapped the same tail in union-coverage shedding. Resolution composes both — union-covered aggregates skip the vote fold but still record coverage, mark the aggregator, and relay. ParsedAggregateAndProof lost its target_epoch field upstream; att_epoch (enforced equal earlier in the handler) is used instead. Assisted-by: Claude:claude-fable-5
alexX512
reviewed
Aug 13, 2026
alexX512
reviewed
Aug 13, 2026
alexX512
reviewed
Aug 13, 2026
vladimir-ea
reviewed
Aug 13, 2026
Reviewer request: pure mechanical move of the inline test module to src/tile/tests.rs; module path and visibility unchanged. Assisted-by: Claude:claude-fable-5
vladimir-ea
reviewed
Aug 13, 2026
| committee_position, | ||
| committee_len, | ||
| &verified, | ||
| ); |
Collaborator
There was a problem hiding this comment.
are we aggregating always / unconditionally?
Collaborator
Author
There was a problem hiding this comment.
Only if it passed verify_single_attestation BLS signature check, and if the update wasn't dropped earlier e.g. because attestation is not new for the given validator/epoch or because subnet/committee mismatch.
Collaborator
Author
There was a problem hiding this comment.
Added [CL-112] to for batching of verifications - this is the more expensive, and non-optional operation, but its batching could bring a good performance improvement for shared AttestationData.
Cargo.toml's flux rev was already updated by the merge from main; Cargo.lock had not caught up yet.
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.
Two halves:
SingleAttestations incrementally BLS-aggregate per (slot, committee, data root) into one-committeeAttestations, serialized in the futureGET aggregate_attestationshape; verification returns its artifacts so aggregation adds zero re-hashing/re-parsing; subnet-consistency REJECT and per-(attester, epoch) first-seen IGNORE close the singles-topic admission gaps.All admission structures are bounded, slot-pruned,
#[timed]where the repo's granularity rule says, and covered by direct lifecycle tests plus handler-level tests including structural proofs (corrupted-signature-subset → Ignore) and an e2e oracle throughhandle_aggregate_and_proofAlso defined
BeaconStateCounters, currently used forSeenAggregatesFullandAttestationPoolFull; may want to define more in the future.Assisted-by: Claude:claude-fable-5