diff --git a/specs/gloas/beacon-chain.md b/specs/gloas/beacon-chain.md index 92a402757b..dc3242e6ca 100644 --- a/specs/gloas/beacon-chain.md +++ b/specs/gloas/beacon-chain.md @@ -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 diff --git a/specs/gloas/builder.md b/specs/gloas/builder.md index fde83e2ccd..c0c4f1906e 100644 --- a/specs/gloas/builder.md +++ b/specs/gloas/builder.md @@ -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 diff --git a/specs/gloas/p2p-interface.md b/specs/gloas/p2p-interface.md index 78326cf127..ca9ed23fe0 100644 --- a/specs/gloas/p2p-interface.md +++ b/specs/gloas/p2p-interface.md @@ -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) @@ -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) @@ -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` @@ -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 @@ -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 + `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 @@ -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`. +- _[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}` diff --git a/specs/gloas/validator.md b/specs/gloas/validator.md index f25cfbac62..593f214385 100644 --- a/specs/gloas/validator.md +++ b/specs/gloas/validator.md @@ -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) @@ -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