lstar/aggregate: skip trivial 1-raw-sig + 0-children case - #748
lstar/aggregate: skip trivial 1-raw-sig + 0-children case#748ch4r10t33r wants to merge 2 commits into
Conversation
Extend the early-skip predicate in `aggregate()` so an `AttestationData` with exactly one raw gossip sig and no child proofs is not aggregated. Why: - A 1-validator "aggregate" carries no information the raw gossip sig doesn't already carry. The sig is on the per-subnet `attestation_signatures` gossip topic at sign time, so any peer aggregator can fold it in as a raw entry next round. - The recursive STARK prover (`xmss/aggregation.py:aggregate`) is roughly constant-cost in input size — building a 1-validator proof costs the same as building a 32-validator one. On the multi-client devnet this caused two zeam aggregators to spend ~10.8 s of FFI per worker run on a single-validator proof, dropping ~50% of slot triggers as `in_flight`. See blockblaz/zeam#907. The unconsumed gossip sig is preserved in `store.attestation_signatures` by the existing bookkeeping (only sigs whose `att_data` produced an aggregate are pruned), so a future round folds it in once another sig or a child shows up. Test changes: - Add `test_aggregate_skips_single_gossip_sig_with_no_children` covering the new skip case and asserting the sig survives in the store. - Update `test_multiple_attestation_data_grouped_separately` to seed two raw sigs per `att_data`. The original test put one sig per data, which is now (correctly) the trivial case that does not produce a proof; the test's intent — that two distinct `AttestationData` produce two distinct proofs — is preserved by upgrading both groups to non-trivial inputs. Closes leanEthereum#747
The fill-ci failures were caused by applying the aggregator-role ``1 raw + 0 children`` skip unconditionally inside ``aggregate()``. The consensus-testing ``BlockSpec`` filler calls ``spec.aggregate(store)`` before ``build_block()`` to fold gossip sigs into ``latest_known_aggregated_payloads``. Many fork-choice fixture tests seed single-validator attestations that way; skipping them broke head selection, reorg depth, and the MAX_ATTESTATIONS_DATA rejection case. Add ``skip_trivial_inputs: bool = True`` to ``aggregate()``: - Interval-2 aggregator ticks (``tick_interval``) keep the default and skip trivial inputs — the intended leanEthereum#747 optimization. - Block-building simulation passes ``skip_trivial_inputs=False`` so every gossip sig the proposer seeded is aggregated into known payloads before ``build_block()`` runs. Verified locally: all 9 previously failing fill tests pass.
|
@ch4r10t33r I owe you an apology — I picked #747 up from the issue tracker and completely missed this PR, and ended up submitting a duplicate implementation as #1191. Closing mine in deference to yours: you wrote the issue, measured the devnet impact, and implemented it first. Main has moved quite a bit since May (the https://github.com/koko1123/leanSpec/tree/skip-trivial-single-signature-aggregation A few notes from having effectively done that rebase, in case they save you time:
Happy to review the rebase or help any other way — this is worth landing either way for the zeam aggregators. |
Closes #747.
Summary
Extend the early-skip predicate in
lstar/spec.py:aggregate():Why
A 1-validator aggregate can't move ⅔ quorum and the raw XMSS sig is already on the per-subnet
attestation_signaturesgossip topic at sign time, so any peer aggregator can fold it in as a raw entry next round. The recursive STARK prover is roughly constant-cost in input size — building a 1-validator proof costs the same as a 32-validator one.On the multi-client devnet, two zeam aggregators sat in this case for 100% of slots over 10 minutes, spending ~10.8 s of FFI per worker run on single-validator proofs and dropping ~50% of slot triggers as
in_flight. Full numbers in blockblaz/zeam#907.Correctness
store.attestation_signaturesbecause the existing bookkeeping only prunes sigs whoseatt_dataproduced an aggregate. A future round folds it in once another sig or a child shows up.Tests
test_aggregate_skips_single_gossip_sig_with_no_children— asserts no aggregation is produced for the 1-sig + 0-children case and that the sig survives in the store.test_multiple_attestation_data_grouped_separately— seeded with two raw sigs peratt_dataso each group is non-trivial. The test's intent (two distinctAttestationData→ two distinct proofs) is preserved.uv run pytest tests/lean_spec/forks/lstar/state tests/lean_spec/forks/lstar/forkchoice→ 47 passed.Related