Add head_v2 event and deprecate head event#590
Conversation
new_head eventhead_v2 event and deprecate head event
chong-he
left a comment
There was a problem hiding this comment.
Added some questions that I have
|
|
||
| After Fulu: | ||
|
|
||
| - event.previous_epoch_dependent_root when `compute_epoch_at_slot(event.slot) == epoch` |
There was a problem hiding this comment.
I use the term previous_epoch_dependent_root here (instead of previous_duty_dependent_root) as I thought after Fulu it is "epoch", not "duty". Is this right? If so, maybe I can also update this part in proposer.v2.yaml:
Edit: Should it actually be current_epoch_dependent_root instead of previous_epoch_dependent_root for post-Fulu case?
There was a problem hiding this comment.
After discussed with @michaelsproul , we revise this part to make it clearer:
- Remove before Fulu / after Fulu as now the chain is at Fulu
- rename to use
current_epoch...instead ofcurrent_duty... - remove
event.block otherwisebullet point as it serves no purpose (and could be confusing) - changes made to:
attester.yaml,proposer.v2.yamlandptc.yaml
Co-authored-by: Nico Flaig <nflaig@protonmail.com>
| description: The node has finished processing, resulting in a new head. previous_epoch_dependent_root is `get_block_root_at_slot(state, compute_start_slot_at_epoch(epoch - 2) - 1)`, current_epoch_dependent_root is `get_block_root_at_slot(state, compute_start_slot_at_epoch(epoch - 1) - 1)` and next_epoch_dependent_root is `get_block_root_at_slot(state, compute_start_slot_at_epoch(epoch) - 1)`. All dependent roots use the genesis block root in the case of underflow. | ||
| value: | | ||
| event: head_v2 | ||
| data: {"version": "gloas", "data":{"slot":"10", "block":"0x9a2fefd2fdb57f74993c7780ea5b9030d2897b615b89f808011ca5aebed54eaf", "state":"0x600e852a08c1200654ddf11025f1ceacb3c2e74bdd5c630cde0838b2591b69f9", "epoch_transition":false, "previous_epoch_dependent_root":"0x5e0043f107cb57913498fbf2f99ff55e730bf1e151f02f221e977c91a90a0e91", "current_epoch_dependent_root":"0x5e0043f107cb57913498fbf2f99ff55e730bf1e151f02f221e977c91a90a0e91", "next_epoch_dependent_root":"0x5e0043f107cb57913498fbf2f99ff55e730bf1e151f02f221e977c91a90a0e91", "execution_optimistic": false}} |
There was a problem hiding this comment.
Just thought of another field we might want to add to this event, possibly only after Gloas: the head's payload status.
With Gloas fork choice it's likely we'll get two head events every slot:
block_root: X, payload_status: pendingblock_root: X, payload_status: full
If we do have versioning on head_v2, we could add the payload_status attribute just for Gloas. Or otherwise we could just default them to pending prior to Gloas (although there are arguments for pending, empty or full for pre-Gloas blocks 😅 ).
There was a problem hiding this comment.
need to think more about it, but head seems unrelated to payload, what's the purpose or use case you had in mind?
regarding pending, I don't think we "leak" that anywhere currently outside of fork choice, so not sure that should be used anywhere else
if we would emit the event twice per slot, I would rather just do full / empty (if not received by ptc deadline), (I guess you would need pending for first time the event is emitted)
but maybe it should rather have payload_status as in status of the previous payload, maybe previous_payload_status
(although there are arguments for pending, empty or full for pre-Gloas blocks 😅 ).
I haven't seen a pending or empty block since the merge happened, using anything other than full for pre-gloas seems confusing to me, at least in lodestar we use full but it's a fork choice implementation detail anyways
There was a problem hiding this comment.
need to think more about it, but head seems unrelated to payload, what's the purpose or use case you had in mind?
I don't think it's unrelated any more, the output of fork choice get_head now includes a payload status. I can imagine that some EL tools could be interested in knowing when certain payloads become enshrined as head. They can use the new *payload events, but I think they'll also want to know that the payload became canonical (not just that it exists).
I haven't seen a pending or empty block since the merge happened, using anything other than full for pre-gloas seems confusing to me, at least in lodestar we use full but it's a fork choice implementation detail anyways
In Lighthouse we treat pre-Gloas blocks as pending in some places (no payload applied), and empty (no payload applied) 😁 But yeah I agree it's an impl detail, conceptually full (or no status) makes the most sense.
There was a problem hiding this comment.
IMO pending inevitably leaks into the state-based APIs, which have to now make the differentiation between block-states (pending) and payload-states (full).
There was a problem hiding this comment.
this kinda ties into discussion here #572 which doesn't seem fully resolved, I would trust @twoeths opinion on this which from his last comment seems like we wanna be more explicit about payload status after gloas
so we probably wanna align what state_id=head returns from the state-based apis, and how the head event is emitted
in that case it might make sense to emit two head events per slot
There was a problem hiding this comment.
I updated the description to have the possibility of having two head events per slot after Gloas, given the payload_status could be received after.
From my understanding, it will be something like:
First head emitted event:
data: {"version": "gloas", "data":{"slot":"10", "block":"x", "state":"y", payload_status: empty, "epoch_transition": ...}}
Second head emitted event for the same slot with full payload which changes the state:
data: {"version": "gloas", "data":{"slot":"10", "block":"x", "state":"z", payload_status: full, "epoch_transition": ...}}
If we do have the payload_status in the head_v2 event, then I think we keep the version field.
There was a problem hiding this comment.
First head emitted event:
what about payload_status: pending?
Second head emitted event for the same slot with full payload which changes the state:
so we would not emit this if there was no payload for the slot? or would you just emit empty after some time in the slot, ie. if not observed after ptc deadline or maybe just if ptc voted for not available?
There was a problem hiding this comment.
Actually I am confused and not sure. Should the first emitted event (after receiving the beacon/consensus part of the block), should the payload_status be pending? This is from here: https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/fork-choice.md#modified-get_head
And if it starts with pending for the first head event, then we update to either empty or full in the second emission?
There was a problem hiding this comment.
I am now anti-pending, the output of get_head is always empty or full, because pending nodes always have at least one child (their empty node), so can never be returned by get_head.
|
happy to run with this if everyone else is now? @nflaig @michaelsproul ? |
|
Yep. LGTM. |
Co-authored-by: Michael Sproul <michaelsproul@users.noreply.github.com>
Co-authored-by: Nico Flaig <nflaig@protonmail.com>
**What type of PR is this?** > Feature **What does this PR do? Why is it needed?** Implements a new `head_v2` event. Ref: ethereum/beacon-APIs#590 Note that it doesn't deprecate the legacy `head` event. So it is **expected** to receive both `head` and `head_v2` events if subscribed for them. **Which issues(s) does this PR fix?** Fixes OffchainLabs#16922 **Other notes for review** ### Kurtosis config ```yaml participants: - el_type: ethrex el_image: ethpandaops/ethrex:glamsterdam-devnet-4 el_extra_params: - --http.api=eth,net,web3,admin cl_type: prysm cl_image: prysm-bn-custom-image:latest vc_image: prysm-vc-custom-image:latest supernode: true count: 3 cl_extra_params: - --subscribe-all-subnets - --verbosity=debug vc_extra_params: - --verbosity=debug - --enable-beacon-rest-api network_params: fulu_fork_epoch: 0 gloas_fork_epoch: 2 seconds_per_slot: 6 genesis_delay: 40 additional_services: - dora global_log_level: debug ``` ### Result Command: ```bash $ curl -X 'GET' \ 'http://<BN>/eth/v1/events?topics=head&topics=head_v2' \ -H 'accept: text/event-stream' ``` so that we can subscribe both `head` and `head_v2`. Outcome: ``` event: head data: {"slot":"5","block":"0xbe03088c8d737d41de220c6758c0dcb920d3df9befdd7e75c6bc156695118611","state":"0x210c280f57344228104b410d1855a9ef720cdd64e242476ed7a304198cb8b76a","epoch_transition":false,"execution_optimistic":false,"previous_duty_dependent_root":"0x7851e4368487090b5a9170ad357563850c5e86a8162de7372bfa4c46133de197","current_duty_dependent_root":"0x7851e4368487090b5a9170ad357563850c5e86a8162de7372bfa4c46133de197"} event: head_v2 data: {"version":"fulu","data":{"slot":"5","block":"0xbe03088c8d737d41de220c6758c0dcb920d3df9befdd7e75c6bc156695118611","state":"0x210c280f57344228104b410d1855a9ef720cdd64e242476ed7a304198cb8b76a","payload_status":"full","current_epoch_dependent_root":"0x7851e4368487090b5a9170ad357563850c5e86a8162de7372bfa4c46133de197","next_epoch_dependent_root":"0x7851e4368487090b5a9170ad357563850c5e86a8162de7372bfa4c46133de197","epoch_transition":false,"execution_optimistic":false}} ... event: head data: {"slot":"83","block":"0xd7507c7df5a5c28813e3c93bbedd4871b378e4f869571bf190f8675fa6293244","state":"0xa10e78912570d65272b3facb9cc9a0d50e209ba73fa3f8f9d9a8961b669fa431","epoch_transition":false,"execution_optimistic":false,"previous_duty_dependent_root":"0x8ab1f18715a944835e8c5a3b966126b31c3b6d282f237f8d20846f4db4cc4c4d","current_duty_dependent_root":"0xa078bc2d1264d2358f611549b6461983ed5c5d1ba58a6c8a6a5e8f6db35cad3b"} event: head_v2 data: {"version":"gloas","data":{"slot":"83","block":"0xd7507c7df5a5c28813e3c93bbedd4871b378e4f869571bf190f8675fa6293244","state":"0xa10e78912570d65272b3facb9cc9a0d50e209ba73fa3f8f9d9a8961b669fa431","payload_status":"empty","current_epoch_dependent_root":"0x8ab1f18715a944835e8c5a3b966126b31c3b6d282f237f8d20846f4db4cc4c4d","next_epoch_dependent_root":"0xa078bc2d1264d2358f611549b6461983ed5c5d1ba58a6c8a6a5e8f6db35cad3b","epoch_transition":false,"execution_optimistic":false}} event: head_v2 data: {"version":"gloas","data":{"slot":"83","block":"0xd7507c7df5a5c28813e3c93bbedd4871b378e4f869571bf190f8675fa6293244","state":"0xa10e78912570d65272b3facb9cc9a0d50e209ba73fa3f8f9d9a8961b669fa431","payload_status":"full","current_epoch_dependent_root":"0x8ab1f18715a944835e8c5a3b966126b31c3b6d282f237f8d20846f4db4cc4c4d","next_epoch_dependent_root":"0xa078bc2d1264d2358f611549b6461983ed5c5d1ba58a6c8a6a5e8f6db35cad3b","epoch_transition":false,"execution_optimistic":false}} ``` Notable points: - Data of `head_v2` is properly versioned. It will give `"fulu"` if the current fork is `fulu`. - `head_v2` is emitted two times for same block, right after the execution payload is received. - `current_epoch_dependent_root` == `previous_duty_dependent_root` and `next_epoch_dependent_root` == `current_duty_dependent_root` as expected. **Acknowledgements** - [x] I have read [CONTRIBUTING.md](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md). - [x] I have included a uniquely named [changelog fragment file](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md#maintaining-changelogmd). - [x] I have added a description with sufficient context for reviewers to understand this PR. - [x] I have tested that my changes work as expected and I added a testing plan to the PR description (if applicable). --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This PR adds a
head_v2event to the event stream as per discussion in #589