Skip to content

[CL-111] Attestation aggregation: in-tile pool, admission gaps, and aggregate-topic verification shedding - #170

Open
Bronek wants to merge 15 commits into
mainfrom
bronek/aggregation_step_1
Open

[CL-111] Attestation aggregation: in-tile pool, admission gaps, and aggregate-topic verification shedding#170
Bronek wants to merge 15 commits into
mainfrom
bronek/aggregation_step_1

Conversation

@Bronek

@Bronek Bronek commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Two halves:

  • Network-level aggregation: gossip-verified SingleAttestations incrementally BLS-aggregate per (slot, committee, data root) into one-committee Attestations, serialized in the future GET aggregate_attestation shape; 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.
  • Aggregate-topic performance (CL-111): per-(aggregator, epoch) first-seen and spec superset-coverage IGNOREs now shed redundant aggregates on bitset/byte compares before the ~1 ms three-signature batch verify — the common case at 16 aggregators/committee — while union-covered messages still verify and relay, skipping only the redundant local vote fold.

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 through handle_aggregate_and_proof

Also defined BeaconStateCounters, currently used for SeenAggregatesFull and AttestationPoolFull; may want to define more in the future.

Assisted-by: Claude:claude-fable-5

Bronek added 11 commits August 13, 2026 10:36
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
Comment thread crates/beacon_state/tile/src/tile/attestation_pool.rs
Comment thread crates/beacon_state/tile/src/tile.rs Outdated
Comment thread crates/beacon_state/tile/src/tile.rs
Comment thread crates/beacon_state/tile/src/tile/attestation_pool.rs
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
committee_position,
committee_len,
&verified,
);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we aggregating always / unconditionally?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@Bronek Bronek Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Bronek added 3 commits August 13, 2026 14:28
Cargo.toml's flux rev was already updated by the merge from main;
Cargo.lock had not caught up yet.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants