Skip to content

Compute real aggregate pubkeys when BLS verification is disabled - #5489

Merged
jtraglia merged 3 commits into
ethereum:masterfrom
nazarhussain:nh/bls-real-aggregate-pubkeys
Jul 29, 2026
Merged

Compute real aggregate pubkeys when BLS verification is disabled#5489
jtraglia merged 3 commits into
ethereum:masterfrom
nazarhussain:nh/bls-real-aggregate-pubkeys

Conversation

@nazarhussain

@nazarhussain nazarhussain commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Motivation

All fast_confirmation test vectors are generated with bls_setting: 2 (introduced in #5376 for generation speed). With BLS inactive, the pyspec stubs AggregatePKs via @only_with_bls(alt_return=STUB_PUBKEY), so it returns the constant 0x2222…22 instead of a real aggregate.

This is fine for signatures: bls_setting: 2 tells clients to skip signature verification, and they can honor that. The aggregate public key is different — it is data committed into the beacon state, not the result of a verification. At every sync committee rotation, get_next_sync_committee writes the AggregatePKs output into state.next_sync_committee.aggregate_pubkey. A client replaying such a vector computes the real aggregate during its state transition and arrives at a different state root, so block import fails with an invalid state root. No
real-crypto client can pass such a vector, and no bls_setting value can make it pass — the stub constant is effectively a generator-private convention. (Even a fake-crypto build wouldn't match: e.g. Lighthouse's fake_crypto
aggregate is the infinity pubkey, not 0x2222…22.)

Concretely, this breaks is_one_confirmed_fails_recently_activated_validator_voting_in_empty_slot (added in #5400), currently the only BLS-disabled test that runs long enough to cross a sync committee period boundary (slot 64 on minimal).

Change

Remove the only_with_bls stub from AggregatePKs so key aggregation is always computed for real, regardless of the BLS setting. Key aggregation is not signature verification, so it should not be gated on bls_active.

Validation

Regenerated is_one_confirmed_fails_recently_activated_validator_voting_in_empty_slot (electra/minimal) before and after the change: the anchor state and all files up to slot 63 are byte-identical; divergence starts exactly at slot 64 (the first sync committee rotation), where the real aggregate replaces the stub. The pyspec test itself passes unchanged, so FCR logic is unaffected — only the state roots change, to values that real-crypto clients can now reproduce.

With `bls_setting: 2`, `AggregatePKs` returned the stub constant
`0x2222...22`, which `get_next_sync_committee` then wrote into
`state.next_sync_committee.aggregate_pubkey` at sync committee
rotations. Unlike signature checks, which clients can skip per
`bls_setting`, the aggregate pubkey is data committed into the state,
so no real-crypto client can reproduce the block state roots of such
vectors. This currently affects the FCR test
`is_one_confirmed_fails_recently_activated_validator_voting_in_empty_slot`,
the only BLS-disabled test crossing a sync committee period boundary.

Key aggregation is not signature verification, so compute it for real
regardless of the BLS setting. Anchor states are unaffected because
genesis states are created with BLS enabled; regenerated vectors are
identical up to the first sync committee rotation.

Follow-up to ethereum#5376 and ethereum#5400.
@github-actions github-actions Bot added the testing CI, actions, tests, testing infra label Jul 28, 2026
@jtraglia
jtraglia requested a review from mkalinin July 28, 2026 14:06
@mkalinin

Copy link
Copy Markdown
Contributor

I completely understand the motivation. We had this problem with compliance tests and the workaround we used in Teku was not computing but reading the aggregate_pubkey from the state.current_sync_committee.aggregate_pubkey in get_next_sync_committee, this would only work if there is 100% or less than 50% sync committee participation, because, otherwise, pubkey aggregation would be called in process_sync_aggregate with pubkeys that did not participate.

After proposed change there will be a slight increase in computations for get_next_sync_committee which is called once per 8 epochs with minimal preset. And also if participation is between 50% and 100% a similar computation will be happening each block, which should be specific to sync committee tests and shouldn’t affect compliance and FCR tests at all cause sync committee participation in these tests is 0.

Thanks for the proposed change!

@jtraglia
jtraglia enabled auto-merge (squash) July 29, 2026 16:40
@jtraglia
jtraglia merged commit 5e40d95 into ethereum:master Jul 29, 2026
16 checks passed
matthewkeil pushed a commit to ChainSafe/lodestar that referenced this pull request Aug 10, 2026
**Motivation**

Update spec tests to v1.7.0-alpha.13 FCR fixes proposed from #9690
([consensus-specs#5489](ethereum/consensus-specs#5489),
[consensus-specs#5490](ethereum/consensus-specs#5490))
and complete the missing FCR assertions.

**Description**

- Unskip `is_one_confirmed_fails_large_validator_slashed`, fixed
upstream by
[consensus-specs#5490](ethereum/consensus-specs#5490)
- Enable the gloas `fast_confirmation` suite — gloas alpha.13 state
transition and `get_safe_execution_block_hash` landed via #9393
- Assert `safe_execution_block_hash`
([consensus-specs#5449](ethereum/consensus-specs#5449))
and the `FastConfirmationStore` variables, exposed via
`IForkChoice.getFastConfirmationStore()` (debug API endpoint in a
follow-up PR)
- Skip vectors blocked on upstream vector artifacts, documented inline;
the schedule-related ones are already fixed upstream
([consensus-specs#5498](ethereum/consensus-specs#5498),
[consensus-specs#5499](ethereum/consensus-specs#5499))
and unskip on the next spec-tests release

`fast_confirmation` (minimal): 1416 passed / 20 skipped / 0 failed. Full
spec suites green: minimal 70,899 passed, mainnet 13,586 passed, 0
failures.

Refs #9690

**AI Assistance Disclosure**

- [x] I have read the [contributor
guidelines](https://github.com/ChainSafe/lodestar/blob/unstable/CONTRIBUTING.md#ai-assistance-notice)
and disclosed my usage of AI below.

Implemented and verified with AI assistance (Claude Code); all changes
reviewed and tests executed locally.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

testing CI, actions, tests, testing infra

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants