Skip to content

fix: emit head event whenever fork choice head changes - #9697

Merged
nflaig merged 2 commits into
unstablefrom
nflaig/head-event-on-recompute
Jul 24, 2026
Merged

fix: emit head event whenever fork choice head changes#9697
nflaig merged 2 commits into
unstablefrom
nflaig/head-event-on-recompute

Conversation

@nflaig

@nflaig nflaig commented Jul 23, 2026

Copy link
Copy Markdown
Member

The head event is only emitted on block import, but the node's head can change without a new block (e.g. attestation weight shifts during prepareNextSlot).

Emit head from recomputeForkChoiceHead on any head root change, so it fires on block import and standalone recomputes (e.g. prepareNextSlot) alike, instead of only from importBlock.

Note we intentionally don't emit on the proposer path (getProposerHead) when proposing a block ourselves, as that head change is superseded once we import our own block.

@nflaig
nflaig requested a review from a team as a code owner July 23, 2026 15:00

@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: 11e1b3a003

鈩癸笍 About Codex in GitHub

Your team has set up Codex to 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 馃憤.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1191 to +1193
const head = this.forkChoice.updateAndGetHead({mode: UpdateHeadOpt.GetCanonicalHead}).head;

if (head.blockRoot !== prevHead.blockRoot) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Emit head events from proposer-head recomputes too

When the first canonical recompute after attestation weight shifts happens through block production, this new emission path is bypassed: BeaconChain.getProposerHead() calls forkChoice.updateAndGetHead({mode: GetProposerHead}), and ForkChoice.updateAndGetHead() runs updateHead() for that mode before returning. In that scenario the cached canonical head changes, but no head SSE event is emitted, so validators watching head events can still miss the duty-dependent-root update until some later recomputeForkChoiceHead() call.

Useful? React with 馃憤聽/ 馃憥.

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.

this is intentional, we explicitly don't want to emit a head event on the proposer path when we are proposing a block ourselves

that head change is superseded once we import our own block, which emits the head event with the correct dependent roots, so head event consumers aren't affected in practice

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Performance Report

鉁旓笍 no performance regression detected

Full benchmark results
Benchmark suite Current: 31e191d Previous: 4606097 Ratio
getPubkeys - index2pubkey - req 1000 vs - 250000 vc 1.4056 ms/op 1.3772 ms/op 1.02
getPubkeys - validatorsArr - req 1000 vs - 250000 vc 40.119 us/op 41.123 us/op 0.98
BLS verify - blst 742.50 us/op 740.92 us/op 1.00
BLS verifyMultipleSignatures 3 - blst 1.3420 ms/op 1.3312 ms/op 1.01
BLS verifyMultipleSignatures 8 - blst 2.1688 ms/op 2.1256 ms/op 1.02
BLS verifyMultipleSignatures 32 - blst 6.8719 ms/op 6.7018 ms/op 1.03
BLS verifyMultipleSignatures 64 - blst 13.449 ms/op 13.603 ms/op 0.99
BLS verifyMultipleSignatures 128 - blst 26.300 ms/op 24.972 ms/op 1.05
BLS deserializing 10000 signatures 635.52 ms/op 617.23 ms/op 1.03
BLS deserializing 100000 signatures 6.2528 s/op 6.2087 s/op 1.01
BLS verifyMultipleSignatures - same message - 3 - blst 693.58 us/op 680.86 us/op 1.02
BLS verifyMultipleSignatures - same message - 8 - blst 873.53 us/op 901.09 us/op 0.97
BLS verifyMultipleSignatures - same message - 32 - blst 1.5363 ms/op 1.5302 ms/op 1.00
BLS verifyMultipleSignatures - same message - 64 - blst 2.3381 ms/op 2.3541 ms/op 0.99
BLS verifyMultipleSignatures - same message - 128 - blst 4.0027 ms/op 3.9236 ms/op 1.02
BLS aggregatePubkeys 32 - blst 17.470 us/op 17.150 us/op 1.02
BLS aggregatePubkeys 128 - blst 61.894 us/op 61.382 us/op 1.01
getSlashingsAndExits - default max 48.964 us/op 50.077 us/op 0.98
getSlashingsAndExits - 2k 332.36 us/op 429.60 us/op 0.77
proposeBlockBody type=full, size=empty 595.37 us/op 615.84 us/op 0.97
isKnown best case - 1 super set check 170.00 ns/op 179.00 ns/op 0.95
isKnown normal case - 2 super set checks 162.00 ns/op 175.00 ns/op 0.93
isKnown worse case - 16 super set checks 156.00 ns/op 177.00 ns/op 0.88
validate api signedAggregateAndProof - struct 1.4656 ms/op 1.5037 ms/op 0.97
validate gossip signedAggregateAndProof - struct 1.4481 ms/op 1.5029 ms/op 0.96
batch validate gossip attestation - vc 640000 - chunk 32 108.34 us/op 107.35 us/op 1.01
batch validate gossip attestation - vc 640000 - chunk 64 92.681 us/op 95.605 us/op 0.97
batch validate gossip attestation - vc 640000 - chunk 128 87.250 us/op 87.073 us/op 1.00
batch validate gossip attestation - vc 640000 - chunk 256 83.834 us/op 84.228 us/op 1.00
bytes32 toHexString 270.00 ns/op 274.00 ns/op 0.99
bytes32 Buffer.toString(hex) 154.00 ns/op 162.00 ns/op 0.95
bytes32 Buffer.toString(hex) from Uint8Array 224.00 ns/op 233.00 ns/op 0.96
bytes32 Buffer.toString(hex) + 0x 155.00 ns/op 166.00 ns/op 0.93
Return object 10000 times 0.20650 ns/op 0.21530 ns/op 0.96
Throw Error 10000 times 3.3317 us/op 3.7524 us/op 0.89
toHex 102.29 ns/op 106.17 ns/op 0.96
Buffer.from 82.292 ns/op 94.464 ns/op 0.87
shared Buffer 56.146 ns/op 58.173 ns/op 0.97
fastMsgIdFn sha256 / 200 bytes 1.4100 us/op 1.4880 us/op 0.95
fastMsgIdFn h32 xxhash / 200 bytes 152.00 ns/op 151.00 ns/op 1.01
fastMsgIdFn h64 xxhash / 200 bytes 192.00 ns/op 204.00 ns/op 0.94
fastMsgIdFn sha256 / 1000 bytes 4.6290 us/op 4.7110 us/op 0.98
fastMsgIdFn h32 xxhash / 1000 bytes 243.00 ns/op 237.00 ns/op 1.03
fastMsgIdFn h64 xxhash / 1000 bytes 245.00 ns/op 256.00 ns/op 0.96
fastMsgIdFn sha256 / 10000 bytes 41.278 us/op 41.965 us/op 0.98
fastMsgIdFn h32 xxhash / 10000 bytes 1.2290 us/op 1.3160 us/op 0.93
fastMsgIdFn h64 xxhash / 10000 bytes 787.00 ns/op 854.00 ns/op 0.92
send data - 1000 256B messages 4.6982 ms/op 4.5293 ms/op 1.04
send data - 1000 512B messages 6.4097 ms/op 5.6685 ms/op 1.13
send data - 1000 1024B messages 6.2698 ms/op 5.6613 ms/op 1.11
send data - 1000 1200B messages 6.8987 ms/op 6.5586 ms/op 1.05
send data - 1000 2048B messages 10.705 ms/op 25.252 ms/op 0.42
send data - 1000 4096B messages 59.997 ms/op 38.055 ms/op 1.58
send data - 1000 16384B messages 480.36 ms/op 251.37 ms/op 1.91
send data - 1000 65536B messages 1.3142 s/op 1.2544 s/op 1.05
enrSubnets - fastDeserialize 64 bits 734.00 ns/op 788.00 ns/op 0.93
enrSubnets - ssz BitVector 64 bits 256.00 ns/op 256.00 ns/op 1.00
enrSubnets - fastDeserialize 4 bits 100.00 ns/op 97.000 ns/op 1.03
enrSubnets - ssz BitVector 4 bits 262.00 ns/op 249.00 ns/op 1.05
prioritizePeers score -10:0 att 32-0.1 sync 2-0 197.01 us/op 204.06 us/op 0.97
prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 222.74 us/op 235.11 us/op 0.95
prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 331.17 us/op 343.03 us/op 0.97
prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 582.84 us/op 589.24 us/op 0.99
prioritizePeers score 0:0 att 64-1 sync 4-1 670.37 us/op 726.83 us/op 0.92
array of 16000 items push then shift 1.2211 us/op 1.2437 us/op 0.98
LinkedList of 16000 items push then shift 7.4520 ns/op 7.8670 ns/op 0.95
array of 16000 items push then pop 68.478 ns/op 77.891 ns/op 0.88
LinkedList of 16000 items push then pop 5.7730 ns/op 5.9720 ns/op 0.97
array of 24000 items push then shift 1.8271 us/op 1.8463 us/op 0.99
LinkedList of 24000 items push then shift 6.9580 ns/op 7.6330 ns/op 0.91
array of 24000 items push then pop 98.880 ns/op 109.23 ns/op 0.91
LinkedList of 24000 items push then pop 5.9720 ns/op 6.0300 ns/op 0.99
intersect bitArray bitLen 8 4.6300 ns/op 4.6960 ns/op 0.99
intersect array and set length 8 28.748 ns/op 28.993 ns/op 0.99
intersect bitArray bitLen 128 23.699 ns/op 23.575 ns/op 1.01
intersect array and set length 128 476.45 ns/op 486.37 ns/op 0.98
bitArray.getTrueBitIndexes() bitLen 128 898.00 ns/op 925.00 ns/op 0.97
bitArray.getTrueBitIndexes() bitLen 248 1.6200 us/op 1.6600 us/op 0.98
bitArray.getTrueBitIndexes() bitLen 512 3.4350 us/op 3.4950 us/op 0.98
Full columns - reconstruct all 6 blobs 120.57 us/op 189.24 us/op 0.64
Full columns - reconstruct half of the blobs out of 6 95.550 us/op 88.236 us/op 1.08
Full columns - reconstruct single blob out of 6 32.736 us/op 31.913 us/op 1.03
Half columns - reconstruct all 6 blobs 372.47 ms/op 386.94 ms/op 0.96
Half columns - reconstruct half of the blobs out of 6 187.36 ms/op 195.33 ms/op 0.96
Half columns - reconstruct single blob out of 6 67.306 ms/op 70.093 ms/op 0.96
Set add up to 64 items then delete first 1.5620 us/op 1.6177 us/op 0.97
OrderedSet add up to 64 items then delete first 2.3907 us/op 2.4882 us/op 0.96
Set add up to 64 items then delete last 1.7847 us/op 1.8435 us/op 0.97
OrderedSet add up to 64 items then delete last 2.6716 us/op 2.7584 us/op 0.97
Set add up to 64 items then delete middle 1.7867 us/op 1.8380 us/op 0.97
OrderedSet add up to 64 items then delete middle 4.1207 us/op 4.2189 us/op 0.98
Set add up to 128 items then delete first 3.5550 us/op 3.6883 us/op 0.96
OrderedSet add up to 128 items then delete first 5.4662 us/op 5.6611 us/op 0.97
Set add up to 128 items then delete last 3.4287 us/op 3.5433 us/op 0.97
OrderedSet add up to 128 items then delete last 5.1311 us/op 5.3183 us/op 0.96
Set add up to 128 items then delete middle 3.4032 us/op 3.5199 us/op 0.97
OrderedSet add up to 128 items then delete middle 10.795 us/op 11.129 us/op 0.97
Set add up to 256 items then delete first 7.1139 us/op 7.4335 us/op 0.96
OrderedSet add up to 256 items then delete first 11.687 us/op 12.848 us/op 0.91
Set add up to 256 items then delete last 6.9181 us/op 7.1283 us/op 0.97
OrderedSet add up to 256 items then delete last 10.689 us/op 12.028 us/op 0.89
Set add up to 256 items then delete middle 6.8046 us/op 7.0911 us/op 0.96
OrderedSet add up to 256 items then delete middle 33.489 us/op 35.060 us/op 0.96
runFastConfirmationRules vc:100000 bc:96 eq:0 4.3010 ms/op 4.4268 ms/op 0.97
runFastConfirmationRules vc:600000 bc:96 eq:0 33.294 ms/op 36.084 ms/op 0.92
runFastConfirmationRules vc:1000000 bc:96 eq:0 56.451 ms/op 59.945 ms/op 0.94
runFastConfirmationRules vc:600000 bc:320 eq:0 34.005 ms/op 36.487 ms/op 0.93
runFastConfirmationRules vc:100000 bc:96 eq:1000 1.0782 s/op 1.3795 s/op 0.78
pass gossip attestations to forkchoice per slot 2.5143 ms/op 2.5570 ms/op 0.98
forkChoice updateHead vc 100000 bc 64 eq 0 393.92 us/op 394.52 us/op 1.00
forkChoice updateHead vc 600000 bc 64 eq 0 2.4446 ms/op 2.3851 ms/op 1.02
forkChoice updateHead vc 1000000 bc 64 eq 0 4.0416 ms/op 4.5882 ms/op 0.88
forkChoice updateHead vc 600000 bc 320 eq 0 2.4700 ms/op 2.3624 ms/op 1.05
forkChoice updateHead vc 600000 bc 1200 eq 0 2.4076 ms/op 2.4023 ms/op 1.00
forkChoice updateHead vc 600000 bc 7200 eq 0 2.8985 ms/op 2.9318 ms/op 0.99
forkChoice updateHead vc 600000 bc 64 eq 1000 2.3645 ms/op 2.3458 ms/op 1.01
forkChoice updateHead vc 600000 bc 64 eq 10000 2.4650 ms/op 2.4208 ms/op 1.02
forkChoice updateHead vc 600000 bc 64 eq 300000 6.1388 ms/op 6.4043 ms/op 0.96
computeDeltas 1400000 validators 0% inactive 11.913 ms/op 11.755 ms/op 1.01
computeDeltas 1400000 validators 10% inactive 11.196 ms/op 11.119 ms/op 1.01
computeDeltas 1400000 validators 20% inactive 10.504 ms/op 10.398 ms/op 1.01
computeDeltas 1400000 validators 50% inactive 8.6402 ms/op 8.5370 ms/op 1.01
computeDeltas 2100000 validators 0% inactive 17.829 ms/op 17.435 ms/op 1.02
computeDeltas 2100000 validators 10% inactive 16.636 ms/op 16.603 ms/op 1.00
computeDeltas 2100000 validators 20% inactive 15.741 ms/op 15.702 ms/op 1.00
computeDeltas 2100000 validators 50% inactive 10.574 ms/op 10.434 ms/op 1.01
altair processAttestation - 250000 vs - 7PWei normalcase 1.8289 ms/op 2.2510 ms/op 0.81
altair processAttestation - 250000 vs - 7PWei worstcase 3.4319 ms/op 3.3455 ms/op 1.03
altair processAttestation - setStatus - 1/6 committees join 101.48 us/op 101.38 us/op 1.00
altair processAttestation - setStatus - 1/3 committees join 197.45 us/op 202.26 us/op 0.98
altair processAttestation - setStatus - 1/2 committees join 268.98 us/op 279.08 us/op 0.96
altair processAttestation - setStatus - 2/3 committees join 354.14 us/op 361.02 us/op 0.98
altair processAttestation - setStatus - 4/5 committees join 489.20 us/op 525.48 us/op 0.93
altair processAttestation - setStatus - 100% committees join 572.06 us/op 618.22 us/op 0.93
altair processBlock - 250000 vs - 7PWei normalcase 4.4424 ms/op 4.0564 ms/op 1.10
altair processBlock - 250000 vs - 7PWei normalcase hashState 17.759 ms/op 17.020 ms/op 1.04
altair processBlock - 250000 vs - 7PWei worstcase 20.424 ms/op 22.325 ms/op 0.91
altair processBlock - 250000 vs - 7PWei worstcase hashState 41.858 ms/op 42.941 ms/op 0.97
phase0 processBlock - 250000 vs - 7PWei normalcase 1.2270 ms/op 1.4844 ms/op 0.83
phase0 processBlock - 250000 vs - 7PWei worstcase 16.328 ms/op 17.321 ms/op 0.94
altair processEth1Data - 250000 vs - 7PWei normalcase 276.64 us/op 301.49 us/op 0.92
getExpectedWithdrawals 250000 eb:1,eth1:1,we:0,wn:0,smpl:16 2.9990 us/op 3.4380 us/op 0.87
getExpectedWithdrawals 250000 eb:0.95,eth1:0.1,we:0.05,wn:0,smpl:220 18.618 us/op 20.017 us/op 0.93
getExpectedWithdrawals 250000 eb:0.95,eth1:0.3,we:0.05,wn:0,smpl:43 5.2690 us/op 6.9880 us/op 0.75
getExpectedWithdrawals 250000 eb:0.95,eth1:0.7,we:0.05,wn:0,smpl:19 3.4660 us/op 4.1600 us/op 0.83
getExpectedWithdrawals 250000 eb:0.1,eth1:0.1,we:0,wn:0,smpl:1021 87.159 us/op 91.289 us/op 0.95
getExpectedWithdrawals 250000 eb:0.03,eth1:0.03,we:0,wn:0,smpl:11778 1.3369 ms/op 1.4212 ms/op 0.94
getExpectedWithdrawals 250000 eb:0.01,eth1:0.01,we:0,wn:0,smpl:16384 1.8596 ms/op 1.8262 ms/op 1.02
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,smpl:16384 1.7676 ms/op 1.8444 ms/op 0.96
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,nocache,smpl:16384 4.3900 ms/op 3.7313 ms/op 1.18
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,smpl:16384 2.0135 ms/op 2.0654 ms/op 0.97
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,nocache,smpl:16384 3.9687 ms/op 4.6928 ms/op 0.85
Tree 40 250000 create 315.92 ms/op 348.59 ms/op 0.91
Tree 40 250000 get(125000) 90.934 ns/op 91.632 ns/op 0.99
Tree 40 250000 set(125000) 968.28 ns/op 988.44 ns/op 0.98
Tree 40 250000 toArray() 16.215 ms/op 17.630 ms/op 0.92
Tree 40 250000 iterate all - toArray() + loop 11.938 ms/op 17.133 ms/op 0.70
Tree 40 250000 iterate all - get(i) 35.624 ms/op 40.029 ms/op 0.89
Array 250000 create 2.2095 ms/op 2.5266 ms/op 0.87
Array 250000 clone - spread 656.69 us/op 778.52 us/op 0.84
Array 250000 get(125000) 0.28200 ns/op 0.29000 ns/op 0.97
Array 250000 set(125000) 0.28600 ns/op 0.29400 ns/op 0.97
Array 250000 iterate all - loop 55.350 us/op 57.212 us/op 0.97
phase0 afterProcessEpoch - 250000 vs - 7PWei 38.298 ms/op 40.411 ms/op 0.95
Array.fill - length 1000000 2.0536 ms/op 2.4380 ms/op 0.84
Array push - length 1000000 8.7950 ms/op 10.467 ms/op 0.84
Array.get 0.19711 ns/op 0.20282 ns/op 0.97
Uint8Array.get 0.23300 ns/op 0.26211 ns/op 0.89
phase0 beforeProcessEpoch - 250000 vs - 7PWei 14.727 ms/op 16.467 ms/op 0.89
altair processEpoch - mainnet_e81889 241.66 ms/op 269.34 ms/op 0.90
mainnet_e81889 - altair beforeProcessEpoch 14.575 ms/op 15.224 ms/op 0.96
mainnet_e81889 - altair processJustificationAndFinalization 5.4480 us/op 17.142 us/op 0.32
mainnet_e81889 - altair processInactivityUpdates 3.6464 ms/op 4.6256 ms/op 0.79
mainnet_e81889 - altair processRewardsAndPenalties 19.039 ms/op 21.148 ms/op 0.90
mainnet_e81889 - altair processRegistryUpdates 512.00 ns/op 549.00 ns/op 0.93
mainnet_e81889 - altair processSlashings 125.00 ns/op 133.00 ns/op 0.94
mainnet_e81889 - altair processEth1DataReset 124.00 ns/op 126.00 ns/op 0.98
mainnet_e81889 - altair processEffectiveBalanceUpdates 2.6578 ms/op 2.5875 ms/op 1.03
mainnet_e81889 - altair processSlashingsReset 674.00 ns/op 777.00 ns/op 0.87
mainnet_e81889 - altair processRandaoMixesReset 1.0670 us/op 1.1000 us/op 0.97
mainnet_e81889 - altair processHistoricalRootsUpdate 123.00 ns/op 128.00 ns/op 0.96
mainnet_e81889 - altair processParticipationFlagUpdates 416.00 ns/op 436.00 ns/op 0.95
mainnet_e81889 - altair processSyncCommitteeUpdates 101.00 ns/op 105.00 ns/op 0.96
mainnet_e81889 - altair afterProcessEpoch 40.072 ms/op 42.083 ms/op 0.95
capella processEpoch - mainnet_e217614 803.25 ms/op 940.80 ms/op 0.85
mainnet_e217614 - capella beforeProcessEpoch 56.587 ms/op 60.966 ms/op 0.93
mainnet_e217614 - capella processJustificationAndFinalization 6.5050 us/op 6.6230 us/op 0.98
mainnet_e217614 - capella processInactivityUpdates 14.976 ms/op 15.426 ms/op 0.97
mainnet_e217614 - capella processRewardsAndPenalties 84.820 ms/op 100.96 ms/op 0.84
mainnet_e217614 - capella processRegistryUpdates 4.3990 us/op 4.4420 us/op 0.99
mainnet_e217614 - capella processSlashings 126.00 ns/op 133.00 ns/op 0.95
mainnet_e217614 - capella processEth1DataReset 125.00 ns/op 128.00 ns/op 0.98
mainnet_e217614 - capella processEffectiveBalanceUpdates 15.131 ms/op 18.594 ms/op 0.81
mainnet_e217614 - capella processSlashingsReset 679.00 ns/op 685.00 ns/op 0.99
mainnet_e217614 - capella processRandaoMixesReset 1.2070 us/op 1.4170 us/op 0.85
mainnet_e217614 - capella processHistoricalRootsUpdate 127.00 ns/op 125.00 ns/op 1.02
mainnet_e217614 - capella processParticipationFlagUpdates 423.00 ns/op 442.00 ns/op 0.96
mainnet_e217614 - capella afterProcessEpoch 109.46 ms/op 108.50 ms/op 1.01
phase0 processEpoch - mainnet_e58758 294.42 ms/op 322.94 ms/op 0.91
mainnet_e58758 - phase0 beforeProcessEpoch 62.704 ms/op 73.085 ms/op 0.86
mainnet_e58758 - phase0 processJustificationAndFinalization 5.9810 us/op 6.7810 us/op 0.88
mainnet_e58758 - phase0 processRewardsAndPenalties 15.474 ms/op 17.420 ms/op 0.89
mainnet_e58758 - phase0 processRegistryUpdates 2.2200 us/op 2.2780 us/op 0.97
mainnet_e58758 - phase0 processSlashings 125.00 ns/op 129.00 ns/op 0.97
mainnet_e58758 - phase0 processEth1DataReset 125.00 ns/op 125.00 ns/op 1.00
mainnet_e58758 - phase0 processEffectiveBalanceUpdates 809.85 us/op 828.68 us/op 0.98
mainnet_e58758 - phase0 processSlashingsReset 890.00 ns/op 990.00 ns/op 0.90
mainnet_e58758 - phase0 processRandaoMixesReset 1.2460 us/op 1.4060 us/op 0.89
mainnet_e58758 - phase0 processHistoricalRootsUpdate 132.00 ns/op 132.00 ns/op 1.00
mainnet_e58758 - phase0 processParticipationRecordUpdates 1.1270 us/op 1.2400 us/op 0.91
mainnet_e58758 - phase0 afterProcessEpoch 33.064 ms/op 32.808 ms/op 1.01
phase0 processEffectiveBalanceUpdates - 250000 normalcase 969.11 us/op 996.74 us/op 0.97
phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 1.5799 ms/op 1.6101 ms/op 0.98
altair processInactivityUpdates - 250000 normalcase 11.045 ms/op 12.857 ms/op 0.86
altair processInactivityUpdates - 250000 worstcase 14.442 ms/op 12.938 ms/op 1.12
phase0 processRegistryUpdates - 250000 normalcase 2.9520 us/op 2.3380 us/op 1.26
phase0 processRegistryUpdates - 250000 badcase_full_deposits 149.00 us/op 152.94 us/op 0.97
phase0 processRegistryUpdates - 250000 worstcase 0.5 63.328 ms/op 67.121 ms/op 0.94
altair processRewardsAndPenalties - 250000 normalcase 15.477 ms/op 17.045 ms/op 0.91
altair processRewardsAndPenalties - 250000 worstcase 14.955 ms/op 16.485 ms/op 0.91
phase0 getAttestationDeltas - 250000 normalcase 5.2713 ms/op 5.6672 ms/op 0.93
phase0 getAttestationDeltas - 250000 worstcase 5.3295 ms/op 5.5789 ms/op 0.96
phase0 processSlashings - 250000 worstcase 60.048 us/op 64.242 us/op 0.93
altair processSyncCommitteeUpdates - 250000 10.619 ms/op 10.109 ms/op 1.05
BeaconState.hashTreeRoot - No change 171.00 ns/op 170.00 ns/op 1.01
BeaconState.hashTreeRoot - 1 full validator 80.109 us/op 81.044 us/op 0.99
BeaconState.hashTreeRoot - 32 full validator 804.51 us/op 872.95 us/op 0.92
BeaconState.hashTreeRoot - 512 full validator 9.6813 ms/op 9.4269 ms/op 1.03
BeaconState.hashTreeRoot - 1 validator.effectiveBalance 112.29 us/op 116.60 us/op 0.96
BeaconState.hashTreeRoot - 32 validator.effectiveBalance 1.5914 ms/op 1.4977 ms/op 1.06
BeaconState.hashTreeRoot - 512 validator.effectiveBalance 22.288 ms/op 23.621 ms/op 0.94
BeaconState.hashTreeRoot - 1 balances 81.210 us/op 82.861 us/op 0.98
BeaconState.hashTreeRoot - 32 balances 690.03 us/op 859.91 us/op 0.80
BeaconState.hashTreeRoot - 512 balances 6.3645 ms/op 7.9575 ms/op 0.80
BeaconState.hashTreeRoot - 250000 balances 144.75 ms/op 142.85 ms/op 1.01
aggregationBits - 2048 els - zipIndexesInBitList 19.646 us/op 21.283 us/op 0.92
regular array get 100000 times 22.900 us/op 23.169 us/op 0.99
wrappedArray get 100000 times 22.930 us/op 23.146 us/op 0.99
arrayWithProxy get 100000 times 10.463 ms/op 10.127 ms/op 1.03
ssz.Root.equals 21.421 ns/op 21.535 ns/op 0.99
byteArrayEquals 21.302 ns/op 21.410 ns/op 0.99
Buffer.compare 8.7870 ns/op 8.8910 ns/op 0.99
processSlot - 1 slots 8.9650 us/op 10.708 us/op 0.84
processSlot - 32 slots 1.8049 ms/op 2.0633 ms/op 0.87
getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei 4.6665 ms/op 6.0375 ms/op 0.77
getCommitteeAssignments - req 1 vs - 250000 vc 1.6768 ms/op 1.6773 ms/op 1.00
getCommitteeAssignments - req 100 vs - 250000 vc 3.4212 ms/op 3.4000 ms/op 1.01
getCommitteeAssignments - req 1000 vs - 250000 vc 3.6990 ms/op 3.6777 ms/op 1.01
findModifiedValidators - 10000 modified validators 717.38 ms/op 780.69 ms/op 0.92
findModifiedValidators - 1000 modified validators 494.57 ms/op 583.64 ms/op 0.85
findModifiedValidators - 100 modified validators 323.76 ms/op 380.44 ms/op 0.85
findModifiedValidators - 10 modified validators 234.35 ms/op 285.13 ms/op 0.82
findModifiedValidators - 1 modified validators 135.08 ms/op 179.12 ms/op 0.75
findModifiedValidators - no difference 140.61 ms/op 192.06 ms/op 0.73
migrate state 1500000 validators, 3400 modified, 2000 new 2.6880 s/op 3.3961 s/op 0.79
RootCache.getBlockRootAtSlot - 250000 vs - 7PWei 3.6600 ns/op 3.7400 ns/op 0.98
state getBlockRootAtSlot - 250000 vs - 7PWei 285.79 ns/op 405.51 ns/op 0.70
computeProposerIndex 100000 validators 1.2926 ms/op 1.3584 ms/op 0.95
getNextSyncCommitteeIndices 1000 validators 2.8692 ms/op 2.9458 ms/op 0.97
getNextSyncCommitteeIndices 10000 validators 24.522 ms/op 25.959 ms/op 0.94
getNextSyncCommitteeIndices 100000 validators 83.973 ms/op 90.959 ms/op 0.92
computeProposers - vc 250000 556.24 us/op 566.70 us/op 0.98
computeEpochShuffling - vc 250000 38.477 ms/op 39.700 ms/op 0.97
getNextSyncCommittee - vc 250000 9.0751 ms/op 10.082 ms/op 0.90
nodejs block root to RootHex using toHex 93.759 ns/op 96.697 ns/op 0.97
nodejs block root to RootHex using toRootHex 62.507 ns/op 62.513 ns/op 1.00
nodejs fromHex(blob) 734.59 us/op 827.41 us/op 0.89
nodejs fromHexInto(blob) 609.55 us/op 619.80 us/op 0.98
nodejs block root to RootHex using the deprecated toHexString 348.83 ns/op 506.98 ns/op 0.69
nodejs byteArrayEquals 32 bytes (block root) 25.211 ns/op 25.704 ns/op 0.98
nodejs byteArrayEquals 48 bytes (pubkey) 36.491 ns/op 37.317 ns/op 0.98
nodejs byteArrayEquals 96 bytes (signature) 33.467 ns/op 33.295 ns/op 1.01
nodejs byteArrayEquals 1024 bytes 40.042 ns/op 41.283 ns/op 0.97
nodejs byteArrayEquals 131072 bytes (blob) 1.6846 us/op 1.7403 us/op 0.97
browser block root to RootHex using toHex 138.27 ns/op 144.11 ns/op 0.96
browser block root to RootHex using toRootHex 125.72 ns/op 128.58 ns/op 0.98
browser fromHex(blob) 1.4674 ms/op 1.6287 ms/op 0.90
browser fromHexInto(blob) 600.36 us/op 622.43 us/op 0.96
browser block root to RootHex using the deprecated toHexString 460.00 ns/op 338.59 ns/op 1.36
browser byteArrayEquals 32 bytes (block root) 27.076 ns/op 27.613 ns/op 0.98
browser byteArrayEquals 48 bytes (pubkey) 38.217 ns/op 38.996 ns/op 0.98
browser byteArrayEquals 96 bytes (signature) 71.205 ns/op 73.209 ns/op 0.97
browser byteArrayEquals 1024 bytes 718.57 ns/op 747.69 ns/op 0.96
browser byteArrayEquals 131072 bytes (blob) 91.658 us/op 93.961 us/op 0.98

by benchmarkbot/action

wemeetagain
wemeetagain previously approved these changes Jul 23, 2026
@nflaig
nflaig requested a review from twoeths July 23, 2026 15:59
const prevHead = this.forkChoice.getHead();
const head = this.forkChoice.updateAndGetHead({mode: UpdateHeadOpt.GetCanonicalHead}).head;

if (head.blockRoot !== prevHead.blockRoot) {

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.

getProposerHead() also changes the head, it's worth to extract this to a separate method to reuse?

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.

nvm, getProposerHead() is different, could be a proposer boost reorg there
maybe just drop a comment on why we don't need to emit a head event over there

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.

as noted in the PR description I intentionally don't wanna consider the proposer path, I think it is fine to rely on the block import path in that case, we can add the comment as you suggested

@nflaig
nflaig merged commit 3c41f4b into unstable Jul 24, 2026
19 checks passed
@nflaig
nflaig deleted the nflaig/head-event-on-recompute branch July 24, 2026 09:00
@wemeetagain

Copy link
Copy Markdown
Member

馃帀 This PR is included in v1.46.0 馃帀

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.

3 participants