feat: implement should_apply_proposer_boost for gloas#9233
Conversation
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>
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
💡 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".
|
| 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
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).
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).
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).
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).
|
@ensi321 is this still relevant? |
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>
For some reason, our unstable is now passing these tests even without this PR and we failed them previously lodestar/packages/beacon-node/test/spec/presets/fork_choice.test.ts Lines 738 to 742 in e6d38ec But we still need to implement Added some new tests ethereum/consensus-specs#5441 to make sure this is covered. |
Codecov Report✅ All modified and coverable lines are covered by tests. 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:
|
There was a problem hiding this comment.
💡 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".
|
|
||
| // Indicate whether block arrives in a timely manner ie. before the 4 second mark | ||
| // Spec: Store.block_timeliness[ATTESTATION_TIMELINESS_INDEX] | ||
| timeliness: boolean; |
There was a problem hiding this comment.
we may want to use a single block_timeliness: number to represent these 2 flags, cc @wemeetagain to confirm
There was a problem hiding this comment.
Is there a real benefit to this? Using bits impact readability, and we are only saving one boolean field per ProtoNode
**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>
…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>
Summary
Implements the
should_apply_proposer_boostlogic 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 itsisHeadWeak()— boost-excluded attestation score + equivocator balance add-back — for the weak-parent check, exactly matching the spec'sis_head_weak(store, parent_root)call.Changes
ptcTimelinessandproposerIndexfields toProtoBlockisBlockPtcTimelyto track PTC deadline timelinessshouldApplyProposerBoostwhich withholds boost when the parent is a weak, equivocating block from the previous slotisHeadWeak()from feat: implement forkchoice is_head_weak() and is_parent_strong() #9654 for the weak-parent checkfindEquivocatingBlocksinProtoArrayto detect proposer equivocationsgetWeightonshouldApplyProposerBoost()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 onunstableand passes on this branch.AI Assistance Disclosure
Created with the help of Claude.
🤖 Generated with Claude Code