Skip to content

feat: add getNodeVersionV2 endpoint#8772

Merged
nflaig merged 10 commits intounstablefrom
nflaig/node-version-v2
Feb 12, 2026
Merged

feat: add getNodeVersionV2 endpoint#8772
nflaig merged 10 commits intounstablefrom
nflaig/node-version-v2

Conversation

@nflaig
Copy link
Member

@nflaig nflaig commented Jan 21, 2026

@gemini-code-assist
Copy link
Contributor

Summary of Changes

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

This pull request enhances the API by adding a new endpoint that provides detailed, structured version information for both the beacon node and its associated execution client. This standardization aligns with the Ethereum Engine API, making it easier to identify the specific client implementations and their versions, which is crucial for network diagnostics and compatibility checks.

Highlights

  • New API Endpoint: Introduced a new endpoint /eth/v2/node/version (getNodeVersionV2) to retrieve structured version information for both the beacon node and its connected execution client.
  • Client Identification Types: Added ClientCode enum, ClientVersion type, and NodeVersionV2 type to standardize client identification, mirroring the Ethereum Engine API specification.
  • Centralized Type Definitions: Refactored ClientCode and ClientVersion definitions to be centralized within packages/api/src/beacon/routes/node.ts and re-exported where needed, removing duplication from packages/beacon-node/src/execution/engine/interface.ts.

🧠 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.

Copy link
Contributor

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

Choose a reason for hiding this comment

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

Code Review

The pull request introduces a new getNodeVersionV2 endpoint, providing structured version information for both the beacon node and its execution client. This change centralizes the ClientCode enum and ClientVersion type definitions in packages/api/src/beacon/routes/node.ts, which are then re-exported and used across the codebase. The implementation correctly retrieves client versions and includes a robust fallback for unknown execution clients. Test data for the new endpoint has also been added, ensuring proper functionality. The changes are well-structured and improve consistency by consolidating type definitions.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 21, 2026

Performance Report

✔️ no performance regression detected

Full benchmark results
Benchmark suite Current: 501a251 Previous: 5552610 Ratio
getPubkeys - index2pubkey - req 1000 vs - 250000 vc 1.2651 ms/op 1.2279 ms/op 1.03
getPubkeys - validatorsArr - req 1000 vs - 250000 vc 42.732 us/op 37.479 us/op 1.14
BLS verify - blst 974.54 us/op 787.15 us/op 1.24
BLS verifyMultipleSignatures 3 - blst 1.3060 ms/op 1.3617 ms/op 0.96
BLS verifyMultipleSignatures 8 - blst 1.9978 ms/op 2.0477 ms/op 0.98
BLS verifyMultipleSignatures 32 - blst 6.0069 ms/op 6.4887 ms/op 0.93
BLS verifyMultipleSignatures 64 - blst 11.758 ms/op 11.380 ms/op 1.03
BLS verifyMultipleSignatures 128 - blst 19.332 ms/op 21.828 ms/op 0.89
BLS deserializing 10000 signatures 721.37 ms/op 697.63 ms/op 1.03
BLS deserializing 100000 signatures 7.2223 s/op 6.6611 s/op 1.08
BLS verifyMultipleSignatures - same message - 3 - blst 1.0736 ms/op 1.0397 ms/op 1.03
BLS verifyMultipleSignatures - same message - 8 - blst 1.0939 ms/op 1.0459 ms/op 1.05
BLS verifyMultipleSignatures - same message - 32 - blst 1.7487 ms/op 1.6745 ms/op 1.04
BLS verifyMultipleSignatures - same message - 64 - blst 2.6630 ms/op 2.4843 ms/op 1.07
BLS verifyMultipleSignatures - same message - 128 - blst 4.4842 ms/op 4.1268 ms/op 1.09
BLS aggregatePubkeys 32 - blst 20.000 us/op 18.125 us/op 1.10
BLS aggregatePubkeys 128 - blst 71.234 us/op 63.757 us/op 1.12
getSlashingsAndExits - default max 71.670 us/op 62.392 us/op 1.15
getSlashingsAndExits - 2k 348.90 us/op 378.56 us/op 0.92
isKnown best case - 1 super set check 219.00 ns/op 400.00 ns/op 0.55
isKnown normal case - 2 super set checks 214.00 ns/op 396.00 ns/op 0.54
isKnown worse case - 16 super set checks 216.00 ns/op 423.00 ns/op 0.51
validate api signedAggregateAndProof - struct 1.4841 ms/op 1.5126 ms/op 0.98
validate gossip signedAggregateAndProof - struct 1.4318 ms/op 1.6065 ms/op 0.89
batch validate gossip attestation - vc 640000 - chunk 32 124.72 us/op 112.91 us/op 1.10
batch validate gossip attestation - vc 640000 - chunk 64 107.29 us/op 94.792 us/op 1.13
batch validate gossip attestation - vc 640000 - chunk 128 99.093 us/op 87.668 us/op 1.13
batch validate gossip attestation - vc 640000 - chunk 256 96.651 us/op 87.459 us/op 1.11
bytes32 toHexString 418.00 ns/op 508.00 ns/op 0.82
bytes32 Buffer.toString(hex) 261.00 ns/op 404.00 ns/op 0.65
bytes32 Buffer.toString(hex) from Uint8Array 363.00 ns/op 503.00 ns/op 0.72
bytes32 Buffer.toString(hex) + 0x 238.00 ns/op 455.00 ns/op 0.52
Return object 10000 times 0.24290 ns/op 0.22800 ns/op 1.07
Throw Error 10000 times 4.1638 us/op 3.3922 us/op 1.23
toHex 137.42 ns/op 100.64 ns/op 1.37
Buffer.from 126.29 ns/op 88.277 ns/op 1.43
shared Buffer 84.575 ns/op 58.040 ns/op 1.46
fastMsgIdFn sha256 / 200 bytes 1.8980 us/op 1.7810 us/op 1.07
fastMsgIdFn h32 xxhash / 200 bytes 212.00 ns/op 356.00 ns/op 0.60
fastMsgIdFn h64 xxhash / 200 bytes 267.00 ns/op 436.00 ns/op 0.61
fastMsgIdFn sha256 / 1000 bytes 6.5070 us/op 4.9900 us/op 1.30
fastMsgIdFn h32 xxhash / 1000 bytes 335.00 ns/op 472.00 ns/op 0.71
fastMsgIdFn h64 xxhash / 1000 bytes 365.00 ns/op 565.00 ns/op 0.65
fastMsgIdFn sha256 / 10000 bytes 56.162 us/op 41.439 us/op 1.36
fastMsgIdFn h32 xxhash / 10000 bytes 1.4930 us/op 1.4910 us/op 1.00
fastMsgIdFn h64 xxhash / 10000 bytes 1.0990 us/op 1.0610 us/op 1.04
send data - 1000 256B messages 16.165 ms/op 12.010 ms/op 1.35
send data - 1000 512B messages 17.020 ms/op 14.582 ms/op 1.17
send data - 1000 1024B messages 24.285 ms/op 19.282 ms/op 1.26
send data - 1000 1200B messages 26.645 ms/op 21.393 ms/op 1.25
send data - 1000 2048B messages 25.440 ms/op 19.268 ms/op 1.32
send data - 1000 4096B messages 27.348 ms/op 18.538 ms/op 1.48
send data - 1000 16384B messages 98.288 ms/op 87.237 ms/op 1.13
send data - 1000 65536B messages 228.06 ms/op 249.09 ms/op 0.92
enrSubnets - fastDeserialize 64 bits 1.0190 us/op 1.0290 us/op 0.99
enrSubnets - ssz BitVector 64 bits 350.00 ns/op 532.00 ns/op 0.66
enrSubnets - fastDeserialize 4 bits 156.00 ns/op 323.00 ns/op 0.48
enrSubnets - ssz BitVector 4 bits 378.00 ns/op 517.00 ns/op 0.73
prioritizePeers score -10:0 att 32-0.1 sync 2-0 236.33 us/op 203.97 us/op 1.16
prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 262.69 us/op 234.72 us/op 1.12
prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 376.65 us/op 339.24 us/op 1.11
prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 713.17 us/op 630.63 us/op 1.13
prioritizePeers score 0:0 att 64-1 sync 4-1 864.38 us/op 899.82 us/op 0.96
array of 16000 items push then shift 1.6553 us/op 1.3340 us/op 1.24
LinkedList of 16000 items push then shift 7.9530 ns/op 8.3070 ns/op 0.96
array of 16000 items push then pop 76.878 ns/op 69.549 ns/op 1.11
LinkedList of 16000 items push then pop 7.7400 ns/op 6.5870 ns/op 1.18
array of 24000 items push then shift 2.4462 us/op 1.9474 us/op 1.26
LinkedList of 24000 items push then shift 8.7390 ns/op 7.6310 ns/op 1.15
array of 24000 items push then pop 112.17 ns/op 97.196 ns/op 1.15
LinkedList of 24000 items push then pop 7.8020 ns/op 6.5610 ns/op 1.19
intersect bitArray bitLen 8 6.1880 ns/op 4.9640 ns/op 1.25
intersect array and set length 8 36.231 ns/op 30.390 ns/op 1.19
intersect bitArray bitLen 128 30.717 ns/op 26.668 ns/op 1.15
intersect array and set length 128 598.33 ns/op 506.20 ns/op 1.18
bitArray.getTrueBitIndexes() bitLen 128 1.1210 us/op 1.3600 us/op 0.82
bitArray.getTrueBitIndexes() bitLen 248 1.8250 us/op 2.0360 us/op 0.90
bitArray.getTrueBitIndexes() bitLen 512 3.9080 us/op 3.9520 us/op 0.99
Full columns - reconstruct all 6 blobs 231.84 us/op 302.83 us/op 0.77
Full columns - reconstruct half of the blobs out of 6 140.64 us/op 87.235 us/op 1.61
Full columns - reconstruct single blob out of 6 37.714 us/op 59.703 us/op 0.63
Half columns - reconstruct all 6 blobs 388.47 ms/op 259.87 ms/op 1.49
Half columns - reconstruct half of the blobs out of 6 143.41 ms/op 124.71 ms/op 1.15
Half columns - reconstruct single blob out of 6 52.825 ms/op 46.179 ms/op 1.14
Full columns - reconstruct all 10 blobs 289.56 us/op 368.59 us/op 0.79
Full columns - reconstruct half of the blobs out of 10 187.05 us/op 186.51 us/op 1.00
Full columns - reconstruct single blob out of 10 43.478 us/op 30.799 us/op 1.41
Half columns - reconstruct all 10 blobs 468.82 ms/op 409.05 ms/op 1.15
Half columns - reconstruct half of the blobs out of 10 236.91 ms/op 221.52 ms/op 1.07
Half columns - reconstruct single blob out of 10 52.502 ms/op 53.619 ms/op 0.98
Full columns - reconstruct all 20 blobs 855.03 us/op 763.49 us/op 1.12
Full columns - reconstruct half of the blobs out of 20 342.28 us/op 340.01 us/op 1.01
Full columns - reconstruct single blob out of 20 43.920 us/op 32.193 us/op 1.36
Half columns - reconstruct all 20 blobs 935.76 ms/op 1.0320 s/op 0.91
Half columns - reconstruct half of the blobs out of 20 467.01 ms/op 394.55 ms/op 1.18
Half columns - reconstruct single blob out of 20 52.717 ms/op 45.360 ms/op 1.16
Set add up to 64 items then delete first 2.1199 us/op 1.5768 us/op 1.34
OrderedSet add up to 64 items then delete first 3.1157 us/op 2.3603 us/op 1.32
Set add up to 64 items then delete last 2.3427 us/op 1.8239 us/op 1.28
OrderedSet add up to 64 items then delete last 3.3864 us/op 2.7718 us/op 1.22
Set add up to 64 items then delete middle 2.3694 us/op 1.7975 us/op 1.32
OrderedSet add up to 64 items then delete middle 4.9692 us/op 4.3383 us/op 1.15
Set add up to 128 items then delete first 4.9380 us/op 3.6810 us/op 1.34
OrderedSet add up to 128 items then delete first 7.3424 us/op 5.1609 us/op 1.42
Set add up to 128 items then delete last 4.7355 us/op 3.5351 us/op 1.34
OrderedSet add up to 128 items then delete last 6.8690 us/op 5.4380 us/op 1.26
Set add up to 128 items then delete middle 4.6791 us/op 3.5451 us/op 1.32
OrderedSet add up to 128 items then delete middle 13.518 us/op 11.513 us/op 1.17
Set add up to 256 items then delete first 10.512 us/op 7.1009 us/op 1.48
OrderedSet add up to 256 items then delete first 16.529 us/op 11.027 us/op 1.50
Set add up to 256 items then delete last 9.3983 us/op 7.2167 us/op 1.30
OrderedSet add up to 256 items then delete last 14.118 us/op 11.808 us/op 1.20
Set add up to 256 items then delete middle 9.8392 us/op 7.0757 us/op 1.39
OrderedSet add up to 256 items then delete middle 41.472 us/op 36.434 us/op 1.14
pass gossip attestations to forkchoice per slot 2.7925 ms/op 2.1539 ms/op 1.30
forkChoice updateHead vc 100000 bc 64 eq 0 522.29 us/op 379.79 us/op 1.38
forkChoice updateHead vc 600000 bc 64 eq 0 3.0912 ms/op 2.2461 ms/op 1.38
forkChoice updateHead vc 1000000 bc 64 eq 0 5.1621 ms/op 3.8060 ms/op 1.36
forkChoice updateHead vc 600000 bc 320 eq 0 3.1093 ms/op 2.2369 ms/op 1.39
forkChoice updateHead vc 600000 bc 1200 eq 0 3.2108 ms/op 2.5511 ms/op 1.26
forkChoice updateHead vc 600000 bc 7200 eq 0 3.6158 ms/op 2.8298 ms/op 1.28
forkChoice updateHead vc 600000 bc 64 eq 1000 3.5477 ms/op 3.0857 ms/op 1.15
forkChoice updateHead vc 600000 bc 64 eq 10000 3.7443 ms/op 2.8710 ms/op 1.30
forkChoice updateHead vc 600000 bc 64 eq 300000 9.7345 ms/op 6.5206 ms/op 1.49
computeDeltas 1400000 validators 0% inactive 15.171 ms/op 12.164 ms/op 1.25
computeDeltas 1400000 validators 10% inactive 14.208 ms/op 11.974 ms/op 1.19
computeDeltas 1400000 validators 20% inactive 13.356 ms/op 10.994 ms/op 1.21
computeDeltas 1400000 validators 50% inactive 10.442 ms/op 8.7328 ms/op 1.20
computeDeltas 2100000 validators 0% inactive 22.731 ms/op 19.608 ms/op 1.16
computeDeltas 2100000 validators 10% inactive 21.165 ms/op 18.238 ms/op 1.16
computeDeltas 2100000 validators 20% inactive 19.790 ms/op 16.673 ms/op 1.19
computeDeltas 2100000 validators 50% inactive 15.658 ms/op 9.6277 ms/op 1.63
altair processAttestation - 250000 vs - 7PWei normalcase 2.1599 ms/op 1.6304 ms/op 1.32
altair processAttestation - 250000 vs - 7PWei worstcase 3.3411 ms/op 2.3423 ms/op 1.43
altair processAttestation - setStatus - 1/6 committees join 118.77 us/op 94.489 us/op 1.26
altair processAttestation - setStatus - 1/3 committees join 235.30 us/op 179.22 us/op 1.31
altair processAttestation - setStatus - 1/2 committees join 339.25 us/op 258.88 us/op 1.31
altair processAttestation - setStatus - 2/3 committees join 432.19 us/op 346.82 us/op 1.25
altair processAttestation - setStatus - 4/5 committees join 565.78 us/op 480.05 us/op 1.18
altair processAttestation - setStatus - 100% committees join 675.18 us/op 559.48 us/op 1.21
altair processBlock - 250000 vs - 7PWei normalcase 4.4204 ms/op 3.5971 ms/op 1.23
altair processBlock - 250000 vs - 7PWei normalcase hashState 18.853 ms/op 14.560 ms/op 1.29
altair processBlock - 250000 vs - 7PWei worstcase 26.450 ms/op 23.516 ms/op 1.12
altair processBlock - 250000 vs - 7PWei worstcase hashState 71.636 ms/op 49.750 ms/op 1.44
phase0 processBlock - 250000 vs - 7PWei normalcase 1.6325 ms/op 1.4358 ms/op 1.14
phase0 processBlock - 250000 vs - 7PWei worstcase 21.065 ms/op 19.231 ms/op 1.10
altair processEth1Data - 250000 vs - 7PWei normalcase 372.43 us/op 278.54 us/op 1.34
getExpectedWithdrawals 250000 eb:1,eth1:1,we:0,wn:0,smpl:16 7.9280 us/op 5.9290 us/op 1.34
getExpectedWithdrawals 250000 eb:0.95,eth1:0.1,we:0.05,wn:0,smpl:220 33.632 us/op 18.581 us/op 1.81
getExpectedWithdrawals 250000 eb:0.95,eth1:0.3,we:0.05,wn:0,smpl:43 17.297 us/op 9.6350 us/op 1.80
getExpectedWithdrawals 250000 eb:0.95,eth1:0.7,we:0.05,wn:0,smpl:19 9.6710 us/op 5.4420 us/op 1.78
getExpectedWithdrawals 250000 eb:0.1,eth1:0.1,we:0,wn:0,smpl:1021 218.95 us/op 135.32 us/op 1.62
getExpectedWithdrawals 250000 eb:0.03,eth1:0.03,we:0,wn:0,smpl:11778 1.9267 ms/op 1.2704 ms/op 1.52
getExpectedWithdrawals 250000 eb:0.01,eth1:0.01,we:0,wn:0,smpl:16384 2.4352 ms/op 1.7711 ms/op 1.37
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,smpl:16384 2.4186 ms/op 1.7370 ms/op 1.39
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,nocache,smpl:16384 4.5863 ms/op 3.8500 ms/op 1.19
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,smpl:16384 2.8872 ms/op 1.9156 ms/op 1.51
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,nocache,smpl:16384 6.4881 ms/op 4.1579 ms/op 1.56
Tree 40 250000 create 393.23 ms/op 331.24 ms/op 1.19
Tree 40 250000 get(125000) 143.32 ns/op 95.577 ns/op 1.50
Tree 40 250000 set(125000) 1.2681 us/op 1.0660 us/op 1.19
Tree 40 250000 toArray() 14.776 ms/op 9.1980 ms/op 1.61
Tree 40 250000 iterate all - toArray() + loop 14.835 ms/op 9.0652 ms/op 1.64
Tree 40 250000 iterate all - get(i) 47.405 ms/op 37.592 ms/op 1.26
Array 250000 create 2.4911 ms/op 1.9569 ms/op 1.27
Array 250000 clone - spread 841.86 us/op 621.42 us/op 1.35
Array 250000 get(125000) 0.41600 ns/op 0.50400 ns/op 0.83
Array 250000 set(125000) 0.39200 ns/op 0.50500 ns/op 0.78
Array 250000 iterate all - loop 63.128 us/op 56.102 us/op 1.13
phase0 afterProcessEpoch - 250000 vs - 7PWei 42.478 ms/op 39.043 ms/op 1.09
Array.fill - length 1000000 3.0135 ms/op 2.0725 ms/op 1.45
Array push - length 1000000 10.796 ms/op 7.6666 ms/op 1.41
Array.get 0.22321 ns/op 0.20381 ns/op 1.10
Uint8Array.get 0.22752 ns/op 0.20110 ns/op 1.13
phase0 beforeProcessEpoch - 250000 vs - 7PWei 23.463 ms/op 15.980 ms/op 1.47
altair processEpoch - mainnet_e81889 253.34 ms/op 205.58 ms/op 1.23
mainnet_e81889 - altair beforeProcessEpoch 17.844 ms/op 14.189 ms/op 1.26
mainnet_e81889 - altair processJustificationAndFinalization 5.4520 us/op 4.5270 us/op 1.20
mainnet_e81889 - altair processInactivityUpdates 3.9364 ms/op 3.1781 ms/op 1.24
mainnet_e81889 - altair processRewardsAndPenalties 23.410 ms/op 17.879 ms/op 1.31
mainnet_e81889 - altair processRegistryUpdates 781.00 ns/op 799.00 ns/op 0.98
mainnet_e81889 - altair processSlashings 210.00 ns/op 359.00 ns/op 0.58
mainnet_e81889 - altair processEth1DataReset 269.00 ns/op 349.00 ns/op 0.77
mainnet_e81889 - altair processEffectiveBalanceUpdates 2.1441 ms/op 1.1045 ms/op 1.94
mainnet_e81889 - altair processSlashingsReset 1.0180 us/op 1.1610 us/op 0.88
mainnet_e81889 - altair processRandaoMixesReset 1.1620 us/op 1.1500 us/op 1.01
mainnet_e81889 - altair processHistoricalRootsUpdate 174.00 ns/op 369.00 ns/op 0.47
mainnet_e81889 - altair processParticipationFlagUpdates 663.00 ns/op 704.00 ns/op 0.94
mainnet_e81889 - altair processSyncCommitteeUpdates 140.00 ns/op 338.00 ns/op 0.41
mainnet_e81889 - altair afterProcessEpoch 44.987 ms/op 40.845 ms/op 1.10
capella processEpoch - mainnet_e217614 807.86 ms/op 780.16 ms/op 1.04
mainnet_e217614 - capella beforeProcessEpoch 72.782 ms/op 61.421 ms/op 1.18
mainnet_e217614 - capella processJustificationAndFinalization 6.4990 us/op 5.3070 us/op 1.22
mainnet_e217614 - capella processInactivityUpdates 16.606 ms/op 11.099 ms/op 1.50
mainnet_e217614 - capella processRewardsAndPenalties 100.02 ms/op 101.28 ms/op 0.99
mainnet_e217614 - capella processRegistryUpdates 5.8540 us/op 4.7880 us/op 1.22
mainnet_e217614 - capella processSlashings 166.00 ns/op 359.00 ns/op 0.46
mainnet_e217614 - capella processEth1DataReset 178.00 ns/op 370.00 ns/op 0.48
mainnet_e217614 - capella processEffectiveBalanceUpdates 17.718 ms/op 5.5340 ms/op 3.20
mainnet_e217614 - capella processSlashingsReset 1000.0 ns/op 984.00 ns/op 1.02
mainnet_e217614 - capella processRandaoMixesReset 1.1260 us/op 1.4850 us/op 0.76
mainnet_e217614 - capella processHistoricalRootsUpdate 171.00 ns/op 381.00 ns/op 0.45
mainnet_e217614 - capella processParticipationFlagUpdates 686.00 ns/op 690.00 ns/op 0.99
mainnet_e217614 - capella afterProcessEpoch 117.81 ms/op 110.11 ms/op 1.07
phase0 processEpoch - mainnet_e58758 252.79 ms/op 223.56 ms/op 1.13
mainnet_e58758 - phase0 beforeProcessEpoch 54.041 ms/op 60.351 ms/op 0.90
mainnet_e58758 - phase0 processJustificationAndFinalization 5.8160 us/op 6.1480 us/op 0.95
mainnet_e58758 - phase0 processRewardsAndPenalties 26.076 ms/op 18.111 ms/op 1.44
mainnet_e58758 - phase0 processRegistryUpdates 2.9800 us/op 2.6700 us/op 1.12
mainnet_e58758 - phase0 processSlashings 210.00 ns/op 426.00 ns/op 0.49
mainnet_e58758 - phase0 processEth1DataReset 167.00 ns/op 402.00 ns/op 0.42
mainnet_e58758 - phase0 processEffectiveBalanceUpdates 941.08 us/op 830.99 us/op 1.13
mainnet_e58758 - phase0 processSlashingsReset 960.00 ns/op 1.2080 us/op 0.79
mainnet_e58758 - phase0 processRandaoMixesReset 1.1970 us/op 1.2100 us/op 0.99
mainnet_e58758 - phase0 processHistoricalRootsUpdate 200.00 ns/op 386.00 ns/op 0.52
mainnet_e58758 - phase0 processParticipationRecordUpdates 960.00 ns/op 1.2680 us/op 0.76
mainnet_e58758 - phase0 afterProcessEpoch 37.416 ms/op 32.506 ms/op 1.15
phase0 processEffectiveBalanceUpdates - 250000 normalcase 1.9171 ms/op 950.03 us/op 2.02
phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 1.7943 ms/op 1.4925 ms/op 1.20
altair processInactivityUpdates - 250000 normalcase 18.519 ms/op 10.459 ms/op 1.77
altair processInactivityUpdates - 250000 worstcase 17.217 ms/op 11.111 ms/op 1.55
phase0 processRegistryUpdates - 250000 normalcase 7.5690 us/op 2.3570 us/op 3.21
phase0 processRegistryUpdates - 250000 badcase_full_deposits 351.86 us/op 249.49 us/op 1.41
phase0 processRegistryUpdates - 250000 worstcase 0.5 72.971 ms/op 59.023 ms/op 1.24
altair processRewardsAndPenalties - 250000 normalcase 19.107 ms/op 14.236 ms/op 1.34
altair processRewardsAndPenalties - 250000 worstcase 18.226 ms/op 14.057 ms/op 1.30
phase0 getAttestationDeltas - 250000 normalcase 7.3578 ms/op 4.9400 ms/op 1.49
phase0 getAttestationDeltas - 250000 worstcase 7.0767 ms/op 4.9453 ms/op 1.43
phase0 processSlashings - 250000 worstcase 82.035 us/op 94.312 us/op 0.87
altair processSyncCommitteeUpdates - 250000 11.560 ms/op 9.5216 ms/op 1.21
BeaconState.hashTreeRoot - No change 205.00 ns/op 417.00 ns/op 0.49
BeaconState.hashTreeRoot - 1 full validator 88.570 us/op 62.366 us/op 1.42
BeaconState.hashTreeRoot - 32 full validator 1.3077 ms/op 800.49 us/op 1.63
BeaconState.hashTreeRoot - 512 full validator 7.6347 ms/op 6.0034 ms/op 1.27
BeaconState.hashTreeRoot - 1 validator.effectiveBalance 101.09 us/op 82.114 us/op 1.23
BeaconState.hashTreeRoot - 32 validator.effectiveBalance 1.8370 ms/op 2.4426 ms/op 0.75
BeaconState.hashTreeRoot - 512 validator.effectiveBalance 17.711 ms/op 25.227 ms/op 0.70
BeaconState.hashTreeRoot - 1 balances 72.258 us/op 93.703 us/op 0.77
BeaconState.hashTreeRoot - 32 balances 950.89 us/op 878.95 us/op 1.08
BeaconState.hashTreeRoot - 512 balances 6.6232 ms/op 5.0942 ms/op 1.30
BeaconState.hashTreeRoot - 250000 balances 174.93 ms/op 114.53 ms/op 1.53
aggregationBits - 2048 els - zipIndexesInBitList 22.025 us/op 19.182 us/op 1.15
regular array get 100000 times 26.056 us/op 22.479 us/op 1.16
wrappedArray get 100000 times 26.208 us/op 22.477 us/op 1.17
arrayWithProxy get 100000 times 19.284 ms/op 9.4588 ms/op 2.04
ssz.Root.equals 25.195 ns/op 21.041 ns/op 1.20
byteArrayEquals 24.695 ns/op 20.644 ns/op 1.20
Buffer.compare 10.562 ns/op 8.6890 ns/op 1.22
processSlot - 1 slots 13.348 us/op 8.0100 us/op 1.67
processSlot - 32 slots 3.9430 ms/op 2.0147 ms/op 1.96
getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei 8.3441 ms/op 3.0484 ms/op 2.74
getCommitteeAssignments - req 1 vs - 250000 vc 1.9815 ms/op 1.6192 ms/op 1.22
getCommitteeAssignments - req 100 vs - 250000 vc 3.8793 ms/op 3.3114 ms/op 1.17
getCommitteeAssignments - req 1000 vs - 250000 vc 4.2215 ms/op 3.5252 ms/op 1.20
findModifiedValidators - 10000 modified validators 596.01 ms/op 543.70 ms/op 1.10
findModifiedValidators - 1000 modified validators 637.54 ms/op 302.14 ms/op 2.11
findModifiedValidators - 100 modified validators 364.27 ms/op 322.49 ms/op 1.13
findModifiedValidators - 10 modified validators 184.78 ms/op 210.95 ms/op 0.88
findModifiedValidators - 1 modified validators 184.15 ms/op 154.40 ms/op 1.19
findModifiedValidators - no difference 199.22 ms/op 127.37 ms/op 1.56
migrate state 1500000 validators, 3400 modified, 2000 new 1.1526 s/op 962.78 ms/op 1.20
RootCache.getBlockRootAtSlot - 250000 vs - 7PWei 4.8300 ns/op 5.8500 ns/op 0.83
state getBlockRootAtSlot - 250000 vs - 7PWei 652.39 ns/op 372.36 ns/op 1.75
computeProposerIndex 100000 validators 1.7139 ms/op 1.3311 ms/op 1.29
getNextSyncCommitteeIndices 1000 validators 141.65 ms/op 99.633 ms/op 1.42
getNextSyncCommitteeIndices 10000 validators 139.31 ms/op 99.544 ms/op 1.40
getNextSyncCommitteeIndices 100000 validators 132.48 ms/op 99.626 ms/op 1.33
computeProposers - vc 250000 709.39 us/op 579.15 us/op 1.22
computeEpochShuffling - vc 250000 44.526 ms/op 40.380 ms/op 1.10
getNextSyncCommittee - vc 250000 11.881 ms/op 9.3963 ms/op 1.26
nodejs block root to RootHex using toHex 150.50 ns/op 109.26 ns/op 1.38
nodejs block root to RootHex using toRootHex 92.143 ns/op 71.064 ns/op 1.30
nodejs fromHex(blob) 316.57 us/op 408.98 us/op 0.77
nodejs fromHexInto(blob) 822.57 us/op 693.47 us/op 1.19
nodejs block root to RootHex using the deprecated toHexString 615.68 ns/op 385.14 ns/op 1.60
nodejs byteArrayEquals 32 bytes (block root) 32.711 ns/op 31.069 ns/op 1.05
nodejs byteArrayEquals 48 bytes (pubkey) 44.697 ns/op 37.978 ns/op 1.18
nodejs byteArrayEquals 96 bytes (signature) 44.599 ns/op 35.938 ns/op 1.24
nodejs byteArrayEquals 1024 bytes 49.224 ns/op 42.630 ns/op 1.15
nodejs byteArrayEquals 131072 bytes (blob) 1.9968 us/op 1.7609 us/op 1.13
browser block root to RootHex using toHex 186.84 ns/op 147.78 ns/op 1.26
browser block root to RootHex using toRootHex 167.03 ns/op 134.67 ns/op 1.24
browser fromHex(blob) 1.3778 ms/op 989.66 us/op 1.39
browser fromHexInto(blob) 764.71 us/op 659.41 us/op 1.16
browser block root to RootHex using the deprecated toHexString 402.86 ns/op 516.64 ns/op 0.78
browser byteArrayEquals 32 bytes (block root) 32.611 ns/op 27.928 ns/op 1.17
browser byteArrayEquals 48 bytes (pubkey) 46.847 ns/op 43.503 ns/op 1.08
browser byteArrayEquals 96 bytes (signature) 90.262 ns/op 74.048 ns/op 1.22
browser byteArrayEquals 1024 bytes 823.65 ns/op 735.37 ns/op 1.12
browser byteArrayEquals 131072 bytes (blob) 107.73 us/op 94.325 us/op 1.14

by benchmarkbot/action

@nflaig
Copy link
Member Author

nflaig commented Feb 3, 2026

@lodekeeper please verify the changes in this PR and make sure it's up to date with ethereum/beacon-APIs#568

@nflaig nflaig marked this pull request as ready for review February 3, 2026 20:50
@nflaig nflaig requested a review from a team as a code owner February 3, 2026 20:50
Copy link
Contributor

@lodekeeper lodekeeper left a comment

Choose a reason for hiding this comment

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

LGTM! Verified implementation matches beacon-APIs PR #568:

  • ✅ ClientCode enum has all 14 codes per spec (BU, EJ, EG, GE, GR, LH, LS, NM, NB, TE, TK, PM, RH, XX)
  • ✅ ClientVersion type has required fields (code, name, version, commit)
  • ✅ Response structure correct with snake_case conversion via JsonOnlyResponseCodec
  • ✅ Falls back to XX/Unknown when EL client version unavailable

@nflaig nflaig marked this pull request as draft February 7, 2026 12:00
@nflaig
Copy link
Member Author

nflaig commented Feb 7, 2026

waiting for spec discussions to resolve

@nflaig
Copy link
Member Author

nflaig commented Feb 8, 2026

/gemini review

Copy link
Contributor

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

Choose a reason for hiding this comment

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

Code Review

This pull request successfully introduces the getNodeVersionV2 endpoint, aligning with the updated Ethereum beacon-APIs specification. The changes are well-structured, including the necessary type definitions, endpoint registration, and implementation. A notable improvement is the refactoring that centralizes the ClientVersion and ClientCode definitions, which enhances maintainability by reducing code duplication. The overall implementation is solid, and I have just one minor suggestion to improve code conciseness.

@nflaig
Copy link
Member Author

nflaig commented Feb 9, 2026

waiting for spec discussions to resolve

discussion has resolved and the PR is up to date with the spec

@nflaig nflaig marked this pull request as ready for review February 9, 2026 15:45
@lodekeeper
Copy link
Contributor

Verified against beacon-APIs PR #568 — the implementation aligns with the spec:

Spec compliance ✅

  • Endpoint: GET /eth/v2/node/version
  • Response shape: { data: { beacon_node: ClientVersionV1, execution_client?: ClientVersionV1 } }
  • execution_client is optional ✓
  • All ClientVersionV1 fields are required (code, name, version, commit) — matching Engine API and the resolved spec discussion ✓
  • JSON wire format uses snake_case via JsonOnlyResponseCodec (beaconNodebeacon_node) ✓
  • ClientCode enum includes all spec values + XX (unknown) as extra fallback ✓

Implementation notes

  • Clean refactor: ClientCode and ClientVersion types moved from execution/engine/interface.ts to api/routes/node.ts (shared between API and engine), re-exported for backwards compat
  • getLodestarClientVersion(opts) correctly uses ClientCode.LS with version/commit from opts
  • chain.executionEngine.clientVersion ?? undefined properly handles null → omit

Looks good to me 👍

@nflaig nflaig marked this pull request as draft February 10, 2026 10:14
@nflaig
Copy link
Member Author

nflaig commented Feb 10, 2026

@lodekeeper please take a look at the latest spec changes and update our implementation to be spec compliant, open a PR against this branch

lodekeeper and others added 3 commits February 10, 2026 15:24
## Description

Updates the `getNodeVersionV2` endpoint to match the latest [beacon-APIs
spec (PR #568)](ethereum/beacon-APIs#568), which
changed `execution_client` from a single `ClientVersionV1` to an
**array** of `ClientVersionV1`.

This supports multiplexed EL connections where
`engine_getClientVersionV1` may return multiple entries.

### Changes

- `NodeVersionV2.executionClient` → `ClientVersion[]` (optional array)
- `IExecutionEngine.clientVersion` → `clientVersions` (stores full array
from Engine API)
- Graffiti uses `clientVersions?.[0]` for the default graffiti string
- Test data updated to match new array type

### Spec Reference

From `apis/node/version.v2.yaml`:
```yaml
execution_client:
  type: array
  items:
    $ref: '../../beacon-node-oapi.yaml#/components/schemas/ClientVersionV1'
```

> The `execution_client` field is an array because the Engine API's
`engine_getClientVersionV1` may return multiple entries when using
multiplexed connections.

---
*This PR was authored with AI assistance (Claude/Lodekeeper 🌟)*

---------

Co-authored-by: lodekeeper <lodekeeper@users.noreply.github.com>
…ent (#8891)

## Description

Aligns `getNodeVersionV2` with the latest spec change in
[ethereum/beacon-APIs#568](ethereum/beacon-APIs#568).

The spec [reverted `execution_client` from an array to a single optional
object](eth2353/beacon-APIs@938aa33d) — if the
Engine API returns multiple values, the beacon node should return only
the first one for consistent behavior across clients.

### Changes
- `NodeVersionV2.executionClient`: `ClientVersion[]` → `ClientVersion`
(optional singular)
- Updated implementation to return single object instead of wrapping in
array
- Updated test data

### Type of change
- [x] Bug fix (non-breaking)

### Checklist
- [x] Types compile (`pnpm build` passes)
- [x] Unit tests pass (`pnpm --filter @lodestar/api test:unit` —
611/611)

_This PR was created with AI assistance (Lodekeeper 🌟)_

Co-authored-by: lodekeeper <lodekeeper@users.noreply.github.com>
@nflaig nflaig marked this pull request as ready for review February 11, 2026 11:02
Copy link

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

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: e7522d303f

ℹ️ 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".

data: {
beaconNode: toSpecClientVersion(getLodestarClientVersion(opts)),
executionClient:
chain.executionEngine.clientVersion != null
Copy link
Contributor

Choose a reason for hiding this comment

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

If execution client code is XX, we shouldn't include the executionClient field in the response.

I think we should still include XX in the block graffiti on the other hand

Copy link
Contributor

Choose a reason for hiding this comment

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

Makes sense to me — if the code is XX we essentially don't know the execution client, so omitting executionClient from the response is cleaner than returning unknown values. The spec leaves executionClient as optional for this reason.

We could filter it like:

executionClient:
  chain.executionEngine.clientVersion != null && chain.executionEngine.clientVersion.code !== ClientCode.XX
    ? toSpecClientVersion(chain.executionEngine.clientVersion)
    : undefined,

Agree on keeping XX in the graffiti though — better to signal that an EL is connected but unidentified than to omit it entirely from blocks.

Copy link
Member Author

@nflaig nflaig Feb 12, 2026

Choose a reason for hiding this comment

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

was debating that too, I think it's fine either way as the spec just states

Version information about the execution client may not be available
at all times and is therefore optional.

in our case, it's available but it's just a EL we don't know yet, but in practice there isn't much of a difference and returning XX is also not defined in the spec

updated in feebf54

Copy link
Member

Choose a reason for hiding this comment

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

I agree not returning XX is the right way to go

Copy link
Member

@matthewkeil matthewkeil left a comment

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 702f793 into unstable Feb 12, 2026
28 of 31 checks passed
@nflaig nflaig deleted the nflaig/node-version-v2 branch February 12, 2026 13:11
@codecov
Copy link

codecov bot commented Feb 12, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 52.34%. Comparing base (5552610) to head (206e533).
⚠️ Report is 1 commits behind head on unstable.

Additional details and impacted files
@@             Coverage Diff              @@
##           unstable    #8772      +/-   ##
============================================
- Coverage     52.34%   52.34%   -0.01%     
============================================
  Files           848      848              
  Lines         63471    63456      -15     
  Branches       4704     4703       -1     
============================================
- Hits          33227    33213      -14     
+ Misses        30175    30174       -1     
  Partials         69       69              
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

4 participants