Skip to content

Commit f58c72d

Browse files
authored
Allow multiple bids compatible with the head view (#5497)
This is an alternative to #5491 requested by @nflaig. In this PR we are not as strict as to only allow exactly what the proposer would do, but rather check that it is one of the two paths.
1 parent 123d1ef commit f58c72d

4 files changed

Lines changed: 44 additions & 16 deletions

File tree

specs/gloas/fork-choice.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -421,9 +421,9 @@ follows the node's payload status. For a *full* node from the previous slot, it
421421
considers the PTC view on both payload timeliness and data availability.
422422

423423
```python
424-
def should_build_on_full(store: Store, head: ForkChoiceNode) -> bool:
424+
def should_build_on_full(store: Store, head: ForkChoiceNode, slot: Slot) -> bool:
425425
assert head.payload_status != PAYLOAD_STATUS_PENDING
426-
if store.blocks[head.root].slot + 1 != get_current_slot(store):
426+
if store.blocks[head.root].slot + 1 != slot:
427427
return head.payload_status == PAYLOAD_STATUS_FULL
428428
if head.payload_status == PAYLOAD_STATUS_EMPTY:
429429
return False

specs/gloas/p2p-interface.md

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,8 @@ where `store` is the fork choice store, and the alias
428428
limitation defined in the consensus layer -- i.e. validate that
429429
`len(bid.blob_kzg_commitments) <= get_blob_parameters(compute_epoch_at_slot(bid.slot)).max_blobs_per_block`.
430430
- _[IGNORE]_ this is the first signed bid seen with a valid signature from the
431-
given builder for this slot.
431+
given builder for the tuple
432+
`(bid.slot, bid.parent_block_hash, bid.parent_block_root)`.
432433
- _[IGNORE]_ this bid is the highest value bid seen for the tuple
433434
`(bid.slot, bid.parent_block_hash, bid.parent_block_root)`.
434435
- _[IGNORE]_ `bid.value` is less or equal than the builder's excess balance --
@@ -438,8 +439,8 @@ where `store` is the fork choice store, and the alias
438439
`is_gas_limit_target_compatible(parent_gas_limit, bid.gas_limit, proposer_preferences.target_gas_limit)`
439440
is `True` where `parent_gas_limit` is the `gas_limit` of that execution
440441
payload.
441-
- _[IGNORE]_ `bid.parent_block_root` is the hash tree root of a known beacon
442-
block in fork choice.
442+
- _[IGNORE]_ The bid is compatible with the current head branch, i.e.
443+
`is_bid_compatible_with_head(store, bid)` returns `True`.
443444
- _[REJECT]_ The bid is for a higher slot than its parent block -- i.e. validate
444445
that `bid.slot` is greater than the slot of the block with root
445446
`bid.parent_block_root`.
@@ -467,6 +468,32 @@ def is_gas_limit_target_compatible(
467468
return gas_limit == min_gas_limit
468469
```
469470

471+
```python
472+
def is_bid_compatible_with_head(store: Store, bid: ExecutionPayloadBid) -> bool:
473+
"""
474+
Check if ``bid`` is compatible with the head branch.
475+
"""
476+
head_node = get_head(store)
477+
head_block = store.blocks[head_node.root]
478+
head_bid = head_block.body.signed_execution_payload_bid.message
479+
480+
builds_on_parent_block = bid.parent_block_root == head_block.parent_root
481+
builds_on_parent_payload = bid.parent_block_hash == head_bid.parent_block_hash
482+
483+
if builds_on_parent_block and builds_on_parent_payload:
484+
return True
485+
486+
if bid.parent_block_root != head_node.root:
487+
return False
488+
489+
builds_on_head_payload = bid.parent_block_hash == head_bid.block_hash
490+
491+
if should_build_on_full(store, head_node, bid.slot):
492+
return builds_on_head_payload
493+
494+
return builds_on_parent_payload
495+
```
496+
470497
*Note*: Implementations SHOULD include DoS prevention measures to mitigate spam
471498
from malicious builders submitting numerous bids with minimal value increments.
472499
Possible strategies include: (1) only forwarding bids that exceed the current

specs/gloas/validator.md

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,8 @@ top of a `state` MUST take the following actions in order to construct the
212212
- The `bid.slot` is for the proposal block slot.
213213
- The `bid.parent_block_hash` equals
214214
`state.latest_execution_payload_bid.block_hash` if
215-
`should_build_on_full(store, head)` is true, otherwise
216-
`state.latest_execution_payload_bid.parent_block_hash`.
215+
`should_build_on_full(store, head, get_current_slot(store))` is true,
216+
otherwise `state.latest_execution_payload_bid.parent_block_hash`.
217217
- The `bid.parent_block_root` equals the current block's `parent_root`.
218218
- The `bid.prev_randao` equals
219219
`get_randao_mix(state, get_current_epoch(state))`.
@@ -248,9 +248,9 @@ parent's execution payload. The proposer constructs this field as follows:
248248

249249
- If the parent block is pre-Gloas (first Gloas block), set
250250
`parent_execution_requests` to an empty `ExecutionRequests()`.
251-
- If `should_build_on_full(store, head)` returns `True` (the proposer is
252-
building on the parent's full payload), set `parent_execution_requests` to
253-
`store.payloads[head.root].execution_requests`.
251+
- If `should_build_on_full(store, head, get_current_slot(store))` returns `True`
252+
(the proposer is building on the parent's full payload), set
253+
`parent_execution_requests` to `store.payloads[head.root].execution_requests`.
254254
- Otherwise (the proposer is building on the parent's empty variant), set
255255
`parent_execution_requests` to an empty `ExecutionRequests()`.
256256

@@ -319,10 +319,11 @@ def get_execution_requests(execution_requests_list: Sequence[bytes]) -> Executio
319319
##### ExecutionPayload
320320

321321
*Note*: `prepare_execution_payload` is modified to build on the parent's full
322-
payload or its empty variant, as decided by `should_build_on_full(store, head)`,
323-
which determines the withdrawals source and the execution head for the new
324-
payload. When building on a full parent, `apply_parent_execution_payload` is
325-
called so that withdrawals are computed against the post-processing state.
322+
payload or its empty variant, as decided by
323+
`should_build_on_full(store, head, get_current_slot(store))`, which determines
324+
the withdrawals source and the execution head for the new payload. When building
325+
on a full parent, `apply_parent_execution_payload` is called so that withdrawals
326+
are computed against the post-processing state.
326327

327328
```python
328329
def prepare_execution_payload(
@@ -340,7 +341,7 @@ def prepare_execution_payload(
340341
) -> Optional[PayloadId]:
341342
# [New in Gloas:EIP7732]
342343
parent_bid = state.latest_execution_payload_bid
343-
if should_build_on_full(store, head):
344+
if should_build_on_full(store, head, get_current_slot(store)):
344345
envelope = store.payloads[head.root]
345346
# Make a copy of the state to avoid mutability issues
346347
state = copy(state)

specs/heze/validator.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ def prepare_execution_payload(
145145
execution_engine: ExecutionEngine,
146146
) -> Optional[PayloadId]:
147147
parent_bid = state.latest_execution_payload_bid
148-
if should_build_on_full(store, head):
148+
if should_build_on_full(store, head, get_current_slot(store)):
149149
envelope = store.payloads[head.root]
150150
# Make a copy of the state to avoid mutability issues
151151
state = copy(state)

0 commit comments

Comments
 (0)