这是indexloc提供的服务,不要输入任何密码
Skip to content

Conversation

@nflaig
Copy link
Member

@nflaig nflaig commented Oct 2, 2025

Motivation

Since #7927 we started to use parent slot (the slot of parent block) to get sync aggregate for block but this can cause BLOCK_ERROR_INVALID_SIGNATURE during fork transition as might include sync aggregates into our block that are older than block slot - 1 which is used to compute domain during signature verification.

const previousSlot = Math.max(block.slot, 1) - 1;

This means we will reject our own block due to invalid signature if there are missed slots during the epoch transition.

we currently hard code parent slot as producedSlot - 1 which is incorrect in reorg scenario

This was the motivation from the previous change but this doesn't seem right, sync committee messages are produced at latest 4 seconds into the slot so if we re-org the block due to it being weak/late then sync committee votes will also be for parent block root of that block and not the block itself.

So irrespective of block being reorged or not we should always include sync committee messages from previous slot.

Description

Use previous slot to get sync aggregate for block to avoid BLOCK_ERROR_INVALID_SIGNATURE in case there are missed slots.

@nflaig nflaig requested a review from a team as a code owner October 2, 2025 15:45
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @nflaig, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request implements a critical fix to the block production logic, specifically concerning the inclusion of sync aggregates. It resolves an issue where using the parentSlot for sync aggregate retrieval could lead to BLOCK_ERROR_INVALID_SIGNATURE errors during fork transitions, especially in scenarios with missed slots. The updated approach ensures that sync aggregates are always fetched based on the immediately preceding slot (slot - 1), thereby enhancing the stability and correctness of block validation and production within the beacon node.

Highlights

  • Sync Aggregate Logic Correction: The mechanism for retrieving sync aggregates has been updated to consistently use slot - 1 (the previous slot) instead of the parentSlot parameter. This ensures that sync aggregates are always sourced from the correct time window.
  • Resolution of Invalid Signature Errors: This fix directly addresses BLOCK_ERROR_INVALID_SIGNATURE errors that could occur during fork transitions, particularly when there were missed slots. The previous logic could lead to blocks including sync aggregates older than block slot - 1, causing signature verification failures.
  • Parameter Refactoring: The parentSlot parameter has been removed from various function signatures and calls across the codebase, including produceBlindedBlock, produceBlock, and produceCommonBlockBody. This simplifies the API and centralizes the logic for determining the correct slot for sync aggregate retrieval.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request correctly addresses a potential BLOCK_ERROR_INVALID_SIGNATURE issue that could arise during fork transitions, especially in scenarios with missed slots. The core of the fix is to consistently use the previous slot (slot - 1) for fetching sync aggregates, rather than the parent block's slot. This change ensures that the sync aggregate included in a new block corresponds to the slot expected by the signature verification logic, which also relies on slot - 1 for domain computation.

The implementation is clean and effective. A significant part of the change involves refactoring to remove the parentSlot parameter from дълга верига от извиквания на функции, което опростява кода и премахва ненужна зависимост. Промените са приложени последователно във всички засегнати файлове, включително актуализации на тестовете, за да отразяват новите сигнатури на функциите. Като цяло, това е стабилна корекция, която подобрява надеждността на процеса на производство на блокове.

@codecov
Copy link

codecov bot commented Oct 2, 2025

Codecov Report

❌ Patch coverage is 0% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 52.24%. Comparing base (8961b06) to head (8fb75de).
⚠️ Report is 1 commits behind head on unstable.

Additional details and impacted files
@@            Coverage Diff            @@
##           unstable    #8494   +/-   ##
=========================================
  Coverage     52.24%   52.24%           
=========================================
  Files           852      852           
  Lines         65005    64991   -14     
  Branches       4772     4772           
=========================================
- Hits          33959    33952    -7     
+ Misses        30977    30970    -7     
  Partials         69       69           
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions
Copy link
Contributor

github-actions bot commented Oct 2, 2025

Performance Report

🚀🚀 Significant benchmark improvement detected

Benchmark suite Current: 54724d0 Previous: 8961b06 Ratio
altair processBlock - 250000 vs - 7PWei normalcase 4.6710 ms/op 15.846 ms/op 0.29
Full benchmark results
Benchmark suite Current: 54724d0 Previous: 8961b06 Ratio
getPubkeys - index2pubkey - req 1000 vs - 250000 vc 956.64 us/op 985.22 us/op 0.97
getPubkeys - validatorsArr - req 1000 vs - 250000 vc 35.634 us/op 36.595 us/op 0.97
BLS verify - blst 856.00 us/op 844.85 us/op 1.01
BLS verifyMultipleSignatures 3 - blst 1.3477 ms/op 1.2367 ms/op 1.09
BLS verifyMultipleSignatures 8 - blst 1.6467 ms/op 1.8844 ms/op 0.87
BLS verifyMultipleSignatures 32 - blst 4.9011 ms/op 5.8107 ms/op 0.84
BLS verifyMultipleSignatures 64 - blst 9.0362 ms/op 10.903 ms/op 0.83
BLS verifyMultipleSignatures 128 - blst 17.279 ms/op 18.304 ms/op 0.94
BLS deserializing 10000 signatures 694.24 ms/op 706.97 ms/op 0.98
BLS deserializing 100000 signatures 6.8078 s/op 7.0527 s/op 0.97
BLS verifyMultipleSignatures - same message - 3 - blst 896.66 us/op 912.62 us/op 0.98
BLS verifyMultipleSignatures - same message - 8 - blst 1.0454 ms/op 1.3608 ms/op 0.77
BLS verifyMultipleSignatures - same message - 32 - blst 1.7394 ms/op 1.6728 ms/op 1.04
BLS verifyMultipleSignatures - same message - 64 - blst 2.6143 ms/op 2.5501 ms/op 1.03
BLS verifyMultipleSignatures - same message - 128 - blst 4.3791 ms/op 4.3076 ms/op 1.02
BLS aggregatePubkeys 32 - blst 19.268 us/op 19.324 us/op 1.00
BLS aggregatePubkeys 128 - blst 69.034 us/op 69.446 us/op 0.99
notSeenSlots=1 numMissedVotes=1 numBadVotes=10 45.453 ms/op 49.110 ms/op 0.93
notSeenSlots=1 numMissedVotes=0 numBadVotes=4 44.366 ms/op 51.549 ms/op 0.86
notSeenSlots=2 numMissedVotes=1 numBadVotes=10 35.306 ms/op 39.334 ms/op 0.90
getSlashingsAndExits - default max 72.837 us/op 77.295 us/op 0.94
getSlashingsAndExits - 2k 282.44 us/op 322.30 us/op 0.88
isKnown best case - 1 super set check 203.00 ns/op 207.00 ns/op 0.98
isKnown normal case - 2 super set checks 202.00 ns/op 204.00 ns/op 0.99
isKnown worse case - 16 super set checks 202.00 ns/op 204.00 ns/op 0.99
InMemoryCheckpointStateCache - add get delete 2.4030 us/op 2.3780 us/op 1.01
validate api signedAggregateAndProof - struct 1.3922 ms/op 2.5890 ms/op 0.54
validate gossip signedAggregateAndProof - struct 1.3995 ms/op 2.5909 ms/op 0.54
batch validate gossip attestation - vc 640000 - chunk 32 115.71 us/op 116.64 us/op 0.99
batch validate gossip attestation - vc 640000 - chunk 64 102.45 us/op 104.43 us/op 0.98
batch validate gossip attestation - vc 640000 - chunk 128 95.057 us/op 97.167 us/op 0.98
batch validate gossip attestation - vc 640000 - chunk 256 96.486 us/op 101.00 us/op 0.96
pickEth1Vote - no votes 1.0066 ms/op 1.0037 ms/op 1.00
pickEth1Vote - max votes 5.3152 ms/op 7.2627 ms/op 0.73
pickEth1Vote - Eth1Data hashTreeRoot value x2048 10.552 ms/op 11.868 ms/op 0.89
pickEth1Vote - Eth1Data hashTreeRoot tree x2048 14.429 ms/op 18.736 ms/op 0.77
pickEth1Vote - Eth1Data fastSerialize value x2048 412.64 us/op 465.53 us/op 0.89
pickEth1Vote - Eth1Data fastSerialize tree x2048 2.0951 ms/op 2.1508 ms/op 0.97
bytes32 toHexString 356.00 ns/op 367.00 ns/op 0.97
bytes32 Buffer.toString(hex) 236.00 ns/op 313.00 ns/op 0.75
bytes32 Buffer.toString(hex) from Uint8Array 333.00 ns/op 340.00 ns/op 0.98
bytes32 Buffer.toString(hex) + 0x 241.00 ns/op 249.00 ns/op 0.97
Object access 1 prop 0.11400 ns/op 0.21500 ns/op 0.53
Map access 1 prop 0.11800 ns/op 0.13500 ns/op 0.87
Object get x1000 5.7620 ns/op 5.9720 ns/op 0.96
Map get x1000 6.2470 ns/op 6.5670 ns/op 0.95
Object set x1000 28.570 ns/op 32.782 ns/op 0.87
Map set x1000 19.394 ns/op 20.597 ns/op 0.94
Return object 10000 times 0.28630 ns/op 0.29210 ns/op 0.98
Throw Error 10000 times 4.3161 us/op 4.3710 us/op 0.99
toHex 143.23 ns/op 135.66 ns/op 1.06
Buffer.from 130.51 ns/op 130.08 ns/op 1.00
shared Buffer 81.791 ns/op 80.349 ns/op 1.02
fastMsgIdFn sha256 / 200 bytes 2.1480 us/op 2.2230 us/op 0.97
fastMsgIdFn h32 xxhash / 200 bytes 203.00 ns/op 253.00 ns/op 0.80
fastMsgIdFn h64 xxhash / 200 bytes 263.00 ns/op 332.00 ns/op 0.79
fastMsgIdFn sha256 / 1000 bytes 7.0790 us/op 7.2590 us/op 0.98
fastMsgIdFn h32 xxhash / 1000 bytes 328.00 ns/op 336.00 ns/op 0.98
fastMsgIdFn h64 xxhash / 1000 bytes 333.00 ns/op 339.00 ns/op 0.98
fastMsgIdFn sha256 / 10000 bytes 63.817 us/op 65.846 us/op 0.97
fastMsgIdFn h32 xxhash / 10000 bytes 1.8070 us/op 1.8210 us/op 0.99
fastMsgIdFn h64 xxhash / 10000 bytes 1.2070 us/op 1.2110 us/op 1.00
send data - 1000 256B messages 15.671 ms/op 15.206 ms/op 1.03
send data - 1000 512B messages 19.125 ms/op 19.325 ms/op 0.99
send data - 1000 1024B messages 28.083 ms/op 27.492 ms/op 1.02
send data - 1000 1200B messages 22.244 ms/op 23.516 ms/op 0.95
send data - 1000 2048B messages 22.910 ms/op 24.477 ms/op 0.94
send data - 1000 4096B messages 26.189 ms/op 27.739 ms/op 0.94
send data - 1000 16384B messages 42.991 ms/op 46.416 ms/op 0.93
send data - 1000 65536B messages 114.83 ms/op 111.56 ms/op 1.03
enrSubnets - fastDeserialize 64 bits 884.00 ns/op 889.00 ns/op 0.99
enrSubnets - ssz BitVector 64 bits 330.00 ns/op 338.00 ns/op 0.98
enrSubnets - fastDeserialize 4 bits 134.00 ns/op 134.00 ns/op 1.00
enrSubnets - ssz BitVector 4 bits 328.00 ns/op 336.00 ns/op 0.98
prioritizePeers score -10:0 att 32-0.1 sync 2-0 236.68 us/op 237.11 us/op 1.00
prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 259.61 us/op 265.58 us/op 0.98
prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 372.65 us/op 369.62 us/op 1.01
prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 699.16 us/op 693.77 us/op 1.01
prioritizePeers score 0:0 att 64-1 sync 4-1 842.08 us/op 823.71 us/op 1.02
array of 16000 items push then shift 1.5846 us/op 1.5833 us/op 1.00
LinkedList of 16000 items push then shift 7.5450 ns/op 7.0430 ns/op 1.07
array of 16000 items push then pop 74.808 ns/op 73.543 ns/op 1.02
LinkedList of 16000 items push then pop 7.5620 ns/op 6.9700 ns/op 1.08
array of 24000 items push then shift 2.3553 us/op 2.3567 us/op 1.00
LinkedList of 24000 items push then shift 7.8620 ns/op 7.0640 ns/op 1.11
array of 24000 items push then pop 109.74 ns/op 97.201 ns/op 1.13
LinkedList of 24000 items push then pop 7.5310 ns/op 6.9260 ns/op 1.09
intersect bitArray bitLen 8 6.3030 ns/op 6.2540 ns/op 1.01
intersect array and set length 8 39.889 ns/op 36.876 ns/op 1.08
intersect bitArray bitLen 128 29.420 ns/op 29.035 ns/op 1.01
intersect array and set length 128 612.89 ns/op 604.98 ns/op 1.01
bitArray.getTrueBitIndexes() bitLen 128 991.00 ns/op 985.00 ns/op 1.01
bitArray.getTrueBitIndexes() bitLen 248 1.7590 us/op 1.7650 us/op 1.00
bitArray.getTrueBitIndexes() bitLen 512 3.6410 us/op 3.5880 us/op 1.01
Buffer.concat 32 items 604.00 ns/op 605.00 ns/op 1.00
Uint8Array.set 32 items 883.00 ns/op 1.0950 us/op 0.81
Buffer.copy 2.0160 us/op 2.1150 us/op 0.95
Uint8Array.set - with subarray 1.4750 us/op 1.6330 us/op 0.90
Uint8Array.set - without subarray 805.00 ns/op 1.1520 us/op 0.70
getUint32 - dataview 187.00 ns/op 194.00 ns/op 0.96
getUint32 - manual 127.00 ns/op 120.00 ns/op 1.06
Set add up to 64 items then delete first 2.1867 us/op 2.2174 us/op 0.99
OrderedSet add up to 64 items then delete first 3.4471 us/op 3.3854 us/op 1.02
Set add up to 64 items then delete last 2.5268 us/op 2.5198 us/op 1.00
OrderedSet add up to 64 items then delete last 3.7601 us/op 4.0136 us/op 0.94
Set add up to 64 items then delete middle 2.3608 us/op 2.3959 us/op 0.99
OrderedSet add up to 64 items then delete middle 5.4850 us/op 5.8578 us/op 0.94
Set add up to 128 items then delete first 5.2123 us/op 5.1697 us/op 1.01
OrderedSet add up to 128 items then delete first 8.3847 us/op 9.3475 us/op 0.90
Set add up to 128 items then delete last 5.2005 us/op 5.7655 us/op 0.90
OrderedSet add up to 128 items then delete last 7.9750 us/op 10.893 us/op 0.73
Set add up to 128 items then delete middle 4.9696 us/op 5.5140 us/op 0.90
OrderedSet add up to 128 items then delete middle 14.584 us/op 15.407 us/op 0.95
Set add up to 256 items then delete first 10.644 us/op 10.132 us/op 1.05
OrderedSet add up to 256 items then delete first 17.167 us/op 15.435 us/op 1.11
Set add up to 256 items then delete last 10.506 us/op 10.485 us/op 1.00
OrderedSet add up to 256 items then delete last 14.793 us/op 16.153 us/op 0.92
Set add up to 256 items then delete middle 9.8517 us/op 9.9617 us/op 0.99
OrderedSet add up to 256 items then delete middle 44.667 us/op 42.287 us/op 1.06
transfer serialized Status (84 B) 2.3230 us/op 2.3390 us/op 0.99
copy serialized Status (84 B) 1.1890 us/op 1.5600 us/op 0.76
transfer serialized SignedVoluntaryExit (112 B) 2.3480 us/op 2.3330 us/op 1.01
copy serialized SignedVoluntaryExit (112 B) 1.2500 us/op 1.2110 us/op 1.03
transfer serialized ProposerSlashing (416 B) 3.1600 us/op 2.4060 us/op 1.31
copy serialized ProposerSlashing (416 B) 1.4050 us/op 2.0670 us/op 0.68
transfer serialized Attestation (485 B) 3.4100 us/op 2.6600 us/op 1.28
copy serialized Attestation (485 B) 1.6980 us/op 1.4660 us/op 1.16
transfer serialized AttesterSlashing (33232 B) 3.0340 us/op 2.6280 us/op 1.15
copy serialized AttesterSlashing (33232 B) 4.1390 us/op 3.3480 us/op 1.24
transfer serialized Small SignedBeaconBlock (128000 B) 3.4000 us/op 3.3030 us/op 1.03
copy serialized Small SignedBeaconBlock (128000 B) 10.413 us/op 9.8590 us/op 1.06
transfer serialized Avg SignedBeaconBlock (200000 B) 3.8870 us/op 3.7970 us/op 1.02
copy serialized Avg SignedBeaconBlock (200000 B) 13.917 us/op 13.195 us/op 1.05
transfer serialized BlobsSidecar (524380 B) 4.0850 us/op 3.3230 us/op 1.23
copy serialized BlobsSidecar (524380 B) 96.094 us/op 61.771 us/op 1.56
transfer serialized Big SignedBeaconBlock (1000000 B) 4.1590 us/op 3.8410 us/op 1.08
copy serialized Big SignedBeaconBlock (1000000 B) 141.27 us/op 108.49 us/op 1.30
pass gossip attestations to forkchoice per slot 2.7534 ms/op 2.8108 ms/op 0.98
forkChoice updateHead vc 100000 bc 64 eq 0 452.10 us/op 461.82 us/op 0.98
forkChoice updateHead vc 600000 bc 64 eq 0 2.8913 ms/op 2.8835 ms/op 1.00
forkChoice updateHead vc 1000000 bc 64 eq 0 4.8206 ms/op 4.8484 ms/op 0.99
forkChoice updateHead vc 600000 bc 320 eq 0 2.9965 ms/op 2.8788 ms/op 1.04
forkChoice updateHead vc 600000 bc 1200 eq 0 2.8779 ms/op 2.8239 ms/op 1.02
forkChoice updateHead vc 600000 bc 7200 eq 0 3.1347 ms/op 3.0464 ms/op 1.03
forkChoice updateHead vc 600000 bc 64 eq 1000 10.381 ms/op 10.384 ms/op 1.00
forkChoice updateHead vc 600000 bc 64 eq 10000 10.344 ms/op 10.204 ms/op 1.01
forkChoice updateHead vc 600000 bc 64 eq 300000 14.267 ms/op 13.300 ms/op 1.07
computeDeltas 500000 validators 300 proto nodes 3.9623 ms/op 3.8053 ms/op 1.04
computeDeltas 500000 validators 1200 proto nodes 3.9827 ms/op 3.8955 ms/op 1.02
computeDeltas 500000 validators 7200 proto nodes 4.0036 ms/op 3.8872 ms/op 1.03
computeDeltas 750000 validators 300 proto nodes 5.8497 ms/op 5.7507 ms/op 1.02
computeDeltas 750000 validators 1200 proto nodes 5.8316 ms/op 5.7921 ms/op 1.01
computeDeltas 750000 validators 7200 proto nodes 5.8393 ms/op 5.9090 ms/op 0.99
computeDeltas 1400000 validators 300 proto nodes 10.980 ms/op 11.266 ms/op 0.97
computeDeltas 1400000 validators 1200 proto nodes 11.004 ms/op 11.399 ms/op 0.97
computeDeltas 1400000 validators 7200 proto nodes 10.957 ms/op 11.460 ms/op 0.96
computeDeltas 2100000 validators 300 proto nodes 16.425 ms/op 17.303 ms/op 0.95
computeDeltas 2100000 validators 1200 proto nodes 16.642 ms/op 18.323 ms/op 0.91
computeDeltas 2100000 validators 7200 proto nodes 16.782 ms/op 18.311 ms/op 0.92
altair processAttestation - 250000 vs - 7PWei normalcase 2.2535 ms/op 3.5742 ms/op 0.63
altair processAttestation - 250000 vs - 7PWei worstcase 3.6978 ms/op 4.7392 ms/op 0.78
altair processAttestation - setStatus - 1/6 committees join 132.52 us/op 144.77 us/op 0.92
altair processAttestation - setStatus - 1/3 committees join 254.92 us/op 288.11 us/op 0.88
altair processAttestation - setStatus - 1/2 committees join 350.85 us/op 394.29 us/op 0.89
altair processAttestation - setStatus - 2/3 committees join 456.93 us/op 519.50 us/op 0.88
altair processAttestation - setStatus - 4/5 committees join 607.80 us/op 769.72 us/op 0.79
altair processAttestation - setStatus - 100% committees join 731.45 us/op 855.87 us/op 0.85
altair processBlock - 250000 vs - 7PWei normalcase 4.6710 ms/op 15.846 ms/op 0.29
altair processBlock - 250000 vs - 7PWei normalcase hashState 31.037 ms/op 55.710 ms/op 0.56
altair processBlock - 250000 vs - 7PWei worstcase 37.793 ms/op 56.371 ms/op 0.67
altair processBlock - 250000 vs - 7PWei worstcase hashState 76.750 ms/op 113.03 ms/op 0.68
phase0 processBlock - 250000 vs - 7PWei normalcase 1.7196 ms/op 3.1064 ms/op 0.55
phase0 processBlock - 250000 vs - 7PWei worstcase 25.917 ms/op 31.627 ms/op 0.82
altair processEth1Data - 250000 vs - 7PWei normalcase 378.34 us/op 664.09 us/op 0.57
getExpectedWithdrawals 250000 eb:1,eth1:1,we:0,wn:0,smpl:15 7.4140 us/op 15.349 us/op 0.48
getExpectedWithdrawals 250000 eb:0.95,eth1:0.1,we:0.05,wn:0,smpl:219 49.622 us/op 65.705 us/op 0.76
getExpectedWithdrawals 250000 eb:0.95,eth1:0.3,we:0.05,wn:0,smpl:42 13.319 us/op 21.265 us/op 0.63
getExpectedWithdrawals 250000 eb:0.95,eth1:0.7,we:0.05,wn:0,smpl:18 7.7330 us/op 14.658 us/op 0.53
getExpectedWithdrawals 250000 eb:0.1,eth1:0.1,we:0,wn:0,smpl:1020 180.56 us/op 255.82 us/op 0.71
getExpectedWithdrawals 250000 eb:0.03,eth1:0.03,we:0,wn:0,smpl:11777 1.9218 ms/op 2.2283 ms/op 0.86
getExpectedWithdrawals 250000 eb:0.01,eth1:0.01,we:0,wn:0,smpl:16384 2.4486 ms/op 2.8213 ms/op 0.87
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,smpl:16384 2.4250 ms/op 2.8564 ms/op 0.85
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,nocache,smpl:16384 4.7949 ms/op 6.3212 ms/op 0.76
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,smpl:16384 2.4622 ms/op 2.9662 ms/op 0.83
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,nocache,smpl:16384 4.9356 ms/op 6.3219 ms/op 0.78
Tree 40 250000 create 436.05 ms/op 727.37 ms/op 0.60
Tree 40 250000 get(125000) 141.95 ns/op 151.13 ns/op 0.94
Tree 40 250000 set(125000) 1.4875 us/op 2.5108 us/op 0.59
Tree 40 250000 toArray() 17.946 ms/op 18.385 ms/op 0.98
Tree 40 250000 iterate all - toArray() + loop 18.541 ms/op 21.271 ms/op 0.87
Tree 40 250000 iterate all - get(i) 55.976 ms/op 57.903 ms/op 0.97
Array 250000 create 3.2947 ms/op 2.5181 ms/op 1.31
Array 250000 clone - spread 821.84 us/op 828.22 us/op 0.99
Array 250000 get(125000) 0.40700 ns/op 0.42500 ns/op 0.96
Array 250000 set(125000) 0.44000 ns/op 0.44400 ns/op 0.99
Array 250000 iterate all - loop 105.03 us/op 86.434 us/op 1.22
phase0 afterProcessEpoch - 250000 vs - 7PWei 42.248 ms/op 42.983 ms/op 0.98
Array.fill - length 1000000 3.4563 ms/op 3.8204 ms/op 0.90
Array push - length 1000000 14.131 ms/op 13.938 ms/op 1.01
Array.get 0.27589 ns/op 0.28689 ns/op 0.96
Uint8Array.get 0.43960 ns/op 0.45544 ns/op 0.97
phase0 beforeProcessEpoch - 250000 vs - 7PWei 16.934 ms/op 18.232 ms/op 0.93
altair processEpoch - mainnet_e81889 240.56 ms/op 264.40 ms/op 0.91
mainnet_e81889 - altair beforeProcessEpoch 19.825 ms/op 18.112 ms/op 1.09
mainnet_e81889 - altair processJustificationAndFinalization 5.3030 us/op 6.7830 us/op 0.78
mainnet_e81889 - altair processInactivityUpdates 4.3104 ms/op 4.3564 ms/op 0.99
mainnet_e81889 - altair processRewardsAndPenalties 39.078 ms/op 38.230 ms/op 1.02
mainnet_e81889 - altair processRegistryUpdates 665.00 ns/op 741.00 ns/op 0.90
mainnet_e81889 - altair processSlashings 176.00 ns/op 184.00 ns/op 0.96
mainnet_e81889 - altair processEth1DataReset 166.00 ns/op 177.00 ns/op 0.94
mainnet_e81889 - altair processEffectiveBalanceUpdates 1.1795 ms/op 1.2616 ms/op 0.93
mainnet_e81889 - altair processSlashingsReset 881.00 ns/op 1.1500 us/op 0.77
mainnet_e81889 - altair processRandaoMixesReset 1.1580 us/op 1.1750 us/op 0.99
mainnet_e81889 - altair processHistoricalRootsUpdate 170.00 ns/op 181.00 ns/op 0.94
mainnet_e81889 - altair processParticipationFlagUpdates 518.00 ns/op 590.00 ns/op 0.88
mainnet_e81889 - altair processSyncCommitteeUpdates 132.00 ns/op 139.00 ns/op 0.95
mainnet_e81889 - altair afterProcessEpoch 44.138 ms/op 45.224 ms/op 0.98
capella processEpoch - mainnet_e217614 976.20 ms/op 987.77 ms/op 0.99
mainnet_e217614 - capella beforeProcessEpoch 63.904 ms/op 64.088 ms/op 1.00
mainnet_e217614 - capella processJustificationAndFinalization 5.2250 us/op 5.6150 us/op 0.93
mainnet_e217614 - capella processInactivityUpdates 15.049 ms/op 15.269 ms/op 0.99
mainnet_e217614 - capella processRewardsAndPenalties 172.75 ms/op 174.28 ms/op 0.99
mainnet_e217614 - capella processRegistryUpdates 6.2880 us/op 7.1480 us/op 0.88
mainnet_e217614 - capella processSlashings 172.00 ns/op 189.00 ns/op 0.91
mainnet_e217614 - capella processEth1DataReset 170.00 ns/op 181.00 ns/op 0.94
mainnet_e217614 - capella processEffectiveBalanceUpdates 13.291 ms/op 4.2353 ms/op 3.14
mainnet_e217614 - capella processSlashingsReset 890.00 ns/op 1.0640 us/op 0.84
mainnet_e217614 - capella processRandaoMixesReset 1.1650 us/op 1.4330 us/op 0.81
mainnet_e217614 - capella processHistoricalRootsUpdate 168.00 ns/op 204.00 ns/op 0.82
mainnet_e217614 - capella processParticipationFlagUpdates 524.00 ns/op 534.00 ns/op 0.98
mainnet_e217614 - capella afterProcessEpoch 115.20 ms/op 119.43 ms/op 0.96
phase0 processEpoch - mainnet_e58758 268.34 ms/op 337.49 ms/op 0.80
mainnet_e58758 - phase0 beforeProcessEpoch 64.876 ms/op 85.563 ms/op 0.76
mainnet_e58758 - phase0 processJustificationAndFinalization 5.3140 us/op 7.2230 us/op 0.74
mainnet_e58758 - phase0 processRewardsAndPenalties 34.890 ms/op 45.346 ms/op 0.77
mainnet_e58758 - phase0 processRegistryUpdates 3.1620 us/op 3.2780 us/op 0.96
mainnet_e58758 - phase0 processSlashings 171.00 ns/op 193.00 ns/op 0.89
mainnet_e58758 - phase0 processEth1DataReset 165.00 ns/op 179.00 ns/op 0.92
mainnet_e58758 - phase0 processEffectiveBalanceUpdates 1.1650 ms/op 1.1650 ms/op 1.00
mainnet_e58758 - phase0 processSlashingsReset 951.00 ns/op 972.00 ns/op 0.98
mainnet_e58758 - phase0 processRandaoMixesReset 1.2140 us/op 1.2310 us/op 0.99
mainnet_e58758 - phase0 processHistoricalRootsUpdate 170.00 ns/op 175.00 ns/op 0.97
mainnet_e58758 - phase0 processParticipationRecordUpdates 889.00 ns/op 904.00 ns/op 0.98
mainnet_e58758 - phase0 afterProcessEpoch 35.373 ms/op 36.006 ms/op 0.98
phase0 processEffectiveBalanceUpdates - 250000 normalcase 1.3487 ms/op 1.3626 ms/op 0.99
phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 1.9886 ms/op 2.0442 ms/op 0.97
altair processInactivityUpdates - 250000 normalcase 17.490 ms/op 23.225 ms/op 0.75
altair processInactivityUpdates - 250000 worstcase 17.490 ms/op 24.317 ms/op 0.72
phase0 processRegistryUpdates - 250000 normalcase 6.6650 us/op 10.350 us/op 0.64
phase0 processRegistryUpdates - 250000 badcase_full_deposits 276.10 us/op 304.20 us/op 0.91
phase0 processRegistryUpdates - 250000 worstcase 0.5 107.18 ms/op 131.58 ms/op 0.81
altair processRewardsAndPenalties - 250000 normalcase 25.864 ms/op 30.451 ms/op 0.85
altair processRewardsAndPenalties - 250000 worstcase 24.422 ms/op 32.190 ms/op 0.76
phase0 getAttestationDeltas - 250000 normalcase 8.3545 ms/op 7.0461 ms/op 1.19
phase0 getAttestationDeltas - 250000 worstcase 6.0954 ms/op 5.7988 ms/op 1.05
phase0 processSlashings - 250000 worstcase 99.398 us/op 124.62 us/op 0.80
altair processSyncCommitteeUpdates - 250000 11.457 ms/op 10.916 ms/op 1.05
BeaconState.hashTreeRoot - No change 213.00 ns/op 217.00 ns/op 0.98
BeaconState.hashTreeRoot - 1 full validator 88.269 us/op 92.573 us/op 0.95
BeaconState.hashTreeRoot - 32 full validator 917.12 us/op 890.44 us/op 1.03
BeaconState.hashTreeRoot - 512 full validator 10.021 ms/op 11.363 ms/op 0.88
BeaconState.hashTreeRoot - 1 validator.effectiveBalance 91.534 us/op 106.59 us/op 0.86
BeaconState.hashTreeRoot - 32 validator.effectiveBalance 1.2672 ms/op 1.6789 ms/op 0.75
BeaconState.hashTreeRoot - 512 validator.effectiveBalance 21.451 ms/op 24.826 ms/op 0.86
BeaconState.hashTreeRoot - 1 balances 75.079 us/op 90.890 us/op 0.83
BeaconState.hashTreeRoot - 32 balances 670.00 us/op 744.05 us/op 0.90
BeaconState.hashTreeRoot - 512 balances 7.8675 ms/op 8.1274 ms/op 0.97
BeaconState.hashTreeRoot - 250000 balances 186.00 ms/op 162.28 ms/op 1.15
aggregationBits - 2048 els - zipIndexesInBitList 21.818 us/op 21.506 us/op 1.01
byteArrayEquals 32 53.414 ns/op 54.151 ns/op 0.99
Buffer.compare 32 17.152 ns/op 20.104 ns/op 0.85
byteArrayEquals 1024 1.5851 us/op 1.5575 us/op 1.02
Buffer.compare 1024 26.543 ns/op 24.548 ns/op 1.08
byteArrayEquals 16384 25.299 us/op 25.068 us/op 1.01
Buffer.compare 16384 201.41 ns/op 197.93 ns/op 1.02
byteArrayEquals 123687377 194.91 ms/op 190.86 ms/op 1.02
Buffer.compare 123687377 7.2056 ms/op 7.5113 ms/op 0.96
byteArrayEquals 32 - diff last byte 53.407 ns/op 52.643 ns/op 1.01
Buffer.compare 32 - diff last byte 17.322 ns/op 17.030 ns/op 1.02
byteArrayEquals 1024 - diff last byte 1.6044 us/op 1.5896 us/op 1.01
Buffer.compare 1024 - diff last byte 26.254 ns/op 24.946 ns/op 1.05
byteArrayEquals 16384 - diff last byte 25.450 us/op 25.272 us/op 1.01
Buffer.compare 16384 - diff last byte 186.19 ns/op 200.59 ns/op 0.93
byteArrayEquals 123687377 - diff last byte 196.94 ms/op 193.50 ms/op 1.02
Buffer.compare 123687377 - diff last byte 8.3510 ms/op 6.4552 ms/op 1.29
byteArrayEquals 32 - random bytes 5.2240 ns/op 5.0880 ns/op 1.03
Buffer.compare 32 - random bytes 17.511 ns/op 17.960 ns/op 0.97
byteArrayEquals 1024 - random bytes 5.2540 ns/op 5.0900 ns/op 1.03
Buffer.compare 1024 - random bytes 17.655 ns/op 17.507 ns/op 1.01
byteArrayEquals 16384 - random bytes 5.2040 ns/op 5.0690 ns/op 1.03
Buffer.compare 16384 - random bytes 17.455 ns/op 17.569 ns/op 0.99
byteArrayEquals 123687377 - random bytes 6.5700 ns/op 6.2700 ns/op 1.05
Buffer.compare 123687377 - random bytes 18.830 ns/op 22.960 ns/op 0.82
regular array get 100000 times 45.344 us/op 39.345 us/op 1.15
wrappedArray get 100000 times 45.610 us/op 33.034 us/op 1.38
arrayWithProxy get 100000 times 13.094 ms/op 12.416 ms/op 1.05
ssz.Root.equals 46.536 ns/op 46.953 ns/op 0.99
byteArrayEquals 46.047 ns/op 46.064 ns/op 1.00
Buffer.compare 10.393 ns/op 10.530 ns/op 0.99
processSlot - 1 slots 17.529 us/op 9.9840 us/op 1.76
processSlot - 32 slots 3.4755 ms/op 1.9802 ms/op 1.76
getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei 3.2832 ms/op 2.9741 ms/op 1.10
getCommitteeAssignments - req 1 vs - 250000 vc 2.1699 ms/op 2.1088 ms/op 1.03
getCommitteeAssignments - req 100 vs - 250000 vc 4.1871 ms/op 4.1224 ms/op 1.02
getCommitteeAssignments - req 1000 vs - 250000 vc 4.4840 ms/op 4.3838 ms/op 1.02
findModifiedValidators - 10000 modified validators 784.47 ms/op 716.54 ms/op 1.09
findModifiedValidators - 1000 modified validators 750.49 ms/op 736.82 ms/op 1.02
findModifiedValidators - 100 modified validators 273.81 ms/op 226.94 ms/op 1.21
findModifiedValidators - 10 modified validators 168.31 ms/op 230.93 ms/op 0.73
findModifiedValidators - 1 modified validators 281.52 ms/op 144.24 ms/op 1.95
findModifiedValidators - no difference 157.21 ms/op 160.87 ms/op 0.98
compare ViewDUs 7.6757 s/op 6.4834 s/op 1.18
compare each validator Uint8Array 1.6622 s/op 1.5802 s/op 1.05
compare ViewDU to Uint8Array 1.3868 s/op 1.2505 s/op 1.11
migrate state 1000000 validators, 24 modified, 0 new 931.55 ms/op 872.09 ms/op 1.07
migrate state 1000000 validators, 1700 modified, 1000 new 1.1779 s/op 1.1341 s/op 1.04
migrate state 1000000 validators, 3400 modified, 2000 new 1.2501 s/op 1.4928 s/op 0.84
migrate state 1500000 validators, 24 modified, 0 new 822.64 ms/op 1.0043 s/op 0.82
migrate state 1500000 validators, 1700 modified, 1000 new 1.1227 s/op 1.1672 s/op 0.96
migrate state 1500000 validators, 3400 modified, 2000 new 1.3016 s/op 1.5986 s/op 0.81
RootCache.getBlockRootAtSlot - 250000 vs - 7PWei 4.2500 ns/op 6.2300 ns/op 0.68
state getBlockRootAtSlot - 250000 vs - 7PWei 555.97 ns/op 616.03 ns/op 0.90
naive computeProposerIndex 100000 validators 54.557 ms/op 68.905 ms/op 0.79
computeProposerIndex 100000 validators 1.5218 ms/op 1.5846 ms/op 0.96
naiveGetNextSyncCommitteeIndices 1000 validators 8.2460 s/op 8.0350 s/op 1.03
getNextSyncCommitteeIndices 1000 validators 112.18 ms/op 119.67 ms/op 0.94
naiveGetNextSyncCommitteeIndices 10000 validators 8.0684 s/op 7.7363 s/op 1.04
getNextSyncCommitteeIndices 10000 validators 114.84 ms/op 112.14 ms/op 1.02
naiveGetNextSyncCommitteeIndices 100000 validators 7.7831 s/op 7.0069 s/op 1.11
getNextSyncCommitteeIndices 100000 validators 115.41 ms/op 117.35 ms/op 0.98
naive computeShuffledIndex 100000 validators 24.942 s/op 23.510 s/op 1.06
cached computeShuffledIndex 100000 validators 567.68 ms/op 550.69 ms/op 1.03
naive computeShuffledIndex 2000000 validators 501.41 s/op 477.43 s/op 1.05
cached computeShuffledIndex 2000000 validators 37.445 s/op 37.885 s/op 0.99
computeProposers - vc 250000 624.87 us/op 592.03 us/op 1.06
computeEpochShuffling - vc 250000 42.461 ms/op 42.185 ms/op 1.01
getNextSyncCommittee - vc 250000 10.483 ms/op 10.528 ms/op 1.00
computeSigningRoot for AttestationData 19.795 us/op 20.530 us/op 0.96
hash AttestationData serialized data then Buffer.toString(base64) 1.5823 us/op 1.6251 us/op 0.97
toHexString serialized data 1.2325 us/op 1.1810 us/op 1.04
Buffer.toString(base64) 156.62 ns/op 155.07 ns/op 1.01
nodejs block root to RootHex using toHex 148.14 ns/op 142.90 ns/op 1.04
nodejs block root to RootHex using toRootHex 86.979 ns/op 88.142 ns/op 0.99
nodejs fromhex(blob) 113.21 ms/op 114.93 ms/op 0.98
nodejs fromHexInto(blob) 94.099 ms/op 95.034 ms/op 0.99
browser block root to RootHex using the deprecated toHexString 212.20 ns/op 211.62 ns/op 1.00
browser block root to RootHex using toHex 172.62 ns/op 172.59 ns/op 1.00
browser block root to RootHex using toRootHex 157.34 ns/op 162.69 ns/op 0.97
browser fromHexInto(blob) 806.65 us/op 826.29 us/op 0.98
browser fromHex(blob) 782.98 ms/op 817.99 ms/op 0.96

by benchmarkbot/action

@philknows
Copy link
Member

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request addresses a critical bug where using the parent slot to fetch sync aggregates could lead to invalid block signatures, especially with missed slots around epoch boundaries. The fix correctly changes this to use the previous slot (slot - 1), aligning with the specification and preventing signature domain mismatches. The changes also include a nice cleanup, removing the now-redundant parentSlot parameter from several function signatures throughout the block production call stack. The implementation is solid and the reasoning is sound. I have no further suggestions.

Copy link
Contributor

@twoeths twoeths left a comment

Choose a reason for hiding this comment

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

looks good to me, adding this spec to remind us later

If a validator is in the current sync committee (i.e. is_assigned_to_sync_committee() above returns True), then for every slot in the current sync committee period, the validator should prepare a SyncCommitteeMessage for the previous slot (slot - 1) according to the logic in get_sync_committee_message as soon as they have determined the head block of slot - 1. This means that when assigned to slot a SyncCommitteeMessage is prepared and broadcast in slot-1  instead of slot.

hence it should be previous slot, not slot of parent block

@nflaig
Copy link
Member Author

nflaig commented Oct 3, 2025

adding this spec to remind us later

that's a hint too but I think the best evidence is in process_sync_aggregate

 previous_slot = max(state.slot, Slot(1)) - Slot(1)
 domain = get_domain(state, DOMAIN_SYNC_COMMITTEE, compute_epoch_at_slot(previous_slot))

since it uses previous slot for getting the domain we need to do the same to pack the block

@nflaig nflaig merged commit c9deb9b into unstable Oct 3, 2025
25 of 27 checks passed
@nflaig nflaig deleted the nflaig/sync-aggregate branch October 3, 2025 10:32
@wemeetagain
Copy link
Member

🎉 This PR is included in v1.35.0 🎉

nflaig added a commit that referenced this pull request Oct 9, 2025
**Motivation**

Since #7927 we started to use
parent slot (the slot of parent block) to get sync aggregate for block
but this can cause `BLOCK_ERROR_INVALID_SIGNATURE` during fork
transition as might include sync aggregates into our block that are
older than block slot - 1 which is used to compute domain during
signature verification.


https://github.com/ChainSafe/lodestar/blob/8961b06c11ac67baab79fe774e0a3d1bfb217ea1/packages/state-transition/src/block/processSyncCommittee.ts#L81

This means we will reject our own block due to invalid signature if
there are missed slots during the epoch transition.


> we currently hard code parent slot as producedSlot - 1 which is
incorrect in reorg scenario

This was the motivation from the previous change but this doesn't seem
right, sync committee messages are produced at latest 4 seconds into the
slot so if we re-org the block due to it being weak/late then sync
committee votes will also be for parent block root of that block and not
the block itself.

So irrespective of block being reorged or not we should always include
sync committee messages from previous slot.

**Description**

Use previous slot to get sync aggregate for block to avoid
`BLOCK_ERROR_INVALID_SIGNATURE` in case there are missed slots.
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.

5 participants