From 0dec828d89c522aa2048e47f681eccb41b5fa282 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 11 Mar 2021 18:33:36 +0600 Subject: [PATCH 01/24] Add initial merge spec --- specs/merge/beacon-chain.md | 257 ++++++++++++++++++++++++++++++++++++ specs/merge/fork-choice.md | 107 +++++++++++++++ specs/merge/validator.md | 77 +++++++++++ 3 files changed, 441 insertions(+) create mode 100644 specs/merge/beacon-chain.md create mode 100644 specs/merge/fork-choice.md create mode 100644 specs/merge/validator.md diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md new file mode 100644 index 000000000..a235dd518 --- /dev/null +++ b/specs/merge/beacon-chain.md @@ -0,0 +1,257 @@ +# Ethereum 2.0 The Merge + +**Notice**: This document is a work-in-progress for researchers and implementers. + +## Table of contents + + + + + +- [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) + + + + +## 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 +``` diff --git a/specs/merge/fork-choice.md b/specs/merge/fork-choice.md new file mode 100644 index 000000000..9d8f175d0 --- /dev/null +++ b/specs/merge/fork-choice.md @@ -0,0 +1,107 @@ +# Ethereum 2.0 The Merge + +**Notice**: This document is a work-in-progress for researchers and implementers. + +## Table of contents + + + + +- [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) + + + + +## 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 +``` + diff --git a/specs/merge/validator.md b/specs/merge/validator.md new file mode 100644 index 000000000..d55ec1c8f --- /dev/null +++ b/specs/merge/validator.md @@ -0,0 +1,77 @@ +# Ethereum 2.0 Phase 1 -- Honest Validator + +**Notice**: This document is a work-in-progress for researchers and implementers. + +## Table of contents + + + + + +- [Introduction](#introduction) +- [Prerequisites](#prerequisites) +- [Beacon chain responsibilities](#beacon-chain-responsibilities) + - [Block proposal](#block-proposal) + - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) + - [Eth1 data](#eth1-data) + - [`get_eth1_data`](#get_eth1_data) + - [Application Payload](#application-payload) + - [`produce_application_payload`](#produce_application_payload) + + + + +## Introduction + +This document represents the changes to be made in the code of an "honest validator" to implement executable beacon chain proposal. + +## Prerequisites + +This document is an extension of the [Phase 0 -- Validator](../phase0/validator.md). All behaviors and definitions defined in the Phase 0 doc carry over unless explicitly noted or overridden. + +All terminology, constants, functions, and protocol mechanics defined in the updated Beacon Chain doc of [The Merge](./beacon-chain.md) are requisite for this document and used throughout. Please see related Beacon Chain doc before continuing and use them as a reference throughout. + +## Beacon chain responsibilities + +All validator responsibilities remain unchanged other than those noted below. Namely, the modification of `Eth1Data` and the addition of `ApplicationPayload`. + +### Block proposal + +#### Constructing the `BeaconBlockBody` + +##### Eth1 data + +The `block.body.eth1_data` field is for block proposers to publish recent Eth1 data. This recent data contains deposit root (as calculated by the get_deposit_root() method of the deposit contract) and deposit count after processing of the parent block. The fork choice verifies Eth1 data of a block, then `state.eth1_data` updates immediately allowing new deposits to be processed. Each deposit in `block.body.deposits` must verify against `state.eth1_data.deposit_root`. + +###### `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`. + +Set `block.body.eth1_data = get_eth1_data(state)`. + + +##### Application Payload + +###### `produce_application_payload` + +Let `produce_application_payload(parent_hash: Bytes32, beacon_chain_data: BeaconChainData) -> ApplicationPayload` be the function that produces new instance of application payload. + + +* Let `randao_reveal` be `block.body.randao_reveal` of the block that is being produced +* Set `block.body.application_payload = get_application_payload(state, randao_reveal)` where: + +```python +def get_application_payload(state: BeaconState, randao_reveal: BLSSignature) -> ApplicationPayload: + application_parent_hash = state.application_block_hash + beacon_chain_data = BeaconChainData( + slot=state.slot, + randao_mix=compute_randao_mix(state, randao_reveal), + timestamp=compute_time_at_slot(state.genesis_time, state.slot), + recent_block_roots=get_evm_beacon_block_roots(state) + ) + + return produce_application_payload(application_parent_hash, beacon_chain_data) +``` + From ee161634b203eb6eab8f108ab03c0f9a4a2fb781 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 11 Mar 2021 18:52:31 +0600 Subject: [PATCH 02/24] Polish beacon chain spec and validator guide --- specs/merge/beacon-chain.md | 16 ++++++++-------- specs/merge/validator.md | 7 +++---- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index a235dd518..4ded574f0 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -66,13 +66,13 @@ order and append any additional fields to the end. ```python class BeaconBlockBody(phase0.BeaconBlockBody): - application_payload: ApplicationPayload # User execution payload + application_payload: ApplicationPayload # [Added] application 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. +*Note*: `BeaconState` fields remain unchanged other than the removal of `eth1_data_votes` and addition of `application_state_root` and `application_block_hash`. + ```python class BeaconState(Container): @@ -88,11 +88,11 @@ class BeaconState(Container): historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] # Eth1 eth1_data: Eth1Data - # [removed] eth1_data_votes + # [Removed] eth1_data_votes eth1_deposit_index: uint64 - # [new] Hash of the root of application state + # [Added] hash of the root of application state application_state_root: Bytes32 - # [new] Hash of recent application block + # [Added] hash of recent application block application_block_hash: Bytes32 # Registry validators: List[Validator, VALIDATOR_REGISTRY_LIMIT] @@ -179,7 +179,7 @@ def get_recent_beacon_block_roots(state: BeaconState, qty: uint64) -> Sequence[B ```python def get_evm_beacon_block_roots(state: BeaconState) -> Sequence[Bytes32]: - num_block_roots = min(BLOCK_ROOTS_FOR_EVM_SIZE, SLOTS_PER_HISTORICAL_ROOT) + num_block_roots = min(EVM_BLOCK_ROOTS_SIZE, SLOTS_PER_HISTORICAL_ROOT) return get_recent_beacon_block_roots(state, num_block_roots) ``` @@ -229,7 +229,7 @@ The body of the function is implementation dependant. 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. +*Note*: `application_state_transition` must throw `AssertionError` if either the transition itself or one of the post-transition verifications has failed. ##### `process_application_payload` diff --git a/specs/merge/validator.md b/specs/merge/validator.md index d55ec1c8f..75e804187 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -1,4 +1,4 @@ -# Ethereum 2.0 Phase 1 -- Honest Validator +# Ethereum 2.0 The Merge **Notice**: This document is a work-in-progress for researchers and implementers. @@ -41,7 +41,7 @@ All validator responsibilities remain unchanged other than those noted below. Na ##### Eth1 data -The `block.body.eth1_data` field is for block proposers to publish recent Eth1 data. This recent data contains deposit root (as calculated by the get_deposit_root() method of the deposit contract) and deposit count after processing of the parent block. The fork choice verifies Eth1 data of a block, then `state.eth1_data` updates immediately allowing new deposits to be processed. Each deposit in `block.body.deposits` must verify against `state.eth1_data.deposit_root`. +The `block.body.eth1_data` field is for block proposers to publish recent Eth1 data. This recent data contains deposit root (as calculated by the `get_deposit_root()` method of the deposit contract) and deposit count after processing of the parent block. The fork choice verifies Eth1 data of a block, then `state.eth1_data` updates immediately allowing new deposits to be processed. Each deposit in `block.body.deposits` must verify against `state.eth1_data.deposit_root`. ###### `get_eth1_data` @@ -57,7 +57,7 @@ Set `block.body.eth1_data = get_eth1_data(state)`. ###### `produce_application_payload` Let `produce_application_payload(parent_hash: Bytes32, beacon_chain_data: BeaconChainData) -> ApplicationPayload` be the function that produces new instance of application payload. - +The body of this function is implementation dependant. * Let `randao_reveal` be `block.body.randao_reveal` of the block that is being produced * Set `block.body.application_payload = get_application_payload(state, randao_reveal)` where: @@ -74,4 +74,3 @@ def get_application_payload(state: BeaconState, randao_reveal: BLSSignature) -> return produce_application_payload(application_parent_hash, beacon_chain_data) ``` - From f6f36872d82d15e6b3ee2a9afc0fd949f4e9ad13 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 17 Mar 2021 15:19:58 +0600 Subject: [PATCH 03/24] Index from GENESIS_SLOT in compute_time_at_slot Co-authored-by: Paul Hauner --- specs/merge/beacon-chain.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 4ded574f0..e12a89e20 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -163,7 +163,8 @@ def compute_randao_mix(state: BeaconState, randao_reveal: BLSSignature) -> Bytes ```python def compute_time_at_slot(state: BeaconState, slot: Slot) -> uint64: - return uint64(state.genesis_time + slot * SECONDS_PER_SLOT) + slots_since_genesis = slot - GENESIS_SLOT + return uint64(state.genesis_time + slots_since_genesis * SECONDS_PER_SLOT) ``` ### Beacon state accessors From 3fb5f2ec8132febcc48ea66c4463b5e0c8a76809 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 17 Mar 2021 15:20:52 +0600 Subject: [PATCH 04/24] Use Vector struct for recent_block_roots field Co-authored-by: Paul Hauner --- specs/merge/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index e12a89e20..e2ed48b3e 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -215,7 +215,7 @@ class BeaconChainData(Container): slot: Slot randao_mix: Bytes32 timestamp: uint64 - recent_block_roots: Sequence[Bytes32] + recent_block_roots: Vector[Bytes32, EVM_BLOCK_ROOTS_SIZE] ``` ##### `get_application_state` From 5435324693ba0e24ebd26eb7c948f48c30bc5ac1 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 17 Mar 2021 15:21:27 +0600 Subject: [PATCH 05/24] Add a line break in get_recent_beacon_block_roots Co-authored-by: Danny Ryan --- specs/merge/beacon-chain.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index e2ed48b3e..4d5e42920 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -173,7 +173,10 @@ def compute_time_at_slot(state: BeaconState, slot: Slot) -> uint64: ```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))] + 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` From 3c9cd855a062db604d9056b10bc316cecd27b0fb Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 17 Mar 2021 15:21:47 +0600 Subject: [PATCH 06/24] Fix spelling Co-authored-by: Danny Ryan --- specs/merge/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 4d5e42920..7f27e80e1 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -226,7 +226,7 @@ class BeaconChainData(Container): *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. +The body of the function is implementation dependent. ##### `application_state_transition` From a368f5d2240dd6648dce3afd9f458f1e77239635 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 17 Mar 2021 15:31:21 +0600 Subject: [PATCH 07/24] Lable Added/Remove notes with Merge explicitly --- specs/merge/beacon-chain.md | 12 ++++++------ specs/merge/fork-choice.md | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 7f27e80e1..1ae96886e 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -66,7 +66,7 @@ order and append any additional fields to the end. ```python class BeaconBlockBody(phase0.BeaconBlockBody): - application_payload: ApplicationPayload # [Added] application payload + application_payload: ApplicationPayload # [Added in Merge] application payload ``` #### `BeaconState` @@ -88,11 +88,11 @@ class BeaconState(Container): historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] # Eth1 eth1_data: Eth1Data - # [Removed] eth1_data_votes + # [Removed in Merge] eth1_data_votes eth1_deposit_index: uint64 - # [Added] hash of the root of application state + # [Added in Merge] hash of the root of application state application_state_root: Bytes32 - # [Added] hash of recent application block + # [Added in Merge] hash of recent application block application_block_hash: Bytes32 # Registry validators: List[Validator, VALIDATOR_REGISTRY_LIMIT] @@ -193,9 +193,9 @@ def get_evm_beacon_block_roots(state: BeaconState) -> Sequence[Bytes32]: 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_eth1_data(state, block.body) # [Modified in Merge] process_operations(state, block.body) - process_application_payload(state, block.body) # [New in The Merge] + process_application_payload(state, block.body) # [New in Merge] ``` #### Modified `process_eth1_data` diff --git a/specs/merge/fork-choice.md b/specs/merge/fork-choice.md index 9d8f175d0..2870e39b0 100644 --- a/specs/merge/fork-choice.md +++ b/specs/merge/fork-choice.md @@ -69,7 +69,7 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: # 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 + # [Added in Merge] Check that Eth1 data is correct assert is_valid_eth1_data(store, block) # Check the block is valid and compute the post-state From b8e16c1610e4af03b38baf6ad98a956aa8317a08 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 17 Mar 2021 15:32:24 +0600 Subject: [PATCH 08/24] Remove min(..., ...) in get_evm_beacon_block_roots --- specs/merge/beacon-chain.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 1ae96886e..364cc4377 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -183,8 +183,8 @@ def get_recent_beacon_block_roots(state: BeaconState, qty: uint64) -> Sequence[B ```python def get_evm_beacon_block_roots(state: BeaconState) -> Sequence[Bytes32]: - num_block_roots = min(EVM_BLOCK_ROOTS_SIZE, SLOTS_PER_HISTORICAL_ROOT) - return get_recent_beacon_block_roots(state, num_block_roots) + # EVM_BLOCK_ROOTS_SIZE must be less or equal to SLOTS_PER_HISTORICAL_ROOT + return get_recent_beacon_block_roots(state, EVM_BLOCK_ROOTS_SIZE) ``` ### Block processing From bf151641a73aaab16b79942ad635652051b5c07d Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 17 Mar 2021 22:39:35 +0600 Subject: [PATCH 09/24] Add rebase-to-Altair warning --- specs/merge/beacon-chain.md | 2 ++ specs/merge/validator.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 364cc4377..043bc2afe 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -1,5 +1,7 @@ # Ethereum 2.0 The Merge +**Warning:** This document is based on [Phase 0](../phase0/beacon-chain.md) and considered to be rebased to [Altair](../altair/beacon-chain.md) once the latter is shipped. + **Notice**: This document is a work-in-progress for researchers and implementers. ## Table of contents diff --git a/specs/merge/validator.md b/specs/merge/validator.md index 75e804187..aff4263b2 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -1,5 +1,7 @@ # Ethereum 2.0 The Merge +**Warning:** This document is based on [Phase 0](../phase0/validator.md) and considered to be rebased to [Altair](../altair/validator.md) once the latter is shipped. + **Notice**: This document is a work-in-progress for researchers and implementers. ## Table of contents From 46fc8a196ddd9ba03426d824d610c65ee1a13d6b Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Sat, 20 Mar 2021 19:21:11 +0600 Subject: [PATCH 10/24] Strip down the merge to the pure consensus upgrade --- specs/merge/beacon-chain.md | 104 ++++++++++-------------------------- specs/merge/fork-choice.md | 33 +++++------- specs/merge/validator.md | 53 +++++++++--------- 3 files changed, 67 insertions(+), 123 deletions(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 043bc2afe..8508fa06c 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -12,6 +12,7 @@ - [Introduction](#introduction) - [Constants](#constants) + - [Transition](#transition) - [Execution](#execution) - [Containers](#containers) - [Extended containers](#extended-containers) @@ -22,15 +23,10 @@ - [`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) + - [`is_transition_completed`](#is_transition_completed) + - [`is_transition_block`](#is_transition_block) - [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) @@ -45,6 +41,11 @@ It enshrines application execution and validity as a first class citizen at the ## Constants +### Transition +| Name | Value | +| - | - | +| `TRANSITION_TOTAL_DIFFICULTY` | _TBD_ | + ### Execution | Name | Value | @@ -52,7 +53,6 @@ It enshrines application execution and validity as a first class citizen at the | `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 @@ -73,7 +73,7 @@ class BeaconBlockBody(phase0.BeaconBlockBody): #### `BeaconState` -*Note*: `BeaconState` fields remain unchanged other than the removal of `eth1_data_votes` and addition of `application_state_root` and `application_block_hash`. +*Note*: `BeaconState` fields remain unchanged other than addition of `application_state_root` and `application_block_hash`. ```python @@ -90,7 +90,7 @@ class BeaconState(Container): historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] # Eth1 eth1_data: Eth1Data - # [Removed in Merge] eth1_data_votes + eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH] eth1_deposit_index: uint64 # [Added in Merge] hash of the root of application state application_state_root: Bytes32 @@ -153,40 +153,18 @@ class ApplicationPayload(Container): ### Misc -#### `compute_randao_mix` +#### `is_transition_completed` ```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)) +def is_transition_completed(state: BeaconState) -> Boolean: + state.application_block_hash != Bytes32() ``` -#### `compute_time_at_slot` +#### `is_transition_block` ```python -def compute_time_at_slot(state: BeaconState, slot: Slot) -> uint64: - slots_since_genesis = slot - GENESIS_SLOT - return uint64(state.genesis_time + slots_since_genesis * 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]: - # EVM_BLOCK_ROOTS_SIZE must be less or equal to SLOTS_PER_HISTORICAL_ROOT - return get_recent_beacon_block_roots(state, EVM_BLOCK_ROOTS_SIZE) +def is_transition_block(state: BeaconState, block_body: BeaconBlockBody) -> boolean: + return state.application_block_hash == Bytes32() and block.body.application_payload.block_hash != Bytes32() ``` ### Block processing @@ -195,34 +173,13 @@ def get_evm_beacon_block_roots(state: BeaconState) -> Sequence[Bytes32]: 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 Merge] + process_eth1_data(state, block.body) process_operations(state, block.body) process_application_payload(state, block.body) # [New in 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: Vector[Bytes32, EVM_BLOCK_ROOTS_SIZE] -``` - ##### `get_application_state` *Note*: `ApplicationState` class is an abstract class representing ethereum application state. @@ -232,8 +189,8 @@ The body of the function is implementation dependent. ##### `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. +Let `application_state_transition(application_state: ApplicationState, application_payload: ApplicationPayload) -> None` be the transition function of ethereum application state. +The body of the function is implementation dependent. *Note*: `application_state_transition` must throw `AssertionError` if either the transition itself or one of the post-transition verifications has failed. @@ -245,19 +202,14 @@ def process_application_payload(state: BeaconState, body: BeaconBlockBody) -> No 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 + if is_transition_completed(state): + application_state = get_application_state(state.application_state_root) + application_state_transition(application_state, body.application_payload) + + state.application_state_root = body.application_payload.state_root + state.application_block_hash = body.application_payload.block_hash + + elif is_transition_block(state, body): + state.application_block_hash = body.application_payload.block_hash ``` diff --git a/specs/merge/fork-choice.md b/specs/merge/fork-choice.md index 2870e39b0..150dba91c 100644 --- a/specs/merge/fork-choice.md +++ b/specs/merge/fork-choice.md @@ -9,8 +9,8 @@ - [Introduction](#introduction) - [Helpers](#helpers) - - [`get_eth1_data`](#get_eth1_data) - - [`is_valid_eth1_data`](#is_valid_eth1_data) + - [`get_total_difficulty`](#get_total_difficulty) + - [`is_valid_transition_block`](#is_valid_transition_block) - [Updated fork-choice handlers](#updated-fork-choice-handlers) - [`on_block`](#on_block) @@ -21,37 +21,31 @@ 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. +*Note*: It introduces the process of transition from the last PoW block to the first PoS block. ### Helpers -#### `get_eth1_data` +#### `get_total_difficulty` -Let `get_eth1_data(state: BeaconState) -> Eth1Data` be the function that returns the `Eth1Data` obtained from the beacon state. +Let `get_total_difficulty(hash: Bytes32) -> uint256` be the function that returns the total difficulty of the PoW block specified by its hash. -*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`. +*Note*: The function returns `0` if the block is either not yet processed or considered invalid. The latter two cases are considered indistinguishable to the current implementation of JSON-RPC. -#### `is_valid_eth1_data` +#### `is_valid_transition_block` 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 +def is_valid_transition_block(block: BeaconBlock) -> boolean: + total_difficulty = get_total_difficulty(block.body.application_payload.block_hash) + return total_difficulty >= TRANSITION_TOTAL_DIFFICULTY ``` ### Updated fork-choice handlers #### `on_block` -*Note*: The only modification is the addition of the `Eth1Data` validity assumption. +*Note*: The only modification is the addition of the verification of transition block conditions. ```python def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: @@ -69,8 +63,9 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: # 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 in Merge] Check that Eth1 data is correct - assert is_valid_eth1_data(store, block) + # [Added in Merge] Consider delaying the beacon block processing until PoW block is accepted by the application node + if is_transition_block(pre_state, block.body): + assert is_valid_transition_block(block) # Check the block is valid and compute the post-state state = pre_state.copy() diff --git a/specs/merge/validator.md b/specs/merge/validator.md index aff4263b2..d55f2ea3e 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -15,9 +15,9 @@ - [Beacon chain responsibilities](#beacon-chain-responsibilities) - [Block proposal](#block-proposal) - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) - - [Eth1 data](#eth1-data) - - [`get_eth1_data`](#get_eth1_data) - [Application Payload](#application-payload) + - [`ApplicaitonBlock`](#applicaitonblock) + - [`get_pow_chain_head`](#get_pow_chain_head) - [`produce_application_payload`](#produce_application_payload) @@ -35,44 +35,41 @@ All terminology, constants, functions, and protocol mechanics defined in the upd ## Beacon chain responsibilities -All validator responsibilities remain unchanged other than those noted below. Namely, the modification of `Eth1Data` and the addition of `ApplicationPayload`. +All validator responsibilities remain unchanged other than those noted below. Namely, the transition block handling and the addition of `ApplicationPayload`. ### Block proposal #### Constructing the `BeaconBlockBody` -##### Eth1 data - -The `block.body.eth1_data` field is for block proposers to publish recent Eth1 data. This recent data contains deposit root (as calculated by the `get_deposit_root()` method of the deposit contract) and deposit count after processing of the parent block. The fork choice verifies Eth1 data of a block, then `state.eth1_data` updates immediately allowing new deposits to be processed. Each deposit in `block.body.deposits` must verify against `state.eth1_data.deposit_root`. - -###### `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`. - -Set `block.body.eth1_data = get_eth1_data(state)`. - - ##### Application Payload +###### `ApplicaitonBlock` +```python +class PowBlock(Container): + block_hash: Bytes32 + total_difficulty: uint256 +``` + +###### `get_pow_chain_head` + +Let `get_pow_chain_head() -> PowBlock` be the function that returns the head of the PoW chain. The body of the function is implementation specific. + ###### `produce_application_payload` -Let `produce_application_payload(parent_hash: Bytes32, beacon_chain_data: BeaconChainData) -> ApplicationPayload` be the function that produces new instance of application payload. -The body of this function is implementation dependant. +Let `produce_application_payload(parent_hash: Bytes32) -> ApplicationPayload` be the function that produces new instance of application payload. +The body of this function is implementation dependent. -* Let `randao_reveal` be `block.body.randao_reveal` of the block that is being produced * Set `block.body.application_payload = get_application_payload(state, randao_reveal)` where: ```python -def get_application_payload(state: BeaconState, randao_reveal: BLSSignature) -> ApplicationPayload: - application_parent_hash = state.application_block_hash - beacon_chain_data = BeaconChainData( - slot=state.slot, - randao_mix=compute_randao_mix(state, randao_reveal), - timestamp=compute_time_at_slot(state.genesis_time, state.slot), - recent_block_roots=get_evm_beacon_block_roots(state) - ) +def get_application_payload(state: BeaconState) -> ApplicationPayload: + if is_transition_completed(state): + application_parent_hash = state.application_block_hash + return produce_application_payload(application_parent_hash) - return produce_application_payload(application_parent_hash, beacon_chain_data) + pow_block = get_pow_chain_head() + if pow_block.total_difficulty >= TRANSITION_TOTAL_DIFFICULTY: + return ApplicationPayload(block_hash = pow_block.block_hash) + else: + return ApplicationPayload() ``` From 3420e51a0f28ef558198b24422c2f2f895b62abb Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Sat, 20 Mar 2021 21:46:20 +0600 Subject: [PATCH 11/24] Verify transition block to be assembled correctly --- specs/merge/beacon-chain.md | 1 + 1 file changed, 1 insertion(+) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 8508fa06c..661e0689d 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -211,5 +211,6 @@ def process_application_payload(state: BeaconState, body: BeaconBlockBody) -> No state.application_block_hash = body.application_payload.block_hash elif is_transition_block(state, body): + assert body.application_payload == ApplicationPayload(block_hash = body.application_payload.block_hash) state.application_block_hash = body.application_payload.block_hash ``` From 24dc8a277fd732e64cd5c7fe8cb1168b8149860c Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Mon, 22 Mar 2021 20:54:44 +0600 Subject: [PATCH 12/24] Fix block_body variable in is_transition_block Co-authored-by: terence tsao --- specs/merge/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 661e0689d..d083a3139 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -164,7 +164,7 @@ def is_transition_completed(state: BeaconState) -> Boolean: ```python def is_transition_block(state: BeaconState, block_body: BeaconBlockBody) -> boolean: - return state.application_block_hash == Bytes32() and block.body.application_payload.block_hash != Bytes32() + return state.application_block_hash == Bytes32() and block_body.application_payload.block_hash != Bytes32() ``` ### Block processing From 38a455c79f5803af8d84b4b14cadf55ff780bb8a Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Mon, 22 Mar 2021 20:58:34 +0600 Subject: [PATCH 13/24] Verify that ApplicationPayload is zeroed before the transition --- specs/merge/beacon-chain.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index d083a3139..710bc5935 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -213,4 +213,7 @@ def process_application_payload(state: BeaconState, body: BeaconBlockBody) -> No elif is_transition_block(state, body): assert body.application_payload == ApplicationPayload(block_hash = body.application_payload.block_hash) state.application_block_hash = body.application_payload.block_hash + + else: + assert body.application_payload == ApplicationPayload() ``` From 83453d212ede8453b0d46bb591f67bc50ce16474 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Mon, 22 Mar 2021 21:14:31 +0600 Subject: [PATCH 14/24] Simplify merge.BeaconState definition --- specs/merge/beacon-chain.md | 31 +------------------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 710bc5935..b2813a2d9 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -77,40 +77,11 @@ class BeaconBlockBody(phase0.BeaconBlockBody): ```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 - eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH] - eth1_deposit_index: uint64 +class BeaconState(phase0.BeaconState): # [Added in Merge] hash of the root of application state application_state_root: Bytes32 # [Added in Merge] 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 From 7e6ac4e7f7fe6d7399c72b54072f7a2313df1c30 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Mon, 22 Mar 2021 21:20:05 +0600 Subject: [PATCH 15/24] Boolean -> boolean --- specs/merge/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index b2813a2d9..ca35679ed 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -127,7 +127,7 @@ class ApplicationPayload(Container): #### `is_transition_completed` ```python -def is_transition_completed(state: BeaconState) -> Boolean: +def is_transition_completed(state: BeaconState) -> boolean: state.application_block_hash != Bytes32() ``` From 96de910b22ba2364f1badc61f0e0c8b5dbf110bc Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Mon, 22 Mar 2021 21:55:35 +0600 Subject: [PATCH 16/24] Distinguish invalid and not processed transition block --- specs/merge/fork-choice.md | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/specs/merge/fork-choice.md b/specs/merge/fork-choice.md index 150dba91c..523316dfc 100644 --- a/specs/merge/fork-choice.md +++ b/specs/merge/fork-choice.md @@ -9,7 +9,8 @@ - [Introduction](#introduction) - [Helpers](#helpers) - - [`get_total_difficulty`](#get_total_difficulty) + - [`PowBlock`](#powblock) + - [`get_pow_block`](#get_pow_block) - [`is_valid_transition_block`](#is_valid_transition_block) - [Updated fork-choice handlers](#updated-fork-choice-handlers) - [`on_block`](#on_block) @@ -25,20 +26,29 @@ This is the modification of the fork choice according to the executable beacon c ### Helpers -#### `get_total_difficulty` +#### `PowBlock` -Let `get_total_difficulty(hash: Bytes32) -> uint256` be the function that returns the total difficulty of the PoW block specified by its hash. +```python +class PowBlock(Container): + is_processed: boolean + is_valid: boolean + total_difficulty: uint256 +``` -*Note*: The function returns `0` if the block is either not yet processed or considered invalid. The latter two cases are considered indistinguishable to the current implementation of JSON-RPC. +#### `get_pow_block` + +Let `get_pow_block(hash: Bytes32) -> PowBlock` be the function that given the hash of the PoW block returns its data. + +*Note*: The `eth_getBlockByHash` JSON-RPC method does not distinguish invalid blocks from blocks that hasn't been processed yet. Either extending of existing method or implementing a new one is required. #### `is_valid_transition_block` Used by fork-choice handler, `on_block` ```python -def is_valid_transition_block(block: BeaconBlock) -> boolean: - total_difficulty = get_total_difficulty(block.body.application_payload.block_hash) - return total_difficulty >= TRANSITION_TOTAL_DIFFICULTY +def is_valid_transition_block(block: PowBlock) -> boolean: + is_total_difficulty_reached = block.total_difficulty >= TRANSITION_TOTAL_DIFFICULTY + return block.is_processed and block.is_valid and is_total_difficulty_reached ``` ### Updated fork-choice handlers @@ -65,7 +75,8 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: # [Added in Merge] Consider delaying the beacon block processing until PoW block is accepted by the application node if is_transition_block(pre_state, block.body): - assert is_valid_transition_block(block) + pow_block = get_pow_block(block.body.application_payload.block_hash) + assert is_valid_transition_block(pow_block) # Check the block is valid and compute the post-state state = pre_state.copy() From ea5f606bd095ad1c569629df1ce2e57b1048ae30 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 24 Mar 2021 16:30:29 +0600 Subject: [PATCH 17/24] Address various cleanups and formatting suggestions --- specs/merge/beacon-chain.md | 32 +++++++++++++++----------------- specs/merge/fork-choice.md | 6 +++--- specs/merge/validator.md | 29 ++++++++++++++++------------- 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index ca35679ed..c343c2ec9 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -1,6 +1,6 @@ # Ethereum 2.0 The Merge -**Warning:** This document is based on [Phase 0](../phase0/beacon-chain.md) and considered to be rebased to [Altair](../altair/beacon-chain.md) once the latter is shipped. +**Warning:** This document is currently based on [Phase 0](../phase0/beacon-chain.md) but will be rebased to [Altair](../altair/beacon-chain.md) once the latter is shipped. **Notice**: This document is a work-in-progress for researchers and implementers. @@ -36,23 +36,24 @@ ## 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. +This is a patch implementing the executable beacon chain proposal. +It enshrines application-layer execution and validity as a first class citizen at the core of the beacon chain. ## Constants ### Transition + | Name | Value | | - | - | -| `TRANSITION_TOTAL_DIFFICULTY` | _TBD_ | +| `TRANSITION_TOTAL_DIFFICULTY` | **TBD** | ### Execution | Name | Value | | - | - | -| `MAX_BYTES_PER_TRANSACTION_PAYLOAD` | `2**20` | -| `MAX_APPLICATION_TRANSACTIONS` | `2**14` | -| `BYTES_PER_LOGS_BLOOM` | `2**8` | +| `MAX_BYTES_PER_TRANSACTION_PAYLOAD` | `uint64(2**20)` (= 1,048,576) | +| `MAX_APPLICATION_TRANSACTIONS` | `uint64(2**14)` (= 16,384) | +| `BYTES_PER_LOGS_BLOOM` | `uint64(2**8)` (= 256) | ## Containers @@ -73,15 +74,13 @@ class BeaconBlockBody(phase0.BeaconBlockBody): #### `BeaconState` -*Note*: `BeaconState` fields remain unchanged other than addition of `application_state_root` and `application_block_hash`. - +*Note*: `BeaconState` fields remain unchanged other than addition of `application_state_root` and `application_block_hash`. ```python class BeaconState(phase0.BeaconState): - # [Added in Merge] hash of the root of application state - application_state_root: Bytes32 - # [Added in Merge] hash of recent application block - application_block_hash: Bytes32 + # Application-layer + application_state_root: Bytes32 # [New in Merge] + application_block_hash: Bytes32 # [New in Merge] ``` ### New containers @@ -128,7 +127,7 @@ class ApplicationPayload(Container): ```python def is_transition_completed(state: BeaconState) -> boolean: - state.application_block_hash != Bytes32() + return state.application_block_hash != Bytes32() ``` #### `is_transition_block` @@ -170,8 +169,7 @@ The body of the function is implementation dependent. ```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 + Note: This function is designed to be able to be run in parallel with the other `process_block` sub-functions """ if is_transition_completed(state): @@ -182,7 +180,7 @@ def process_application_payload(state: BeaconState, body: BeaconBlockBody) -> No state.application_block_hash = body.application_payload.block_hash elif is_transition_block(state, body): - assert body.application_payload == ApplicationPayload(block_hash = body.application_payload.block_hash) + assert body.application_payload == ApplicationPayload(block_hash=body.application_payload.block_hash) state.application_block_hash = body.application_payload.block_hash else: diff --git a/specs/merge/fork-choice.md b/specs/merge/fork-choice.md index 523316dfc..25f83dbf6 100644 --- a/specs/merge/fork-choice.md +++ b/specs/merge/fork-choice.md @@ -39,11 +39,11 @@ class PowBlock(Container): Let `get_pow_block(hash: Bytes32) -> PowBlock` be the function that given the hash of the PoW block returns its data. -*Note*: The `eth_getBlockByHash` JSON-RPC method does not distinguish invalid blocks from blocks that hasn't been processed yet. Either extending of existing method or implementing a new one is required. +*Note*: The `eth_getBlockByHash` JSON-RPC method does not distinguish invalid blocks from blocks that haven't been processed yet. Either extending this existing method or implementing a new one is required. #### `is_valid_transition_block` -Used by fork-choice handler, `on_block` +Used by fork-choice handler, `on_block`. ```python def is_valid_transition_block(block: PowBlock) -> boolean: @@ -73,7 +73,7 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: # 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 in Merge] Consider delaying the beacon block processing until PoW block is accepted by the application node + # [New in Merge] Consider delaying the beacon block processing until PoW block is accepted by the application node if is_transition_block(pre_state, block.body): pow_block = get_pow_block(block.body.application_payload.block_hash) assert is_valid_transition_block(pow_block) diff --git a/specs/merge/validator.md b/specs/merge/validator.md index d55f2ea3e..4f4c28b3d 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -1,6 +1,6 @@ # Ethereum 2.0 The Merge -**Warning:** This document is based on [Phase 0](../phase0/validator.md) and considered to be rebased to [Altair](../altair/validator.md) once the latter is shipped. +**Warning:** This document is currently based on [Phase 0](../phase0/validator.md) but will be rebased to [Altair](../altair/validator.md) once the latter is shipped. **Notice**: This document is a work-in-progress for researchers and implementers. @@ -16,7 +16,7 @@ - [Block proposal](#block-proposal) - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) - [Application Payload](#application-payload) - - [`ApplicaitonBlock`](#applicaitonblock) + - [`PowBlock`](#powblock) - [`get_pow_chain_head`](#get_pow_chain_head) - [`produce_application_payload`](#produce_application_payload) @@ -43,7 +43,7 @@ All validator responsibilities remain unchanged other than those noted below. Na ##### Application Payload -###### `ApplicaitonBlock` +###### `PowBlock` ```python class PowBlock(Container): block_hash: Bytes32 @@ -59,17 +59,20 @@ Let `get_pow_chain_head() -> PowBlock` be the function that returns the head of Let `produce_application_payload(parent_hash: Bytes32) -> ApplicationPayload` be the function that produces new instance of application payload. The body of this function is implementation dependent. -* Set `block.body.application_payload = get_application_payload(state, randao_reveal)` where: +* Set `block.body.application_payload = get_application_payload(state)` where: ```python def get_application_payload(state: BeaconState) -> ApplicationPayload: - if is_transition_completed(state): - application_parent_hash = state.application_block_hash - return produce_application_payload(application_parent_hash) - - pow_block = get_pow_chain_head() - if pow_block.total_difficulty >= TRANSITION_TOTAL_DIFFICULTY: - return ApplicationPayload(block_hash = pow_block.block_hash) - else: - return ApplicationPayload() + if not is_transition_completed(state): + pow_block = get_pow_chain_head() + if pow_block.total_difficulty < TRANSITION_TOTAL_DIFFICULTY: + # Pre-merge, empty payload + return ApplicationPayload() + else: + # Signify merge via last PoW block_hash and an otherwise empty payload + return ApplicationPayload(block_hash=pow_block.block_hash) + + # Post-merge, normal payload + application_parent_hash = state.application_block_hash + return produce_application_payload(state.application_block_hash) ``` From 63ae9f2bdbe7b54f5a188e6c64facb0fc5563c82 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 24 Mar 2021 20:58:31 +0600 Subject: [PATCH 18/24] Standardise PowBlock between fork-choice and validator --- specs/merge/fork-choice.md | 1 + specs/merge/validator.md | 8 -------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/specs/merge/fork-choice.md b/specs/merge/fork-choice.md index 25f83dbf6..c022de4ab 100644 --- a/specs/merge/fork-choice.md +++ b/specs/merge/fork-choice.md @@ -30,6 +30,7 @@ This is the modification of the fork choice according to the executable beacon c ```python class PowBlock(Container): + block_hash: Bytes32 is_processed: boolean is_valid: boolean total_difficulty: uint256 diff --git a/specs/merge/validator.md b/specs/merge/validator.md index 4f4c28b3d..110a2a883 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -16,7 +16,6 @@ - [Block proposal](#block-proposal) - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) - [Application Payload](#application-payload) - - [`PowBlock`](#powblock) - [`get_pow_chain_head`](#get_pow_chain_head) - [`produce_application_payload`](#produce_application_payload) @@ -43,13 +42,6 @@ All validator responsibilities remain unchanged other than those noted below. Na ##### Application Payload -###### `PowBlock` -```python -class PowBlock(Container): - block_hash: Bytes32 - total_difficulty: uint256 -``` - ###### `get_pow_chain_head` Let `get_pow_chain_head() -> PowBlock` be the function that returns the head of the PoW chain. The body of the function is implementation specific. From ee5ecf8e2b1980daecde0732ab5e7ceb6fd7cd4c Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 25 Mar 2021 17:49:13 +0600 Subject: [PATCH 19/24] Address a new portion of comments and fixes --- specs/merge/beacon-chain.md | 11 ++++------- specs/merge/fork-choice.md | 6 ++++-- specs/merge/validator.md | 14 +++++++------- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index c343c2ec9..b8362771e 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -69,7 +69,7 @@ order and append any additional fields to the end. ```python class BeaconBlockBody(phase0.BeaconBlockBody): - application_payload: ApplicationPayload # [Added in Merge] application payload + application_payload: ApplicationPayload # [New in Merge] application payload ``` #### `BeaconState` @@ -79,8 +79,8 @@ class BeaconBlockBody(phase0.BeaconBlockBody): ```python class BeaconState(phase0.BeaconState): # Application-layer - application_state_root: Bytes32 # [New in Merge] - application_block_hash: Bytes32 # [New in Merge] + application_state_root: Bytes32 # [New in Merge] + application_block_hash: Bytes32 # [New in Merge] ``` ### New containers @@ -96,7 +96,7 @@ class Transaction(Container): gas_limit: uint64 recipient: Bytes20 value: uint256 - input: List[Bytes1, MAX_BYTES_PER_TRANSACTION_PAYLOAD] + data: List[byte, MAX_BYTES_PER_TRANSACTION_PAYLOAD] v: uint256 r: uint256 s: uint256 @@ -115,7 +115,6 @@ class ApplicationPayload(Container): 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] ``` @@ -178,11 +177,9 @@ def process_application_payload(state: BeaconState, body: BeaconBlockBody) -> No state.application_state_root = body.application_payload.state_root state.application_block_hash = body.application_payload.block_hash - elif is_transition_block(state, body): assert body.application_payload == ApplicationPayload(block_hash=body.application_payload.block_hash) state.application_block_hash = body.application_payload.block_hash - else: assert body.application_payload == ApplicationPayload() ``` diff --git a/specs/merge/fork-choice.md b/specs/merge/fork-choice.md index c022de4ab..d299a8247 100644 --- a/specs/merge/fork-choice.md +++ b/specs/merge/fork-choice.md @@ -49,7 +49,7 @@ Used by fork-choice handler, `on_block`. ```python def is_valid_transition_block(block: PowBlock) -> boolean: is_total_difficulty_reached = block.total_difficulty >= TRANSITION_TOTAL_DIFFICULTY - return block.is_processed and block.is_valid and is_total_difficulty_reached + return block.is_valid and is_total_difficulty_reached ``` ### Updated fork-choice handlers @@ -74,9 +74,11 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: # 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 - # [New in Merge] Consider delaying the beacon block processing until PoW block is accepted by the application node + # [New in Merge] if is_transition_block(pre_state, block.body): pow_block = get_pow_block(block.body.application_payload.block_hash) + # Delay consideration of block until PoW block is processed by the PoW node + assert pow_block.is_processed assert is_valid_transition_block(pow_block) # Check the block is valid and compute the post-state diff --git a/specs/merge/validator.md b/specs/merge/validator.md index 110a2a883..49f7f6137 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -56,13 +56,13 @@ The body of this function is implementation dependent. ```python def get_application_payload(state: BeaconState) -> ApplicationPayload: if not is_transition_completed(state): - pow_block = get_pow_chain_head() - if pow_block.total_difficulty < TRANSITION_TOTAL_DIFFICULTY: - # Pre-merge, empty payload - return ApplicationPayload() - else: - # Signify merge via last PoW block_hash and an otherwise empty payload - return ApplicationPayload(block_hash=pow_block.block_hash) + pow_block = get_pow_chain_head() + if pow_block.total_difficulty < TRANSITION_TOTAL_DIFFICULTY: + # Pre-merge, empty payload + return ApplicationPayload() + else: + # Signify merge via last PoW block_hash and an otherwise empty payload + return ApplicationPayload(block_hash=pow_block.block_hash) # Post-merge, normal payload application_parent_hash = state.application_block_hash From a23bde347be4184877a22788d81f32d541ce3202 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 25 Mar 2021 17:51:32 +0600 Subject: [PATCH 20/24] Bytes1 to byte in ApplicationPayload.logs_bloom --- specs/merge/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index b8362771e..0d434db4f 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -114,7 +114,7 @@ class ApplicationPayload(Container): gas_limit: uint64 gas_used: uint64 receipt_root: Bytes32 - logs_bloom: Vector[Bytes1, BYTES_PER_LOGS_BLOOM] + logs_bloom: Vector[byte, BYTES_PER_LOGS_BLOOM] transactions: List[Transaction, MAX_APPLICATION_TRANSACTIONS] ``` From 260a0a527378505a333abbcab083502fe93d5e4b Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 25 Mar 2021 17:53:15 +0600 Subject: [PATCH 21/24] Polish merge/fork-choice.md --- specs/merge/fork-choice.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/merge/fork-choice.md b/specs/merge/fork-choice.md index d299a8247..430128c12 100644 --- a/specs/merge/fork-choice.md +++ b/specs/merge/fork-choice.md @@ -76,8 +76,8 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: # [New in Merge] if is_transition_block(pre_state, block.body): - pow_block = get_pow_block(block.body.application_payload.block_hash) # Delay consideration of block until PoW block is processed by the PoW node + pow_block = get_pow_block(block.body.application_payload.block_hash) assert pow_block.is_processed assert is_valid_transition_block(pow_block) From 81a2c2c2b5b8536adfaddd8ff42f7372a1122b4f Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 25 Mar 2021 18:41:00 +0600 Subject: [PATCH 22/24] Use ByteList[N] and ByteVector[N] types --- specs/merge/beacon-chain.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 0d434db4f..694df161b 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -96,7 +96,7 @@ class Transaction(Container): gas_limit: uint64 recipient: Bytes20 value: uint256 - data: List[byte, MAX_BYTES_PER_TRANSACTION_PAYLOAD] + data: ByteList[MAX_BYTES_PER_TRANSACTION_PAYLOAD] v: uint256 r: uint256 s: uint256 @@ -114,7 +114,7 @@ class ApplicationPayload(Container): gas_limit: uint64 gas_used: uint64 receipt_root: Bytes32 - logs_bloom: Vector[byte, BYTES_PER_LOGS_BLOOM] + logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] transactions: List[Transaction, MAX_APPLICATION_TRANSACTIONS] ``` From 41a087a78dd4ff88a2222b709843bf4472b9f3c9 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 26 Mar 2021 13:12:53 -0600 Subject: [PATCH 23/24] minor edits from code review Co-authored-by: terence tsao --- specs/merge/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 694df161b..9ddd448a4 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -87,7 +87,7 @@ class BeaconState(phase0.BeaconState): #### `Transaction` -Application transaction fields structured as an SSZ object for inclusion in an `ApplicationPayload` contained within a `BeaconBlock`. +Application transaction fields structured as an SSZ object for inclusion in an `ApplicationPayload` contained within a `BeaconBlockBody`. ```python class Transaction(Container): From 223aba3e782aa5aee755c784ab7a79b09995fc69 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 26 Mar 2021 13:20:23 -0600 Subject: [PATCH 24/24] byte-list for opaque transaction payload --- specs/merge/beacon-chain.md | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 9ddd448a4..ce6df0dd9 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -39,6 +39,14 @@ This is a patch implementing the executable beacon chain proposal. It enshrines application-layer execution and validity as a first class citizen at the core of the beacon chain. +## Custom types + +We define the following Python custom types for type hinting and readability: + +| Name | SSZ equivalent | Description | +| - | - | - | +| `OpaqueTransaction` | `ByteList[MAX_BYTES_PER_OPAQUE_TRANSACTION]` | a byte-list containing a single [typed transaction envelope](https://eips.ethereum.org/EIPS/eip-2718#opaque-byte-array-rather-than-an-rlp-array) structured as `TransactionType \|\| TransactionPayload` | + ## Constants ### Transition @@ -51,11 +59,10 @@ It enshrines application-layer execution and validity as a first class citizen a | Name | Value | | - | - | -| `MAX_BYTES_PER_TRANSACTION_PAYLOAD` | `uint64(2**20)` (= 1,048,576) | +| `MAX_BYTES_PER_OPAQUE_TRANSACTION` | `uint64(2**20)` (= 1,048,576) | | `MAX_APPLICATION_TRANSACTIONS` | `uint64(2**14)` (= 16,384) | | `BYTES_PER_LOGS_BLOOM` | `uint64(2**8)` (= 256) | - ## Containers ### Extended containers @@ -85,26 +92,9 @@ class BeaconState(phase0.BeaconState): ### New containers -#### `Transaction` - -Application transaction fields structured as an SSZ object for inclusion in an `ApplicationPayload` contained within a `BeaconBlockBody`. - -```python -class Transaction(Container): - nonce: uint64 - gas_price: uint256 - gas_limit: uint64 - recipient: Bytes20 - value: uint256 - data: ByteList[MAX_BYTES_PER_TRANSACTION_PAYLOAD] - v: uint256 - r: uint256 - s: uint256 -``` - #### `ApplicationPayload` -The application payload included in a `BeaconBlock`. +The application payload included in a `BeaconBlockBody`. ```python class ApplicationPayload(Container): @@ -115,7 +105,7 @@ class ApplicationPayload(Container): gas_used: uint64 receipt_root: Bytes32 logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] - transactions: List[Transaction, MAX_APPLICATION_TRANSACTIONS] + transactions: List[OpaqueTransaction, MAX_APPLICATION_TRANSACTIONS] ``` ## Helper functions