From 6e6681836c5d80d225e332b33b4da50f50285736 Mon Sep 17 00:00:00 2001 From: Cayman Date: Tue, 28 Apr 2026 04:45:54 -0400 Subject: [PATCH 1/8] Track nay PTC votes --- specs/gloas/fork-choice.md | 41 +++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/specs/gloas/fork-choice.md b/specs/gloas/fork-choice.md index 0502e9fad8a..7de4aaf253c 100644 --- a/specs/gloas/fork-choice.md +++ b/specs/gloas/fork-choice.md @@ -61,6 +61,7 @@ This is the modification of the fork-choice accompanying the Gloas upgrade. | Name | SSZ equivalent | Description | | --------------- | -------------- | ----------------------------------------------- | | `PayloadStatus` | `uint8` | Possible status of a payload in the fork-choice | +| `PayloadVote` | `uint8` | Possible payload vote value from a PTC member | ## Constants @@ -71,6 +72,9 @@ This is the modification of the fork-choice accompanying the Gloas upgrade. | `PAYLOAD_STATUS_EMPTY` | `PayloadStatus(0)` | | `PAYLOAD_STATUS_FULL` | `PayloadStatus(1)` | | `PAYLOAD_STATUS_PENDING` | `PayloadStatus(2)` | +| `PAYLOAD_VOTE_UNKNOWN` | `PayloadVote(0)` | +| `PAYLOAD_VOTE_YEA` | `PayloadVote(1)` | +| `PAYLOAD_VOTE_NAY` | `PayloadVote(2)` | | `ATTESTATION_TIMELINESS_INDEX` | `0` | | `PTC_TIMELINESS_INDEX` | `1` | | `NUM_BLOCK_TIMELINESS_DEADLINES` | `2` | @@ -163,9 +167,11 @@ class Store(object): # [New in Gloas:EIP7732] payloads: Dict[Root, ExecutionPayloadEnvelope] = field(default_factory=dict) # [New in Gloas:EIP7732] - payload_timeliness_vote: Dict[Root, Vector[boolean, PTC_SIZE]] = field(default_factory=dict) + payload_timeliness_vote: Dict[Root, Vector[PayloadVote, PTC_SIZE]] = field( + default_factory=dict + ) # [New in Gloas:EIP7732] - payload_data_availability_vote: Dict[Root, Vector[boolean, PTC_SIZE]] = field( + payload_data_availability_vote: Dict[Root, Vector[PayloadVote, PTC_SIZE]] = field( default_factory=dict ) ``` @@ -258,7 +264,9 @@ def is_payload_timely(store: Store, root: Root) -> bool: if not is_payload_verified(store, root): return False - return sum(store.payload_timeliness_vote[root]) > PAYLOAD_TIMELY_THRESHOLD + votes = store.payload_timeliness_vote[root] + yea_votes = sum(vote == PAYLOAD_VOTE_YEA for vote in votes) + return yea_votes > PAYLOAD_TIMELY_THRESHOLD ``` ### New `is_payload_data_available` @@ -277,7 +285,9 @@ def is_payload_data_available(store: Store, root: Root) -> bool: if not is_payload_verified(store, root): return False - return sum(store.payload_data_availability_vote[root]) > DATA_AVAILABILITY_TIMELY_THRESHOLD + votes = store.payload_data_availability_vote[root] + yea_votes = sum(vote == PAYLOAD_VOTE_YEA for vote in votes) + return yea_votes > DATA_AVAILABILITY_TIMELY_THRESHOLD ``` ### New `get_parent_payload_status` @@ -861,8 +871,8 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: # Add new state for this block to the store store.block_states[block_root] = state # Add a new PTC voting for this block to the store - store.payload_timeliness_vote[block_root] = [False] * PTC_SIZE - store.payload_data_availability_vote[block_root] = [False] * PTC_SIZE + store.payload_timeliness_vote[block_root] = [PAYLOAD_VOTE_UNKNOWN] * PTC_SIZE + store.payload_data_availability_vote[block_root] = [PAYLOAD_VOTE_UNKNOWN] * PTC_SIZE # Notify the store about the payload_attestations in the block notify_ptc_messages(store, state, block.body.payload_attestations) @@ -939,8 +949,9 @@ def on_payload_attestation_message( # The beacon block root must be known data = ptc_message.data # PTC attestation must be for a known block. If block is unknown, delay consideration until the block is found - assert data.beacon_block_root in store.block_states - state = store.block_states[data.beacon_block_root] + beacon_block_root = data.beacon_block_root + assert beacon_block_root in store.block_states + state = store.block_states[beacon_block_root] ptc = get_ptc(state, data.slot) # PTC votes can only change the vote for their assigned beacon block, return early otherwise if data.slot != state.slot: @@ -963,8 +974,14 @@ def on_payload_attestation_message( ) # Update the votes for the block ptc_index = ptc.index(ptc_message.validator_index) - payload_timeliness_vote = store.payload_timeliness_vote[data.beacon_block_root] - payload_timeliness_vote[ptc_index] = data.payload_present - payload_data_availability_vote = store.payload_data_availability_vote[data.beacon_block_root] - payload_data_availability_vote[ptc_index] = data.blob_data_available + payload_timeliness_vote = store.payload_timeliness_vote[beacon_block_root] + payload_timeliness_vote[ptc_index] = ( + PAYLOAD_VOTE_YEA if data.payload_present else PAYLOAD_VOTE_NAY + ) + payload_data_availability_vote = store.payload_data_availability_vote[ + beacon_block_root + ] + payload_data_availability_vote[ptc_index] = ( + PAYLOAD_VOTE_YEA if data.blob_data_available else PAYLOAD_VOTE_NAY + ) ``` From d2e5a7e85ab8e77b9872a5bbd053058e31efdb38 Mon Sep 17 00:00:00 2001 From: Cayman Date: Tue, 28 Apr 2026 06:03:44 -0400 Subject: [PATCH 2/8] chore: fix lint errors --- specs/gloas/fork-choice.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/specs/gloas/fork-choice.md b/specs/gloas/fork-choice.md index 7de4aaf253c..4673d9770cf 100644 --- a/specs/gloas/fork-choice.md +++ b/specs/gloas/fork-choice.md @@ -167,9 +167,7 @@ class Store(object): # [New in Gloas:EIP7732] payloads: Dict[Root, ExecutionPayloadEnvelope] = field(default_factory=dict) # [New in Gloas:EIP7732] - payload_timeliness_vote: Dict[Root, Vector[PayloadVote, PTC_SIZE]] = field( - default_factory=dict - ) + payload_timeliness_vote: Dict[Root, Vector[PayloadVote, PTC_SIZE]] = field(default_factory=dict) # [New in Gloas:EIP7732] payload_data_availability_vote: Dict[Root, Vector[PayloadVote, PTC_SIZE]] = field( default_factory=dict @@ -978,9 +976,7 @@ def on_payload_attestation_message( payload_timeliness_vote[ptc_index] = ( PAYLOAD_VOTE_YEA if data.payload_present else PAYLOAD_VOTE_NAY ) - payload_data_availability_vote = store.payload_data_availability_vote[ - beacon_block_root - ] + payload_data_availability_vote = store.payload_data_availability_vote[beacon_block_root] payload_data_availability_vote[ptc_index] = ( PAYLOAD_VOTE_YEA if data.blob_data_available else PAYLOAD_VOTE_NAY ) From 4d8dc4730363479e3cb0385633d9bf5f26451e3e Mon Sep 17 00:00:00 2001 From: Cayman Date: Wed, 29 Apr 2026 05:57:01 -0400 Subject: [PATCH 3/8] Update specs/gloas/fork-choice.md Co-authored-by: Nico Flaig --- specs/gloas/fork-choice.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/gloas/fork-choice.md b/specs/gloas/fork-choice.md index 4673d9770cf..3f62cf92da9 100644 --- a/specs/gloas/fork-choice.md +++ b/specs/gloas/fork-choice.md @@ -72,9 +72,9 @@ This is the modification of the fork-choice accompanying the Gloas upgrade. | `PAYLOAD_STATUS_EMPTY` | `PayloadStatus(0)` | | `PAYLOAD_STATUS_FULL` | `PayloadStatus(1)` | | `PAYLOAD_STATUS_PENDING` | `PayloadStatus(2)` | -| `PAYLOAD_VOTE_UNKNOWN` | `PayloadVote(0)` | -| `PAYLOAD_VOTE_YEA` | `PayloadVote(1)` | -| `PAYLOAD_VOTE_NAY` | `PayloadVote(2)` | +| `PAYLOAD_VOTE_EMPTY` | `PayloadVote(0)` | +| `PAYLOAD_VOTE_FULL` | `PayloadVote(1)` | +| `PAYLOAD_VOTE_UNKNOWN` | `PayloadVote(2)` | | `ATTESTATION_TIMELINESS_INDEX` | `0` | | `PTC_TIMELINESS_INDEX` | `1` | | `NUM_BLOCK_TIMELINESS_DEADLINES` | `2` | From b20d384f84445bcdcddfd4c0bcccda444555cd5a Mon Sep 17 00:00:00 2001 From: Cayman Date: Wed, 29 Apr 2026 06:08:59 -0400 Subject: [PATCH 4/8] chore: use Optional[boolean] to track ptc votes --- specs/gloas/fork-choice.md | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/specs/gloas/fork-choice.md b/specs/gloas/fork-choice.md index 3f62cf92da9..e265286da4b 100644 --- a/specs/gloas/fork-choice.md +++ b/specs/gloas/fork-choice.md @@ -61,7 +61,6 @@ This is the modification of the fork-choice accompanying the Gloas upgrade. | Name | SSZ equivalent | Description | | --------------- | -------------- | ----------------------------------------------- | | `PayloadStatus` | `uint8` | Possible status of a payload in the fork-choice | -| `PayloadVote` | `uint8` | Possible payload vote value from a PTC member | ## Constants @@ -72,9 +71,6 @@ This is the modification of the fork-choice accompanying the Gloas upgrade. | `PAYLOAD_STATUS_EMPTY` | `PayloadStatus(0)` | | `PAYLOAD_STATUS_FULL` | `PayloadStatus(1)` | | `PAYLOAD_STATUS_PENDING` | `PayloadStatus(2)` | -| `PAYLOAD_VOTE_EMPTY` | `PayloadVote(0)` | -| `PAYLOAD_VOTE_FULL` | `PayloadVote(1)` | -| `PAYLOAD_VOTE_UNKNOWN` | `PayloadVote(2)` | | `ATTESTATION_TIMELINESS_INDEX` | `0` | | `PTC_TIMELINESS_INDEX` | `1` | | `NUM_BLOCK_TIMELINESS_DEADLINES` | `2` | @@ -167,9 +163,11 @@ class Store(object): # [New in Gloas:EIP7732] payloads: Dict[Root, ExecutionPayloadEnvelope] = field(default_factory=dict) # [New in Gloas:EIP7732] - payload_timeliness_vote: Dict[Root, Vector[PayloadVote, PTC_SIZE]] = field(default_factory=dict) + payload_timeliness_vote: Dict[Root, Vector[Optional[boolean], PTC_SIZE]] = field( + default_factory=dict + ) # [New in Gloas:EIP7732] - payload_data_availability_vote: Dict[Root, Vector[PayloadVote, PTC_SIZE]] = field( + payload_data_availability_vote: Dict[Root, Vector[Optional[boolean], PTC_SIZE]] = field( default_factory=dict ) ``` @@ -262,9 +260,7 @@ def is_payload_timely(store: Store, root: Root) -> bool: if not is_payload_verified(store, root): return False - votes = store.payload_timeliness_vote[root] - yea_votes = sum(vote == PAYLOAD_VOTE_YEA for vote in votes) - return yea_votes > PAYLOAD_TIMELY_THRESHOLD + return sum(store.payload_timeliness_vote[root]) > PAYLOAD_TIMELY_THRESHOLD ``` ### New `is_payload_data_available` @@ -283,9 +279,7 @@ def is_payload_data_available(store: Store, root: Root) -> bool: if not is_payload_verified(store, root): return False - votes = store.payload_data_availability_vote[root] - yea_votes = sum(vote == PAYLOAD_VOTE_YEA for vote in votes) - return yea_votes > DATA_AVAILABILITY_TIMELY_THRESHOLD + return sum(store.payload_data_availability_vote[root]) > DATA_AVAILABILITY_TIMELY_THRESHOLD ``` ### New `get_parent_payload_status` @@ -869,8 +863,8 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: # Add new state for this block to the store store.block_states[block_root] = state # Add a new PTC voting for this block to the store - store.payload_timeliness_vote[block_root] = [PAYLOAD_VOTE_UNKNOWN] * PTC_SIZE - store.payload_data_availability_vote[block_root] = [PAYLOAD_VOTE_UNKNOWN] * PTC_SIZE + store.payload_timeliness_vote[block_root] = [None] * PTC_SIZE + store.payload_data_availability_vote[block_root] = [None] * PTC_SIZE # Notify the store about the payload_attestations in the block notify_ptc_messages(store, state, block.body.payload_attestations) @@ -973,11 +967,8 @@ def on_payload_attestation_message( # Update the votes for the block ptc_index = ptc.index(ptc_message.validator_index) payload_timeliness_vote = store.payload_timeliness_vote[beacon_block_root] - payload_timeliness_vote[ptc_index] = ( - PAYLOAD_VOTE_YEA if data.payload_present else PAYLOAD_VOTE_NAY - ) + payload_timeliness_vote[ptc_index] = data.payload_present + payload_data_availability_vote = store.payload_data_availability_vote[beacon_block_root] - payload_data_availability_vote[ptc_index] = ( - PAYLOAD_VOTE_YEA if data.blob_data_available else PAYLOAD_VOTE_NAY - ) + payload_data_availability_vote[ptc_index] = data.blob_data_available ``` From e4e10440b4806d81875a2daf513ab85dee5378d2 Mon Sep 17 00:00:00 2001 From: Cayman Date: Wed, 29 Apr 2026 06:14:07 -0400 Subject: [PATCH 5/8] chore: remove stray diff --- specs/gloas/fork-choice.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/specs/gloas/fork-choice.md b/specs/gloas/fork-choice.md index e265286da4b..5c9ccff8afe 100644 --- a/specs/gloas/fork-choice.md +++ b/specs/gloas/fork-choice.md @@ -941,9 +941,8 @@ def on_payload_attestation_message( # The beacon block root must be known data = ptc_message.data # PTC attestation must be for a known block. If block is unknown, delay consideration until the block is found - beacon_block_root = data.beacon_block_root - assert beacon_block_root in store.block_states - state = store.block_states[beacon_block_root] + assert data.beacon_block_root in store.block_states + state = store.block_states[data.beacon_block_root] ptc = get_ptc(state, data.slot) # PTC votes can only change the vote for their assigned beacon block, return early otherwise if data.slot != state.slot: @@ -966,9 +965,9 @@ def on_payload_attestation_message( ) # Update the votes for the block ptc_index = ptc.index(ptc_message.validator_index) - payload_timeliness_vote = store.payload_timeliness_vote[beacon_block_root] + payload_timeliness_vote = store.payload_timeliness_vote[data.beacon_block_root] payload_timeliness_vote[ptc_index] = data.payload_present - payload_data_availability_vote = store.payload_data_availability_vote[beacon_block_root] + payload_data_availability_vote = store.payload_data_availability_vote[data.beacon_block_root] payload_data_availability_vote[ptc_index] = data.blob_data_available ``` From b45d75cf8f3d1956f78b52cb691b2a0415c25da9 Mon Sep 17 00:00:00 2001 From: Cayman Date: Wed, 29 Apr 2026 06:19:19 -0400 Subject: [PATCH 6/8] chore: remove stray whitespace --- specs/gloas/fork-choice.md | 1 - 1 file changed, 1 deletion(-) diff --git a/specs/gloas/fork-choice.md b/specs/gloas/fork-choice.md index 5c9ccff8afe..2de75c0ead6 100644 --- a/specs/gloas/fork-choice.md +++ b/specs/gloas/fork-choice.md @@ -967,7 +967,6 @@ def on_payload_attestation_message( ptc_index = ptc.index(ptc_message.validator_index) payload_timeliness_vote = store.payload_timeliness_vote[data.beacon_block_root] payload_timeliness_vote[ptc_index] = data.payload_present - payload_data_availability_vote = store.payload_data_availability_vote[data.beacon_block_root] payload_data_availability_vote[ptc_index] = data.blob_data_available ``` From dfd2af25a14274d131f523aad10a8517894011f4 Mon Sep 17 00:00:00 2001 From: Cayman Date: Wed, 29 Apr 2026 08:08:05 -0400 Subject: [PATCH 7/8] chore: fix summing yea ptc votes --- specs/gloas/fork-choice.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/specs/gloas/fork-choice.md b/specs/gloas/fork-choice.md index 2de75c0ead6..8a27125e808 100644 --- a/specs/gloas/fork-choice.md +++ b/specs/gloas/fork-choice.md @@ -260,7 +260,9 @@ def is_payload_timely(store: Store, root: Root) -> bool: if not is_payload_verified(store, root): return False - return sum(store.payload_timeliness_vote[root]) > PAYLOAD_TIMELY_THRESHOLD + return ( + sum(vote is True for vote in store.payload_timeliness_vote[root]) > PAYLOAD_TIMELY_THRESHOLD + ) ``` ### New `is_payload_data_available` @@ -279,7 +281,10 @@ def is_payload_data_available(store: Store, root: Root) -> bool: if not is_payload_verified(store, root): return False - return sum(store.payload_data_availability_vote[root]) > DATA_AVAILABILITY_TIMELY_THRESHOLD + return ( + sum(vote is True for vote in store.payload_data_availability_vote[root]) + > DATA_AVAILABILITY_TIMELY_THRESHOLD + ) ``` ### New `get_parent_payload_status` From 4e383724eb055df1e8acc2596cf7f0bcf1d999fa Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Wed, 29 Apr 2026 14:38:46 +0200 Subject: [PATCH 8/8] Break vote check into multiple lines --- specs/gloas/fork-choice.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/specs/gloas/fork-choice.md b/specs/gloas/fork-choice.md index 8a27125e808..faf41809011 100644 --- a/specs/gloas/fork-choice.md +++ b/specs/gloas/fork-choice.md @@ -260,9 +260,8 @@ def is_payload_timely(store: Store, root: Root) -> bool: if not is_payload_verified(store, root): return False - return ( - sum(vote is True for vote in store.payload_timeliness_vote[root]) > PAYLOAD_TIMELY_THRESHOLD - ) + votes = store.payload_timeliness_vote[root] + return sum(vote is True for vote in votes) > PAYLOAD_TIMELY_THRESHOLD ``` ### New `is_payload_data_available` @@ -281,10 +280,8 @@ def is_payload_data_available(store: Store, root: Root) -> bool: if not is_payload_verified(store, root): return False - return ( - sum(vote is True for vote in store.payload_data_availability_vote[root]) - > DATA_AVAILABILITY_TIMELY_THRESHOLD - ) + votes = store.payload_data_availability_vote[root] + return sum(vote is True for vote in votes) > DATA_AVAILABILITY_TIMELY_THRESHOLD ``` ### New `get_parent_payload_status`