Add GET /eth/v2/debug/fork_choice endpoint#615
Conversation
Adds a Gloas (EIP-7732) aware fork choice debug endpoint. Unlike v1, it emits one node per (block_root, payload_status) tuple, adds a payload_status field (pending/empty/full), and exposes individual PTC vote counts in each node's extra_data. v1 is left unchanged as it remains valid for pre-Gloas chains. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| NodeV2ExtraData: | ||
| type: object | ||
| additionalProperties: true | ||
| description: 'Optional extra data that clients may provide, which could differ from client to client. The properties documented below are commonly provided; clients may add their own.' |
There was a problem hiding this comment.
why would we put this into extra_data, this seems to be a free form field which clients can populate as they would like, so defining that in the spec seems the opposite of what we want for it
| type: object | ||
| description: "Debugging context of fork choice" | ||
| required: [justified_checkpoint, finalized_checkpoint, fork_choice_nodes] | ||
| properties: |
There was a problem hiding this comment.
if we got with a v2 endpoint, this should be wrapped into a data container to follow the standard response format
| | [#590](https://github.com/ethereum/beacon-APIs/pull/590) `head_v2 EVENT` added | | | | | | | ||
| | [#590](https://github.com/ethereum/beacon-APIs/pull/590) `head EVENT` deprecated | | | | | | | ||
| | [#598](https://github.com/ethereum/beacon-APIs/pull/598) `fast_confirmation EVENT` added | | | | | | | ||
| | [#615](https://github.com/ethereum/beacon-APIs/pull/615) `GET /eth/v2/debug/fork_choice` added | | | | | | |
There was a problem hiding this comment.
in case we go with v2, should also deprecate the previous endpoint so we can eventually remove it
|
It sounds like all the extra data fields are pretty much included by all clients: |
|
does it make sense to still keep one forkchoice node per slot. something like this; pre Gloassimilar to v1 forkchoice nodes {
"slot": "6",
"block_root": "0xc44ec78e1f44a06cbd383b7e89f7ada54cccb5420d9ef6eb95beb46bae7d4c5e",
"parent_root": "0x59c7d6e88e0cdedd2dec59b635732b6b1eb378f684bb6d0b928035da9ff3b97c",
"weight": "307200000000",
"validity": "valid",
"execution_block_hash": "0xf73a47c1f47ae3445e7b5d2b1605f619a106699df639f89f90327bce0bea6ec5",
"gas_limit": "150000000",
"justified_epoch": "0",
"finalized_epoch": "0",
"extra_data": {
"balance": "307200000000",
"target_root": "0x63d3084d2a0ee8c05c2b2bdd13fc0ffd580bef7838800dcf8179c3ad187f2832",
"unrealized_justified_epoch": "0",
"unrealized_finalized_epoch": "0",
"block_seen_ms": "1781071852070"
}
}post Gloas{
"slot": "257",
"block_root": "0x9c63a225c893e7cc7d7b71ee1c4211d9f16515e13d75ae460f68aca64fe6771a",
"parent_root": "0x5abdb01eee2237f5bd3718074f3a34212c12036450f589049e34ef738ad5f478",
"parent_payload_status": "full",
"payload_status": "full",
"weight": "24883200000000",
"validity": "valid",
"execution_block_hash": "0xfee367b74767f5fa36598624f458aa3b246502405bcb16213c5072816e7575db",
"gas_limit": "150000000",
"justified_epoch": "7",
"finalized_epoch": "6",
"payload_attester_count": "24",
"payload_availability_yes_count": "24",
"payload_data_availability_yes_count": "24",
"empty_weight": "0",
"full_weight": "24883200000000",
"extra_data": {
"balance": "0",
"target_root": "0x5abdb01eee2237f5bd3718074f3a34212c12036450f589049e34ef738ad5f478",
"unrealized_justified_epoch": "7",
"unrealized_finalized_epoch": "6",
"block_seen_ms": "1781137466056",
"payload_seen_ms": "1781137466255"
}
}I had some initial thoughts dumped here |
|
i think im not set on the v1 output, most of the extra data will be needed for all clients, so having it at top level to me makes sense. |
Sample from my implementation... I grabbed all of a single slot, which has 3 entries at least in our implementation. |
i'm all for bringing extra data down a level! two questions;
|
because it's a debug endpoint, thats our internal representation... so theres 3 nodes in our memory which is why we're outputting 3... internally its the weights that dictate direction, o the above example has all the weight in the 'pending' route. This is why it might be useful to keep simple and just output our data structures though, because that would help us debugging what's going on. The more we abstract the structures away, the more interpretation is needed... Pre-gloas is simpler because there's not multiple states per node... |
|
the only thing that pre-gloas teku uses payload status to be |
AI generated to match Prysm's implementation, even this description is fully AI:
Adds a Gloas (EIP-7732) aware fork choice debug endpoint at
GET /eth/v2/debug/fork_choice.Corresponds to Prysm PR OffchainLabs/prysm#16862.
Why a new version
Under EIP-7732 a single block root can have multiple fork-choice nodes depending on its payload status (the payload may be pending, revealed empty, or revealed full), and the node now tracks individual PTC (Payload Timeliness Committee) votes. The v1 response cannot represent this: it has one node per block root and no payload status. The v2 response:
(block_root, payload_status)tuple,payload_statusfield with enumpending/empty/full,payload_attester_count,payload_availability_yes_count,payload_data_availability_yes_count) in each node'sextra_data./eth/v1/debug/fork_choiceis left unchanged, as it remains valid for pre-Gloas chains.Changes
apis/debug/fork_choice.v2.yaml.NodeV2/NodeV2ExtraDataschemas intypes/fork_choice.yaml, registered inbeacon-node-oapi.yaml./eth/v2/debug/fork_choiceregistered inbeacon-node-oapi.yaml.CHANGES.mdrow added.🤖 Generated with Claude Code