Skip to content
9 changes: 5 additions & 4 deletions specs/gloas/beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,11 @@ Gloas is a consensus-layer upgrade containing a number of features. Including:

### Domain types

| Name | Value |
| ----------------------- | -------------------------- |
| `DOMAIN_BEACON_BUILDER` | `DomainType('0x0B000000')` |
| `DOMAIN_PTC_ATTESTER` | `DomainType('0x0C000000')` |
| Name | Value |
| ----------------------------- | -------------------------- |
| `DOMAIN_BEACON_BUILDER` | `DomainType('0x0B000000')` |
| `DOMAIN_PTC_ATTESTER` | `DomainType('0x0C000000')` |
| `DOMAIN_PROPOSER_PREFERENCES` | `DomainType('0x0D000000')` |

### Misc

Expand Down
10 changes: 5 additions & 5 deletions specs/gloas/builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,11 @@ to include. They produce a `SignedExecutionPayloadBid` as follows.
05. Set `bid.prev_randao` to be the previous RANDAO of the constructed payload,
that is `payload.prev_randao`.
06. Set `bid.fee_recipient` to be an execution address to receive the payment.
This address can be obtained from the proposer directly via a request or can
be set from the withdrawal credentials of the proposer. The burn address can
be used as a fallback.
07. Set `bid.gas_limit` to be the gas limit of the constructed payload, that is
`payload.gas_limit`.
The proposer's preferred fee recipient can be obtained from the
`SignedProposerPreferences` associated with `bid.slot`.
07. Set `bid.gas_limit` to be the gas limit of the constructed payload. The
proposer's preferred gas limit can be obtained from the
`SignedProposerPreferences` associated with `bid.slot`.
08. Set `bid.builder_index` to be the index of the builder performing these
actions.
09. Set `bid.slot` to be the slot for which this bid is aimed. This slot
Expand Down
65 changes: 64 additions & 1 deletion specs/gloas/p2p-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
- [Configuration](#configuration)
- [Containers](#containers)
- [Modified `DataColumnSidecar`](#modified-datacolumnsidecar)
- [New `ProposerPreferences`](#new-proposerpreferences)
- [New `SignedProposerPreferences`](#new-signedproposerpreferences)
- [Helpers](#helpers)
- [Modified `verify_data_column_sidecar`](#modified-verify_data_column_sidecar)
- [The gossip domain: gossipsub](#the-gossip-domain-gossipsub)
Expand All @@ -21,6 +23,7 @@
- [`execution_payload`](#execution_payload)
- [`payload_attestation_message`](#payload_attestation_message)
- [`execution_payload_bid`](#execution_payload_bid)
- [`proposer_preferences`](#proposer_preferences)
- [Blob subnets](#blob-subnets)
- [`data_column_sidecar_{subnet_id}`](#data_column_sidecar_subnet_id)
- [Attestation subnets](#attestation-subnets)
Expand Down Expand Up @@ -103,6 +106,28 @@ class DataColumnSidecar(Container):
beacon_block_root: Root
```

#### New `ProposerPreferences`

*[New in Gloas:EIP7732]*

```python
class ProposerPreferences(Container):
proposal_slot: Slot
validator_index: ValidatorIndex
fee_recipient: ExecutionAddress
gas_limit: uint64
```

#### New `SignedProposerPreferences`

*[New in Gloas:EIP7732]*

```python
class SignedProposerPreferences(Container):
message: ProposerPreferences
signature: BLSSignature
```

### Helpers

##### Modified `verify_data_column_sidecar`
Expand Down Expand Up @@ -157,6 +182,7 @@ are given in this table:
| `execution_payload_bid` | `SignedExecutionPayloadBid` |
| `execution_payload` | `SignedExecutionPayloadEnvelope` |
| `payload_attestation_message` | `PayloadAttestationMessage` |
| `proposer_preferences` | `SignedProposerPreferences` |

##### Global topics

Expand Down Expand Up @@ -275,9 +301,16 @@ The following validations MUST pass before forwarding the
`signed_execution_payload_bid` on the network, assuming the alias
`bid = signed_execution_payload_bid.message`:

- _[IGNORE]_ `bid.slot` is the current slot or the next slot.
- _[IGNORE]_ the `SignedProposerPreferences` where `preferences.proposal_slot`
is equal to `bid.slot` has been seen.
- _[REJECT]_ `bid.builder_index` is a valid/active builder index -- i.e.
`is_active_builder(state, bid.builder_index)` returns `True`.
- _[REJECT]_ `bid.execution_payment` is zero.
- _[REJECT]_ `bid.fee_recipient` matches the `fee_recipient` from the proposer's
Copy link
Contributor

@ensi321 ensi321 Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just want to point out that these rules are not enforceable at all.

eg. Proposer can still accept bid that has different fee recipient from ProposerPreferences and there is no consequence to it because process_execution_payload_bid will not check if the bid matches the preference. Ultimately proposer can choose whatever bid he is happy with, whether his preference has been met or not

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the purpose of this is just to protect the proposer itself so the beacon node doesn't select a bid that violates the preference

`SignedProposerPreferences` associated with `bid.slot`.
- _[REJECT]_ `bid.gas_limit` matches the `gas_limit` from the proposer's
`SignedProposerPreferences` associated with `bid.slot`.
- _[IGNORE]_ this is the first signed bid seen with a valid signature from the
given builder for this slot.
- _[IGNORE]_ this bid is the highest value bid seen for the corresponding slot
Expand All @@ -288,10 +321,40 @@ The following validations MUST pass before forwarding the
payload in fork choice.
- _[IGNORE]_ `bid.parent_block_root` is the hash tree root of a known beacon
block in fork choice.
- _[IGNORE]_ `bid.slot` is the current slot or the next slot.
- _[REJECT]_ `signed_execution_payload_bid.signature` is valid with respect to
the `bid.builder_index`.

###### `proposer_preferences`

*[New in Gloas:EIP7732]*

This topic is used to propagate signed proposer preferences as
`SignedProposerPreferences`. These messages allow validators to communicate
their preferred `fee_recipient` and `gas_limit` to builders.

The following validations MUST pass before forwarding the
`signed_proposer_preferences` on the network, assuming the alias
`preferences = signed_proposer_preferences.message`:

- _[IGNORE]_ `preferences.proposal_slot` is in the next epoch -- i.e.
`compute_epoch_at_slot(preferences.proposal_slot) == get_current_epoch(state) + 1`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't it be better if we also allow to broadcast preferences for the current epoch, eg. if a node was offline for whatever reason it still gets a chance to submit the preference, in practice it really just needs to reach the builder 1-2 slots before the proposal

change this to something like

- _[IGNORE]_ `preferences.proposal_slot` is in the current or next epoch -- i.e.
  `compute_epoch_at_slot(preferences.proposal_slot)` is in
  `[get_current_epoch(state), get_current_epoch(state) + 1]`.

(there are few other functions we would have to update to support this)

- _[REJECT]_ `preferences.validator_index` is present at the correct slot in the
next epoch's portion of `state.proposer_lookahead` -- i.e.
`is_valid_proposal_slot(state, preferences)` returns `True`.
- _[IGNORE]_ The `signed_proposer_preferences` is the first valid message
received from the validator with index `preferences.validator_index`.
- _[REJECT]_ `signed_proposer_preferences.signature` is valid with respect to
the validator's public key.

```python
def is_valid_proposal_slot(state: BeaconState, preferences: ProposerPreferences) -> bool:
"""
Check if the validator is the proposer for the given slot in the next epoch.
"""
index = SLOTS_PER_EPOCH + preferences.proposal_slot % SLOTS_PER_EPOCH
return state.proposer_lookahead[index] == preferences.validator_index
```

##### Blob subnets

###### `data_column_sidecar_{subnet_id}`
Expand Down
51 changes: 51 additions & 0 deletions specs/gloas/validator.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- [Attestation](#attestation)
- [Sync Committee participations](#sync-committee-participations)
- [Block proposal](#block-proposal)
- [Broadcasting `SignedProposerPreferences`](#broadcasting-signedproposerpreferences)
- [Constructing `signed_execution_payload_bid`](#constructing-signed_execution_payload_bid)
- [Constructing `payload_attestations`](#constructing-payload_attestations)
- [Blob sidecars](#blob-sidecars)
Expand Down Expand Up @@ -119,6 +120,56 @@ any slot during which `is_proposer(state, validator_index)` returns `True`. The
mechanism to prepare this beacon block and related sidecars differs from
previous forks as follows

#### Broadcasting `SignedProposerPreferences`

At the beginning of each epoch, a validator MAY broadcast
`SignedProposerPreferences` messages to the `proposer_preferences` gossip topic
for each slot returned by `get_upcoming_proposal_slots(state, validator_index)`.
This allows builders to construct execution payloads with the validator's
preferred `fee_recipient` and `gas_limit`. If a validator does not broadcast a
`SignedProposerPreferences` message, this implies that the validator will not
accept any trustless bids for that slot.

```python
def get_upcoming_proposal_slots(
state: BeaconState, validator_index: ValidatorIndex
) -> Sequence[Slot]:
"""
Get the slots in the next epoch for which ``validator_index`` is proposing.
"""
return [
Slot(compute_start_slot_at_epoch(get_current_epoch(state) + Epoch(1)) + offset)
for offset, proposer_index in enumerate(state.proposer_lookahead[SLOTS_PER_EPOCH:])
if validator_index == proposer_index
]
```

To construct each `SignedProposerPreferences`:

1. Instantiate a new `ProposerPreferences` object as `preferences`.
2. Set `preferences.proposal_slot` to `upcoming_proposal_slots[i]`.
3. Set `preferences.validator_index` to the validator's index.
4. Set `preferences.fee_recipient` to the execution address where the validator
wishes to receive the builder payment.
5. Set `preferences.gas_limit` to the validator's preferred gas limit for this
execution payload.
6.
7. Instantiate a new `SignedProposerPreferences` object as `signed_preferences`.
8. Set `signed_preferences.message` to `preferences`.
9. Set `signed_preferences.signature` to the result of
`get_proposer_preferences_signature(state, preferences, privkey)`.

```python
def get_proposer_preferences_signature(
state: BeaconState, preferences: ProposerPreferences, privkey: int
) -> BLSSignature:
domain = get_domain(
state, DOMAIN_PROPOSER_PREFERENCES, compute_epoch_at_slot(preferences.proposal_slot)
)
signing_root = compute_signing_root(preferences, domain)
return bls.Sign(privkey, signing_root)
```

#### Constructing `signed_execution_payload_bid`

To obtain `signed_execution_payload_bid`, a block proposer building a block on
Expand Down