Ensure payload envelope streamer always serves canonical envelopes after the split slot#9085
Conversation
…t are canonical according to fork choice
63cce7a to
50f374c
Compare
|
Some required checks have failed. Could you please take a look @eserilev? 🙏 |
…oas-envelope-streamer-serves-canonical
| if node.full_payload_weight >= node.empty_payload_weight { | ||
| return Some(PayloadStatus::Full); | ||
| } |
There was a problem hiding this comment.
Sorry I lied saying it was just a weight comparison 😅
I think this also needs to call get_payload_status_tiebreaker to choose between the full and empty nodes when their weights are equal (which is likely for the head block before it receives attestations). See:
https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/fork-choice.md#modified-get_head
I think we have all the info needed here to make that call (the IndexedForkChoiceNodes can be synthesised, and the block roots &
We don't need to compare roots because both empty and full children have the same block root.
We also don't need to do anything if payload_received is false (then the status is always just Empty).
There was a problem hiding this comment.
I was hoping to get away with the head block case further upstream here (by just looking at the cached head). Because if we're not checking the head root then full >= empty is the only check we need i believe
but maybe this is a bit of a footgun, wdyt?
There was a problem hiding this comment.
I think it's a bit sketchy because there could definitely be non-head blocks with equal full and empty weights. I've seen a bunch of places where it would probably be good to use this function, so a general purpose + correct impl is worth it IMO
| justified_state_balances: vec![1], | ||
| expected_head: get_root(3), | ||
| current_slot: Slot::new(0), | ||
| expected_payload_status: None, |
There was a problem hiding this comment.
Not sure why this is None, but haven't looked super closely. I would expect all Gloas blocks have a status?
There was a problem hiding this comment.
maybe this doesn't need to be Option since find_head always return a payload status for pre-gloas (empty)?
There was a problem hiding this comment.
it doesn't need to be None but I think it'd be better to handle this in a separate PR. theres like 80+ call sites that need to be updated if we make this change
| let index = *self.proto_array.indices.get(block_root)?; | ||
| let node = self.proto_array.nodes.get(index)?; | ||
|
|
||
| let v29 = node.as_v29().ok()?; |
There was a problem hiding this comment.
I think accessing the fields with the functions be more forward compatible?
node.payload_received()?There was a problem hiding this comment.
ok refactored things a bit
|
|
||
| let tiebreaker_for_status = |status| { | ||
| let node_ref = IndexedForkChoiceNode { | ||
| root: node.root(), | ||
| proto_node_index: index, | ||
| payload_status: status, | ||
| }; | ||
| self.proto_array.get_payload_status_tiebreaker::<E>( | ||
| &node_ref, | ||
| node, | ||
| current_slot, | ||
| proposer_boost_root, | ||
| ) | ||
| }; | ||
|
|
||
| let full_tiebreaker = tiebreaker_for_status(PayloadStatus::Full).ok()?; | ||
| let empty_tiebreaker = tiebreaker_for_status(PayloadStatus::Empty).ok()?; | ||
|
|
||
| if full_tiebreaker >= empty_tiebreaker { | ||
| Some(PayloadStatus::Full) | ||
| } else { | ||
| Some(PayloadStatus::Empty) | ||
| } |
There was a problem hiding this comment.
I'm not sure if the tie breaker logic is covered in spec tests, can we add some unit tests if they are not?
There was a problem hiding this comment.
oh yeah just read the tests in gloas_payload.rs, i think we can test this scenario there?
| let full_tiebreaker = tiebreaker_for_status(PayloadStatus::Full).ok()?; | ||
| let empty_tiebreaker = tiebreaker_for_status(PayloadStatus::Empty).ok()?; | ||
|
|
||
| if full_tiebreaker >= empty_tiebreaker { |
There was a problem hiding this comment.
this can never be equal right?
There was a problem hiding this comment.
yeah it can never be equal
| justified_state_balances: vec![1], | ||
| expected_head: get_root(3), | ||
| current_slot: Slot::new(0), | ||
| expected_payload_status: None, |
There was a problem hiding this comment.
maybe this doesn't need to be Option since find_head always return a payload status for pre-gloas (empty)?
…oas-envelope-streamer-serves-canonical
|
the fork choice function to verify payload envelope canonicity was incorrect. I've updated it and added tests that verify specific edge cases that the previous impl wasn't handling correctly |
michaelsproul
left a comment
There was a problem hiding this comment.
Looks good. Didn't review the fork choice test changes in depth, but can after the merge conflicts are resolved.
|
@mergify queue |
Merge Queue Status
This pull request spent 27 minutes 12 seconds in the queue, including 25 minutes 43 seconds running CI. Required conditions to merge
|
If an envelope is after the split slot check fork choice to for envelope payload weight to see if the envelope is canonical or not
Note that we still need envelope pruning to ensure we serve canonical envelopes before the split slot