Skip to content

feat: fast confirmation rule - #8837

Merged
nflaig merged 75 commits into
unstablefrom
feature/fast-confirmation
Jun 7, 2026
Merged

feat: fast confirmation rule#8837
nflaig merged 75 commits into
unstablefrom
feature/fast-confirmation

Conversation

@nazarhussain

Copy link
Copy Markdown
Contributor

Motivation

Introduce assumption based fast confirmation rule.

Description

Specs: ethereum/consensus-specs#4747

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

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

This pull request introduces a significant new feature: an assumption-based Fast Confirmation Rule (FCR) for the beacon node. The primary goal is to accelerate the confirmation of blocks by implementing a sophisticated algorithm that evaluates various network and validator-related metrics. This involves deep integration into the existing fork choice mechanism, providing a new configurable option for users, and updating how 'safe' blocks are identified within the system. The changes are comprehensive, spanning core logic, configuration, state management, and observability.

Highlights

  • Fast Confirmation Rule (FCR) Implementation: Introduced a new FastConfirmationRule class and its supporting types and metrics, which encapsulates the logic for an assumption-based fast block confirmation mechanism. This rule aims to provide faster block finality by evaluating attestation scores, committee weights, and other factors.
  • Fork Choice Integration: The core ForkChoice class has been updated to optionally enable and integrate the Fast Confirmation Rule. When active, the FCR's logic is executed at the start of each slot to determine and update a 'confirmed root', which can then be queried by other parts of the system.
  • Configurability and CLI Options: A new configuration option, enableFastConfirmation, has been added to the chain options, allowing users to enable or disable this experimental feature via a CLI argument. Additionally, a CONFIRMATION_BYZANTINE_THRESHOLD constant has been introduced to chain configurations, providing a configurable parameter for the FCR.
  • Enhanced Safe Block Determination: The getSafeBeaconBlockRoot and getSafeExecutionBlockHash functions now prioritize the FCR's determined 'confirmed root' if the feature is enabled, falling back to the traditional justified checkpoint if the FCR is not active or doesn't provide a confirmed root. This ensures that the fastest available confirmation is used.
  • State Management and Metrics: The ForkChoiceStore and related interfaces have been extended to store the necessary internal state for the FCR, such as observed justified checkpoints and balances. New Prometheus metrics have also been added to monitor the FCR's performance, confirmed epochs/slots, and any resets.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

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

Invoking Gemini

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

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

Customization

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

Limitations & Feedback

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

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

Footnotes

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

@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 introduces a fast confirmation rule, an experimental feature designed to provide faster block confirmations. The changes are comprehensive, including the core logic in a new FastConfirmationRule class, configuration options to enable it, and integration into the existing ForkChoice and safe block determination logic. The implementation appears to align well with the provided specification. My review includes a couple of minor suggestions to enhance code quality and readability.

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

github-actions Bot commented Feb 3, 2026

Copy link
Copy Markdown
Contributor

Performance Report

✔️ no performance regression detected

Full benchmark results
Benchmark suite Current: b312891 Previous: 41c6b9d Ratio
getPubkeys - index2pubkey - req 1000 vs - 250000 vc 879.13 us/op 884.39 us/op 0.99
getPubkeys - validatorsArr - req 1000 vs - 250000 vc 38.729 us/op 37.579 us/op 1.03
BLS verify - blst 647.23 us/op 704.82 us/op 0.92
BLS verifyMultipleSignatures 3 - blst 1.3627 ms/op 1.2871 ms/op 1.06
BLS verifyMultipleSignatures 8 - blst 2.1854 ms/op 2.0417 ms/op 1.07
BLS verifyMultipleSignatures 32 - blst 7.0185 ms/op 6.4680 ms/op 1.09
BLS verifyMultipleSignatures 64 - blst 13.545 ms/op 12.473 ms/op 1.09
BLS verifyMultipleSignatures 128 - blst 26.275 ms/op 24.266 ms/op 1.08
BLS deserializing 10000 signatures 640.16 ms/op 614.21 ms/op 1.04
BLS deserializing 100000 signatures 6.3593 s/op 6.1648 s/op 1.03
BLS verifyMultipleSignatures - same message - 3 - blst 764.05 us/op 686.12 us/op 1.11
BLS verifyMultipleSignatures - same message - 8 - blst 910.91 us/op 868.72 us/op 1.05
BLS verifyMultipleSignatures - same message - 32 - blst 1.5671 ms/op 1.4968 ms/op 1.05
BLS verifyMultipleSignatures - same message - 64 - blst 2.3918 ms/op 2.3365 ms/op 1.02
BLS verifyMultipleSignatures - same message - 128 - blst 4.0678 ms/op 3.9551 ms/op 1.03
BLS aggregatePubkeys 32 - blst 17.510 us/op 17.327 us/op 1.01
BLS aggregatePubkeys 128 - blst 62.613 us/op 61.625 us/op 1.02
getSlashingsAndExits - default max 50.324 us/op 45.679 us/op 1.10
getSlashingsAndExits - 2k 326.51 us/op 333.19 us/op 0.98
proposeBlockBody type=full, size=empty 798.88 us/op 869.08 us/op 0.92
isKnown best case - 1 super set check 165.00 ns/op 168.00 ns/op 0.98
isKnown normal case - 2 super set checks 166.00 ns/op 171.00 ns/op 0.97
isKnown worse case - 16 super set checks 168.00 ns/op 165.00 ns/op 1.02
validate api signedAggregateAndProof - struct 1.5272 ms/op 1.4702 ms/op 1.04
validate gossip signedAggregateAndProof - struct 1.5222 ms/op 1.4586 ms/op 1.04
batch validate gossip attestation - vc 640000 - chunk 32 105.97 us/op 104.21 us/op 1.02
batch validate gossip attestation - vc 640000 - chunk 64 92.801 us/op 90.667 us/op 1.02
batch validate gossip attestation - vc 640000 - chunk 128 86.656 us/op 85.522 us/op 1.01
batch validate gossip attestation - vc 640000 - chunk 256 83.534 us/op 81.482 us/op 1.03
bytes32 toHexString 291.00 ns/op 289.00 ns/op 1.01
bytes32 Buffer.toString(hex) 167.00 ns/op 162.00 ns/op 1.03
bytes32 Buffer.toString(hex) from Uint8Array 227.00 ns/op 224.00 ns/op 1.01
bytes32 Buffer.toString(hex) + 0x 162.00 ns/op 163.00 ns/op 0.99
Return object 10000 times 0.21490 ns/op 0.20850 ns/op 1.03
Throw Error 10000 times 3.2820 us/op 3.2039 us/op 1.02
toHex 91.774 ns/op 83.945 ns/op 1.09
Buffer.from 83.812 ns/op 77.237 ns/op 1.09
shared Buffer 56.163 ns/op 50.172 ns/op 1.12
fastMsgIdFn sha256 / 200 bytes 1.4710 us/op 1.4000 us/op 1.05
fastMsgIdFn h32 xxhash / 200 bytes 152.00 ns/op 146.00 ns/op 1.04
fastMsgIdFn h64 xxhash / 200 bytes 207.00 ns/op 203.00 ns/op 1.02
fastMsgIdFn sha256 / 1000 bytes 4.7470 us/op 4.5680 us/op 1.04
fastMsgIdFn h32 xxhash / 1000 bytes 244.00 ns/op 232.00 ns/op 1.05
fastMsgIdFn h64 xxhash / 1000 bytes 258.00 ns/op 246.00 ns/op 1.05
fastMsgIdFn sha256 / 10000 bytes 42.105 us/op 40.128 us/op 1.05
fastMsgIdFn h32 xxhash / 10000 bytes 1.2790 us/op 1.2210 us/op 1.05
fastMsgIdFn h64 xxhash / 10000 bytes 845.00 ns/op 788.00 ns/op 1.07
send data - 1000 256B messages 4.1139 ms/op 4.1074 ms/op 1.00
send data - 1000 512B messages 4.1944 ms/op 4.1796 ms/op 1.00
send data - 1000 1024B messages 4.3897 ms/op 4.3493 ms/op 1.01
send data - 1000 1200B messages 4.6858 ms/op 4.6642 ms/op 1.00
send data - 1000 2048B messages 4.5962 ms/op 5.1675 ms/op 0.89
send data - 1000 4096B messages 5.3944 ms/op 6.0952 ms/op 0.89
send data - 1000 16384B messages 18.837 ms/op 27.405 ms/op 0.69
send data - 1000 65536B messages 137.87 ms/op 194.66 ms/op 0.71
enrSubnets - fastDeserialize 64 bits 739.00 ns/op 775.00 ns/op 0.95
enrSubnets - ssz BitVector 64 bits 274.00 ns/op 262.00 ns/op 1.05
enrSubnets - fastDeserialize 4 bits 115.00 ns/op 105.00 ns/op 1.10
enrSubnets - ssz BitVector 4 bits 262.00 ns/op 256.00 ns/op 1.02
prioritizePeers score -10:0 att 32-0.1 sync 2-0 205.41 us/op 203.13 us/op 1.01
prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 499.10 us/op 231.38 us/op 2.16
prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 348.85 us/op 335.65 us/op 1.04
prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 611.97 us/op 598.51 us/op 1.02
prioritizePeers score 0:0 att 64-1 sync 4-1 724.53 us/op 693.49 us/op 1.04
array of 16000 items push then shift 1.3105 us/op 1.2366 us/op 1.06
LinkedList of 16000 items push then shift 7.0800 ns/op 7.3480 ns/op 0.96
array of 16000 items push then pop 67.375 ns/op 66.568 ns/op 1.01
LinkedList of 16000 items push then pop 6.0190 ns/op 5.9870 ns/op 1.01
array of 24000 items push then shift 1.9360 us/op 1.7827 us/op 1.09
LinkedList of 24000 items push then shift 6.6820 ns/op 6.7730 ns/op 0.99
array of 24000 items push then pop 97.345 ns/op 89.131 ns/op 1.09
LinkedList of 24000 items push then pop 6.0690 ns/op 5.8250 ns/op 1.04
intersect bitArray bitLen 8 4.7870 ns/op 4.5840 ns/op 1.04
intersect array and set length 8 29.806 ns/op 28.123 ns/op 1.06
intersect bitArray bitLen 128 24.419 ns/op 23.103 ns/op 1.06
intersect array and set length 128 504.61 ns/op 474.86 ns/op 1.06
bitArray.getTrueBitIndexes() bitLen 128 1.0850 us/op 1.0110 us/op 1.07
bitArray.getTrueBitIndexes() bitLen 248 1.8320 us/op 1.8150 us/op 1.01
bitArray.getTrueBitIndexes() bitLen 512 3.7220 us/op 3.5960 us/op 1.04
Full columns - reconstruct all 6 blobs 216.50 us/op 201.36 us/op 1.08
Full columns - reconstruct half of the blobs out of 6 95.974 us/op 102.24 us/op 0.94
Full columns - reconstruct single blob out of 6 33.063 us/op 30.201 us/op 1.09
Half columns - reconstruct all 6 blobs 385.41 ms/op 376.32 ms/op 1.02
Half columns - reconstruct half of the blobs out of 6 196.87 ms/op 189.29 ms/op 1.04
Half columns - reconstruct single blob out of 6 71.050 ms/op 68.142 ms/op 1.04
Full columns - reconstruct all 10 blobs 222.18 us/op 248.60 us/op 0.89
Full columns - reconstruct half of the blobs out of 10 101.08 us/op 168.71 us/op 0.60
Full columns - reconstruct single blob out of 10 35.918 us/op 31.180 us/op 1.15
Half columns - reconstruct all 10 blobs 640.92 ms/op 616.65 ms/op 1.04
Half columns - reconstruct half of the blobs out of 10 325.56 ms/op 311.87 ms/op 1.04
Half columns - reconstruct single blob out of 10 66.430 ms/op 66.309 ms/op 1.00
Full columns - reconstruct all 20 blobs 592.79 us/op 518.33 us/op 1.14
Full columns - reconstruct half of the blobs out of 20 221.21 us/op 206.16 us/op 1.07
Full columns - reconstruct single blob out of 20 30.542 us/op 28.377 us/op 1.08
Half columns - reconstruct all 20 blobs 1.2803 s/op 1.2353 s/op 1.04
Half columns - reconstruct half of the blobs out of 20 635.64 ms/op 621.07 ms/op 1.02
Half columns - reconstruct single blob out of 20 70.312 ms/op 66.961 ms/op 1.05
Set add up to 64 items then delete first 2.1503 us/op 2.4240 us/op 0.89
OrderedSet add up to 64 items then delete first 3.4006 us/op 3.1633 us/op 1.07
Set add up to 64 items then delete last 2.1371 us/op 2.2534 us/op 0.95
OrderedSet add up to 64 items then delete last 3.3062 us/op 3.1440 us/op 1.05
Set add up to 64 items then delete middle 2.1350 us/op 2.0240 us/op 1.05
OrderedSet add up to 64 items then delete middle 4.7756 us/op 4.5222 us/op 1.06
Set add up to 128 items then delete first 4.3125 us/op 4.0601 us/op 1.06
OrderedSet add up to 128 items then delete first 6.6138 us/op 6.3102 us/op 1.05
Set add up to 128 items then delete last 4.1089 us/op 3.6701 us/op 1.12
OrderedSet add up to 128 items then delete last 5.8628 us/op 5.5268 us/op 1.06
Set add up to 128 items then delete middle 3.9096 us/op 3.6386 us/op 1.07
OrderedSet add up to 128 items then delete middle 12.034 us/op 12.128 us/op 0.99
Set add up to 256 items then delete first 10.285 us/op 7.7757 us/op 1.32
OrderedSet add up to 256 items then delete first 12.264 us/op 11.647 us/op 1.05
Set add up to 256 items then delete last 11.755 us/op 7.2818 us/op 1.61
OrderedSet add up to 256 items then delete last 11.624 us/op 10.969 us/op 1.06
Set add up to 256 items then delete middle 7.7079 us/op 7.2330 us/op 1.07
OrderedSet add up to 256 items then delete middle 35.725 us/op 33.970 us/op 1.05
runFastConfirmationRules vc:100000 bc:96 eq:0 3.0770 us/op
runFastConfirmationRules vc:600000 bc:96 eq:0 4.3840 us/op
runFastConfirmationRules vc:1000000 bc:96 eq:0 6.9120 us/op
runFastConfirmationRules vc:600000 bc:320 eq:0 7.7750 us/op
runFastConfirmationRules vc:600000 bc:1200 eq:0 22.855 us/op
runFastConfirmationRules vc:600000 bc:96 eq:1000 3.5410 us/op
runFastConfirmationRules vc:600000 bc:96 eq:10000 3.7200 us/op
runFastConfirmationRules vc:600000 bc:96 eq:300000 5.9530 us/op
pass gossip attestations to forkchoice per slot 2.5992 ms/op 2.5517 ms/op 1.02
forkChoice updateHead vc 100000 bc 64 eq 0 424.69 us/op 375.48 us/op 1.13
forkChoice updateHead vc 600000 bc 64 eq 0 2.6255 ms/op 2.2818 ms/op 1.15
forkChoice updateHead vc 1000000 bc 64 eq 0 4.2862 ms/op 3.7644 ms/op 1.14
forkChoice updateHead vc 600000 bc 320 eq 0 2.5738 ms/op 2.2441 ms/op 1.15
forkChoice updateHead vc 600000 bc 1200 eq 0 2.6798 ms/op 2.2670 ms/op 1.18
forkChoice updateHead vc 600000 bc 7200 eq 0 2.9900 ms/op 2.5710 ms/op 1.16
forkChoice updateHead vc 600000 bc 64 eq 1000 2.5994 ms/op 2.7928 ms/op 0.93
forkChoice updateHead vc 600000 bc 64 eq 10000 2.7902 ms/op 2.8959 ms/op 0.96
forkChoice updateHead vc 600000 bc 64 eq 300000 7.0193 ms/op 6.5796 ms/op 1.07
computeDeltas 1400000 validators 0% inactive 12.749 ms/op 12.037 ms/op 1.06
computeDeltas 1400000 validators 10% inactive 11.968 ms/op 11.502 ms/op 1.04
computeDeltas 1400000 validators 20% inactive 11.174 ms/op 10.418 ms/op 1.07
computeDeltas 1400000 validators 50% inactive 8.3752 ms/op 8.0595 ms/op 1.04
computeDeltas 2100000 validators 0% inactive 19.280 ms/op 18.332 ms/op 1.05
computeDeltas 2100000 validators 10% inactive 18.192 ms/op 17.073 ms/op 1.07
computeDeltas 2100000 validators 20% inactive 16.748 ms/op 15.598 ms/op 1.07
computeDeltas 2100000 validators 50% inactive 9.6316 ms/op 9.1374 ms/op 1.05
altair processAttestation - 250000 vs - 7PWei normalcase 1.6945 ms/op 1.8944 ms/op 0.89
altair processAttestation - 250000 vs - 7PWei worstcase 2.4537 ms/op 2.7574 ms/op 0.89
altair processAttestation - setStatus - 1/6 committees join 98.519 us/op 100.13 us/op 0.98
altair processAttestation - setStatus - 1/3 committees join 205.96 us/op 197.25 us/op 1.04
altair processAttestation - setStatus - 1/2 committees join 293.60 us/op 283.63 us/op 1.04
altair processAttestation - setStatus - 2/3 committees join 374.83 us/op 370.24 us/op 1.01
altair processAttestation - setStatus - 4/5 committees join 515.13 us/op 503.07 us/op 1.02
altair processAttestation - setStatus - 100% committees join 606.23 us/op 597.48 us/op 1.01
altair processBlock - 250000 vs - 7PWei normalcase 3.4603 ms/op 3.8099 ms/op 0.91
altair processBlock - 250000 vs - 7PWei normalcase hashState 14.231 ms/op 15.899 ms/op 0.90
altair processBlock - 250000 vs - 7PWei worstcase 21.315 ms/op 20.509 ms/op 1.04
altair processBlock - 250000 vs - 7PWei worstcase hashState 38.673 ms/op 42.099 ms/op 0.92
phase0 processBlock - 250000 vs - 7PWei normalcase 1.3137 ms/op 1.2555 ms/op 1.05
phase0 processBlock - 250000 vs - 7PWei worstcase 17.505 ms/op 17.175 ms/op 1.02
altair processEth1Data - 250000 vs - 7PWei normalcase 310.58 us/op 284.62 us/op 1.09
getExpectedWithdrawals 250000 eb:1,eth1:1,we:0,wn:0,smpl:16 3.2750 us/op 4.1480 us/op 0.79
getExpectedWithdrawals 250000 eb:0.95,eth1:0.1,we:0.05,wn:0,smpl:220 21.171 us/op 18.778 us/op 1.13
getExpectedWithdrawals 250000 eb:0.95,eth1:0.3,we:0.05,wn:0,smpl:43 5.6730 us/op 5.3130 us/op 1.07
getExpectedWithdrawals 250000 eb:0.95,eth1:0.7,we:0.05,wn:0,smpl:19 3.7080 us/op 3.2100 us/op 1.16
getExpectedWithdrawals 250000 eb:0.1,eth1:0.1,we:0,wn:0,smpl:1021 93.019 us/op 81.931 us/op 1.14
getExpectedWithdrawals 250000 eb:0.03,eth1:0.03,we:0,wn:0,smpl:11778 1.3821 ms/op 1.2666 ms/op 1.09
getExpectedWithdrawals 250000 eb:0.01,eth1:0.01,we:0,wn:0,smpl:16384 1.8266 ms/op 1.6855 ms/op 1.08
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,smpl:16384 1.8029 ms/op 1.6730 ms/op 1.08
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,nocache,smpl:16384 3.7410 ms/op 3.4259 ms/op 1.09
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,smpl:16384 2.0572 ms/op 1.9065 ms/op 1.08
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,nocache,smpl:16384 4.0831 ms/op 3.7531 ms/op 1.09
Tree 40 250000 create 316.02 ms/op 295.53 ms/op 1.07
Tree 40 250000 get(125000) 97.884 ns/op 87.412 ns/op 1.12
Tree 40 250000 set(125000) 1.0498 us/op 940.29 ns/op 1.12
Tree 40 250000 toArray() 9.3795 ms/op 10.671 ms/op 0.88
Tree 40 250000 iterate all - toArray() + loop 14.958 ms/op 10.887 ms/op 1.37
Tree 40 250000 iterate all - get(i) 40.780 ms/op 34.392 ms/op 1.19
Array 250000 create 2.0946 ms/op 2.0448 ms/op 1.02
Array 250000 clone - spread 670.55 us/op 639.40 us/op 1.05
Array 250000 get(125000) 0.30300 ns/op 0.27800 ns/op 1.09
Array 250000 set(125000) 0.30600 ns/op 0.28300 ns/op 1.08
Array 250000 iterate all - loop 58.641 us/op 54.438 us/op 1.08
phase0 afterProcessEpoch - 250000 vs - 7PWei 42.419 ms/op 59.273 ms/op 0.72
Array.fill - length 1000000 2.2107 ms/op 1.9601 ms/op 1.13
Array push - length 1000000 7.7292 ms/op 8.5845 ms/op 0.90
Array.get 0.21269 ns/op 0.19283 ns/op 1.10
Uint8Array.get 0.25372 ns/op 0.23370 ns/op 1.09
phase0 beforeProcessEpoch - 250000 vs - 7PWei 12.116 ms/op 16.863 ms/op 0.72
altair processEpoch - mainnet_e81889 255.91 ms/op 245.82 ms/op 1.04
mainnet_e81889 - altair beforeProcessEpoch 13.761 ms/op 13.731 ms/op 1.00
mainnet_e81889 - altair processJustificationAndFinalization 5.6060 us/op 5.4340 us/op 1.03
mainnet_e81889 - altair processInactivityUpdates 3.5727 ms/op 3.2411 ms/op 1.10
mainnet_e81889 - altair processRewardsAndPenalties 17.130 ms/op 18.343 ms/op 0.93
mainnet_e81889 - altair processRegistryUpdates 529.00 ns/op 486.00 ns/op 1.09
mainnet_e81889 - altair processSlashings 132.00 ns/op 129.00 ns/op 1.02
mainnet_e81889 - altair processEth1DataReset 126.00 ns/op 130.00 ns/op 0.97
mainnet_e81889 - altair processEffectiveBalanceUpdates 1.4405 ms/op 2.4678 ms/op 0.58
mainnet_e81889 - altair processSlashingsReset 698.00 ns/op 684.00 ns/op 1.02
mainnet_e81889 - altair processRandaoMixesReset 1.0540 us/op 1.0780 us/op 0.98
mainnet_e81889 - altair processHistoricalRootsUpdate 131.00 ns/op 126.00 ns/op 1.04
mainnet_e81889 - altair processParticipationFlagUpdates 417.00 ns/op 402.00 ns/op 1.04
mainnet_e81889 - altair processSyncCommitteeUpdates 103.00 ns/op 103.00 ns/op 1.00
mainnet_e81889 - altair afterProcessEpoch 42.110 ms/op 38.848 ms/op 1.08
capella processEpoch - mainnet_e217614 727.72 ms/op 755.90 ms/op 0.96
mainnet_e217614 - capella beforeProcessEpoch 56.185 ms/op 59.671 ms/op 0.94
mainnet_e217614 - capella processJustificationAndFinalization 5.2470 us/op 5.7010 us/op 0.92
mainnet_e217614 - capella processInactivityUpdates 11.159 ms/op 13.324 ms/op 0.84
mainnet_e217614 - capella processRewardsAndPenalties 84.416 ms/op 82.653 ms/op 1.02
mainnet_e217614 - capella processRegistryUpdates 4.4330 us/op 4.3350 us/op 1.02
mainnet_e217614 - capella processSlashings 132.00 ns/op 135.00 ns/op 0.98
mainnet_e217614 - capella processEth1DataReset 126.00 ns/op 130.00 ns/op 0.97
mainnet_e217614 - capella processEffectiveBalanceUpdates 5.2164 ms/op 11.542 ms/op 0.45
mainnet_e217614 - capella processSlashingsReset 680.00 ns/op 664.00 ns/op 1.02
mainnet_e217614 - capella processRandaoMixesReset 1.0760 us/op 1.2030 us/op 0.89
mainnet_e217614 - capella processHistoricalRootsUpdate 128.00 ns/op 125.00 ns/op 1.02
mainnet_e217614 - capella processParticipationFlagUpdates 419.00 ns/op 408.00 ns/op 1.03
mainnet_e217614 - capella afterProcessEpoch 109.37 ms/op 106.38 ms/op 1.03
phase0 processEpoch - mainnet_e58758 254.57 ms/op 287.65 ms/op 0.88
mainnet_e58758 - phase0 beforeProcessEpoch 51.457 ms/op 60.723 ms/op 0.85
mainnet_e58758 - phase0 processJustificationAndFinalization 5.1270 us/op 5.8290 us/op 0.88
mainnet_e58758 - phase0 processRewardsAndPenalties 15.122 ms/op 15.790 ms/op 0.96
mainnet_e58758 - phase0 processRegistryUpdates 2.2420 us/op 2.1940 us/op 1.02
mainnet_e58758 - phase0 processSlashings 128.00 ns/op 126.00 ns/op 1.02
mainnet_e58758 - phase0 processEth1DataReset 126.00 ns/op 125.00 ns/op 1.01
mainnet_e58758 - phase0 processEffectiveBalanceUpdates 825.36 us/op 796.38 us/op 1.04
mainnet_e58758 - phase0 processSlashingsReset 849.00 ns/op 847.00 ns/op 1.00
mainnet_e58758 - phase0 processRandaoMixesReset 1.2740 us/op 1.1190 us/op 1.14
mainnet_e58758 - phase0 processHistoricalRootsUpdate 134.00 ns/op 135.00 ns/op 0.99
mainnet_e58758 - phase0 processParticipationRecordUpdates 984.00 ns/op 987.00 ns/op 1.00
mainnet_e58758 - phase0 afterProcessEpoch 33.323 ms/op 32.594 ms/op 1.02
phase0 processEffectiveBalanceUpdates - 250000 normalcase 1.1051 ms/op 950.89 us/op 1.16
phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 1.5730 ms/op 1.4785 ms/op 1.06
altair processInactivityUpdates - 250000 normalcase 10.355 ms/op 10.020 ms/op 1.03
altair processInactivityUpdates - 250000 worstcase 10.397 ms/op 10.071 ms/op 1.03
phase0 processRegistryUpdates - 250000 normalcase 2.1190 us/op 2.2010 us/op 0.96
phase0 processRegistryUpdates - 250000 badcase_full_deposits 144.69 us/op 140.96 us/op 1.03
phase0 processRegistryUpdates - 250000 worstcase 0.5 54.398 ms/op 59.347 ms/op 0.92
altair processRewardsAndPenalties - 250000 normalcase 13.586 ms/op 15.366 ms/op 0.88
altair processRewardsAndPenalties - 250000 worstcase 13.268 ms/op 15.035 ms/op 0.88
phase0 getAttestationDeltas - 250000 normalcase 5.2609 ms/op 5.0507 ms/op 1.04
phase0 getAttestationDeltas - 250000 worstcase 5.4017 ms/op 5.0808 ms/op 1.06
phase0 processSlashings - 250000 worstcase 63.977 us/op 57.383 us/op 1.11
altair processSyncCommitteeUpdates - 250000 10.144 ms/op 10.073 ms/op 1.01
BeaconState.hashTreeRoot - No change 171.00 ns/op 165.00 ns/op 1.04
BeaconState.hashTreeRoot - 1 full validator 66.154 us/op 75.506 us/op 0.88
BeaconState.hashTreeRoot - 32 full validator 686.28 us/op 831.33 us/op 0.83
BeaconState.hashTreeRoot - 512 full validator 6.6215 ms/op 7.9695 ms/op 0.83
BeaconState.hashTreeRoot - 1 validator.effectiveBalance 75.060 us/op 94.076 us/op 0.80
BeaconState.hashTreeRoot - 32 validator.effectiveBalance 1.3178 ms/op 1.3976 ms/op 0.94
BeaconState.hashTreeRoot - 512 validator.effectiveBalance 14.878 ms/op 15.875 ms/op 0.94
BeaconState.hashTreeRoot - 1 balances 62.777 us/op 71.773 us/op 0.87
BeaconState.hashTreeRoot - 32 balances 603.04 us/op 719.27 us/op 0.84
BeaconState.hashTreeRoot - 512 balances 5.1513 ms/op 5.9399 ms/op 0.87
BeaconState.hashTreeRoot - 250000 balances 106.37 ms/op 123.97 ms/op 0.86
aggregationBits - 2048 els - zipIndexesInBitList 18.947 us/op 18.939 us/op 1.00
regular array get 100000 times 22.556 us/op 22.739 us/op 0.99
wrappedArray get 100000 times 22.574 us/op 22.636 us/op 1.00
arrayWithProxy get 100000 times 10.397 ms/op 10.172 ms/op 1.02
ssz.Root.equals 21.253 ns/op 21.138 ns/op 1.01
byteArrayEquals 21.039 ns/op 21.117 ns/op 1.00
Buffer.compare 8.8400 ns/op 8.6910 ns/op 1.02
processSlot - 1 slots 8.0070 us/op 8.4380 us/op 0.95
processSlot - 32 slots 1.5280 ms/op 1.9260 ms/op 0.79
getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei 3.2988 ms/op 5.0600 ms/op 0.65
getCommitteeAssignments - req 1 vs - 250000 vc 1.6393 ms/op 1.6352 ms/op 1.00
getCommitteeAssignments - req 100 vs - 250000 vc 3.3724 ms/op 3.3260 ms/op 1.01
getCommitteeAssignments - req 1000 vs - 250000 vc 3.6235 ms/op 3.5693 ms/op 1.02
findModifiedValidators - 10000 modified validators 708.36 ms/op 737.58 ms/op 0.96
findModifiedValidators - 1000 modified validators 567.75 ms/op 428.86 ms/op 1.32
findModifiedValidators - 100 modified validators 292.90 ms/op 267.28 ms/op 1.10
findModifiedValidators - 10 modified validators 253.90 ms/op 211.50 ms/op 1.20
findModifiedValidators - 1 modified validators 171.15 ms/op 143.74 ms/op 1.19
findModifiedValidators - no difference 185.25 ms/op 154.20 ms/op 1.20
migrate state 1500000 validators, 3400 modified, 2000 new 2.6125 s/op 2.8348 s/op 0.92
RootCache.getBlockRootAtSlot - 250000 vs - 7PWei 3.6900 ns/op 3.6600 ns/op 1.01
state getBlockRootAtSlot - 250000 vs - 7PWei 282.28 ns/op 343.10 ns/op 0.82
computeProposerIndex 100000 validators 1.2962 ms/op 1.2995 ms/op 1.00
getNextSyncCommitteeIndices 1000 validators 2.8157 ms/op 2.8748 ms/op 0.98
getNextSyncCommitteeIndices 10000 validators 24.838 ms/op 24.901 ms/op 1.00
getNextSyncCommitteeIndices 100000 validators 82.030 ms/op 82.140 ms/op 1.00
computeProposers - vc 250000 539.45 us/op 578.40 us/op 0.93
computeEpochShuffling - vc 250000 38.947 ms/op 40.345 ms/op 0.97
getNextSyncCommittee - vc 250000 9.3114 ms/op 9.5887 ms/op 0.97
nodejs block root to RootHex using toHex 92.397 ns/op 88.354 ns/op 1.05
nodejs block root to RootHex using toRootHex 57.618 ns/op 52.596 ns/op 1.10
nodejs fromHex(blob) 833.40 us/op 711.58 us/op 1.17
nodejs fromHexInto(blob) 635.99 us/op 614.78 us/op 1.03
nodejs block root to RootHex using the deprecated toHexString 512.73 ns/op 460.63 ns/op 1.11
nodejs byteArrayEquals 32 bytes (block root) 25.623 ns/op 25.746 ns/op 1.00
nodejs byteArrayEquals 48 bytes (pubkey) 37.053 ns/op 36.855 ns/op 1.01
nodejs byteArrayEquals 96 bytes (signature) 38.920 ns/op 32.688 ns/op 1.19
nodejs byteArrayEquals 1024 bytes 44.595 ns/op 40.166 ns/op 1.11
nodejs byteArrayEquals 131072 bytes (blob) 1.7508 us/op 1.6951 us/op 1.03
browser block root to RootHex using toHex 144.87 ns/op 138.56 ns/op 1.05
browser block root to RootHex using toRootHex 129.58 ns/op 125.66 ns/op 1.03
browser fromHex(blob) 1.5175 ms/op 1.4918 ms/op 1.02
browser fromHexInto(blob) 644.64 us/op 609.20 us/op 1.06
browser block root to RootHex using the deprecated toHexString 351.63 ns/op 454.24 ns/op 0.77
browser byteArrayEquals 32 bytes (block root) 30.222 ns/op 27.349 ns/op 1.11
browser byteArrayEquals 48 bytes (pubkey) 43.200 ns/op 38.533 ns/op 1.12
browser byteArrayEquals 96 bytes (signature) 81.627 ns/op 71.852 ns/op 1.14
browser byteArrayEquals 1024 bytes 746.95 ns/op 725.60 ns/op 1.03
browser byteArrayEquals 131072 bytes (blob) 94.364 us/op 92.260 us/op 1.02

by benchmarkbot/action

@jtraglia

jtraglia commented Feb 3, 2026

Copy link
Copy Markdown
Contributor

Hey @nazarhussain, we chatted about running FCR tests at the meeting today.

This is how you can generate these tests:

  1. Clone Mikhail's fork/branch with FCR
git clone git@github.com:mkalinin/eth2.0-specs.git -b fast-conf-rule
  1. Generate the tests
cd eth2.0-specs
make reftests runner=fast_confirmation

This will write the tests to ../consensus-spec-tests.

Note: There is currently a failing test with Gloas, which sort of breaks test generation. So you could generate the reference tests for a specific fork instead, something like the following. After Mikhail fixes the issue, you can use the command above.

make reftests runner=fast_confirmation fork=fulu

@nazarhussain

Copy link
Copy Markdown
Contributor Author

@lodekeeper Here are the followup issues #9438 #9437

@lodekeeper

Copy link
Copy Markdown
Contributor

Thanks — #9437 and #9438 both match what was asked for. Tracked.

@nazarhussain

Copy link
Copy Markdown
Contributor Author

@lodekeeper If all points are addressed please approve the RP. :)

@lodekeeper

Copy link
Copy Markdown
Contributor

Walking through my points against the current head (`9a0c038` / `500226c2d8` "as per feedback"):

Inline — all resolved in `500226c2`:

  • `r3329133070` loop conditions → renamed (`shouldAdvanceThroughPreviousEpoch`, ...)
  • `r3329133074` tuple-string keys → `Map<RootHex, Map<Epoch, number>>`
  • `r3329133077` `Map<string, ...>` for sourceKey → `BalanceSourceKey` literal union
  • `r3329133080` duplicated `ensureVoteMaps` branches → unified with `indices` / `isSlashed` predicates
  • `r3329133083` generic `confirmed_reset` reason → `FastConfirmationDecisionReason` enum
  • `r3329133084` repeated neutral-threshold literal → `SAFETY_THRESHOLD_UNREACHABLE`
  • `r3329133087` `fc.getConfirmedRoot?.()` → optional chaining dropped in `safeBlocks.ts`
  • `r3329133089` `FCR*` re-export aliases → exports use canonical `FastConfirmation*` names
  • `r3329133091` dead `decision.stop` field → removed from type and runner
  • `r3329133092` `getSupportDiscount` passthrough → removed
  • `r3329133094` `COMMITTEE_WEIGHT_ESTIMATION_ADJUSTMENT_FACTOR=5` + `+ 999` → spec link at lines 23-25 + extra comment in `adjustCommitteeWeightEstimateToEnsureSafety` (acked in-thread)
  • `r3329133096` empty meta on FCR failure warn → now includes `slot`/`head`/`confirmedRoot`
  • `r3329133097` `confirmedSlot=0` ambiguity → no-block else-branch removed
  • `r3329133099` `EndTimer` optional-in-type → `EndTimer = () => number`, optionality moved to parameter sites

Issue-level — deferred but tracked:

From my review surface every point I raised is addressed or tracked. One caveat I want to repeat from the original review (`pullrequestreview-4395743466`): the bugs-class reviewer timed out and I flagged "recommend a focused bug-class pass before merge" — that part hasn't happened yet, just noting.

On the formal APPROVE — I'm staying with a COMMENTED review here (that's the lodekeeper pattern across PRs #9350 / #9332 / #9317 / #9430 / #9334). The merge gate is better held by maintainer review — Nico's "did you confirm on mainnet? consistent with Prysm?" question + twoeths' `onFastConfirmation` integration discussion are the active ones from my side.

@nflaig

nflaig commented Jun 3, 2026

Copy link
Copy Markdown
Member

@nazarhussain did you confirm this on mainnet already, does our implementation align with how prysm (and other clients) work, in terms of returning the same result consistently?

https://ethpandaops.io/posts/fcr-simulator/ has some great data points

Comment thread dashboards/lodestar_fork_choice.json
Comment thread dashboards/lodestar_fork_choice.json
Comment thread packages/api/src/beacon/routes/lodestar.ts Outdated
Comment thread packages/config/src/chainConfig/configs/mainnet.ts
Comment thread docs/pages/contribution/advanced-topics/fast-confirmation.md Outdated
Comment thread packages/fork-choice/src/forkChoice/forkChoice.ts
Comment thread packages/fork-choice/src/forkChoice/forkChoice.ts Outdated
Comment thread packages/fork-choice/src/forkChoice/safeBlocks.ts
Comment thread packages/validator/src/util/params.ts Outdated
Comment thread packages/fork-choice/src/forkChoice/fastConfirmation/utils.ts

@lodekeeper lodekeeper 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.

All concerns from my multi-persona review (#8837 (review)) are now either addressed in-PR or tracked as follow-ups:

Addressed in-PR (commit 500226c2 "update code as per feedback" + a8559a53 "align FCR chain safety check for ethereum/consensus-specs#5288"):

  • All 14 inline wisdom items (loop renames, tuple-keys removal, BalanceSourceKey, ensureVoteMaps dedup, FastConfirmationDecisionReason enum, SAFETY_THRESHOLD_UNREACHABLE, optional-chaining drop, FCR* aliases removed, dead decision.stop removed, passthrough removed, spec link on = 5, FCR warning meta, null instead of 0 for missing block, EndTimer type alias)
  • Spec alignment with ethereum/consensus-specs#5288 (FCR chain safety check using get_checkpoint_for_block)
  • DA #1 (public API): superseded by #9439 which adds the standardized fast_confirmation SSE event per ethereum/beacon-APIs#598 + #611 and removes the Lodestar-namespaced polling endpoint

Tracked as follow-up issues (both opened by @nazarhussain 2026-06-01):

  • #9437 — DA #2 / A2: isolate FCR state from core fork-choice store
  • #9438 — A1: narrow state provider boundary for FCR

Minor remaining (non-blocker):

  • getTrackedVotesCount is a per-slot linear scan; fine at current call frequency.

CI green incl. spec tests. LGTM.

🤖 Generated with AI assistance.

@nazarhussain
nazarhussain requested a review from nflaig June 5, 2026 09:13
Comment on lines 1020 to 1030
while (this.fcStore.currentSlot < currentSlot) {
const previousSlot = this.fcStore.currentSlot;
// Note: we are relying upon `onTick` to update `fcStore.time` to ensure we don't get stuck in a loop.
this.onTick(previousSlot + 1);
this.queuedAttestationsPreviousSlot = 0;
// Process any attestations that might now be eligible before running FCR for this slot.
this.processAttestationQueue();
this.runFastConfirmation();
this.validatedAttestationDatas = new Set();
}

this.queuedAttestationsPreviousSlot = 0;
// Process any attestations that might now be eligible.
this.processAttestationQueue();
this.validatedAttestationDatas = new Set();
}

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.

worth pointing out we moved this inside the while-loop cc @twoeths @nazarhussain, not a blocker for merging this, just need confirmation this was intended, suggests we had a bug before here?

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.

Intentional — runFastConfirmation() makes per-slot decisions, so the queue has to be drained against each slot's fcStore.currentSlot (not the final one), and the reset/snapshot lines come along with processAttestationQueue to keep the FCR-input ordering correct: reset counter → drain queued attestations into latestMessagesupdateHead()+FCR snapshot → clear validatedAttestationDatas.

Pre-PR wasn't a correctness bug as far as I can see: in the single-tick hot path, loop-body vs after-loop are equivalent. For the rare multi-slot tick (resync / clock jump), the old code drained the queue once against the final slot — late but not wrong, since findHead consumers always run after updateTime, so all the queued attestations were always applied before any vote was read. The new code just tightens per-slot semantics so FCR can hook in cleanly.

Minor follow-up worth a separate PR: the docstring above updateTime ("calling this multiple times in the same slot does not update votes…") still reads like the all-at-end model — could be refreshed to note the per-slot inner work.

@nflaig nflaig 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.

LGTM

@nflaig
nflaig merged commit a879adb into unstable Jun 7, 2026
19 checks passed
@nflaig
nflaig deleted the feature/fast-confirmation branch June 7, 2026 12:14
@wemeetagain

Copy link
Copy Markdown
Member

🎉 This PR is included in v1.44.0 🎉

nazarhussain added a commit that referenced this pull request Jul 24, 2026
File used .ts relative imports since #8837; every other test file in
the package uses .js per repo convention.

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.

6 participants