-
-
owned this note
-
Published
Linked with GitHub
# [DRAFT] Alternative ePBS design – Payload View-Merge (PVM)
*by [mike](https://twitter.com/mikeneuder) & [francesco](https://twitter.com/fradmt) in close collaboration with [potuz](https://twitter.com/potuz1) & [terence](https://twitter.com/terencechain) – june 29, 2023*
*tl;dr; We present Payload View-Merge: the new slot anatomy, the new honest attesting/proposing/building behavior, and the properties of the design.*
<!-- *Many thanks to [Caspar](https://twitter.com/casparschwa), [Chris](https://twitter.com/metachris), [Terence](https://twitter.com/terencechain), [Dan Marzec](https://twitter.com/_danielmarzec), [Anders](https://twitter.com/weboftrees), [Tim](https://twitter.com/timbeiko), [Danny](https://twitter.com/dannyryan), [Jim](https://twitter.com/jgm), and [Rajiv](https://twitter.com/rajivpoc) for comments on draft versions of this document.* -->
<!-- --- -->
## New concepts
- ***Payload view*** – A local state update done by the attesting committee for slot `N+1` about the timeliness of the payload revelation in slot `N`.
<!-- - ***DA votes*** – The set of votes cast by the Payload DA Committee.
- *The vote options are (i) payload present, (ii) payload unavailable (also used in the case of an invalid payload), (iii) non-unique builder.*
- The DA votes for slot `N` are only used in the fork choice for slot `N+1`.
- The DA votes for slot `N` determine the proportion of the fork-choice weight is given to the `full` vs. `empty` forks. E.g., a DA for for `40%` empty `60%` full gives `40%` of the attesting weight of the slot to the fork with `Empty`. -->
- ***`full, empty, missing` block*** – A `full` (CL) block is a block that has a valid `ExecutionPayload` (contains an EL payload) that becomes canonical. An `empty` (CL) block does not have a canonical `ExecutionPayload`, and thus did not result in a EL state transition. A `missing` (CL) block is an empty slot that becomes canonical. With block-slot voting, `missing` blocks can have fork-choice weight.
## Old slot anatomy
The 12s slot is partitioned into the following phases. Figure from [Time, Slots](https://www.paradigm.xyz/2023/04/mev-boost-ethereum-consensus).
<img src="https://storage.googleapis.com/ethereum-hackmd/upload_dc43b4cbebcf51e00a4024e29599183d.png" width=100%>
## New slot anatomy
*Note – timings are not set in stone and can be modified. This is just a sketch with an extra 4s period at the end. It is potentially possible to squeeze the slot timings down to avoid increasing the total duration beyond 12s.*
<img src="https://storage.googleapis.com/ethereum-hackmd/upload_5e52d5970c609c442ffc5ec8708cbac0.png" width=100%>
.
*Explanatory notes:*
1. The slot `N` block that is published at `t=0` is the CL block proposed by the elected PoS validator. This block contains a builder bid.
2. The attestation deadline at `t=4` is unchanged. The entire attesting committee for slot `N` uses the fork-choice rule to determine the head of the chain in their view and makes an attestation.
3. The broadcast of aggregate attestations for the slot still begins at `t=8`. Additionally, the builder publishes their execution payload if they have not seen an equivocation from the proposer (the builder does not need to have a full view of the attestations).
4. At `t=12` the attesting committee for slot `N+1` fixes a local view of if the payload was released on time.
5. At `t=16` the slot `N+1` proposer publishes their block, building on either the full or empty block.
- If the proposer builds on the empty block, the attesting committee uses the fork-choice rule and their local view of the payload timeliness to determine what CL block to vote for.
- If the proposer builds on the full block, the attesting committee votes for that block so long as it is head according to their fork-choice.
- If the proposer builds on an empty slot, the attesters run the fork-choice rule to determine their head.
## Honest attesting behavior
*Premise: we have block-slot voting.*
Consider the honest attesting committee at `t=4` of slot `N+1`. Assume that there was a block in slot `N` and the slot `N+1` proposer built a child block (it extends W.L.O.G to empty blocks). The slot `N+1` block could either build on the `empty` or `full` block from slot `N`. Similarly, the committee members set their view (`full`, `empty`) based on the payload timeliness at `t=12` of the previous slot. Let `(self=,proposer=)` denote the self view and the proposer parent.
- `(self=full,proposer=full)` $\implies$ vote for `N+1` as the head. Happy path, both agreeing on the payload.
- `(self=empty,proposer=empty)` $\implies$ vote for `N+1` as the head. Both agreeing on no payload.
- `(self=empty,proposer=full)` $\implies$ vote for `N+1` as the head. Proposer saw the payload, so they impose their view onto the attesting committee, and the committee is happy to vote for `N+1`.
> *^^^ **note**: this is the payload view-merge!! The proposer view of the payload is merged into the attesting committee view.*
- `(self=full,proposer=empty)` $\implies$ vote \~against\~ `N+1` (so vote for a missing block in `N+1`).
> *^^^ **note**: this is the only scenario where the attesting committee votes against the proposer! The rationale is that the attesting committee set their view at `t=12` of slot `N`. The proposer has another 4 seconds until `t=16` before they publish their block, so they must have had time to see the paylaod. Thus their block should be orphaned by losing to the `missing` block vote.*
## Block production
<img src="https://storage.googleapis.com/ethereum-hackmd/upload_15837a984f3860bc6723bc9687cb0546.png" width=80%>
.
The slot `N` committee votes on the CL block. The slot `N+1` committee observes for payload timeliness, which sets their locla view for their attesting duties in the next slot.
<!-- 1. The consensus layer (CL) block is still proposed by the elected PoS validator.
2. The block contains an `ExecutionPayloadHeader` and the associated `BuilderBid`.
- The `ExecPayloadHeader` is a commitment to a builder produced block that and the `BuilderBid` contains the bid metadata (value, builder pubkey, signature).
3. At the attestation deadline, the full attesting committee casts LMD-GHOST votes for the CL block (same as done today).
4. After the aggregation and propagation of the attestations, the builder publishes (or decides not to) the `ExecutionPayload` (the list of transactions & withdrawals).
5. The Payload DA Committee votes on the timliness of the publication.
- These votes do not impact fork choice, only signify the presence of the payload.
- These votes determine whether the fork-choice weight of the attestations for the CL block are given to the `empty` or `full` block.
- Honest Payload DA committee members will attest for a payload *only if they determine a unique builder, otherwise they vote for option (iii).*
6. The DA votes are propogated.
7. The next proposer uses HLMD-GHOST to choose the CL block that they will base their next block on.
8. The next proposer uses the Payload DA votes for that CL block to decide on building on the `empty` or `full` block. -->
#### Properties
1. **honest builder payment safety** – If an honest builder is selected \~and\~ their payment is processed, their payload became canonical.
2. **honest proposer safety** – If an honest proposer commits to single block on-time, they will unconditionally receive the payment from the builder for the bid they committed to.
3. **honest builder same-slot payload safety** – If an honest builder publishes a payload, they can be assured that no competing payload for that same slot will become canonical. This protects builders from same-slot unbundling. ***note**: This property relies on a 2/3 honest majority assumption of the validator set.*
#### Non-properties
1. **honest builder payload safety** – The builder cannot be sure that if they publish the payload, it will become canonical. The builder is not protected from next-slot unbundling (the builder is not protected from that in `mev-boost` either as length-1 reorgs happen regularly).
<!-- 2. **honest proposer head certainty** – Proposer's must determine whether or not to build on the `empty` or `full` CL blocks. If their view differs from the attesting committee, they may end up building on an incorrect head. See [Payload DA Splitting](https://notes.ethereum.org/EvB_vuwTRQSF0EmUWeZwzA#Payload-DA-committee-splitting). -->
<!-- #### New fork-choice logic
To be applied by the honest attesting committee in slot `N` at the attestation deadline.
1. Perform HLMD-GHOST with block-slot voting to determine the head of the chain for slot `N` (with block-slot the head could be empty).
2. If there exists a block at slot `N`, use the DA votes from slot `N-1` and proposer boost for slot `N` to determine the payload state for slot `N-1`.
3. Cast the attestation according to the new head. -->
#### Builder payment processing
<!-- Builder payments are processed during the epoch finalization process (open for discussion, could just be on a one-epoch lag). The payment takes place if:
1. The builder `ExecutionPayloadHeader` and the corresponding `ExecutionPayload` are part of the canonical chain (the happy-path) (i.e., the CL block for that slot is `full`).
- Implied by the current head of the chain. If the `ExecutionPayload` for a specific slot was included, then the subsequent block reflects that.
2. The builder `ExecutionPayloadHeader` is part of the canonical chain even if the corresponding `ExecutionPayload` is not (consensus that the builder was not on time) (i.e., the CL block for that slot is `empty`).
- Implied by the current head of the chain. The `N+1` attesting committee will only vote for an empty `N+1` block if their local view did not include a timely payload release.
3. There is no evidence of proposer equivocation.
- A builder can publish an equivocation proof in order to avoid the unconditional payment. -->
<!-- 3. Half of the Payload DA Committee votes that are present vote for "unavailable payload."
- This implies that the builder did not produce a timely, valid payload, and without equivocation evidence, the proposer needs to be paid. -->
Builder payments are processed during the epoch finalization process (open for discussion, could just be on a one-epoch lag). The payment takes place if both of these conditions are satisfied:
1. The builder `ExecutionPayloadHeader` is part of the canonical chain (i.e., the CL block for that slot is *not* `missing`). This includes two cases:
- the corresponding `ExecutionPayload` is also part of the canonical chain (the happy-path) (i.e., the CL block for that slot is `full`).
- The builder `ExecutionPayloadHeader` is part of the canonical chain even if the corresponding `ExecutionPayload` is not (consensus that the builder was not on time) (i.e., the CL block for that slot is `empty`).
2. There is no evidence of proposer equivocation.
- A builder can publish an equivocation proof in order to avoid the unconditional payment.
#### Differences from other designs
1. The payload timeliness is just a property observed by the subsequent attesting committee.
2. The payload view determines how the subsequent attesting committee votes. In almost all cases, they vote with the proposer.
3. The builder is never given proposer boost, nor LMD-GHOST weight.
<!-- #### Payload DA committee splitting
One new attack vector enabled by this design is splitting the Payload DA committee. The bulider can strategically time the release of their payload so that approximately 70% of the honest committee members see it on-time, while the other 30% (including the next proposer) see it as late. This impacts the next proposer, because they need to decide whether to build on the `empty` or `full` block. See the figure below for an example.
<img src="https://storage.googleapis.com/ethereum-hackmd/upload_1ff1a76e759b2749de80d5fd1f01ec04.png" width=50%>
Consider the above view from a member of the `N+1` attesting commitee.
- 70% of the DA committee that they see has voted for `N,full`. This means `N,full` has 70% weight of the CL attestations for slot `N`.
- The slot `N+1` proposer built on `N,empty`, which has 30% weight of the CL attestations and gets 40% proposer boost.
- Honest attesters in `N+1` see a tie, and may end up orphaning `N+1`.
**Implication** – The builder of the block at slot `N` splits the DA committee view by strategically timing the release of the payload. The slot `N+1` proposer is griefed into publishing on a non-canonical block.
**Comparison to today** – This splitting attack is not possible today because there is no block-slot voting. The next proposer only needs to determine if they are going to build on the `N-1` block or the `N` block, which they will do by evaluating the attestation weight of each. If the attestation weight is >20% on `N`, they use it as the head, otherwise the `honest reorg` logic dictates that they build on `N-1` to reorg `N`. With block-slot voting, this attack would be possible by every proposer, and with ePBS it would also be possible for each builder.
-->
### Full behavior specification
#### Attestation behavior
We go through the attestation logic of an attester at slot `N+1`. Before attesting, there are two local variables which are set at the payload view-merge deadline (`t = 12s`) of the previous slot `N`: `equivocation_detected: Boolean`, `payload: ExecutionPayload`. `equivocation_detected` is set based on whether an a a pair of equivocating proposals for this slot has been observed up to this point. `payload` is set only if `equivocation_detected = False` and if a (single) proposal and associated `ExecutionPayload` have been observed up to this point, in which case `payload` is set to the latter.
As usual, an attester at a slot `N+1` attests either at the attestation deadline or when it receives a proposal, whichever comes first. The attestation logic is then the following:
1. [**Merge**] If a proposal `B'` is received, do the following:
- Set `equivocation_detected = true` if `B'` contains equivocation evidence from slot `N`
- If `payload` is not set and `B'` extends a `full` CL block from slot `N`, with `P` as `ExecutionPayload`, set `payload = P`
2. [**Fork-choice**] Run the fork-choice rule, identify a head of the chain `B`
3. [**Attest**] In the following, we say "attest to an extension of a block", to mean that the vote should go to a proposal extending the block, if one has been received, and to the block itself otherwise. The attestation behavior is then:
- If `slot(B) < N`, then attest to an extension of `B` (normal attestation behavior)
- If `slot(B) = N`:
- If `equivocation_detected = false` and `payload` is set:
- if `payload` matches the `ExecutionPayloadHeader` of `B`, attest to an extension of the `full` version of `B`.
- if `payload` does not match the `ExecutionPayloadHeader` of `B`, attest to an extension of the `empty` version of `B`.
- If `equivocation_detected = false` and `payload` is not set, attest either to an extension of the`full` or `empty` version of `B` (choose `empty` if no proposal has been received)
- If `equivocation_detected = true`, attest to an extension of the `empty` version of `B`
#### Builder behavior
We specify one possibility for the builder publication logic. It is possible for builders to follow a different logic, for example incorporating information from the attestations, which they are able to obtain before the execution payload publication deadline, if they subscribe to all attestation subnets. Nevertheless, this simple behavior is sufficient to obtain the honest builder properties, i.e. *honest builder payment safety* and *honest builder same-slot payload safety*.
The builder publishes an `ExecutionPayload` at the execution payload deadline (`t = 8s`) of slot `N` if:
- A CL block from slot `N` with the corresponding `ExecutionPayloadHeader` has been observed
- No conflicting CL block has been observed, i.e., no equivocation
#### Proposer behavior
The proposer of slot `N+1` decides what to extend as follows:
1. Run the fork-choice rule, identify a head of the chain `B`
2. If `slot(B) < N`, extend `B`
3. If `slot(B) = N`:
- If it has observed an equivocation from slot `N`, it extends the `empty` version of `B` and includes equivocation evidence in the proposal
- Otherwise, extend the `full` version of `B` if the corresponding `ExecutionPayload` is available, or the `empty` version of `B` if not.
### Proof of properties
#### Honest builder same-slot safety
In the following, we are going to assume prefix-stability in slot `N`, i.e., that the chain until slot `N-1` will not be reorged. In other words, there is some stable block `A` which the proposer of slot `N` is forced to extend, as it is guaranteed to stay canonical. Moreover, all builders in slot `N` will bid with payloads extending `A`, because they also see it as head of the chain. The reasoning behind this assumption is that, where that not to be the case, searchers and builders would be able to observe that and behave accordingly, with caution: searchers can avoid submitting bundles whose unbundling would be very costly to them, and builders can bid conservatively.
Say that a builder publishes `P`, an `ExecutionPayload` for block `B` at slot `N` (built on `A`), after following the publication logic outlined above, meaning that by time `t=8s` they have not observed an equivocation. Since that is the case, no proposals other than `B` could have been observed by any honest attester by time `t=4s`, so all honest attestations from slot `N` are either for `B` or `missing`, i.e., for (`A`,`N`) (since we use (block, slot) attestations). Later, we will make use of this fact.
Consider now a different `ExecutionPayload` for slot `N`, `P'` associated to block `B'`, which contains transactions taken from `P`. Consider also an attester of slot `N+1`. At the view-merge deadline of slot `N`, at time `t=12s`, they observe `P`. Now, the following can happen:
- They also observe another proposal conflicting with `B` by the view-merge deadline, and set `equivocation_detected = true`. Then, `equivocation_detected` stays `true` no matter what, and there are two cases:
- the head of the chain is `A`, and, since `slot(A) < N`, the attester votes for a proposal extending it
- the head of the chain is a block from slot `N`, so the attester votes for the`empty` version of it
- They do not observe an equivocation, and thus set `payload = P`. Now, for them to attest to the `full` version of *some* block from slot `N`, it must be the case that `payload` matches the `ExecutionPayloadHeader` in the block. Since `paylaod = P`, that only happens if the block is `B`. Otherwise, they attest either to a block from a slot `< N`, i.e., to `A`, or to the `empty` version of a block from slot `N`.
Therefore, the attester only ever attests to a `full` block from slot `N` if the block is `B`. Since this is the case for any honest attester, a majority of the committee of slot `N+1` does so
## Splitting considerations
### Proposer-initiated splitting
Temporarily splitting the view of the chain is possible in this design. First, consider the case where the proposer splits the chain in an attempt to grief the builder.
<img src="https://storage.googleapis.com/ethereum-hackmd/upload_4a9818706657ecdd395a897e9f8d27f9.png" width=90%>
.
The sequence of events is as follows:
1. The slot `N` proposer releases their block near the attestation deadline, causing a split of the honest attesting set. 50% of the honest attesters saw it on-time and voted for the block, while the other 50% did not see it on-time, and thus voted for a `missing` block.
2. The builder of the header included in the block must make a decision about releasing the payload that corresponds to this block. The block is either `full` or `empty` based on that decision.
3. The slot `N+1` proposer resolves the split by building on the `missing`, `full`, or `empty` head. Because the proposer will have boost, the fork is resolved.
Now consider the builder options.
1. **Publish the payload** – If the `missing` block becomes canonical, they published the payload but it never made it on-chain. Otherwise, the `full` payload became canonical and the builder is happy.
2. **Withhold the payload** – If the `empty` block becomes canonical, the unconditional payment goes through, but they never get the rewards from the payload being included. If the `missing` block becomes canonical, then they didn't needlessly reveal their payload.
***Key point 1 –*** *By not giving fork-choice weight to the builder, we cannot protect them from the proposer splitting in an attempt to grief. However, the builder can be certain that their block will not be reorged by a block in the same slot, so they can protect their transactions by bounding them to slot `N`.*
***Key point 2 –*** *Today, such splitting possible as well, it just looks slightly different. If the proposer intentionally delays their publication such that the next proposer might try to reorg their block using the honest-reorg strategy, the `mev-boost` builders have no certainty that their block won't be one-block reorged. Indeed, we see many [one-block reorgs](https://etherscan.io/blocks_forked) presently. See [Time, Slots](https://www.paradigm.xyz/2023/04/mev-boost-ethereum-consensus) for more context.*
### Builder-initiated splitting
Builders cannot meaningfully grief the `N+1` slot proposer. The only situation in which the attesting committee for `N+1` votes against the proposer is if the attesting committee has seen the payload on-time, but the proposer builds on the empty block `(self=full,proposer=empty)`. This is only possible if the payload was actually released before `t=12`. Given the proposer doesn't make their decision on the `full` or `empty` block until `t=16`, the builder has no way to stop proposer from seeing the block and building on the full block (assuming 4s network synchrony). As long as the proposer builds on the full block, the attesting committee will payload view-merge and accept that block as canonical, even if their local view was for `empty`.