chore: implement forkchoice debug endpoint v2#9444
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces the /eth/v2/debug/fork_choice endpoint (getDebugForkChoiceV2) to retrieve the current fork choice context, including Payload Timeliness Committee (PTC) vote tallies and unrealized checkpoints. It adds the necessary SSZ container types, implements the API handler, and extends ForkChoice and ProtoArray to support retrieving PTC vote counts. Feedback on the implementation suggests correcting the gasLimit logic, which currently returns null for pre-Gloas blocks despite them being FULL blocks with valid gas limits, by directly checking the node's payload status.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Performance Report✔️ no performance regression detected Full benchmark results
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## unstable #9444 +/- ##
=========================================
Coverage 52.56% 52.57%
=========================================
Files 848 848
Lines 60937 60924 -13
Branches 4485 4485
=========================================
- Hits 32031 32029 -2
+ Misses 28844 28833 -11
Partials 62 62 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bc28c6388c
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| forkChoiceNodes: forkChoice.getAllNodes().map((node) => { | ||
| // Payload-specific fields apply only to a revealed Gloas payload = the FULL variant of a | ||
| // Gloas block | ||
| const ptc = node.payloadStatus === PayloadStatus.FULL ? forkChoice.getPTCVoteCounts(node.blockRoot) : null; |
There was a problem hiding this comment.
Report PTC tallies on the pending fork-choice node
For Gloas blocks with PTC messages, this FULL-only guard makes the PENDING node return null for payload_attester_count and the yes counts even though ProtoArray initializes and updates those vote maps by block root as soon as the beacon block is seen. The referenced v2 fork-choice model carries the PTC attester/availability tallies on the PENDING consensus node, while FULL/EMPTY are payload variants, so this hides the counts during the pending period and later attaches them to the wrong node after the payload arrives.
Useful? React with 👍 / 👎.
| }, | ||
| }, | ||
| getDebugForkChoiceV2: { | ||
| url: "/eth/v2/debug/fork_choice", |
There was a problem hiding this comment.
do we really want a v2 for that? I am thinking since it's a debug endpoint and just used by us and since it only adds new fields + is json only, we should be fine with keeping v1 and adding new fields
There was a problem hiding this comment.
I guess there's a consumer of this api, it's in a spec for a reason
can change once we have an official beacon-api spec for it
There was a problem hiding this comment.
yes there are consumers like https://github.com/ethpandaops/forky, but what I am saying is that it might be simpler to add the new fields to v1 rather than introducing a v2 if it's a backward compatible change for all consumers, which someone has to confirm, I would expect so because it already returns a very loosely defined json dump, eg. it also has this extra_data field. But we can further discuss this if there is a spec PR for it, for now, this api as is will be useful for us to debug devnet issues
There was a problem hiding this comment.
there is a spec PR now but it's essentially based on the Prysm format, so I would assume our implementation already follows that
| transform: { | ||
| toResponse: (data) => ({ | ||
| ...(data as ForkChoiceResponseV2), | ||
| }), | ||
| fromResponse: (resp) => ({ | ||
| data: resp as ForkChoiceResponseV2, | ||
| }), |
There was a problem hiding this comment.
if we go with a v2 endpoint, should at least make sure we wrap this inside data container to not have to do this custom transformation, I raised the same on the spec PR here ethereum/beacon-APIs#615 (comment)
| FULU_FORK_EPOCH: 1, | ||
| FULU_FORK_EPOCH: 0, | ||
| GLOAS_FORK_EPOCH: 1, |
There was a problem hiding this comment.
I don't think it matters much but was this intentional? cc @twoeths
| proposerBoostRoot: rootHex, | ||
| previousProposerBoostRoot: rootHex, | ||
| headRoot: rootHex, | ||
| }, |
There was a problem hiding this comment.
we should remove this from extra_data field and move to top-level fields, related comment ethereum/beacon-APIs#615 (comment)
|
link to spec PR, may need to adopt later if there're something changed ethereum/beacon-APIs#615 |
|
🎉 This PR is included in v1.44.0 🎉 |
Motivation
Description
{ "payload_status": "empty", "slot": "9", "block_root": "0x37d931610a0f4ff0ec7a4057f9905799e459a75fe49c76f258d5e11d47d1d79c", "parent_root": "0x2301a10ddc86d8cea5bbb05d0aa0dca8823123d3bfa6c2f8a7a6ff4450636d6e", "weight": "0", "validity": "valid", "execution_block_hash": "0xbbc1f010c338817286e384fcc129b96885d63619e206ce2729969f135252b81d", "extra_data": { "execution_optimistic": false, "timestamp": "1780386554", "target": "0x2301a10ddc86d8cea5bbb05d0aa0dca8823123d3bfa6c2f8a7a6ff4450636d6e", "justified_epoch": "0", "finalized_epoch": "0", "unrealized_justified_epoch": "0", "unrealized_finalized_epoch": "0", "payload_attester_count": null, "payload_availability_yes_count": null, "payload_data_availability_yes_count": null, "gas_limit": null } }{ "payload_status": "full", "slot": "9", "block_root": "0x37d931610a0f4ff0ec7a4057f9905799e459a75fe49c76f258d5e11d47d1d79c", "parent_root": "0x2301a10ddc86d8cea5bbb05d0aa0dca8823123d3bfa6c2f8a7a6ff4450636d6e", "weight": "44", "validity": "valid", "execution_block_hash": "0xec7766e17b1df22a82a447d97c457301c4b6be3b66928d13c87224dc3bc0ab76", "extra_data": { "execution_optimistic": false, "timestamp": "1780386554", "target": "0x2301a10ddc86d8cea5bbb05d0aa0dca8823123d3bfa6c2f8a7a6ff4450636d6e", "justified_epoch": "0", "finalized_epoch": "0", "unrealized_justified_epoch": "0", "unrealized_finalized_epoch": "0", "payload_attester_count": "16", "payload_availability_yes_count": "16", "payload_data_availability_yes_count": "16", "gas_limit": "30000000" } }Testing
node ./bin/lodestar.js dev --genesisValidators 8 --startValidators 0..7 --params.GLOAS_FORK_EPOCH 1 --enr.ip 127.0.0.1 --dataDir .lodestar/gloas-node --reset --rest --rest.namespace '*' --metricscurl http://localhost:9596/eth/v2/debug/fork_choiceAI Assistance Disclosure