Repository navigation
perf(crypto): sum validated public keys through blst's batched affine addition - #737
MegaRedHand wants to merge 2 commits into
Conversation
… addition A committee's key sum is the largest cost left in verifying an attestation once its keys are cached. Folding the keys one by one through AggregatePublicKey::aggregate pays a projective addition per key; blst_p1s_add (reached through the safe MultiPoint::add) shares one field inversion across the batch and runs 1.6-2.3x faster in a single-threaded measurement. From 384 points up blst also spreads the work over its own thread pool. fast_aggregate_verify and eth_aggregate_pubkeys now sum through one private helper, sum_validated_points, which guards the empty slice (MultiPoint::add indexes the first element). The helper returns the same group element as the sequential sum, including for repeated keys (sync committees are drawn with replacement, so P + P occurs), which the new tests check at sizes around the 384-point threshold. Accepted and rejected inputs are unchanged.
… addition (#737) perf(crypto): sum validated public keys through blst's batched affine addition A committee's key sum is the largest cost left in verifying an attestation once its keys are cached. Folding the keys one by one through AggregatePublicKey::aggregate pays a projective addition per key; blst_p1s_add (reached through the safe MultiPoint::add) shares one field inversion across the batch and runs 1.6-2.3x faster in a single-threaded measurement. From 384 points up blst also spreads the work over its own thread pool. fast_aggregate_verify and eth_aggregate_pubkeys now sum through one private helper, sum_validated_points, which guards the empty slice (MultiPoint::add indexes the first element). The helper returns the same group element as the sequential sum, including for repeated keys (sync committees are drawn with replacement, so P + P occurs), which the new tests check at sizes around the 384-point threshold. Accepted and rejected inputs are unchanged. Cherry-picked from perf/bls-batched-key-sums at 54be2b2.
A key and its negation are both valid, so a registry can hold both and an aggregate can name both. Pin that the batched sum still matches the sequential one when keys cancel, that such a pair aggregates to the identity, and that the identity never verifies. Also state the CPU-count condition on blst's pool.
🤖 Claude Code ReviewReview of PR 737: batched public-key sum in I read the diff only. I did not build it or run the tests. The change looks correct and is well tested. I found no blocking issues.
Nits, optional
Approve. Automated review by Claude (Anthropic) · sonnet · custom prompt |
🤖 Codex Code ReviewLooks good overall. The change is narrowly scoped to BLS pubkey aggregation, improves the hot path for attestation verification, and the added tests cover the main edge cases that matter for consensus safety: repeated keys, cancellation, and identity results.
One minor maintainability note:
I don’t see fork-choice, justification/finalization, state transition, XMSS, or SSZ concerns in this PR since the diff is isolated to BLS aggregation logic. Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
🤖 Kimi Code ReviewI'll review this PR which optimizes BLS public key aggregation using blst's batched Overall AssessmentThis is a well-crafted performance optimization with excellent test coverage. The core mathematical reasoning is sound, and the safety considerations around point validation and edge cases are properly handled. Issues Found1. Critical:
|
| Severity | Count | Description |
|---|---|---|
| Critical | 1 | Misleading comment in fast_aggregate_verify masks empty-input handling; needs explicit check or corrected comment |
| Minor | 1 | sum_validated_points error semantics slightly conflate empty input with other failures |
| Test gap | 1 | Missing empty-pubkeys test for fast_aggregate_verify |
The core optimization is correct and safe. Address Point 1 before merge—it's a latent maintainability issue that could lead to bugs if validated_pubkeys behavior changes.
Automated review by Kimi (Moonshot AI) · kimi-k2.6 · custom prompt
… addition (#737): review fixes test(crypto): cover keys that cancel in the batched sum A key and its negation are both valid, so a registry can hold both and an aggregate can name both. Pin that the batched sum still matches the sequential one when keys cancel, that such a pair aggregates to the identity, and that the identity never verifies. Also state the CPU-count condition on blst's pool. Updates nightly's #737 from perf/bls-batched-key-sums at 54be2b2 to bac0aa3.
Block import benchmark (offline replay of mainnet blocks, steady state)
Method
|
Stack 1/4 of the BLS verification work: (a) batched key sums -> (b) per-block signature batch -> (c) committee key sums -> (d) index-keyed pubkey cache.
What changed
crates/common/crypto/src/bls.rsonly. A private helper,sum_validated_points, sums already-validated public keys through blst's safeMultiPoint::addfor[PublicKey], which callsblst_p1s_add: a batched affine addition with one shared field inversion.fast_aggregate_verify(now ending inSignature::fast_aggregate_verify_pre_aggregated) andeth_aggregate_pubkeysuse it instead ofAggregatePublicKey::aggregate. Nounsafe, no signature changes.aggregate_verify(distinct messages) never sums keys, so it is untouched.Notes on the helper:
MultiPoint::addindexes the first element, so the empty slice is guarded (None). Both callers already reject empty input before.P + Poccurs) give the same element as the sequential sum.Why
Measured on blst 0.3.16, block shape of 11 signature sets and 33k signers, single-threaded, on a heavily loaded M3 (ratios only; absolutes to be taken on a quiet x86 host):
blst_p1s_add(this PR)Behavior
No change: every input accepted before is accepted, every input rejected before is rejected, including the identity aggregate:
Pand-Pare both valid keys (flip the sort flag in the compressed encoding; whoever holdsskcan sign-P's deposit with-sk), so a registry can hold both and an aggregate can name both.eth_aggregate_pubkeys([P, -P])returns the identity's bytes on both paths, andfast_aggregate_verifystays false because blst refuses an infinite key at pairing time.Tests
New unit tests compare
sum_validated_pointswithAggregatePublicKey::aggregate: empty input, sizes 1, 2, 3, 17, 383, 384, 385, 767 and 2000,P + P, and runs with scattered repeats at 10, 400 and 1000 keys;fast_aggregate_verifywith a repeated signer verifies (and fails with the key listed once);eth_aggregate_pubkeysequals the sequential sum with repeats; keys that cancel (adjacentP, -Ppairs and two halves that cancel each other, at 8, 200, 400 and 1100 pairs) match the sequential sum, and[P, -P]aggregates to the identity and never verifies, not even against the identity signature.Run:
cargo test -p ethlambda-crypto --profile release-fast --lib: 17 passed, 14 ignoredcargo test -p ethlambda-state-transition --profile release-fast --lib --features beacon-spec-tests(BLS fixture vectors): 645 passed... --test beacon_spec_tests --features beacon-spec-tests -- /sync_aggregate/: 182 passed... -- /attestation/: 356 passed... -- sanity/: 595 passedcargo fmt --all,cargo clippy -p ethlambda-crypto --all-targets -- -D warnings: cleanRerun after the review fixes, on the stack's tip (all four PRs,
--profile release-fast):ethlambda-crypto --lib47 passed, 14 ignored;ethlambda-state-transition --lib --features beacon-spec-tests(with the BLS fixture vectors) 658 passed; storage 213, blockchain 291, p2p 362, rpc 335 passed; fullbeacon_spec_tests7493 passed, 977 ignored, 0 failed;make lintclean, also on a trial merge of currentmain.