forked from ethereum/consensus-specs
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
441 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,257 @@ | ||
# Ethereum 2.0 The Merge | ||
|
||
**Notice**: This document is a work-in-progress for researchers and implementers. | ||
|
||
## 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 --> | ||
|
||
- [Introduction](#introduction) | ||
- [Constants](#constants) | ||
- [Execution](#execution) | ||
- [Containers](#containers) | ||
- [Extended containers](#extended-containers) | ||
- [`BeaconBlockBody`](#beaconblockbody) | ||
- [`BeaconState`](#beaconstate) | ||
- [New containers](#new-containers) | ||
- [`Transaction`](#transaction) | ||
- [`ApplicationPayload`](#applicationpayload) | ||
- [Helper functions](#helper-functions) | ||
- [Misc](#misc) | ||
- [`compute_randao_mix`](#compute_randao_mix) | ||
- [`compute_time_at_slot`](#compute_time_at_slot) | ||
- [Beacon state accessors](#beacon-state-accessors) | ||
- [`get_recent_beacon_block_roots`](#get_recent_beacon_block_roots) | ||
- [`get_evm_beacon_block_roots`](#get_evm_beacon_block_roots) | ||
- [Block processing](#block-processing) | ||
- [Modified `process_eth1_data`](#modified-process_eth1_data) | ||
- [Application payload processing](#application-payload-processing) | ||
- [`BeaconChainData`](#beaconchaindata) | ||
- [`get_application_state`](#get_application_state) | ||
- [`application_state_transition`](#application_state_transition) | ||
- [`process_application_payload`](#process_application_payload) | ||
|
||
<!-- END doctoc generated TOC please keep comment here to allow auto update --> | ||
<!-- /TOC --> | ||
|
||
## Introduction | ||
|
||
This is a patch implementing executable beacon chain proposal. | ||
It enshrines application execution and validity as a first class citizen at the core of the beacon chain. | ||
|
||
## Constants | ||
|
||
### Execution | ||
|
||
| Name | Value | | ||
| - | - | | ||
| `MAX_BYTES_PER_TRANSACTION_PAYLOAD` | `2**20` | | ||
| `MAX_APPLICATION_TRANSACTIONS` | `2**14` | | ||
| `BYTES_PER_LOGS_BLOOM` | `2**8` | | ||
| `EVM_BLOCK_ROOTS_SIZE` | `2**8` | | ||
|
||
|
||
## Containers | ||
|
||
### Extended containers | ||
|
||
*Note*: Extended SSZ containers inherit all fields from the parent in the original | ||
order and append any additional fields to the end. | ||
|
||
#### `BeaconBlockBody` | ||
|
||
*Note*: `BeaconBlockBody` fields remain unchanged other than the addition of `application_payload`. | ||
|
||
```python | ||
class BeaconBlockBody(phase0.BeaconBlockBody): | ||
application_payload: ApplicationPayload # User execution payload | ||
``` | ||
|
||
#### `BeaconState` | ||
|
||
*Note*: `BeaconState` fields remain unchanged other than the removal of `eth1_data_votes` and addition of `application_state_root`. | ||
The latter stores the root hash of ethereum application state. | ||
|
||
```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] | ||
# Eth1 | ||
eth1_data: Eth1Data | ||
# [removed] eth1_data_votes | ||
eth1_deposit_index: uint64 | ||
# [new] Hash of the root of application state | ||
application_state_root: Bytes32 | ||
# [new] Hash of recent application block | ||
application_block_hash: Bytes32 | ||
# 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 | ||
# Attestations | ||
previous_epoch_attestations: List[PendingAttestation, MAX_ATTESTATIONS * SLOTS_PER_EPOCH] | ||
current_epoch_attestations: List[PendingAttestation, MAX_ATTESTATIONS * SLOTS_PER_EPOCH] | ||
# Finality | ||
justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] # Bit set for every recent justified epoch | ||
previous_justified_checkpoint: Checkpoint # Previous epoch snapshot | ||
current_justified_checkpoint: Checkpoint | ||
finalized_checkpoint: Checkpoint | ||
``` | ||
|
||
### New containers | ||
|
||
#### `Transaction` | ||
|
||
Application transaction fields structured as an SSZ object for inclusion in an `ApplicationPayload` contained within a `BeaconBlock`. | ||
|
||
```python | ||
class Transaction(Container): | ||
nonce: uint64 | ||
gas_price: uint256 | ||
gas_limit: uint64 | ||
recipient: Bytes20 | ||
value: uint256 | ||
input: List[Bytes1, MAX_BYTES_PER_TRANSACTION_PAYLOAD] | ||
v: uint256 | ||
r: uint256 | ||
s: uint256 | ||
``` | ||
|
||
#### `ApplicationPayload` | ||
|
||
The application payload included in a `BeaconBlock`. | ||
|
||
```python | ||
class ApplicationPayload(Container): | ||
block_hash: Bytes32 # Hash of application block | ||
coinbase: Bytes20 | ||
state_root: Bytes32 | ||
gas_limit: uint64 | ||
gas_used: uint64 | ||
receipt_root: Bytes32 | ||
logs_bloom: Vector[Bytes1, BYTES_PER_LOGS_BLOOM] | ||
difficulty: uint64 # Temporary field, will be removed later on | ||
transactions: List[Transaction, MAX_APPLICATION_TRANSACTIONS] | ||
``` | ||
|
||
## Helper functions | ||
|
||
### Misc | ||
|
||
#### `compute_randao_mix` | ||
|
||
```python | ||
def compute_randao_mix(state: BeaconState, randao_reveal: BLSSignature) -> Bytes32: | ||
epoch = get_current_epoch(state) | ||
return xor(get_randao_mix(state, epoch), hash(randao_reveal)) | ||
``` | ||
|
||
#### `compute_time_at_slot` | ||
|
||
```python | ||
def compute_time_at_slot(state: BeaconState, slot: Slot) -> uint64: | ||
return uint64(state.genesis_time + slot * SECONDS_PER_SLOT) | ||
``` | ||
|
||
### Beacon state accessors | ||
|
||
#### `get_recent_beacon_block_roots` | ||
|
||
```python | ||
def get_recent_beacon_block_roots(state: BeaconState, qty: uint64) -> Sequence[Bytes32]: | ||
return [get_block_root_at_slot(state.slot - i) if GENESIS_SLOT + i < state.slot else Bytes32() for i in reversed(range(1, qty + 1))] | ||
``` | ||
|
||
#### `get_evm_beacon_block_roots` | ||
|
||
```python | ||
def get_evm_beacon_block_roots(state: BeaconState) -> Sequence[Bytes32]: | ||
num_block_roots = min(BLOCK_ROOTS_FOR_EVM_SIZE, SLOTS_PER_HISTORICAL_ROOT) | ||
return get_recent_beacon_block_roots(state, num_block_roots) | ||
``` | ||
|
||
### Block processing | ||
|
||
```python | ||
def process_block(state: BeaconState, block: BeaconBlock) -> None: | ||
process_block_header(state, block) | ||
process_randao(state, block.body) | ||
process_eth1_data(state, block.body) # [Modified in The Merge] | ||
process_operations(state, block.body) | ||
process_application_payload(state, block.body) # [New in The Merge] | ||
``` | ||
|
||
#### Modified `process_eth1_data` | ||
|
||
*Note*: The function `process_eth1_data` is modified to update `state.eth1_data` with `eth1_data` of each block. | ||
|
||
```python | ||
def process_eth1_data(state: BeaconState, body: BeaconBlockBody) -> None: | ||
state.eth1_data = body.eth1_data | ||
``` | ||
|
||
#### Application payload processing | ||
|
||
##### `BeaconChainData` | ||
|
||
*Note*: `BeaconChainData` contains beacon state data that is used by the application state transition function. | ||
|
||
```python | ||
class BeaconChainData(Container): | ||
slot: Slot | ||
randao_mix: Bytes32 | ||
timestamp: uint64 | ||
recent_block_roots: Sequence[Bytes32] | ||
``` | ||
|
||
##### `get_application_state` | ||
|
||
*Note*: `ApplicationState` class is an abstract class representing ethereum application state. | ||
|
||
Let `get_application_state(application_state_root: Bytes32) -> ApplicationState` be the function that given the root hash returns a copy of ethereum application state. | ||
The body of the function is implementation dependant. | ||
|
||
##### `application_state_transition` | ||
|
||
Let `application_state_transition(application_state: ApplicationState, beacon_chain_data: BeaconChainData, application_payload: ApplicationPayload) -> None` be the transition function of ethereum application state. | ||
The body of the function is implementation dependant. | ||
|
||
*Note*: `application_state_transition` must throw `AssertionError` if either transition itself or post-transition verifications has failed. | ||
|
||
##### `process_application_payload` | ||
|
||
```python | ||
def process_application_payload(state: BeaconState, body: BeaconBlockBody) -> None: | ||
""" | ||
Note: This function is designed to be able to be run in parallel with | ||
the other `process_block` sub-functions | ||
""" | ||
|
||
# Utilizes `compute_randao_mix` to avoid any assumptions about | ||
# the processing of other `process_block` sub-functions | ||
beacon_chain_data = BeaconChainData( | ||
slot=state.slot, | ||
randao_mix=compute_randao_mix(state, body.randao_reveal), | ||
timestamp=compute_time_at_slot(state.genesis_time, state.slot), | ||
recent_block_roots=get_evm_beacon_block_roots(state) | ||
) | ||
|
||
application_state = get_application_state(state.application_state_root) | ||
application_state_transition(application_state, beacon_chain_data, body.application_payload) | ||
|
||
state.application_state_root = body.application_payload.state_root | ||
state.application_block_hash = body.application_payload.block_hash | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
# Ethereum 2.0 The Merge | ||
|
||
**Notice**: This document is a work-in-progress for researchers and implementers. | ||
|
||
## 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 --> | ||
|
||
- [Introduction](#introduction) | ||
- [Helpers](#helpers) | ||
- [`get_eth1_data`](#get_eth1_data) | ||
- [`is_valid_eth1_data`](#is_valid_eth1_data) | ||
- [Updated fork-choice handlers](#updated-fork-choice-handlers) | ||
- [`on_block`](#on_block) | ||
|
||
<!-- END doctoc generated TOC please keep comment here to allow auto update --> | ||
<!-- /TOC --> | ||
|
||
## Introduction | ||
|
||
This is the modification of the fork choice according to the executable beacon chain proposal. | ||
|
||
*Note*: It introduces the following change. `Eth1Data` included in a block must correspond to the application state produced by the parent block. This acts as an additional filter on the block subtree under consideration for the beacon block fork choice. | ||
|
||
### Helpers | ||
|
||
#### `get_eth1_data` | ||
|
||
Let `get_eth1_data(state: BeaconState) -> Eth1Data` be the function that returns the `Eth1Data` obtained from the beacon state. | ||
|
||
*Note*: This is mostly a function of the state of the beacon chain deposit contract. It can be read from the application state and/or logs. The `block_hash` value of `Eth1Data` must be set to `state.application_block_hash`. | ||
|
||
#### `is_valid_eth1_data` | ||
|
||
Used by fork-choice handler, `on_block` | ||
|
||
```python | ||
def is_valid_eth1_data(store: Store, block: BeaconBlock) -> boolean: | ||
parent_state = store.block_states[block.parent_root] | ||
expected_eth1_data = get_eth1_data(parent_state) | ||
actual_eth1_data = block.body.eth1_data | ||
|
||
is_correct_root = expected_eth1_data.deposit_root == actual_eth1_data.deposit_root | ||
is_correct_count = expected_eth1_data.deposit_count == actual_eth1_data.deposit_count | ||
is_correct_block_hash = expected_eth1_data.block_hash == actual_eth1_data.block_hash | ||
return is_correct_root and is_correct_count and is_correct_block_hash | ||
``` | ||
|
||
### Updated fork-choice handlers | ||
|
||
#### `on_block` | ||
|
||
*Note*: The only modification is the addition of the `Eth1Data` validity assumption. | ||
|
||
```python | ||
def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: | ||
block = signed_block.message | ||
# Parent block must be known | ||
assert block.parent_root in store.block_states | ||
# Make a copy of the state to avoid mutability issues | ||
pre_state = copy(store.block_states[block.parent_root]) | ||
# Blocks cannot be in the future. If they are, their consideration must be delayed until the are in the past. | ||
assert get_current_slot(store) >= block.slot | ||
|
||
# Check that block is later than the finalized epoch slot (optimization to reduce calls to get_ancestor) | ||
finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) | ||
assert block.slot > finalized_slot | ||
# Check block is a descendant of the finalized block at the checkpoint finalized slot | ||
assert get_ancestor(store, block.parent_root, finalized_slot) == store.finalized_checkpoint.root | ||
|
||
# [Added] Check that Eth1 data is correct | ||
assert is_valid_eth1_data(store, block) | ||
|
||
# Check the block is valid and compute the post-state | ||
state = pre_state.copy() | ||
state_transition(state, signed_block, True) | ||
# Add new block to the store | ||
store.blocks[hash_tree_root(block)] = block | ||
# Add new state for this block to the store | ||
store.block_states[hash_tree_root(block)] = state | ||
|
||
# Update justified checkpoint | ||
if state.current_justified_checkpoint.epoch > store.justified_checkpoint.epoch: | ||
if state.current_justified_checkpoint.epoch > store.best_justified_checkpoint.epoch: | ||
store.best_justified_checkpoint = state.current_justified_checkpoint | ||
if should_update_justified_checkpoint(store, state.current_justified_checkpoint): | ||
store.justified_checkpoint = state.current_justified_checkpoint | ||
|
||
# Update finalized checkpoint | ||
if state.finalized_checkpoint.epoch > store.finalized_checkpoint.epoch: | ||
store.finalized_checkpoint = state.finalized_checkpoint | ||
|
||
# Potentially update justified if different from store | ||
if store.justified_checkpoint != state.current_justified_checkpoint: | ||
# Update justified if new justified is later than store justified | ||
if state.current_justified_checkpoint.epoch > store.justified_checkpoint.epoch: | ||
store.justified_checkpoint = state.current_justified_checkpoint | ||
return | ||
|
||
# Update justified if store justified is not in chain with finalized checkpoint | ||
finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) | ||
ancestor_at_finalized_slot = get_ancestor(store, store.justified_checkpoint.root, finalized_slot) | ||
if ancestor_at_finalized_slot != store.finalized_checkpoint.root: | ||
store.justified_checkpoint = state.current_justified_checkpoint | ||
``` | ||
|
Oops, something went wrong.