Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

minimal ePBS with forward inclusion lists and PTC #1

Open
wants to merge 57 commits into
base: dev
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
9374766
init
potuz Aug 7, 2023
82cf8dd
Add engine methods and check IL validity
potuz Aug 8, 2023
4cff1ae
add small design notes for ILs
potuz Aug 8, 2023
e6b24a3
check for gas usage after payload insertion
potuz Aug 8, 2023
f701bf8
Add helpers that change on ePBS
potuz Aug 8, 2023
97b2f40
switch to gas limit instead of usage as pointed by V. Buterin
potuz Aug 9, 2023
0bf7382
Added more changed helpers
potuz Aug 9, 2023
28e6f9f
change design notes
potuz Aug 22, 2023
48415b1
minor changes
potuz Aug 25, 2023
8b69b9b
First forkchoice changes.
potuz Aug 25, 2023
2a6400a
don't modify verify_block_signature
potuz Aug 25, 2023
c852a4e
remove clutter
potuz Aug 25, 2023
05022af
Check IL compatibility with parent block hash
potuz Aug 26, 2023
59048f9
Do not broadcast the parent_block_hash but use it as parameter to the EL
potuz Aug 26, 2023
188d09d
process payment immediately
potuz Aug 26, 2023
ea645a1
Deal with withdrawals
potuz Aug 26, 2023
92ffab6
fix withdrawals, check only in the CL
potuz Aug 26, 2023
25fb473
remove unused request dataclass
potuz Aug 27, 2023
51847e2
pass the propsoer_index to process_execution_payload_header
potuz Aug 27, 2023
95e4eb2
run doctoc
potuz Aug 27, 2023
cbe0618
only cache the withdrawals root in the state
potuz Aug 27, 2023
ef11fb4
ptc rewards pass 1
potuz Aug 28, 2023
7014513
only pass relevant IL info to the EL
potuz Aug 28, 2023
cbee247
Use envelopes instead of adding builder info to the payload
potuz Aug 28, 2023
c48d050
ptc message handlers in forkchoice
potuz Aug 29, 2023
e81721a
take ptc members from all committees
potuz Aug 29, 2023
cd7c007
add forkchoice helper to get committee voted
potuz Aug 29, 2023
e317ed8
add rewards and ptc attestations to the design doc
potuz Aug 29, 2023
b4afb3a
doctoc
potuz Aug 29, 2023
e1a904d
Design decisions in forkchoice
potuz Aug 30, 2023
f52bbe4
checkpoint descriptions
potuz Aug 30, 2023
1478a19
fix PTC rewards for old blocks
potuz Aug 30, 2023
c767fbf
update forkchoice weights, take 1
potuz Aug 30, 2023
73ee96b
doctoc updated
potuz Aug 30, 2023
9c7b739
add head_root
potuz Aug 30, 2023
6046d59
add comment on equivocation
potuz Sep 1, 2023
4053e67
remove signed execution payload header
potuz Sep 1, 2023
7beb2eb
typos
potuz Sep 1, 2023
1d0444c
add max EB changes
potuz Sep 4, 2023
d06a874
add el withdraws in the beacon block body
potuz Sep 4, 2023
470b1f4
doctoc
potuz Sep 4, 2023
a6c5557
add slot to IL
potuz Sep 8, 2023
86e0e7d
use head_root in get_head
potuz Sep 11, 2023
feb30e6
design notes
potuz Sep 11, 2023
d7a814e
onboard builders
potuz Sep 11, 2023
85f9f26
block-slot is missing
potuz Sep 11, 2023
178fb14
block slot
potuz Sep 12, 2023
8451f12
typos
potuz Sep 14, 2023
998ca41
typos Terence found with chatgpt
potuz Sep 18, 2023
0431f0f
add helper to get payload hash
potuz Sep 20, 2023
d4f4b06
minimal churn for transfers
potuz Oct 10, 2023
874e4c7
add mapping for exclusion
potuz Oct 11, 2023
a9d2793
Revert "add mapping for exclusion"
potuz Oct 11, 2023
a908e5e
fix design for exclusion list
potuz Oct 11, 2023
7cb7de7
fix signature verification
potuz Oct 13, 2023
0bc82df
fix il proposers
potuz Oct 13, 2023
f31929a
add censoring description
potuz Oct 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
331 changes: 331 additions & 0 deletions specs/_features/epbs/beacon-chain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,331 @@
# ePBS -- The Beacon Chain

## Table of contents

<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->

<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->

## Introduction

This is the beacon chain specification of the enshrined proposer builder separation feature.

*Note:* This specification is built upon [Deneb](../../deneb/beacon-chain.md) and is under active development.

This feature adds new staked consensus participants called *Builders* and new honest validators duties called *payload timeliness attestations*. The slot is divided in **four** intervals as opposed to the current three. Honest validators gather *signed bids* from builders and submit their consensus blocks (a `SignedBlindedBeaconBlock`) at the beginning of the slot. At the start of the second interval, honest validators submit attestations just as they do previous to this feature). At the start of the third interval, aggregators aggregate these attestations (exactly as before this feature) and the honest builder reveals the full payload. At the start of the fourth interval, some honest validators selected to be members of the new **Payload Timeliness Committee** attest to the presence of the builder's payload.
potuz marked this conversation as resolved.
Show resolved Hide resolved
potuz marked this conversation as resolved.
Show resolved Hide resolved

At any given slot, the status of the blockchain's head may be either
- A *full* block from a previous slot (eg. the current slot's proposer did not submit its block).
potuz marked this conversation as resolved.
Show resolved Hide resolved
- An *empty* block from the current slot (eg. the proposer submitted a timely block, but the builder did not reveal the payload on time).
- A full block for the current slot (both the proposer and the builder revealed on time).

For a further introduction please refer to this [ethresear.ch article](https://ethresear.ch/t/payload-timeliness-committee-ptc-an-epbs-design/16054)

## Preset

### Misc

| Name | Value |
| - | - |
| `PTC_SIZE` | `uint64(2**9)` (=512) |

### Domain types

| Name | Value |
| - | - |
| `DOMAIN_BEACON_BUILDER` | `DomainType('0x0B000000')` |

### State list lengths

| Name | Value | Unit | Duration |
| - | - | :-: | :-: |
| `BUILDER_REGISTRY_LIMIT` | `uint64(2**20)` (=1,048,576) | builders |

### Gwei values

| Name | Value |
| - | - |
| `BUILDER_MIN_BALANCE` | `Gwei(2**10 * 10**9)` = (1,024,000,000,000) |

### Incentivization weights

| Name | Value |
| - | - |
| `PTC_PENALTY_WEIGHT` | `uint64(2)` |

### Execution
| Name | Value |
| - | - |
| MAX_TRANSACTIONS_PER_INCLUSION_LIST | `2**4` (=16) |

## Containers

### New Containers

#### `Builder`

``` python
class Builder(Container):
pubkey: BLSPubkey
withdrawal_address: ExecutionAddress # Commitment to pubkey for withdrawals
effective_balance: Gwei # Balance at stake
potuz marked this conversation as resolved.
Show resolved Hide resolved
exit_epoch: Epoch
withdrawable_epoch: Epoch # When builder can withdraw funds
```

#### `SignedExecutionPayloadHeader`

```python
class SignedExecutionPayloadHeader(Container):
message: ExecutionPayloadHeader
signature: BLSSignature
```
potuz marked this conversation as resolved.
Show resolved Hide resolved

#### `ExecutionPayloadEnvelope`

```python
class ExecutionPayloadEnvelope(Container):
payload: ExecutionPayload
state_root: Root
```

#### `SignedExecutionPayloadEnvelope`

```python
class SignedExecutionPayloadEnvelope(Container):
message: ExecutionPayloadEnvelope
signature: BLSSignature
```

### Modified Containers

#### `ExecutionPayload`

```python
class ExecutionPayload(Container):
# Execution block header fields
parent_hash: Hash32
fee_recipient: ExecutionAddress # 'beneficiary' in the yellow paper
state_root: Bytes32
receipts_root: Bytes32
logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM]
prev_randao: Bytes32 # 'difficulty' in the yellow paper
block_number: uint64 # 'number' in the yellow paper
gas_limit: uint64
gas_used: uint64
timestamp: uint64
extra_data: ByteList[MAX_EXTRA_DATA_BYTES]
base_fee_per_gas: uint256
# Extra payload fields
block_hash: Hash32 # Hash of execution block
transactions: List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD]
withdrawals: List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD]
builder_index: uint64 # [New in ePBS]
```

#### `ExecutionPayloadHeader`

```python
class ExecutionPayloadHeader(Container):
# Execution block header fields
parent_hash: Hash32
fee_recipient: ExecutionAddress
state_root: Bytes32
receipts_root: Bytes32
logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM]
prev_randao: Bytes32
block_number: uint64
gas_limit: uint64
gas_used: uint64
timestamp: uint64
extra_data: ByteList[MAX_EXTRA_DATA_BYTES]
base_fee_per_gas: uint256
# Extra payload fields
block_hash: Hash32 # Hash of execution block
transactions_root: Root
withdrawals_root: Root
builder_index: uint64 # [New in ePBS]
```

#### `BeaconBlockBody`

```python
class BeaconBlockBody(Container):
randao_reveal: BLSSignature
eth1_data: Eth1Data # Eth1 data vote
graffiti: Bytes32 # Arbitrary data
# Operations
proposer_slashings: List[ProposerSlashing, MAX_PROPOSER_SLASHINGS]
attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS]
attestations: List[Attestation, MAX_ATTESTATIONS]
deposits: List[Deposit, MAX_DEPOSITS]
voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS]
sync_aggregate: SyncAggregate
execution_payload_header: SignedExecutionPayloadHeader # [Modified in ePBS]
potuz marked this conversation as resolved.
Show resolved Hide resolved
bls_to_execution_changes: List[SignedBLSToExecutionChange, MAX_BLS_TO_EXECUTION_CHANGES]
tx_inclusion_list: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST]
```


#### `BeaconState`
*Note*: the beacon state is modified to store a signed latest execution payload header and it adds a registry of builders, their balances and two transaction inclusion lists.

```python
class BeaconState(Container):
# Versioning
genesis_time: uint64
genesis_validators_root: Root
slot: Slot
fork: Fork
# History
latest_block_header: BeaconBlockHeader
block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT]
state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT]
historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] # Frozen in Capella, replaced by historical_summaries
# Eth1
eth1_data: Eth1Data
eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH]
eth1_deposit_index: uint64
# Registry
validators: List[Validator, VALIDATOR_REGISTRY_LIMIT]
balances: List[Gwei, VALIDATOR_REGISTRY_LIMIT]
# Randomness
randao_mixes: Vector[Bytes32, EPOCHS_PER_HISTORICAL_VECTOR]
# Slashings
slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] # Per-epoch sums of slashed effective balances
# Participation
previous_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT]
current_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT]
# Finality
justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] # Bit set for every recent justified epoch
previous_justified_checkpoint: Checkpoint
current_justified_checkpoint: Checkpoint
finalized_checkpoint: Checkpoint
# Inactivity
inactivity_scores: List[uint64, VALIDATOR_REGISTRY_LIMIT]
# Sync
current_sync_committee: SyncCommittee
next_sync_committee: SyncCommittee
# Execution
latest_execution_payload_header: ExecutionPayloadHeader
# Withdrawals
next_withdrawal_index: WithdrawalIndex
next_withdrawal_validator_index: ValidatorIndex
# Deep history valid from Capella onwards
historical_summaries: List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT]
# PBS
builders: List[Builder, BUILDER_REGISTRY_LIMIT] # [New in ePBS]
builder_balances: List[Gwei, BUILDER_REGISTRY_LIMIT] # [New in ePBS]
previous_tx_inclusion_list: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST] # [New in ePBS]
current_tx_inclusion_list: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST] # [New in ePBS]
current_signed_execution_payload_header: SignedExecutionPayloadHeader # [New in ePBS]
```

## Beacon chain state transition function

*Note*: state transition is fundamentally modified in ePBS. The full state transition is broken in two parts, first importing a signed block and then importing an execution payload.

The post-state corresponding to a pre-state `state` and a signed block `signed_block` is defined as `state_transition(state, signed_block)`. State transitions that trigger an unhandled exception (e.g. a failed `assert` or an out-of-range list access) are considered invalid. State transitions that cause a `uint64` overflow or underflow are also considered invalid.

The post-state corresponding to a pre-state `state` and a signed execution payload `signed_execution_payload` is defined as `process_execution_payload(state, signed_execution_payload)`. State transitions that trigger an unhandled exception (e.g. a failed `assert` or an out-of-range list access) are considered invalid. State transitions that cause a `uint64` overflow or underflow are also considered invalid.

### Block processing

*Note*: the function `process_block` is modified to only process the consensus block. The full state-transition process is broken into separate functions, one to process a `BeaconBlock` and another to process a `SignedExecutionPayload`.

```python
def process_block(state: BeaconState, block: BeaconBlock) -> None:
process_block_header(state, block)
process_execution_payload_header(state, block.body.execution_payload_header) # [Modified in ePBS]
# Removed process_withdrawal in ePBS is processed during payload processing [Modified in ePBS]
process_randao(state, block.body)
process_eth1_data(state, block.body)
process_operations(state, block.body) # [Modified in ePBS]
process_sync_aggregate(state, block.body.sync_aggregate)
process_tx_inclusion_list(state, block) # [New in ePBS]
```

#### New `update_tx_inclusion_lists`

```python
def update_tx_inclusion_lists(state: BeaconState, payload: ExecutionPayload) -> None:
old_transactions = payload.transactions[:len(state.previous_tx_inclusion_list)]
assert state.previous_tx_inclusion_list == old_transactions

new_transactions = payload.transactions[len(state.previous_tx_inclusion_list):]
state.previous_tx_inclusion_list = [tx for tx in state.current_tx_inclusion_list if x not in new_transactions]

#TODO: check validity of the IL for the next block, requires engine changes
```
#### New `verify_execution_payload_header_signature`

```python
def verify_execution_payload_header_signature(state: BeaconState, signed_header: SignedExecutionPayloadHeader) -> bool:
builder = state.builders[signed_header.message.builder_index]
signing_root = compute_signing_root(signed_header.message, get_domain(state, DOMAIN_BEACON_BUILDER))
return bls.Verify(builder.pubkey, signing_root, signed_header.signature)
```

#### New `verify_execution_payload_signature`

```python
def verify_execution_envelope_signature(state: BeaconState, signed_envelope: SignedExecutionPayloadEnvelope) -> bool:
builder = state.builders[signed_envelope.message.payload.builder_index]
signing_root = compute_signing_root(signed_envelope.message, get_domain(state, DOMAIN_BEACON_BUILDER))
return bls.Verify(builder.pubkey, signing_root, signed_envelope.signature)
```

#### New `process_execution_payload_header`

```python
def process_execution_payload_header(state: BeaconState, signed_header: SignedExecutionPayloadHeader) -> None:
assert verify_execution_payload_header_signature(state, signed_header)
header = signed_header.message
# Verify consistency of the parent hash with respect to the previous execution payload header
assert header.parent_hash == state.latest_execution_payload_header.block_hash
# Verify prev_randao
assert header.prev_randao == get_randao_mix(state, get_current_epoch(state))
# Verify timestamp
assert header.timestamp == compute_timestamp_at_slot(state, state.slot)
# Cache execution payload header
state.current_signed_execution_payload_header = signed_header
```

#### Modified `process_execution_payload`
*Note*: `process_execution_payload` is now an independent check in state transition. It is called when importing a signed execution payload proposed by the builder of the current slot.

TODO: Deal with the case when the payload becomes invalid because of the forward inclusion list.

```python
def process_execution_payload(state: BeaconState, signed_envelope: SignedExecutionPayloadEnvelope, execution_engine: ExecutionEngine) -> None:
# Verify signature [New in ePBS]
assert verify_execution_envelope_signature(state, signed_envelope)
payload = signed_envelope.message.payload
# Verify consistency with the committed header
hash = hash_tree_root(payload)
previous_hash = hash_tree_root(state.current_signed_execution_payload_header.message)
assert hash == previous_hash
# Verify and update the proposers inclusion lists
update_tx_inclusion_lists(state, payload)
# Verify the execution payload is valid
assert execution_engine.verify_and_notify_new_payload(NewPayloadRequest(execution_payload=payload))
# Process Withdrawals in the payload
process_withdrawals(state, payload)
# Cache the execution payload header
state.latest_execution_payload_header = state.current_signed_execution_payload_header.message
# Verify the state root
assert signed_envelope.message.state_root == hash_tree_root(state)
```

#### New `process_tx_inclusion_list`

```python
def process_tx_inclusion_list(state: BeaconState, block: BeaconBlock) -> None:
# TODO: cap gas usage, comunicate with the engine.
state.previous_tx_inclusion_list = state.current_tx_inclusion_list
potuz marked this conversation as resolved.
Show resolved Hide resolved
state.current_tx_inclusion_list = block.body.tx_inclusion_list
```