Skip to content

feat: implement should_apply_proposer_boost for gloas#9233

Open
ensi321 wants to merge 10 commits into
unstablefrom
nc/should-apply-proposer-boost
Open

feat: implement should_apply_proposer_boost for gloas#9233
ensi321 wants to merge 10 commits into
unstablefrom
nc/should-apply-proposer-boost

Conversation

@ensi321

@ensi321 ensi321 commented Apr 18, 2026

Copy link
Copy Markdown
Member

Summary

Implements the should_apply_proposer_boost logic ethereum/consensus-specs/pull/4807 (gloas/fork-choice.md#new-should_apply_proposer_boost).

Was blocked on is_head_weak(); now rebased on top of #9654 (merged) and reuses its isHeadWeak() — boost-excluded attestation score + equivocator balance add-back — for the weak-parent check, exactly matching the spec's is_head_weak(store, parent_root) call.

Changes

  • Add ptcTimeliness and proposerIndex fields to ProtoBlock
  • Add isBlockPtcTimely to track PTC deadline timeliness
  • Add shouldApplyProposerBoost which withholds boost when the parent is a weak, equivocating block from the previous slot
  • Reuse isHeadWeak() from feat: implement forkchoice is_head_weak() and is_parent_strong() #9654 for the weak-parent check
  • Add findEquivocatingBlocks in ProtoArray to detect proposer equivocations
  • Gate proposer boost in getWeight on shouldApplyProposerBoost()
  • Pre-gloas blocks retain unconditional boost (backward compatible)

Note on test coverage

The existing gloas fork_choice vectors pass with or without this gate (the withhold branch executes but never decides the asserted head), so CI alone doesn't discriminate it. Discriminating vectors are proposed upstream in consensus-specs (test_should_apply_proposer_boost.py, MC/DC over the three apply conditions); the withheld case fails on unstable and passes on this branch.

AI Assistance Disclosure

Created with the help of Claude.

🤖 Generated with Claude Code

Implement the `should_apply_proposer_boost` logic from consensus-specs
commit 71d1151 (PR #4807). This addresses the builder reveal safety
concern where a colluding next-slot proposer could use proposer boost
to override a legitimately revealed block.

Changes:
- Add `ptcTimeliness` and `proposerIndex` fields to ProtoBlock
- Add `isBlockPtcTimely` to track PTC deadline timeliness
- Add `shouldApplyProposerBoost` which withholds boost when the parent
  is a weak, equivocating block from the previous slot
- Add `findEquivocatingBlocks` in ProtoArray to detect proposer
  equivocations by scanning for PTC-timely blocks at the same slot
  from the same proposer
- Gate proposer boost in `getWeight` on `shouldApplyProposerBoost()`
- Pre-gloas blocks retain unconditional boost (backward compatible)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ensi321
ensi321 requested a review from a team as a code owner April 18, 2026 02:23

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

Copy link
Copy Markdown
Contributor

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 implements the Gloas fork-choice logic for proposer boost and PTC (Payload Timeliness Committee) timeliness. It adds ptcTimeliness and proposerIndex to block metadata and introduces the shouldApplyProposerBoost logic, which considers parent block weight and proposer equivocations. Feedback focuses on preventing a crash during the fork transition by using dynamic payload status for parent nodes and optimizing the performance of the equivocation check to avoid O(N) map iterations during head updates.

Comment thread packages/fork-choice/src/forkChoice/forkChoice.ts Outdated
Comment thread packages/fork-choice/src/protoArray/protoArray.ts Outdated
@ensi321
ensi321 marked this pull request as draft April 18, 2026 02:25

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9bc6eb6ce5

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/fork-choice/src/forkChoice/forkChoice.ts Outdated
@github-actions

github-actions Bot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

⚠️ Performance Alert ⚠️

Possible performance regression was detected for some benchmarks.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold.

Benchmark suite Current: 78920ed Previous: 4001398 Ratio
isKnown best case - 1 super set check 637.00 ns/op 182.00 ns/op 3.50
isKnown normal case - 2 super set checks 630.00 ns/op 166.00 ns/op 3.80
isKnown worse case - 16 super set checks 632.00 ns/op 184.00 ns/op 3.43
bytes32 Buffer.toString(hex) 594.00 ns/op 179.00 ns/op 3.32
bytes32 Buffer.toString(hex) + 0x 591.00 ns/op 181.00 ns/op 3.27
fastMsgIdFn h32 xxhash / 200 bytes 576.00 ns/op 162.00 ns/op 3.56
enrSubnets - fastDeserialize 4 bits 503.00 ns/op 105.00 ns/op 4.79
BeaconState.hashTreeRoot - No change 614.00 ns/op 193.00 ns/op 3.18
Full benchmark results
Benchmark suite Current: 78920ed Previous: 4001398 Ratio
getPubkeys - index2pubkey - req 1000 vs - 250000 vc 874.90 us/op 892.51 us/op 0.98
getPubkeys - validatorsArr - req 1000 vs - 250000 vc 39.992 us/op 39.707 us/op 1.01
BLS verify - blst 663.33 us/op 712.85 us/op 0.93
BLS verifyMultipleSignatures 3 - blst 1.4161 ms/op 1.3668 ms/op 1.04
BLS verifyMultipleSignatures 8 - blst 2.2435 ms/op 2.1764 ms/op 1.03
BLS verifyMultipleSignatures 32 - blst 7.0223 ms/op 6.8732 ms/op 1.02
BLS verifyMultipleSignatures 64 - blst 13.309 ms/op 13.605 ms/op 0.98
BLS verifyMultipleSignatures 128 - blst 25.951 ms/op 25.973 ms/op 1.00
BLS deserializing 10000 signatures 644.54 ms/op 645.65 ms/op 1.00
BLS deserializing 100000 signatures 6.4438 s/op 6.6012 s/op 0.98
BLS verifyMultipleSignatures - same message - 3 - blst 830.70 us/op 789.92 us/op 1.05
BLS verifyMultipleSignatures - same message - 8 - blst 964.84 us/op 948.19 us/op 1.02
BLS verifyMultipleSignatures - same message - 32 - blst 1.5697 ms/op 1.5389 ms/op 1.02
BLS verifyMultipleSignatures - same message - 64 - blst 2.3922 ms/op 2.4690 ms/op 0.97
BLS verifyMultipleSignatures - same message - 128 - blst 4.1238 ms/op 4.1134 ms/op 1.00
BLS aggregatePubkeys 32 - blst 17.933 us/op 17.859 us/op 1.00
BLS aggregatePubkeys 128 - blst 63.584 us/op 63.461 us/op 1.00
getSlashingsAndExits - default max 51.341 us/op 46.159 us/op 1.11
getSlashingsAndExits - 2k 325.11 us/op 330.92 us/op 0.98
proposeBlockBody type=full, size=empty 719.93 us/op 659.29 us/op 1.09
isKnown best case - 1 super set check 637.00 ns/op 182.00 ns/op 3.50
isKnown normal case - 2 super set checks 630.00 ns/op 166.00 ns/op 3.80
isKnown worse case - 16 super set checks 632.00 ns/op 184.00 ns/op 3.43
validate api signedAggregateAndProof - struct 1.5807 ms/op 1.5395 ms/op 1.03
validate gossip signedAggregateAndProof - struct 1.5760 ms/op 1.5358 ms/op 1.03
batch validate gossip attestation - vc 640000 - chunk 32 108.22 us/op 111.93 us/op 0.97
batch validate gossip attestation - vc 640000 - chunk 64 94.318 us/op 97.778 us/op 0.96
batch validate gossip attestation - vc 640000 - chunk 128 87.211 us/op 91.163 us/op 0.96
batch validate gossip attestation - vc 640000 - chunk 256 83.316 us/op 87.106 us/op 0.96
bytes32 toHexString 755.00 ns/op 294.00 ns/op 2.57
bytes32 Buffer.toString(hex) 594.00 ns/op 179.00 ns/op 3.32
bytes32 Buffer.toString(hex) from Uint8Array 661.00 ns/op 246.00 ns/op 2.69
bytes32 Buffer.toString(hex) + 0x 591.00 ns/op 181.00 ns/op 3.27
Return object 10000 times 0.24890 ns/op 0.21660 ns/op 1.15
Throw Error 10000 times 3.4207 us/op 3.4863 us/op 0.98
toHex 101.96 ns/op 101.27 ns/op 1.01
Buffer.from 89.983 ns/op 95.288 ns/op 0.94
shared Buffer 62.054 ns/op 60.527 ns/op 1.03
fastMsgIdFn sha256 / 200 bytes 1.9680 us/op 1.5070 us/op 1.31
fastMsgIdFn h32 xxhash / 200 bytes 576.00 ns/op 162.00 ns/op 3.56
fastMsgIdFn h64 xxhash / 200 bytes 636.00 ns/op 218.00 ns/op 2.92
fastMsgIdFn sha256 / 1000 bytes 5.3140 us/op 4.8890 us/op 1.09
fastMsgIdFn h32 xxhash / 1000 bytes 668.00 ns/op 244.00 ns/op 2.74
fastMsgIdFn h64 xxhash / 1000 bytes 689.00 ns/op 265.00 ns/op 2.60
fastMsgIdFn sha256 / 10000 bytes 43.674 us/op 43.176 us/op 1.01
fastMsgIdFn h32 xxhash / 10000 bytes 1.6990 us/op 1.2870 us/op 1.32
fastMsgIdFn h64 xxhash / 10000 bytes 1.2710 us/op 834.00 ns/op 1.52
send data - 1000 256B messages 4.1449 ms/op 5.1457 ms/op 0.81
send data - 1000 512B messages 5.0322 ms/op 5.9728 ms/op 0.84
send data - 1000 1024B messages 5.5776 ms/op 6.2512 ms/op 0.89
send data - 1000 1200B messages 6.2966 ms/op 8.5089 ms/op 0.74
send data - 1000 2048B messages 10.278 ms/op 17.122 ms/op 0.60
send data - 1000 4096B messages 79.887 ms/op 112.62 ms/op 0.71
send data - 1000 16384B messages 364.56 ms/op 375.64 ms/op 0.97
send data - 1000 65536B messages 1.1182 s/op 1.7644 s/op 0.63
enrSubnets - fastDeserialize 64 bits 1.2350 us/op 786.00 ns/op 1.57
enrSubnets - ssz BitVector 64 bits 687.00 ns/op 272.00 ns/op 2.53
enrSubnets - fastDeserialize 4 bits 503.00 ns/op 105.00 ns/op 4.79
enrSubnets - ssz BitVector 4 bits 687.00 ns/op 274.00 ns/op 2.51
prioritizePeers score -10:0 att 32-0.1 sync 2-0 211.15 us/op 213.18 us/op 0.99
prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 233.90 us/op 241.41 us/op 0.97
prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 341.03 us/op 355.15 us/op 0.96
prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 595.49 us/op 638.56 us/op 0.93
prioritizePeers score 0:0 att 64-1 sync 4-1 694.33 us/op 721.89 us/op 0.96
array of 16000 items push then shift 1.3172 us/op 1.3181 us/op 1.00
LinkedList of 16000 items push then shift 7.2100 ns/op 7.6070 ns/op 0.95
array of 16000 items push then pop 71.708 ns/op 76.460 ns/op 0.94
LinkedList of 16000 items push then pop 6.4020 ns/op 6.2120 ns/op 1.03
array of 24000 items push then shift 1.9514 us/op 1.9740 us/op 0.99
LinkedList of 24000 items push then shift 6.7550 ns/op 7.2490 ns/op 0.93
array of 24000 items push then pop 100.44 ns/op 111.78 ns/op 0.90
LinkedList of 24000 items push then pop 6.4300 ns/op 6.2740 ns/op 1.02
intersect bitArray bitLen 8 4.3210 ns/op 4.0250 ns/op 1.07
intersect array and set length 8 31.834 ns/op 30.477 ns/op 1.04
intersect bitArray bitLen 128 23.612 ns/op 24.098 ns/op 0.98
intersect array and set length 128 520.61 ns/op 513.07 ns/op 1.01
bitArray.getTrueBitIndexes() bitLen 128 1.3470 us/op 967.00 ns/op 1.39
bitArray.getTrueBitIndexes() bitLen 248 2.1130 us/op 1.7410 us/op 1.21
bitArray.getTrueBitIndexes() bitLen 512 3.8840 us/op 3.6740 us/op 1.06
Full columns - reconstruct all 6 blobs 149.92 us/op 156.33 us/op 0.96
Full columns - reconstruct half of the blobs out of 6 162.63 us/op 85.463 us/op 1.90
Full columns - reconstruct single blob out of 6 61.675 us/op 35.587 us/op 1.73
Half columns - reconstruct all 6 blobs 383.80 ms/op 408.72 ms/op 0.94
Half columns - reconstruct half of the blobs out of 6 192.44 ms/op 203.88 ms/op 0.94
Half columns - reconstruct single blob out of 6 67.729 ms/op 71.196 ms/op 0.95
Set add up to 64 items then delete first 1.7367 us/op 1.7187 us/op 1.01
OrderedSet add up to 64 items then delete first 2.5990 us/op 2.6213 us/op 0.99
Set add up to 64 items then delete last 1.9541 us/op 1.9439 us/op 1.01
OrderedSet add up to 64 items then delete last 3.2005 us/op 2.8931 us/op 1.11
Set add up to 64 items then delete middle 2.0265 us/op 1.9262 us/op 1.05
OrderedSet add up to 64 items then delete middle 4.5214 us/op 4.3902 us/op 1.03
Set add up to 128 items then delete first 3.9009 us/op 3.8600 us/op 1.01
OrderedSet add up to 128 items then delete first 5.6276 us/op 5.9104 us/op 0.95
Set add up to 128 items then delete last 3.8700 us/op 3.7490 us/op 1.03
OrderedSet add up to 128 items then delete last 5.7677 us/op 5.6032 us/op 1.03
Set add up to 128 items then delete middle 3.8776 us/op 3.7668 us/op 1.03
OrderedSet add up to 128 items then delete middle 12.035 us/op 11.597 us/op 1.04
Set add up to 256 items then delete first 7.7535 us/op 7.6673 us/op 1.01
OrderedSet add up to 256 items then delete first 11.467 us/op 12.095 us/op 0.95
Set add up to 256 items then delete last 7.7177 us/op 7.2314 us/op 1.07
OrderedSet add up to 256 items then delete last 11.929 us/op 11.588 us/op 1.03
Set add up to 256 items then delete middle 7.6520 us/op 7.2067 us/op 1.06
OrderedSet add up to 256 items then delete middle 35.557 us/op 35.470 us/op 1.00
runFastConfirmationRules vc:100000 bc:96 eq:0 5.2672 ms/op 4.5352 ms/op 1.16
runFastConfirmationRules vc:600000 bc:96 eq:0 35.685 ms/op 34.787 ms/op 1.03
runFastConfirmationRules vc:1000000 bc:96 eq:0 58.831 ms/op 57.986 ms/op 1.01
runFastConfirmationRules vc:600000 bc:320 eq:0 35.331 ms/op 35.322 ms/op 1.00
runFastConfirmationRules vc:100000 bc:96 eq:1000 1.1072 s/op 1.1136 s/op 0.99
pass gossip attestations to forkchoice per slot 2.5792 ms/op 2.6281 ms/op 0.98
forkChoice updateHead vc 100000 bc 64 eq 0 478.85 us/op 412.66 us/op 1.16
forkChoice updateHead vc 600000 bc 64 eq 0 2.8487 ms/op 2.4515 ms/op 1.16
forkChoice updateHead vc 1000000 bc 64 eq 0 4.7035 ms/op 4.0830 ms/op 1.15
forkChoice updateHead vc 600000 bc 320 eq 0 2.8222 ms/op 2.4786 ms/op 1.14
forkChoice updateHead vc 600000 bc 1200 eq 0 2.8863 ms/op 2.6070 ms/op 1.11
forkChoice updateHead vc 600000 bc 7200 eq 0 3.1859 ms/op 2.8684 ms/op 1.11
forkChoice updateHead vc 600000 bc 64 eq 1000 2.8756 ms/op 2.4628 ms/op 1.17
forkChoice updateHead vc 600000 bc 64 eq 10000 2.9901 ms/op 2.5672 ms/op 1.16
forkChoice updateHead vc 600000 bc 64 eq 300000 7.0174 ms/op 6.5621 ms/op 1.07
forkChoice updateHead vc 600000 bc 64 eq 0 gloas boosted 2.8429 ms/op
computeDeltas 1400000 validators 0% inactive 13.566 ms/op 12.303 ms/op 1.10
computeDeltas 1400000 validators 10% inactive 12.889 ms/op 11.626 ms/op 1.11
computeDeltas 1400000 validators 20% inactive 12.168 ms/op 10.963 ms/op 1.11
computeDeltas 1400000 validators 50% inactive 9.8331 ms/op 8.9129 ms/op 1.10
computeDeltas 2100000 validators 0% inactive 20.345 ms/op 18.412 ms/op 1.10
computeDeltas 2100000 validators 10% inactive 19.278 ms/op 17.531 ms/op 1.10
computeDeltas 2100000 validators 20% inactive 18.147 ms/op 16.351 ms/op 1.11
computeDeltas 2100000 validators 50% inactive 14.700 ms/op 10.910 ms/op 1.35
altair processAttestation - 250000 vs - 7PWei normalcase 1.7227 ms/op 1.8797 ms/op 0.92
altair processAttestation - 250000 vs - 7PWei worstcase 2.4860 ms/op 2.4435 ms/op 1.02
altair processAttestation - setStatus - 1/6 committees join 105.80 us/op 102.05 us/op 1.04
altair processAttestation - setStatus - 1/3 committees join 217.67 us/op 209.59 us/op 1.04
altair processAttestation - setStatus - 1/2 committees join 295.65 us/op 291.33 us/op 1.01
altair processAttestation - setStatus - 2/3 committees join 384.50 us/op 380.88 us/op 1.01
altair processAttestation - setStatus - 4/5 committees join 525.19 us/op 536.85 us/op 0.98
altair processAttestation - setStatus - 100% committees join 633.00 us/op 625.88 us/op 1.01
altair processBlock - 250000 vs - 7PWei normalcase 2.9203 ms/op 3.5830 ms/op 0.82
altair processBlock - 250000 vs - 7PWei normalcase hashState 14.318 ms/op 15.421 ms/op 0.93
altair processBlock - 250000 vs - 7PWei worstcase 19.527 ms/op 20.872 ms/op 0.94
altair processBlock - 250000 vs - 7PWei worstcase hashState 39.148 ms/op 41.282 ms/op 0.95
phase0 processBlock - 250000 vs - 7PWei normalcase 1.3470 ms/op 1.3561 ms/op 0.99
phase0 processBlock - 250000 vs - 7PWei worstcase 17.107 ms/op 16.414 ms/op 1.04
altair processEth1Data - 250000 vs - 7PWei normalcase 303.85 us/op 316.43 us/op 0.96
getExpectedWithdrawals 250000 eb:1,eth1:1,we:0,wn:0,smpl:16 3.7760 us/op 3.4460 us/op 1.10
getExpectedWithdrawals 250000 eb:0.95,eth1:0.1,we:0.05,wn:0,smpl:220 23.787 us/op 21.942 us/op 1.08
getExpectedWithdrawals 250000 eb:0.95,eth1:0.3,we:0.05,wn:0,smpl:43 6.7000 us/op 5.9890 us/op 1.12
getExpectedWithdrawals 250000 eb:0.95,eth1:0.7,we:0.05,wn:0,smpl:19 4.2560 us/op 4.8150 us/op 0.88
getExpectedWithdrawals 250000 eb:0.1,eth1:0.1,we:0,wn:0,smpl:1021 103.27 us/op 90.481 us/op 1.14
getExpectedWithdrawals 250000 eb:0.03,eth1:0.03,we:0,wn:0,smpl:11778 1.4302 ms/op 1.4215 ms/op 1.01
getExpectedWithdrawals 250000 eb:0.01,eth1:0.01,we:0,wn:0,smpl:16384 1.8727 ms/op 1.8590 ms/op 1.01
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,smpl:16384 1.8787 ms/op 1.8706 ms/op 1.00
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,nocache,smpl:16384 3.6068 ms/op 3.7336 ms/op 0.97
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,smpl:16384 2.1262 ms/op 2.1087 ms/op 1.01
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,nocache,smpl:16384 3.8437 ms/op 4.2521 ms/op 0.90
Tree 40 250000 create 325.95 ms/op 312.19 ms/op 1.04
Tree 40 250000 get(125000) 107.29 ns/op 95.075 ns/op 1.13
Tree 40 250000 set(125000) 1.1086 us/op 1.0274 us/op 1.08
Tree 40 250000 toArray() 9.8793 ms/op 10.225 ms/op 0.97
Tree 40 250000 iterate all - toArray() + loop 10.024 ms/op 10.086 ms/op 0.99
Tree 40 250000 iterate all - get(i) 34.206 ms/op 39.272 ms/op 0.87
Array 250000 create 2.2946 ms/op 2.4119 ms/op 0.95
Array 250000 clone - spread 722.86 us/op 760.03 us/op 0.95
Array 250000 get(125000) 0.67500 ns/op 0.29700 ns/op 2.27
Array 250000 set(125000) 0.68800 ns/op 0.30200 ns/op 2.28
Array 250000 iterate all - loop 57.084 us/op 58.595 us/op 0.97
phase0 afterProcessEpoch - 250000 vs - 7PWei 59.646 ms/op 62.389 ms/op 0.96
Array.fill - length 1000000 2.4167 ms/op 2.2139 ms/op 1.09
Array push - length 1000000 8.2445 ms/op 8.4684 ms/op 0.97
Array.get 0.20806 ns/op 0.20966 ns/op 0.99
Uint8Array.get 0.26246 ns/op 0.24103 ns/op 1.09
phase0 beforeProcessEpoch - 250000 vs - 7PWei 11.766 ms/op 13.246 ms/op 0.89
altair processEpoch - mainnet_e81889 250.72 ms/op 268.93 ms/op 0.93
mainnet_e81889 - altair beforeProcessEpoch 14.341 ms/op 37.041 ms/op 0.39
mainnet_e81889 - altair processJustificationAndFinalization 5.5520 us/op 5.3900 us/op 1.03
mainnet_e81889 - altair processInactivityUpdates 3.8776 ms/op 3.6947 ms/op 1.05
mainnet_e81889 - altair processRewardsAndPenalties 18.156 ms/op 17.392 ms/op 1.04
mainnet_e81889 - altair processRegistryUpdates 1.0120 us/op 583.00 ns/op 1.74
mainnet_e81889 - altair processSlashings 590.00 ns/op 147.00 ns/op 4.01
mainnet_e81889 - altair processEth1DataReset 582.00 ns/op 147.00 ns/op 3.96
mainnet_e81889 - altair processEffectiveBalanceUpdates 1.6548 ms/op 1.3027 ms/op 1.27
mainnet_e81889 - altair processSlashingsReset 1.1500 us/op 713.00 ns/op 1.61
mainnet_e81889 - altair processRandaoMixesReset 1.6790 us/op 1.0840 us/op 1.55
mainnet_e81889 - altair processHistoricalRootsUpdate 586.00 ns/op 143.00 ns/op 4.10
mainnet_e81889 - altair processParticipationFlagUpdates 882.00 ns/op 459.00 ns/op 1.92
mainnet_e81889 - altair processSyncCommitteeUpdates 551.00 ns/op 122.00 ns/op 4.52
mainnet_e81889 - altair afterProcessEpoch 41.146 ms/op 41.501 ms/op 0.99
capella processEpoch - mainnet_e217614 724.98 ms/op 761.08 ms/op 0.95
mainnet_e217614 - capella beforeProcessEpoch 65.916 ms/op 56.640 ms/op 1.16
mainnet_e217614 - capella processJustificationAndFinalization 6.2020 us/op 5.5380 us/op 1.12
mainnet_e217614 - capella processInactivityUpdates 12.571 ms/op 13.196 ms/op 0.95
mainnet_e217614 - capella processRewardsAndPenalties 91.645 ms/op 90.285 ms/op 1.02
mainnet_e217614 - capella processRegistryUpdates 4.9640 us/op 4.5780 us/op 1.08
mainnet_e217614 - capella processSlashings 584.00 ns/op 140.00 ns/op 4.17
mainnet_e217614 - capella processEth1DataReset 576.00 ns/op 136.00 ns/op 4.24
mainnet_e217614 - capella processEffectiveBalanceUpdates 5.4187 ms/op 12.265 ms/op 0.44
mainnet_e217614 - capella processSlashingsReset 1.1230 us/op 692.00 ns/op 1.62
mainnet_e217614 - capella processRandaoMixesReset 1.5730 us/op 1.1920 us/op 1.32
mainnet_e217614 - capella processHistoricalRootsUpdate 580.00 ns/op 139.00 ns/op 4.17
mainnet_e217614 - capella processParticipationFlagUpdates 869.00 ns/op 460.00 ns/op 1.89
mainnet_e217614 - capella afterProcessEpoch 108.19 ms/op 109.82 ms/op 0.99
phase0 processEpoch - mainnet_e58758 257.12 ms/op 292.29 ms/op 0.88
mainnet_e58758 - phase0 beforeProcessEpoch 51.877 ms/op 61.189 ms/op 0.85
mainnet_e58758 - phase0 processJustificationAndFinalization 6.1090 us/op 5.9150 us/op 1.03
mainnet_e58758 - phase0 processRewardsAndPenalties 15.752 ms/op 15.279 ms/op 1.03
mainnet_e58758 - phase0 processRegistryUpdates 2.6820 us/op 2.2790 us/op 1.18
mainnet_e58758 - phase0 processSlashings 576.00 ns/op 155.00 ns/op 3.72
mainnet_e58758 - phase0 processEth1DataReset 574.00 ns/op 140.00 ns/op 4.10
mainnet_e58758 - phase0 processEffectiveBalanceUpdates 1.1660 ms/op 826.91 us/op 1.41
mainnet_e58758 - phase0 processSlashingsReset 1.2600 us/op 839.00 ns/op 1.50
mainnet_e58758 - phase0 processRandaoMixesReset 1.6700 us/op 1.6210 us/op 1.03
mainnet_e58758 - phase0 processHistoricalRootsUpdate 577.00 ns/op 142.00 ns/op 4.06
mainnet_e58758 - phase0 processParticipationRecordUpdates 1.4940 us/op 1.0550 us/op 1.42
mainnet_e58758 - phase0 afterProcessEpoch 33.080 ms/op 33.903 ms/op 0.98
phase0 processEffectiveBalanceUpdates - 250000 normalcase 1.0467 ms/op 1.0199 ms/op 1.03
phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 1.6701 ms/op 1.2445 ms/op 1.34
altair processInactivityUpdates - 250000 normalcase 10.767 ms/op 10.738 ms/op 1.00
altair processInactivityUpdates - 250000 worstcase 10.686 ms/op 10.763 ms/op 0.99
phase0 processRegistryUpdates - 250000 normalcase 2.7460 us/op 3.4130 us/op 0.80
phase0 processRegistryUpdates - 250000 badcase_full_deposits 151.04 us/op 145.05 us/op 1.04
phase0 processRegistryUpdates - 250000 worstcase 0.5 51.613 ms/op 61.481 ms/op 0.84
altair processRewardsAndPenalties - 250000 normalcase 13.878 ms/op 13.971 ms/op 0.99
altair processRewardsAndPenalties - 250000 worstcase 13.327 ms/op 14.063 ms/op 0.95
phase0 getAttestationDeltas - 250000 normalcase 5.5887 ms/op 5.6743 ms/op 0.98
phase0 getAttestationDeltas - 250000 worstcase 5.6050 ms/op 5.7361 ms/op 0.98
phase0 processSlashings - 250000 worstcase 61.207 us/op 62.054 us/op 0.99
altair processSyncCommitteeUpdates - 250000 10.066 ms/op 10.186 ms/op 0.99
BeaconState.hashTreeRoot - No change 614.00 ns/op 193.00 ns/op 3.18
BeaconState.hashTreeRoot - 1 full validator 57.457 us/op 59.750 us/op 0.96
BeaconState.hashTreeRoot - 32 full validator 642.27 us/op 655.24 us/op 0.98
BeaconState.hashTreeRoot - 512 full validator 6.1327 ms/op 5.8810 ms/op 1.04
BeaconState.hashTreeRoot - 1 validator.effectiveBalance 70.177 us/op 78.784 us/op 0.89
BeaconState.hashTreeRoot - 32 validator.effectiveBalance 1.0459 ms/op 1.2407 ms/op 0.84
BeaconState.hashTreeRoot - 512 validator.effectiveBalance 13.261 ms/op 14.032 ms/op 0.95
BeaconState.hashTreeRoot - 1 balances 56.545 us/op 59.438 us/op 0.95
BeaconState.hashTreeRoot - 32 balances 551.84 us/op 576.20 us/op 0.96
BeaconState.hashTreeRoot - 512 balances 4.6487 ms/op 4.5820 ms/op 1.01
BeaconState.hashTreeRoot - 250000 balances 89.079 ms/op 105.28 ms/op 0.85
aggregationBits - 2048 els - zipIndexesInBitList 20.203 us/op 19.585 us/op 1.03
regular array get 100000 times 23.043 us/op 23.167 us/op 0.99
wrappedArray get 100000 times 23.032 us/op 23.137 us/op 1.00
arrayWithProxy get 100000 times 13.172 ms/op 10.379 ms/op 1.27
ssz.Root.equals 21.788 ns/op 21.742 ns/op 1.00
byteArrayEquals 21.606 ns/op 21.541 ns/op 1.00
Buffer.compare 9.9500 ns/op 8.9510 ns/op 1.11
processSlot - 1 slots 8.5090 us/op 9.3890 us/op 0.91
processSlot - 32 slots 1.6074 ms/op 1.9757 ms/op 0.81
getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei 2.2681 ms/op 2.1699 ms/op 1.05
getCommitteeAssignments - req 1 vs - 250000 vc 1.6919 ms/op 1.7141 ms/op 0.99
getCommitteeAssignments - req 100 vs - 250000 vc 3.4517 ms/op 3.4900 ms/op 0.99
getCommitteeAssignments - req 1000 vs - 250000 vc 3.6868 ms/op 3.7213 ms/op 0.99
findModifiedValidators - 10000 modified validators 732.65 ms/op 788.58 ms/op 0.93
findModifiedValidators - 1000 modified validators 466.74 ms/op 518.42 ms/op 0.90
findModifiedValidators - 100 modified validators 248.30 ms/op 301.30 ms/op 0.82
findModifiedValidators - 10 modified validators 215.77 ms/op 260.45 ms/op 0.83
findModifiedValidators - 1 modified validators 163.37 ms/op 145.37 ms/op 1.12
findModifiedValidators - no difference 143.26 ms/op 146.62 ms/op 0.98
migrate state 1500000 validators, 3400 modified, 2000 new 2.6567 s/op 2.5361 s/op 1.05
RootCache.getBlockRootAtSlot - 250000 vs - 7PWei 7.7000 ns/op 3.7700 ns/op 2.04
state getBlockRootAtSlot - 250000 vs - 7PWei 280.08 ns/op 280.53 ns/op 1.00
computeProposerIndex 100000 validators 1.3375 ms/op 1.3457 ms/op 0.99
getNextSyncCommitteeIndices 1000 validators 2.8910 ms/op 2.8965 ms/op 1.00
getNextSyncCommitteeIndices 10000 validators 25.364 ms/op 25.473 ms/op 1.00
getNextSyncCommitteeIndices 100000 validators 88.185 ms/op 87.952 ms/op 1.00
computeProposers - vc 250000 548.26 us/op 553.08 us/op 0.99
computeEpochShuffling - vc 250000 40.607 ms/op 39.342 ms/op 1.03
getNextSyncCommittee - vc 250000 9.5088 ms/op 9.4688 ms/op 1.00
nodejs block root to RootHex using toHex 106.27 ns/op 97.077 ns/op 1.09
nodejs block root to RootHex using toRootHex 64.687 ns/op 62.602 ns/op 1.03
nodejs fromHex(blob) 758.10 us/op 729.84 us/op 1.04
nodejs fromHexInto(blob) 672.89 us/op 633.81 us/op 1.06
nodejs block root to RootHex using the deprecated toHexString 498.11 ns/op 378.50 ns/op 1.32
nodejs byteArrayEquals 32 bytes (block root) 26.184 ns/op 26.053 ns/op 1.01
nodejs byteArrayEquals 48 bytes (pubkey) 37.754 ns/op 37.733 ns/op 1.00
nodejs byteArrayEquals 96 bytes (signature) 41.845 ns/op 35.905 ns/op 1.17
nodejs byteArrayEquals 1024 bytes 43.587 ns/op 43.048 ns/op 1.01
nodejs byteArrayEquals 131072 bytes (blob) 1.7775 us/op 1.7574 us/op 1.01
browser block root to RootHex using toHex 148.97 ns/op 145.43 ns/op 1.02
browser block root to RootHex using toRootHex 134.10 ns/op 129.97 ns/op 1.03
browser fromHex(blob) 1.5773 ms/op 1.6342 ms/op 0.97
browser fromHexInto(blob) 671.32 us/op 634.64 us/op 1.06
browser block root to RootHex using the deprecated toHexString 347.89 ns/op 482.15 ns/op 0.72
browser byteArrayEquals 32 bytes (block root) 28.066 ns/op 28.082 ns/op 1.00
browser byteArrayEquals 48 bytes (pubkey) 39.421 ns/op 39.625 ns/op 0.99
browser byteArrayEquals 96 bytes (signature) 73.414 ns/op 74.525 ns/op 0.99
browser byteArrayEquals 1024 bytes 757.68 ns/op 758.09 ns/op 1.00
browser byteArrayEquals 131072 bytes (blob) 95.315 us/op 95.555 us/op 1.00

by benchmarkbot/action

GrapeBaBa added a commit to GrapeBaBa/lodestar that referenced this pull request May 1, 2026
Wire the consensus-specs Fork Choice Compliance suite (ChainSafe#3831) into
the existing `forkChoiceTest` runner. The on-disk layout matches the
standard spec-test layout
(`tests/<preset>/<fork>/fork_choice_compliance/<handler>/<suite>/<case>/`),
so it slots in alongside `fork_choice` and `sync` runners.

Three test-only accommodations the compliance fixtures require:

1. `bls_setting: 2` — every compliance fixture uses placeholder
   signatures. Pass `validSignatures: testcase.meta?.bls_setting !==
   BigInt(1)` to `chain.processBlock` so verification short-circuits.
   Standard `fork_choice` fixtures use `bls_setting: 1` so behavior
   there is unchanged.

2. `BLOCK_ERROR_ALREADY_KNOWN` — compliance fixtures intentionally
   re-import the same block (`dup_shift` mutations in their
   `meta.yaml`). Spec semantics for `on_block(store, known_block)` is
   a no-op success. Production block import correctly rejects with
   ALREADY_KNOWN; this runner treats that case as success only when
   the step is `valid: true`.

3. Cross-epoch attestation shuffling — `on_attestation` decodes
   aggregation_bits using the state at the attestation's target
   checkpoint, not the head state. The runner now resolves the right
   shuffling via ShufflingCache + regen (mirroring the production
   validation path) instead of `headState.epochCtx.getIndexedAttestation`,
   which only worked when the attestation's epoch happened to be in
   the head's epoch cache (±1 epoch) and broke on cross-epoch fork
   attestations surfaced by the compliance suite.

Adds support for two compliance-only check fields:

- `viable_for_head_roots_and_weights` (consensus-specs#3831): compared
  via `getViableHeads()`. Both sides are sorted by root before
  comparison since the spec doesn't fix order.
- `head_payload_status` (gloas): mapped between our internal enum
  ordering (PENDING=0, EMPTY=1, FULL=2) and spec ordering (EMPTY=0,
  FULL=1, PENDING=2).

Pass rate against the latest comptests workflow `small.tar.gz` artifact:

  fulu/fork_choice_compliance: 253/1472 cases pass (17.2%)

Top remaining failures:
- ~80% `Invalid proposer boost root` — consensus-specs#4807 introduced
  a `block.proposer_index == get_beacon_proposer_index(head_state)`
  guard in `update_proposer_boost_root` that we do not yet implement;
  affects all forks (not just gloas equivocation handling). Tracked
  for follow-up alongside ChainSafe#9233.
- ~1% `Invalid viable heads` — proposer-boost rounding on minimal
  preset (see `getViableHeads()` weight note).
GrapeBaBa added a commit to GrapeBaBa/lodestar that referenced this pull request May 1, 2026
Wire the consensus-specs Fork Choice Compliance suite (ChainSafe#3831) into
the existing `forkChoiceTest` runner. The on-disk layout matches the
standard spec-test layout
(`tests/<preset>/<fork>/fork_choice_compliance/<handler>/<suite>/<case>/`),
so it slots in alongside `fork_choice` and `sync` runners.

Three test-only accommodations the compliance fixtures require:

1. `bls_setting: 2` — every compliance fixture uses placeholder
   signatures. Pass `validSignatures: testcase.meta?.bls_setting !==
   BigInt(1)` to `chain.processBlock` so verification short-circuits.
   Standard `fork_choice` fixtures use `bls_setting: 1` so behavior
   there is unchanged.

2. `BLOCK_ERROR_ALREADY_KNOWN` — compliance fixtures intentionally
   re-import the same block (`dup_shift` mutations in their
   `meta.yaml`). Spec semantics for `on_block(store, known_block)` is
   a no-op success. Production block import correctly rejects with
   ALREADY_KNOWN; this runner treats that case as success only when
   the step is `valid: true`.

3. Cross-epoch attestation shuffling — `on_attestation` decodes
   aggregation_bits using the state at the attestation's target
   checkpoint, not the head state. The runner now resolves the right
   shuffling via ShufflingCache + regen (mirroring the production
   validation path) instead of `headState.epochCtx.getIndexedAttestation`,
   which only worked when the attestation's epoch happened to be in
   the head's epoch cache (±1 epoch) and broke on cross-epoch fork
   attestations surfaced by the compliance suite.

Adds support for two compliance-only check fields:

- `viable_for_head_roots_and_weights` (consensus-specs#3831): compared
  via `getViableHeads()`. Both sides are sorted by root before
  comparison since the spec doesn't fix order.
- `head_payload_status` (gloas): mapped between our internal enum
  ordering (PENDING=0, EMPTY=1, FULL=2) and spec ordering (EMPTY=0,
  FULL=1, PENDING=2).

Pass rate against the latest comptests workflow `small.tar.gz` artifact:

  fulu/fork_choice_compliance: 253/1472 cases pass (17.2%)

Top remaining failures:
- ~80% `Invalid proposer boost root` — consensus-specs#4807 introduced
  a `block.proposer_index == get_beacon_proposer_index(head_state)`
  guard in `update_proposer_boost_root` that we do not yet implement;
  affects all forks (not just gloas equivocation handling). Tracked
  for follow-up alongside ChainSafe#9233.
- ~1% `Invalid viable heads` — proposer-boost rounding on minimal
  preset (see `getViableHeads()` weight note).
GrapeBaBa added a commit to GrapeBaBa/lodestar that referenced this pull request May 1, 2026
Wire the consensus-specs Fork Choice Compliance suite (ChainSafe#3831) into
the existing `forkChoiceTest` runner. The on-disk layout matches the
standard spec-test layout
(`tests/<preset>/<fork>/fork_choice_compliance/<handler>/<suite>/<case>/`),
so it slots in alongside `fork_choice` and `sync` runners.

Three test-only accommodations the compliance fixtures require:

1. `bls_setting: 2` — every compliance fixture uses placeholder
   signatures. Pass `validSignatures: testcase.meta?.bls_setting !==
   BigInt(1)` to `chain.processBlock` so verification short-circuits.
   Standard `fork_choice` fixtures use `bls_setting: 1` so behavior
   there is unchanged.

2. `BLOCK_ERROR_ALREADY_KNOWN` — compliance fixtures intentionally
   re-import the same block (`dup_shift` mutations in their
   `meta.yaml`). Spec semantics for `on_block(store, known_block)` is
   a no-op success. Production block import correctly rejects with
   ALREADY_KNOWN; this runner treats that case as success only when
   the step is `valid: true`.

3. Cross-epoch attestation shuffling — `on_attestation` decodes
   aggregation_bits using the state at the attestation's target
   checkpoint, not the head state. The runner now resolves the right
   shuffling via ShufflingCache + regen (mirroring the production
   validation path) instead of `headState.epochCtx.getIndexedAttestation`,
   which only worked when the attestation's epoch happened to be in
   the head's epoch cache (±1 epoch) and broke on cross-epoch fork
   attestations surfaced by the compliance suite.

Adds support for two compliance-only check fields:

- `viable_for_head_roots_and_weights` (consensus-specs#3831): compared
  via `getViableHeads()`. Both sides are sorted by root before
  comparison since the spec doesn't fix order.
- `head_payload_status` (gloas): mapped between our internal enum
  ordering (PENDING=0, EMPTY=1, FULL=2) and spec ordering (EMPTY=0,
  FULL=1, PENDING=2).

Pass rate against the latest comptests workflow `small.tar.gz` artifact:

  fulu/fork_choice_compliance: 253/1472 cases pass (17.2%)

Top remaining failures:
- ~80% `Invalid proposer boost root` — consensus-specs#4807 introduced
  a `block.proposer_index == get_beacon_proposer_index(head_state)`
  guard in `update_proposer_boost_root` that we do not yet implement;
  affects all forks (not just gloas equivocation handling). Tracked
  for follow-up alongside ChainSafe#9233.
- ~1% `Invalid viable heads` — proposer-boost rounding on minimal
  preset (see `getViableHeads()` weight note).
GrapeBaBa added a commit to GrapeBaBa/lodestar that referenced this pull request Jul 7, 2026
Wire the consensus-specs Fork Choice Compliance suite (ChainSafe#3831) into
the existing `forkChoiceTest` runner. The on-disk layout matches the
standard spec-test layout
(`tests/<preset>/<fork>/fork_choice_compliance/<handler>/<suite>/<case>/`),
so it slots in alongside `fork_choice` and `sync` runners.

Three test-only accommodations the compliance fixtures require:

1. `bls_setting: 2` — every compliance fixture uses placeholder
   signatures. Pass `validSignatures: testcase.meta?.bls_setting !==
   BigInt(1)` to `chain.processBlock` so verification short-circuits.
   Standard `fork_choice` fixtures use `bls_setting: 1` so behavior
   there is unchanged.

2. `BLOCK_ERROR_ALREADY_KNOWN` — compliance fixtures intentionally
   re-import the same block (`dup_shift` mutations in their
   `meta.yaml`). Spec semantics for `on_block(store, known_block)` is
   a no-op success. Production block import correctly rejects with
   ALREADY_KNOWN; this runner treats that case as success only when
   the step is `valid: true`.

3. Cross-epoch attestation shuffling — `on_attestation` decodes
   aggregation_bits using the state at the attestation's target
   checkpoint, not the head state. The runner now resolves the right
   shuffling via ShufflingCache + regen (mirroring the production
   validation path) instead of `headState.epochCtx.getIndexedAttestation`,
   which only worked when the attestation's epoch happened to be in
   the head's epoch cache (±1 epoch) and broke on cross-epoch fork
   attestations surfaced by the compliance suite.

Adds support for two compliance-only check fields:

- `viable_for_head_roots_and_weights` (consensus-specs#3831): compared
  via `getViableHeads()`. Both sides are sorted by root before
  comparison since the spec doesn't fix order.
- `head_payload_status` (gloas): mapped between our internal enum
  ordering (PENDING=0, EMPTY=1, FULL=2) and spec ordering (EMPTY=0,
  FULL=1, PENDING=2).

Pass rate against the latest comptests workflow `small.tar.gz` artifact:

  fulu/fork_choice_compliance: 253/1472 cases pass (17.2%)

Top remaining failures:
- ~80% `Invalid proposer boost root` — consensus-specs#4807 introduced
  a `block.proposer_index == get_beacon_proposer_index(head_state)`
  guard in `update_proposer_boost_root` that we do not yet implement;
  affects all forks (not just gloas equivocation handling). Tracked
  for follow-up alongside ChainSafe#9233.
- ~1% `Invalid viable heads` — proposer-boost rounding on minimal
  preset (see `getViableHeads()` weight note).
@nflaig

nflaig commented Jul 8, 2026

Copy link
Copy Markdown
Member

@ensi321 is this still relevant?

ensi321 and others added 4 commits July 8, 2026 20:03
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Hardcoding PayloadStatus.PENDING in the weak-parent lookup crashes at
the gloas fork transition: the first gloas block's parent is pre-gloas
(FULL-only), and getNodeIndexByRootAndStatus throws INVALID_NODE_INDEX
when PENDING is requested for a pre-gloas root. The adjacent-parent
guard does not short-circuit this path at the boundary. Use
parentBlock.payloadStatus instead, matching the pattern already used
for the strong-variant lookup elsewhere in this file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ensi321

ensi321 commented Jul 9, 2026

Copy link
Copy Markdown
Member Author

@ensi321 is this still relevant?

For some reason, our unstable is now passing these tests even without this PR and we failed them previously

(name.includes("gloas") &&
(name.includes("simple_attempted_reorg_without_enough_ffg_votes") ||
name.includes("include_votes_another_empty_chain_with_enough_ffg_votes_current_epoch") ||
name.includes("include_votes_another_empty_chain_with_enough_ffg_votes_previous_epoch") ||
name.includes("include_votes_another_empty_chain_without_enough_ffg_votes_current_epoch"))),

But we still need to implement shouldApplyProposerBoost to enforce the correct behaviour.

Added some new tests ethereum/consensus-specs#5441 to make sure this is covered.

@ensi321
ensi321 marked this pull request as ready for review July 9, 2026 14:36
@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 52.49%. Comparing base (e6d38ec) to head (d2bb968).
⚠️ Report is 3 commits behind head on unstable.

Additional details and impacted files
@@            Coverage Diff            @@
##           unstable    #9233   +/-   ##
=========================================
  Coverage     52.49%   52.49%           
=========================================
  Files           848      848           
  Lines         60487    60486    -1     
  Branches       4466     4465    -1     
=========================================
  Hits          31755    31755           
  Misses        28670    28670           
+ Partials         62       61    -1     
🚀 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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d2bb9684f2

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/fork-choice/src/forkChoice/forkChoice.ts Outdated

// Indicate whether block arrives in a timely manner ie. before the 4 second mark
// Spec: Store.block_timeliness[ATTESTATION_TIMELINESS_INDEX]
timeliness: boolean;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

we may want to use a single block_timeliness: number to represent these 2 flags, cc @wemeetagain to confirm

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Is there a real benefit to this? Using bits impact readability, and we are only saving one boolean field per ProtoNode

@twoeths twoeths left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

implemented is_head_weak() in #9654
need to have it landed before this PR

wemeetagain pushed a commit that referenced this pull request Jul 24, 2026
**Motivation**

- is_head_weak() is needed for should_apply_proposer_boost() #9233
- we also need it for `getProposerHead()`

**Description**
- implement cross-fork
[is_head_weak()](https://github.com/ethereum/consensus-specs/blob/6408b990c0b4e58d4f50ae49b59384e82e5e06d8/specs/gloas/fork-choice.md?plain=1#L739)
spec api and use it in `getProposerHead()`
- track attestation weight in ProtoNode separately, weight still means
total weight
- back propagate boost deltas and attestation deltas separately
- also implement is_parent_strong()


**AI Assistance Disclosure**

- created with the help of Claude

---------

Co-authored-by: Tuyen Nguyen <twoeths@users.noreply.github.com>
Co-authored-by: bing <spiralladder@fastmail.com>
ensi321 and others added 4 commits July 24, 2026 18:08
…roposer-boost

# Conflicts:
#	packages/beacon-node/test/spec/presets/fork_choice.test.ts
…ProposerBoost

is_head_weak() landed via #9654 with the exact gloas semantics (boost-excluded
attestation score + committee-scoped equivocator balance add-back), so the
weak-parent check can call it directly instead of approximating with raw
node.weight against the reorg threshold.

Also adapt findEquivocatingBlocks to the current VariantIndices layout via
getDefaultNodeIndex, and add the new ProtoBlock fields to test fixtures that
were added on unstable since the last merge.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
should_apply_proposer_boost judges the parent via is_head_weak against the
attestations known to the store, but the decision ran before applyScoreChanges()
landed this round's deltas, so it read the previous round's attestation scores.
A parent crossing the weak threshold on the pending batch kept the boost
withheld, and a newly detected equivocator kept its discounted vote counted.

For a gloas boosted block, split the score update in two passes: attestation
deltas first, then the boost decision, then the boost deltas. Pre-gloas keeps
the single pass since the boost is unconditional there.

Also replace findEquivocatingBlocks() with hasEquivocatingBlock(): the only
caller tests emptiness, so return on first match instead of allocating a list.

The two ordering tests fail on the previous commit and pass on this one; the
spec vectors cannot catch this because the check step recomputes the head,
self-healing the transient decision before assertions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Measures the two-pass applyScoreChanges overhead against the single-pass
pre-gloas row: 2.96 ms/op vs 2.72 ms/op at vc 600k bc 64 (~9%), paid only
while a gloas block holds proposer boost.

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants