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() ```