From b530e017619b79b7a8c4d9105177c3425d5d9770 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Mon, 20 Sep 2021 20:57:45 +0600 Subject: [PATCH 01/26] Update calls to execution_engine --- setup.py | 19 ++-- specs/merge/beacon-chain.md | 28 ++++-- specs/merge/fork-choice.md | 32 ++----- specs/merge/validator.md | 92 +++++++++++++------ .../test_process_execution_payload.py | 2 +- 5 files changed, 107 insertions(+), 66 deletions(-) diff --git a/setup.py b/setup.py index 6e55cfdde..1d647c2a1 100644 --- a/setup.py +++ b/setup.py @@ -522,16 +522,23 @@ def get_pow_chain_head() -> PowBlock: class NoopExecutionEngine(ExecutionEngine): - def on_payload(self, execution_payload: ExecutionPayload) -> bool: + def execute_payload(self, execution_payload: ExecutionPayload) -> bool: return True - def set_head(self, block_hash: Hash32) -> bool: - return True + def consensus_validated(self: ExecutionEngine, execution_payload: ExecutionPayload) -> None: + pass - def finalize_block(self, block_hash: Hash32) -> bool: - return True + def forkchoice_updated(self: ExecutionEngine, head_block_hash: Hash32, finalized_block_hash: Hash32) -> None: + pass - def assemble_block(self, block_hash: Hash32, timestamp: uint64, random: Bytes32) -> ExecutionPayload: + def prepare_payload(self: ExecutionEngine, + parent_hash: Hash32, + timestamp: uint64, + random: Bytes32, + feeRecipient: Bytes20) -> uint64: + raise NotImplementedError("no default block production") + + def get_payload(self: ExecutionEngine, payload_id: uint64) -> ExecutionPayload: raise NotImplementedError("no default block production") diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 03bf01155..a5fc4101d 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -31,7 +31,8 @@ - [`compute_timestamp_at_slot`](#compute_timestamp_at_slot) - [Beacon chain state transition function](#beacon-chain-state-transition-function) - [Execution engine](#execution-engine) - - [`on_payload`](#on_payload) + - [`execute_payload`](#execute_payload) + - [`consensus_validated`](#consensus_validated) - [Block processing](#block-processing) - [Execution payload processing](#execution-payload-processing) - [`is_valid_gas_limit`](#is_valid_gas_limit) @@ -237,19 +238,31 @@ def compute_timestamp_at_slot(state: BeaconState, slot: Slot) -> uint64: The implementation-dependent `ExecutionEngine` protocol encapsulates the execution sub-system logic via: * a state object `self.execution_state` of type `ExecutionState` -* a state transition function `self.on_payload` which mutates `self.execution_state` +* a state transition function `self.execute_payload` which applies changes to the `self.execution_state` +* a method `self.consensus_validated` which notifies that the block holding the execution payload +is valid with respect to the consensus rule set -#### `on_payload` +#### `execute_payload` ```python -def on_payload(self: ExecutionEngine, execution_payload: ExecutionPayload) -> bool: +def execute_payload(self: ExecutionEngine, execution_payload: ExecutionPayload) -> bool: """ Returns ``True`` iff ``execution_payload`` is valid with respect to ``self.execution_state``. """ ... ``` -The above function is accessed through the `EXECUTION_ENGINE` module which instantiates the `ExecutionEngine` protocol. +#### `consensus_validated` + +```python +def consensus_validated(self: ExecutionEngine, execution_payload: ExecutionPayload) -> None: + ... +``` + +The above functions are accessed through the `EXECUTION_ENGINE` module which instantiates the `ExecutionEngine` protocol. + +The body of each of these functions is implementation dependent. +The Engine API may be used to implement them with an external execution engine. ### Block processing @@ -264,6 +277,9 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: process_eth1_data(state, block.body) process_operations(state, block.body) process_sync_aggregate(state, block.body.sync_aggregate) + if is_execution_enabled(state, block.body): + # Notify the block is valid with respect to the consensus state transition function + EXECUTION_ENGINE.consensus_validated(block.body.execution_payload) # [New in Merge] ``` ### Execution payload processing @@ -304,7 +320,7 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe # Verify timestamp assert payload.timestamp == compute_timestamp_at_slot(state, state.slot) # Verify the execution payload is valid - assert execution_engine.on_payload(payload) + assert execution_engine.execute_payload(payload) # Cache execution payload header state.latest_execution_payload_header = ExecutionPayloadHeader( parent_hash=payload.parent_hash, diff --git a/specs/merge/fork-choice.md b/specs/merge/fork-choice.md index 8051ab3eb..d8b3426ea 100644 --- a/specs/merge/fork-choice.md +++ b/specs/merge/fork-choice.md @@ -10,8 +10,7 @@ - [Introduction](#introduction) - [Protocols](#protocols) - [`ExecutionEngine`](#executionengine) - - [`set_head`](#set_head) - - [`finalize_block`](#finalize_block) + - [`forkchoice_updated`](#forkchoice_updated) - [Helpers](#helpers) - [`PowBlock`](#powblock) - [`get_pow_block`](#get_pow_block) @@ -34,36 +33,19 @@ This is the modification of the fork choice according to the executable beacon c The following methods are added to the `ExecutionEngine` protocol for use in the fork choice: -#### `set_head` +#### `forkchoice_updated` -Re-organizes the execution payload chain and corresponding state to make `block_hash` the head. - -The body of this function is implementation dependent. -The Consensus API may be used to implement this with an external execution engine. +* Re-organizes the execution payload chain and corresponding state to make `head_block_hash` the head. +* Applies finality to the execution state: it irreversibly persists the chain of all execution payloads +and corresponding state, up to and including `finalized_block_hash`. ```python -def set_head(self: ExecutionEngine, block_hash: Hash32) -> bool: - """ - Returns True if the ``block_hash`` was successfully set as head of the execution payload chain. - """ +def forkchoice_updated(self: ExecutionEngine, head_block_hash: Hash32, finalized_block_hash: Hash32) -> None: ... ``` -#### `finalize_block` - -Applies finality to the execution state: it irreversibly persists the chain of all execution payloads -and corresponding state, up to and including `block_hash`. - The body of this function is implementation dependent. -The Consensus API may be used to implement this with an external execution engine. - -```python -def finalize_block(self: ExecutionEngine, block_hash: Hash32) -> bool: - """ - Returns True if the data up to and including ``block_hash`` was successfully finalized. - """ - ... -``` +The Engine API may be used to implement this with an external execution engine. ## Helpers diff --git a/specs/merge/validator.md b/specs/merge/validator.md index 645e1967b..7dfcecbc1 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -12,11 +12,12 @@ - [Prerequisites](#prerequisites) - [Protocols](#protocols) - [`ExecutionEngine`](#executionengine) - - [`assemble_block`](#assemble_block) + - [`prepare_payload`](#prepare_payload) + - [`get_payload`](#get_payload) - [Beacon chain responsibilities](#beacon-chain-responsibilities) - [Block proposal](#block-proposal) - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) - - [Execution Payload](#execution-payload) + - [ExecutionPayload](#executionpayload) @@ -39,19 +40,39 @@ Please see related Beacon Chain doc before continuing and use them as a referenc The following methods are added to the `ExecutionEngine` protocol for use as a validator: -#### `assemble_block` +#### `prepare_payload` -Produces a new instance of an execution payload, with the specified `timestamp`, -on top of the execution payload chain tip identified by `block_hash`. - -The body of this function is implementation dependent. -The Consensus API may be used to implement this with an external execution engine. +Given the set of execution payload attributes initiates a process of building an execution payload +on top of the execution chain tip identified by `parent_hash`. ```python -def assemble_block(self: ExecutionEngine, block_hash: Hash32, timestamp: uint64, random: Bytes32) -> ExecutionPayload: +def prepare_payload(self: ExecutionEngine, + parent_hash: Hash32, + timestamp: uint64, + random: Bytes32, + fee_recipient: Bytes20) -> uint64: + """ + Returns ``payload_id`` that is used to obtain the execution payload in a subsequent ``get_payload`` call. + """ ... ``` +#### `get_payload` + +Given the `payload_id` returns the most recent version of the execution payload that +has been built since the corresponding call to `prepare_payload` method. + +```python +def get_payload(self: ExecutionEngine, payload_id: uint64) -> ExecutionPayload: + """ + Returns ``execution_payload`` object. + """ + ... +``` + +The body of each of these functions is implementation dependent. +The Engine API may be used to implement them with an external execution engine. + ## Beacon chain responsibilities All validator responsibilities remain unchanged other than those noted below. Namely, the transition block handling and the addition of `ExecutionPayload`. @@ -60,9 +81,14 @@ All validator responsibilities remain unchanged other than those noted below. Na #### Constructing the `BeaconBlockBody` -##### Execution Payload +##### ExecutionPayload -* Set `block.body.execution_payload = get_execution_payload(state, execution_engine, pow_chain)` where: +To obtain an execution payload a proposer of a block must take the following actions: + +1. Set `payload_id = prepare_execution_payload(state, pow_chain, fee_recipient, execution_engine)`, where: + * `state` is the state for which `is_proposer(state, validator_index)` returns `True` + * `pow_chain` is a list that abstractly represents all blocks in the PoW chain + * `fee_recipient` is the value suggested to be used for the `coinbase` field of the execution payload ```python def get_pow_block_at_total_difficulty(total_difficulty: uint256, pow_chain: Sequence[PowBlock]) -> Optional[PowBlock]: @@ -75,27 +101,37 @@ def get_pow_block_at_total_difficulty(total_difficulty: uint256, pow_chain: Sequ return None -def produce_execution_payload(state: BeaconState, - parent_hash: Hash32, - execution_engine: ExecutionEngine) -> ExecutionPayload: - timestamp = compute_timestamp_at_slot(state, state.slot) - randao_mix = get_randao_mix(state, get_current_epoch(state)) - return execution_engine.assemble_block(parent_hash, timestamp, randao_mix) - - -def get_execution_payload(state: BeaconState, - execution_engine: ExecutionEngine, - pow_chain: Sequence[PowBlock]) -> ExecutionPayload: +def prepare_execution_payload(state: BeaconState, + pow_chain: Sequence[PowBlock], + fee_recipient: Bytes20, + execution_engine: ExecutionEngine) -> Optional[uint64]: if not is_merge_complete(state): terminal_pow_block = get_pow_block_at_total_difficulty(TERMINAL_TOTAL_DIFFICULTY, pow_chain) if terminal_pow_block is None: - # Pre-merge, empty payload - return ExecutionPayload() + # Pre-merge, no prepare payload call is needed + return None else: # Signify merge via producing on top of the last PoW block - return produce_execution_payload(state, terminal_pow_block.block_hash, execution_engine) + parent_hash = terminal_pow_block.block_hash + else: + # Post-merge, normal payload + parent_hash = state.latest_execution_payload_header.block_hash - # Post-merge, normal payload - parent_hash = state.latest_execution_payload_header.block_hash - return produce_execution_payload(state, parent_hash, execution_engine) + timestamp = compute_timestamp_at_slot(state, state.slot) + random = get_randao_mix(state, get_current_epoch(state)) + return execution_engine.prepare_payload(parent_hash, timestamp, random, fee_recipient) ``` + +2. Set `block.body.execution_payload = get_execution_payload(payload_id, execution_engine)`, where: + +```python +def get_execution_payload(payload_id: Optional[uint64], execution_engine: ExecutionEngine) -> ExecutionPayload: + if payload_id is None: + # Pre-merge, empty payload + return ExecutionPayload() + else: + return execution_engine.get_payload(payload_id) +``` + +*Note*: It is recommended for a validator to call `prepare_execution_payload` as soon as input parameters become known, +and make subsequent calls to this function if any of these parameters has been updated. diff --git a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py index 4c68034d4..a3aadc470 100644 --- a/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/merge/block_processing/test_process_execution_payload.py @@ -25,7 +25,7 @@ def run_execution_payload_processing(spec, state, execution_payload, valid=True, called_new_block = False class TestEngine(spec.NoopExecutionEngine): - def on_payload(self, payload) -> bool: + def execute_payload(self, payload) -> bool: nonlocal called_new_block, execution_valid called_new_block = True assert payload == execution_payload From 18bfe3555f7b6b7f856096af0678085fdde30357 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 22 Sep 2021 17:45:26 +0600 Subject: [PATCH 02/26] Apply suggestions from code review Co-authored-by: Danny Ryan --- specs/merge/beacon-chain.md | 2 +- specs/merge/fork-choice.md | 1 + specs/merge/validator.md | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index a5fc4101d..6a5a2730b 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -239,7 +239,7 @@ The implementation-dependent `ExecutionEngine` protocol encapsulates the executi * a state object `self.execution_state` of type `ExecutionState` * a state transition function `self.execute_payload` which applies changes to the `self.execution_state` -* a method `self.consensus_validated` which notifies that the block holding the execution payload +* a method `self.consensus_validated` which signals that the beacon block containing the execution payload is valid with respect to the consensus rule set #### `execute_payload` diff --git a/specs/merge/fork-choice.md b/specs/merge/fork-choice.md index d8b3426ea..15d47a650 100644 --- a/specs/merge/fork-choice.md +++ b/specs/merge/fork-choice.md @@ -35,6 +35,7 @@ The following methods are added to the `ExecutionEngine` protocol for use in the #### `forkchoice_updated` +This method performs two actions *atomically*: * Re-organizes the execution payload chain and corresponding state to make `head_block_hash` the head. * Applies finality to the execution state: it irreversibly persists the chain of all execution payloads and corresponding state, up to and including `finalized_block_hash`. diff --git a/specs/merge/validator.md b/specs/merge/validator.md index 7dfcecbc1..a84384dd6 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -42,7 +42,7 @@ The following methods are added to the `ExecutionEngine` protocol for use as a v #### `prepare_payload` -Given the set of execution payload attributes initiates a process of building an execution payload +Given the set of execution payload attributes, `prepare_payload` initiates a process of building an execution payload on top of the execution chain tip identified by `parent_hash`. ```python @@ -59,7 +59,7 @@ def prepare_payload(self: ExecutionEngine, #### `get_payload` -Given the `payload_id` returns the most recent version of the execution payload that +Given the `payload_id`, `get_payload` returns the most recent version of the execution payload that has been built since the corresponding call to `prepare_payload` method. ```python @@ -83,7 +83,7 @@ All validator responsibilities remain unchanged other than those noted below. Na ##### ExecutionPayload -To obtain an execution payload a proposer of a block must take the following actions: +To obtain an execution payload, the proposer of a block must take the following actions: 1. Set `payload_id = prepare_execution_payload(state, pow_chain, fee_recipient, execution_engine)`, where: * `state` is the state for which `is_proposer(state, validator_index)` returns `True` From 06107ce7d8be3a66ef40803bca30c808d1cb13c7 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 22 Sep 2021 18:10:57 +0600 Subject: [PATCH 03/26] Polish the description of execution engine functions --- specs/merge/beacon-chain.md | 12 ++++++------ specs/merge/fork-choice.md | 10 +++++----- specs/merge/validator.md | 10 +++++----- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 3e107b7a9..a1ab61848 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -242,9 +242,14 @@ The implementation-dependent `ExecutionEngine` protocol encapsulates the executi * a state object `self.execution_state` of type `ExecutionState` * a state transition function `self.execute_payload` which applies changes to the `self.execution_state` -* a method `self.consensus_validated` which signals that the beacon block containing the execution payload +* a function `self.consensus_validated` which signals that the beacon block containing the execution payload is valid with respect to the consensus rule set +*Note*: `execute_payload` and `consensus_validated` are functions accessed through the `EXECUTION_ENGINE` module which instantiates the `ExecutionEngine` protocol. + +The body of each of these functions is implementation dependent. +The Engine API may be used to implement them with an external execution engine. + #### `execute_payload` ```python @@ -262,11 +267,6 @@ def consensus_validated(self: ExecutionEngine, execution_payload: ExecutionPaylo ... ``` -The above functions are accessed through the `EXECUTION_ENGINE` module which instantiates the `ExecutionEngine` protocol. - -The body of each of these functions is implementation dependent. -The Engine API may be used to implement them with an external execution engine. - ### Block processing *Note*: The call to the `process_execution_payload` must happen before the call to the `process_randao` as the former depends on the `randao_mix` computed with the reveal of the previous block. diff --git a/specs/merge/fork-choice.md b/specs/merge/fork-choice.md index 15d47a650..5adeded16 100644 --- a/specs/merge/fork-choice.md +++ b/specs/merge/fork-choice.md @@ -31,11 +31,14 @@ This is the modification of the fork choice according to the executable beacon c ### `ExecutionEngine` -The following methods are added to the `ExecutionEngine` protocol for use in the fork choice: +*Note*: The `forkchoice_updated` function is added to the `ExecutionEngine` protocol to signal the fork choice updates. + +The body of this function is implementation dependent. +The Engine API may be used to implement it with an external execution engine. #### `forkchoice_updated` -This method performs two actions *atomically*: +This function performs two actions *atomically*: * Re-organizes the execution payload chain and corresponding state to make `head_block_hash` the head. * Applies finality to the execution state: it irreversibly persists the chain of all execution payloads and corresponding state, up to and including `finalized_block_hash`. @@ -45,9 +48,6 @@ def forkchoice_updated(self: ExecutionEngine, head_block_hash: Hash32, finalized ... ``` -The body of this function is implementation dependent. -The Engine API may be used to implement this with an external execution engine. - ## Helpers ### `PowBlock` diff --git a/specs/merge/validator.md b/specs/merge/validator.md index a84384dd6..aa5efc8ed 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -38,7 +38,10 @@ Please see related Beacon Chain doc before continuing and use them as a referenc ### `ExecutionEngine` -The following methods are added to the `ExecutionEngine` protocol for use as a validator: +*Note*: `prepare_payload` and `get_payload` functions are added to the `ExecutionEngine` protocol for use as a validator. + +The body of each of these functions is implementation dependent. +The Engine API may be used to implement them with an external execution engine. #### `prepare_payload` @@ -70,9 +73,6 @@ def get_payload(self: ExecutionEngine, payload_id: uint64) -> ExecutionPayload: ... ``` -The body of each of these functions is implementation dependent. -The Engine API may be used to implement them with an external execution engine. - ## Beacon chain responsibilities All validator responsibilities remain unchanged other than those noted below. Namely, the transition block handling and the addition of `ExecutionPayload`. @@ -134,4 +134,4 @@ def get_execution_payload(payload_id: Optional[uint64], execution_engine: Execut ``` *Note*: It is recommended for a validator to call `prepare_execution_payload` as soon as input parameters become known, -and make subsequent calls to this function if any of these parameters has been updated. +and make subsequent calls to this function when any of these parameters gets updated. From 24bacafeee3511b70741c7e7d4196856bcf9eb11 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 22 Sep 2021 23:52:15 +0600 Subject: [PATCH 04/26] Update consensus_validated description --- setup.py | 4 ++-- specs/merge/beacon-chain.md | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index 1d647c2a1..6d75caeb1 100644 --- a/setup.py +++ b/setup.py @@ -522,10 +522,10 @@ def get_pow_chain_head() -> PowBlock: class NoopExecutionEngine(ExecutionEngine): - def execute_payload(self, execution_payload: ExecutionPayload) -> bool: + def execute_payload(self: ExecutionEngine, execution_payload: ExecutionPayload) -> bool: return True - def consensus_validated(self: ExecutionEngine, execution_payload: ExecutionPayload) -> None: + def consensus_validated(self: ExecutionEngine, block_hash: Hash32, valid: bool) -> None: pass def forkchoice_updated(self: ExecutionEngine, head_block_hash: Hash32, finalized_block_hash: Hash32) -> None: diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index a1ab61848..eccb7f047 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -263,10 +263,15 @@ def execute_payload(self: ExecutionEngine, execution_payload: ExecutionPayload) #### `consensus_validated` ```python -def consensus_validated(self: ExecutionEngine, execution_payload: ExecutionPayload) -> None: +def consensus_validated(self: ExecutionEngine, block_hash: Hash32, valid: bool) -> None: ... ``` +The call of this function depends on the result of the state transition and must be done when call to the [`state_transition`](../phase0/beacon-chain.md#beacon-chain-state-transition-function) function finishes. The value of the `valid` parameter must be set as follows: + +* `True` if `state_transition` function call succeedes +* `False` if `state_transition` function call fails + ### Block processing *Note*: The call to the `process_execution_payload` must happen before the call to the `process_randao` as the former depends on the `randao_mix` computed with the reveal of the previous block. @@ -280,9 +285,6 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: process_eth1_data(state, block.body) process_operations(state, block.body) process_sync_aggregate(state, block.body.sync_aggregate) - if is_execution_enabled(state, block.body): - # Notify the block is valid with respect to the consensus state transition function - EXECUTION_ENGINE.consensus_validated(block.body.execution_payload) # [New in Merge] ``` ### Execution payload processing From 38a4fd907e941d8aabb17fdc3090eb39002c33d6 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 22 Sep 2021 23:56:10 +0600 Subject: [PATCH 05/26] Fix spelling Co-authored-by: Hsiao-Wei Wang --- specs/merge/validator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/merge/validator.md b/specs/merge/validator.md index aa5efc8ed..115b992b0 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -68,7 +68,7 @@ has been built since the corresponding call to `prepare_payload` method. ```python def get_payload(self: ExecutionEngine, payload_id: uint64) -> ExecutionPayload: """ - Returns ``execution_payload`` object. + Return ``execution_payload`` object. """ ... ``` From f4bd37a10859206d47a1dd0754e4da8067d72544 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 22 Sep 2021 23:58:04 +0600 Subject: [PATCH 06/26] Fix spelling 2 --- 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 eccb7f047..a99036fe5 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -269,7 +269,7 @@ def consensus_validated(self: ExecutionEngine, block_hash: Hash32, valid: bool) The call of this function depends on the result of the state transition and must be done when call to the [`state_transition`](../phase0/beacon-chain.md#beacon-chain-state-transition-function) function finishes. The value of the `valid` parameter must be set as follows: -* `True` if `state_transition` function call succeedes +* `True` if `state_transition` function call succeeds * `False` if `state_transition` function call fails ### Block processing From eada985e9e10c40da7cd9bf773920d0ab93aac7e Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 23 Sep 2021 13:16:18 +0600 Subject: [PATCH 07/26] Better wording for consensus_validated 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 a99036fe5..4a1c9c42f 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -267,7 +267,7 @@ def consensus_validated(self: ExecutionEngine, block_hash: Hash32, valid: bool) ... ``` -The call of this function depends on the result of the state transition and must be done when call to the [`state_transition`](../phase0/beacon-chain.md#beacon-chain-state-transition-function) function finishes. The value of the `valid` parameter must be set as follows: +The inputs to this function depend on the result of the state transition. A call to `consensus_validated` must be made after the [`state_transition`](../phase0/beacon-chain.md#beacon-chain-state-transition-function) function finishes. The value of the `valid` parameter must be set as follows: * `True` if `state_transition` function call succeeds * `False` if `state_transition` function call fails From c815f695cd01dbdfe79d80d06bb7570fa38bf7db Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 23 Sep 2021 13:17:55 +0600 Subject: [PATCH 08/26] Polish comment in get_payload body Co-authored-by: Hsiao-Wei Wang --- specs/merge/validator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/merge/validator.md b/specs/merge/validator.md index 115b992b0..4500d610c 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -55,7 +55,7 @@ def prepare_payload(self: ExecutionEngine, random: Bytes32, fee_recipient: Bytes20) -> uint64: """ - Returns ``payload_id`` that is used to obtain the execution payload in a subsequent ``get_payload`` call. + Return ``payload_id`` that is used to obtain the execution payload in a subsequent ``get_payload`` call. """ ... ``` From 7ef938da772886c31cb655e4b4fbd989188a2afb Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 23 Sep 2021 13:37:52 +0600 Subject: [PATCH 09/26] Add notify_ prefix to EE functions sending notifications --- setup.py | 4 ++-- specs/merge/beacon-chain.md | 12 ++++++------ specs/merge/fork-choice.md | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/setup.py b/setup.py index 6d75caeb1..39eccad53 100644 --- a/setup.py +++ b/setup.py @@ -525,10 +525,10 @@ class NoopExecutionEngine(ExecutionEngine): def execute_payload(self: ExecutionEngine, execution_payload: ExecutionPayload) -> bool: return True - def consensus_validated(self: ExecutionEngine, block_hash: Hash32, valid: bool) -> None: + def notify_consensus_validated(self: ExecutionEngine, block_hash: Hash32, valid: bool) -> None: pass - def forkchoice_updated(self: ExecutionEngine, head_block_hash: Hash32, finalized_block_hash: Hash32) -> None: + def notify_forkchoice_updated(self: ExecutionEngine, head_block_hash: Hash32, finalized_block_hash: Hash32) -> None: pass def prepare_payload(self: ExecutionEngine, diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 4a1c9c42f..9be89fcea 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -32,7 +32,7 @@ - [Beacon chain state transition function](#beacon-chain-state-transition-function) - [Execution engine](#execution-engine) - [`execute_payload`](#execute_payload) - - [`consensus_validated`](#consensus_validated) + - [`notify_consensus_validated`](#notify_consensus_validated) - [Block processing](#block-processing) - [Execution payload processing](#execution-payload-processing) - [`is_valid_gas_limit`](#is_valid_gas_limit) @@ -242,10 +242,10 @@ The implementation-dependent `ExecutionEngine` protocol encapsulates the executi * a state object `self.execution_state` of type `ExecutionState` * a state transition function `self.execute_payload` which applies changes to the `self.execution_state` -* a function `self.consensus_validated` which signals that the beacon block containing the execution payload +* a function `self.notify_consensus_validated` which signals that the beacon block containing the execution payload is valid with respect to the consensus rule set -*Note*: `execute_payload` and `consensus_validated` are functions accessed through the `EXECUTION_ENGINE` module which instantiates the `ExecutionEngine` protocol. +*Note*: `execute_payload` and `notify_consensus_validated` are functions accessed through the `EXECUTION_ENGINE` module which instantiates the `ExecutionEngine` protocol. The body of each of these functions is implementation dependent. The Engine API may be used to implement them with an external execution engine. @@ -260,14 +260,14 @@ def execute_payload(self: ExecutionEngine, execution_payload: ExecutionPayload) ... ``` -#### `consensus_validated` +#### `notify_consensus_validated` ```python -def consensus_validated(self: ExecutionEngine, block_hash: Hash32, valid: bool) -> None: +def notify_consensus_validated(self: ExecutionEngine, block_hash: Hash32, valid: bool) -> None: ... ``` -The inputs to this function depend on the result of the state transition. A call to `consensus_validated` must be made after the [`state_transition`](../phase0/beacon-chain.md#beacon-chain-state-transition-function) function finishes. The value of the `valid` parameter must be set as follows: +The inputs to this function depend on the result of the state transition. A call to `notify_consensus_validated` must be made after the [`state_transition`](../phase0/beacon-chain.md#beacon-chain-state-transition-function) function finishes. The value of the `valid` parameter must be set as follows: * `True` if `state_transition` function call succeeds * `False` if `state_transition` function call fails diff --git a/specs/merge/fork-choice.md b/specs/merge/fork-choice.md index 5adeded16..7b8643e99 100644 --- a/specs/merge/fork-choice.md +++ b/specs/merge/fork-choice.md @@ -10,7 +10,7 @@ - [Introduction](#introduction) - [Protocols](#protocols) - [`ExecutionEngine`](#executionengine) - - [`forkchoice_updated`](#forkchoice_updated) + - [`notify_forkchoice_updated`](#notify_forkchoice_updated) - [Helpers](#helpers) - [`PowBlock`](#powblock) - [`get_pow_block`](#get_pow_block) @@ -31,12 +31,12 @@ This is the modification of the fork choice according to the executable beacon c ### `ExecutionEngine` -*Note*: The `forkchoice_updated` function is added to the `ExecutionEngine` protocol to signal the fork choice updates. +*Note*: The `notify_forkchoice_updated` function is added to the `ExecutionEngine` protocol to signal the fork choice updates. The body of this function is implementation dependent. The Engine API may be used to implement it with an external execution engine. -#### `forkchoice_updated` +#### `notify_forkchoice_updated` This function performs two actions *atomically*: * Re-organizes the execution payload chain and corresponding state to make `head_block_hash` the head. @@ -44,7 +44,7 @@ This function performs two actions *atomically*: and corresponding state, up to and including `finalized_block_hash`. ```python -def forkchoice_updated(self: ExecutionEngine, head_block_hash: Hash32, finalized_block_hash: Hash32) -> None: +def notify_forkchoice_updated(self: ExecutionEngine, head_block_hash: Hash32, finalized_block_hash: Hash32) -> None: ... ``` From b7deef16293916a0fa03c00544aac0b5ea43540d Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 23 Sep 2021 13:50:46 +0600 Subject: [PATCH 10/26] Clarity which state should be used in prepare_payload in a better way --- specs/merge/validator.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/merge/validator.md b/specs/merge/validator.md index 4500d610c..dc67997c7 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -83,10 +83,10 @@ All validator responsibilities remain unchanged other than those noted below. Na ##### ExecutionPayload -To obtain an execution payload, the proposer of a block must take the following actions: +To obtain an execution payload, a block proposer bulding a block on top of a `state` must take the following actions: 1. Set `payload_id = prepare_execution_payload(state, pow_chain, fee_recipient, execution_engine)`, where: - * `state` is the state for which `is_proposer(state, validator_index)` returns `True` + * `state` is the state object after applying `process_slots(state, slot)` transition to the resulting state of the parent block processing * `pow_chain` is a list that abstractly represents all blocks in the PoW chain * `fee_recipient` is the value suggested to be used for the `coinbase` field of the execution payload From 7d577ed422e7504238e7451247806aa1f3e960f7 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 23 Sep 2021 14:35:55 +0600 Subject: [PATCH 11/26] Introduce ExecutionAddress type --- setup.py | 2 +- specs/merge/beacon-chain.md | 5 +++-- specs/merge/validator.md | 4 ++-- tests/core/pyspec/eth2spec/test/helpers/execution_payload.py | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index 39eccad53..efd4c29d5 100644 --- a/setup.py +++ b/setup.py @@ -535,7 +535,7 @@ class NoopExecutionEngine(ExecutionEngine): parent_hash: Hash32, timestamp: uint64, random: Bytes32, - feeRecipient: Bytes20) -> uint64: + feeRecipient: ExecutionAddress) -> uint64: raise NotImplementedError("no default block production") def get_payload(self: ExecutionEngine, payload_id: uint64) -> ExecutionPayload: diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 9be89fcea..1be41a81e 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -54,6 +54,7 @@ This patch adds transaction execution to the beacon chain as part of the Merge f | - | - | - | | `OpaqueTransaction` | `ByteList[MAX_BYTES_PER_OPAQUE_TRANSACTION]` | a [typed transaction envelope](https://eips.ethereum.org/EIPS/eip-2718#opaque-byte-array-rather-than-an-rlp-array) structured as `TransactionType \|\| TransactionPayload` | | `Transaction` | `Union[OpaqueTransaction]` | a transaction | +| `ExecutionAddress` | `Bytes20` | Address of account on the execution layer | ## Constants @@ -159,7 +160,7 @@ class BeaconState(Container): class ExecutionPayload(Container): # Execution block header fields parent_hash: Hash32 - coinbase: Bytes20 # 'beneficiary' in the yellow paper + coinbase: ExecutionAddress # 'beneficiary' in the yellow paper state_root: Bytes32 receipt_root: Bytes32 # 'receipts root' in the yellow paper logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] @@ -181,7 +182,7 @@ class ExecutionPayload(Container): class ExecutionPayloadHeader(Container): # Execution block header fields parent_hash: Hash32 - coinbase: Bytes20 + coinbase: ExecutionAddress state_root: Bytes32 receipt_root: Bytes32 logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM] diff --git a/specs/merge/validator.md b/specs/merge/validator.md index dc67997c7..e175a083e 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -53,7 +53,7 @@ def prepare_payload(self: ExecutionEngine, parent_hash: Hash32, timestamp: uint64, random: Bytes32, - fee_recipient: Bytes20) -> uint64: + fee_recipient: ExecutionAddress) -> uint64: """ Return ``payload_id`` that is used to obtain the execution payload in a subsequent ``get_payload`` call. """ @@ -103,7 +103,7 @@ def get_pow_block_at_total_difficulty(total_difficulty: uint256, pow_chain: Sequ def prepare_execution_payload(state: BeaconState, pow_chain: Sequence[PowBlock], - fee_recipient: Bytes20, + fee_recipient: ExecutionAddress, execution_engine: ExecutionEngine) -> Optional[uint64]: if not is_merge_complete(state): terminal_pow_block = get_pow_block_at_total_difficulty(TERMINAL_TOTAL_DIFFICULTY, pow_chain) diff --git a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py index 6126346a9..0d03447a7 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py @@ -11,7 +11,7 @@ def build_empty_execution_payload(spec, state, randao_mix=None): payload = spec.ExecutionPayload( parent_hash=latest.block_hash, - coinbase=spec.Bytes20(), + coinbase=spec.ExecutionAddress(), state_root=latest.state_root, # no changes to the state receipt_root=b"no receipts here" + b"\x00" * 16, # TODO: root of empty MPT may be better. logs_bloom=spec.ByteVector[spec.BYTES_PER_LOGS_BLOOM](), # TODO: zeroed logs bloom for empty logs ok? From 41e06aec1bb093663c9bf28268deacfbce59e899 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 23 Sep 2021 15:03:43 +0600 Subject: [PATCH 12/26] Introduce PayloadId type --- setup.py | 4 ++-- specs/merge/validator.md | 15 +++++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index efd4c29d5..9c1846737 100644 --- a/setup.py +++ b/setup.py @@ -535,10 +535,10 @@ class NoopExecutionEngine(ExecutionEngine): parent_hash: Hash32, timestamp: uint64, random: Bytes32, - feeRecipient: ExecutionAddress) -> uint64: + feeRecipient: ExecutionAddress) -> PayloadId: raise NotImplementedError("no default block production") - def get_payload(self: ExecutionEngine, payload_id: uint64) -> ExecutionPayload: + def get_payload(self: ExecutionEngine, payload_id: PayloadId) -> ExecutionPayload: raise NotImplementedError("no default block production") diff --git a/specs/merge/validator.md b/specs/merge/validator.md index e175a083e..54ecbb64a 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -10,6 +10,7 @@ - [Introduction](#introduction) - [Prerequisites](#prerequisites) +- [Custom types](#custom-types) - [Protocols](#protocols) - [`ExecutionEngine`](#executionengine) - [`prepare_payload`](#prepare_payload) @@ -34,6 +35,12 @@ All behaviors and definitions defined in this document, and documents it extends 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. +## Custom types + +| Name | SSZ equivalent | Description | +| - | - | - | +| `PayloadId` | `uint64` | Identifier of a payload building process | + ## Protocols ### `ExecutionEngine` @@ -53,7 +60,7 @@ def prepare_payload(self: ExecutionEngine, parent_hash: Hash32, timestamp: uint64, random: Bytes32, - fee_recipient: ExecutionAddress) -> uint64: + fee_recipient: ExecutionAddress) -> PayloadId: """ Return ``payload_id`` that is used to obtain the execution payload in a subsequent ``get_payload`` call. """ @@ -66,7 +73,7 @@ Given the `payload_id`, `get_payload` returns the most recent version of the exe has been built since the corresponding call to `prepare_payload` method. ```python -def get_payload(self: ExecutionEngine, payload_id: uint64) -> ExecutionPayload: +def get_payload(self: ExecutionEngine, payload_id: PayloadId) -> ExecutionPayload: """ Return ``execution_payload`` object. """ @@ -104,7 +111,7 @@ def get_pow_block_at_total_difficulty(total_difficulty: uint256, pow_chain: Sequ def prepare_execution_payload(state: BeaconState, pow_chain: Sequence[PowBlock], fee_recipient: ExecutionAddress, - execution_engine: ExecutionEngine) -> Optional[uint64]: + execution_engine: ExecutionEngine) -> Optional[PayloadId]: if not is_merge_complete(state): terminal_pow_block = get_pow_block_at_total_difficulty(TERMINAL_TOTAL_DIFFICULTY, pow_chain) if terminal_pow_block is None: @@ -125,7 +132,7 @@ def prepare_execution_payload(state: BeaconState, 2. Set `block.body.execution_payload = get_execution_payload(payload_id, execution_engine)`, where: ```python -def get_execution_payload(payload_id: Optional[uint64], execution_engine: ExecutionEngine) -> ExecutionPayload: +def get_execution_payload(payload_id: Optional[PayloadId], execution_engine: ExecutionEngine) -> ExecutionPayload: if payload_id is None: # Pre-merge, empty payload return ExecutionPayload() From 0e2eb506751c99e82f4e004d2a95aa5a41020c8c Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 23 Sep 2021 15:30:14 +0600 Subject: [PATCH 13/26] Fix spelling --- specs/merge/validator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/merge/validator.md b/specs/merge/validator.md index 54ecbb64a..1bc36b02d 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -90,7 +90,7 @@ All validator responsibilities remain unchanged other than those noted below. Na ##### ExecutionPayload -To obtain an execution payload, a block proposer bulding a block on top of a `state` must take the following actions: +To obtain an execution payload, a block proposer building a block on top of a `state` must take the following actions: 1. Set `payload_id = prepare_execution_payload(state, pow_chain, fee_recipient, execution_engine)`, where: * `state` is the state object after applying `process_slots(state, slot)` transition to the resulting state of the parent block processing From 0c65f79d824065606b5854002f37891f573b048e Mon Sep 17 00:00:00 2001 From: Etan Kissling Date: Thu, 23 Sep 2021 15:20:18 +0200 Subject: [PATCH 14/26] fix merkle proof test for mainnet The `test_next_sync_committee_tree` currently only supports the minimal preset, as it incorrectly initializes the `next_sync_committee`. On the mainnet preset, `SYNC_COMMITTEE_SIZE` is 512, but the default states use only 256 validators, leading to an IndexError during the test execution. `next_sync_committee` is already initialized correctly prior to the test run using the spec's `get_next_sync_committee` function, which fills up extra committee slots with duplicate validators in this scenario. This makes it unnecessary to manually initialize the `next_sync_committee`. Removed the incorrect initialization to allow testing on mainnet preset. --- .../core/pyspec/eth2spec/test/altair/unittests/test_helpers.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/altair/unittests/test_helpers.py b/tests/core/pyspec/eth2spec/test/altair/unittests/test_helpers.py index c837f06c3..137539113 100644 --- a/tests/core/pyspec/eth2spec/test/altair/unittests/test_helpers.py +++ b/tests/core/pyspec/eth2spec/test/altair/unittests/test_helpers.py @@ -9,9 +9,6 @@ from eth2spec.test.helpers.merkle import build_proof @with_phases([ALTAIR]) @spec_state_test def test_next_sync_committee_tree(spec, state): - state.next_sync_committee: object = spec.SyncCommittee( - pubkeys=[state.validators[i]for i in range(spec.SYNC_COMMITTEE_SIZE)] - ) next_sync_committee_branch = build_proof(state.get_backing(), spec.NEXT_SYNC_COMMITTEE_INDEX) assert spec.is_valid_merkle_branch( leaf=state.next_sync_committee.hash_tree_root(), From 08ca12ba970eece4d98d02a5ee55ae6efb555960 Mon Sep 17 00:00:00 2001 From: Mark Mackey Date: Thu, 23 Sep 2021 13:16:57 -0500 Subject: [PATCH 15/26] Fixed Altair p2p-interface.md Link --- specs/merge/p2p-interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/merge/p2p-interface.md b/specs/merge/p2p-interface.md index 85a96c31e..3febe9712 100644 --- a/specs/merge/p2p-interface.md +++ b/specs/merge/p2p-interface.md @@ -89,7 +89,7 @@ Alias `block = signed_beacon_block.message`, `execution_payload = block.body.exe ### Transitioning the gossip -See gossip transition details found in the [Altair document](../altair/p2p) for +See gossip transition details found in the [Altair document](../altair/p2p-interface.md#transitioning-the-gossip) for details on how to handle transitioning gossip topics for the Merge. ## The Req/Resp domain From d9a183b5cb049f4f0bce966365d40533d1403851 Mon Sep 17 00:00:00 2001 From: Mark Mackey Date: Thu, 23 Sep 2021 13:30:43 -0500 Subject: [PATCH 16/26] Modify Merge Gossip Block Validation Conditions --- specs/merge/p2p-interface.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/specs/merge/p2p-interface.md b/specs/merge/p2p-interface.md index 85a96c31e..73bacbdde 100644 --- a/specs/merge/p2p-interface.md +++ b/specs/merge/p2p-interface.md @@ -68,12 +68,10 @@ See the Merge [state transition document](./beacon-chain.md#beaconblockbody) for In addition to the gossip validations for this topic from prior specifications, the following validations MUST pass before forwarding the `signed_beacon_block` on the network. Alias `block = signed_beacon_block.message`, `execution_payload = block.body.execution_payload`. -- If the merge is complete with respect to the head state -- i.e. `is_merge_complete(state)` -- +- If the execution is enabled for the block or the block state -- i.e. `is_execution_enabled(state, block.body)` then validate the following: - _[REJECT]_ The block's execution payload must be non-empty -- i.e. `execution_payload != ExecutionPayload()` -- If the execution is enabled for the block -- i.e. `is_execution_enabled(state, block.body)` - then validate the following: - _[REJECT]_ The block's execution payload timestamp is correct with respect to the slot -- i.e. `execution_payload.timestamp == compute_time_at_slot(state, block.slot)`. - _[REJECT]_ Gas used is less than the gas limit -- From 11840ce51ff69614d19747e546e8c1fa351a1a37 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Fri, 24 Sep 2021 16:31:09 +0600 Subject: [PATCH 17/26] Map EE calls on the PoS events defined in EIP-3675 --- specs/merge/beacon-chain.md | 2 ++ specs/merge/fork-choice.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 1be41a81e..053c829f1 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -273,6 +273,8 @@ The inputs to this function depend on the result of the state transition. A call * `True` if `state_transition` function call succeeds * `False` if `state_transition` function call fails +*Note*: The call of the `notify_consensus_validated` function with `valid = True` maps on the `POS_CONSENSUS_VALIDATED` event defined in the [EIP-3675](https://eips.ethereum.org/EIPS/eip-3675#definitions). + ### Block processing *Note*: The call to the `process_execution_payload` must happen before the call to the `process_randao` as the former depends on the `randao_mix` computed with the reveal of the previous block. diff --git a/specs/merge/fork-choice.md b/specs/merge/fork-choice.md index 7b8643e99..4cf413b10 100644 --- a/specs/merge/fork-choice.md +++ b/specs/merge/fork-choice.md @@ -48,6 +48,8 @@ def notify_forkchoice_updated(self: ExecutionEngine, head_block_hash: Hash32, fi ... ``` +*Note*: The call of the `notify_forkchoice_updated` function maps on the `POS_FORKCHOICE_UPDATED` event defined in the [EIP-3675](https://eips.ethereum.org/EIPS/eip-3675#definitions). + ## Helpers ### `PowBlock` From e0eec44e34184ddcd371d4b15a9d591e015e869d Mon Sep 17 00:00:00 2001 From: Fredrik Svantes Date: Fri, 24 Sep 2021 12:45:37 +0200 Subject: [PATCH 18/26] No longer importing "Paragraph" removed Paragraph from being imported from marko.ext.gfm.elements as it is not being used --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 6e55cfdde..d509e8779 100644 --- a/setup.py +++ b/setup.py @@ -32,7 +32,7 @@ except ImportError: from marko.block import Heading, FencedCode, LinkRefDef, BlankLine from marko.inline import CodeSpan from marko.ext.gfm import gfm -from marko.ext.gfm.elements import Table, Paragraph +from marko.ext.gfm.elements import Table # Definitions in context.py From d7665681a2bffb0e6405fa48f21b28342eff0fea Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 24 Sep 2021 06:08:01 -0600 Subject: [PATCH 19/26] simplify merge block gossip conditions --- specs/merge/p2p-interface.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/specs/merge/p2p-interface.md b/specs/merge/p2p-interface.md index 73bacbdde..487b8f864 100644 --- a/specs/merge/p2p-interface.md +++ b/specs/merge/p2p-interface.md @@ -68,10 +68,8 @@ See the Merge [state transition document](./beacon-chain.md#beaconblockbody) for In addition to the gossip validations for this topic from prior specifications, the following validations MUST pass before forwarding the `signed_beacon_block` on the network. Alias `block = signed_beacon_block.message`, `execution_payload = block.body.execution_payload`. -- If the execution is enabled for the block or the block state -- i.e. `is_execution_enabled(state, block.body)` +- If the execution is enabled for the block -- i.e. `is_execution_enabled(state, block.body)` then validate the following: - - _[REJECT]_ The block's execution payload must be non-empty -- - i.e. `execution_payload != ExecutionPayload()` - _[REJECT]_ The block's execution payload timestamp is correct with respect to the slot -- i.e. `execution_payload.timestamp == compute_time_at_slot(state, block.slot)`. - _[REJECT]_ Gas used is less than the gas limit -- From 9f037180bad44c4093a922b5cc5e8486ac30779b Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 24 Sep 2021 12:14:39 -0600 Subject: [PATCH 20/26] add ALTAIR_FORK_EPOCH for mainnet configuration --- configs/mainnet.yaml | 2 +- specs/altair/fork.md | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/configs/mainnet.yaml b/configs/mainnet.yaml index 6f2f582fa..90e5d8736 100644 --- a/configs/mainnet.yaml +++ b/configs/mainnet.yaml @@ -29,7 +29,7 @@ GENESIS_DELAY: 604800 # Altair ALTAIR_FORK_VERSION: 0x01000000 -ALTAIR_FORK_EPOCH: 18446744073709551615 +ALTAIR_FORK_EPOCH: 74240 # Oct 27, 2021, 10:56:23pm UTC # Merge MERGE_FORK_VERSION: 0x02000000 MERGE_FORK_EPOCH: 18446744073709551615 diff --git a/specs/altair/fork.md b/specs/altair/fork.md index f80064a83..739da80a5 100644 --- a/specs/altair/fork.md +++ b/specs/altair/fork.md @@ -1,7 +1,5 @@ # Altair -- Fork Logic -**Notice**: This document is a work-in-progress for researchers and implementers. - ## Table of contents @@ -26,13 +24,13 @@ Warning: this configuration is not definitive. | Name | Value | | - | - | | `ALTAIR_FORK_VERSION` | `Version('0x01000000')` | -| `ALTAIR_FORK_EPOCH` | `Epoch(18446744073709551615)` **TBD** | +| `ALTAIR_FORK_EPOCH` | `Epoch(74240)` (Oct 27, 2021, 10:56:23pm UTC) | ## Fork to Altair ### Fork trigger -TBD. Social consensus, along with state conditions such as epoch boundary, finality, deposits, active validator count, etc. may be part of the decision process to trigger the fork. For now we assume the condition will be triggered at epoch `ALTAIR_FORK_EPOCH`. +The fork is triggered at epoch `ALTAIR_FORK_EPOCH`. Note that for the pure Altair networks, we don't apply `upgrade_to_altair` since it starts with Altair version logic. From 8d5106808fbb1c0a8d748625ef683e1e0707ced0 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 24 Sep 2021 12:26:40 -0600 Subject: [PATCH 21/26] bump version to v1.1.0-beta.5 --- tests/core/pyspec/eth2spec/VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/VERSION.txt b/tests/core/pyspec/eth2spec/VERSION.txt index 3bec004b5..fbbb0af4d 100644 --- a/tests/core/pyspec/eth2spec/VERSION.txt +++ b/tests/core/pyspec/eth2spec/VERSION.txt @@ -1 +1 @@ -1.1.0-beta.4 \ No newline at end of file +1.1.0-beta.5 \ No newline at end of file From 7b0e7ceafae30861da7101ff8e9eaba91efd5359 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 24 Sep 2021 12:28:46 -0600 Subject: [PATCH 22/26] Update configs/mainnet.yaml Co-authored-by: terence tsao --- configs/mainnet.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/mainnet.yaml b/configs/mainnet.yaml index 90e5d8736..be61d070c 100644 --- a/configs/mainnet.yaml +++ b/configs/mainnet.yaml @@ -29,7 +29,7 @@ GENESIS_DELAY: 604800 # Altair ALTAIR_FORK_VERSION: 0x01000000 -ALTAIR_FORK_EPOCH: 74240 # Oct 27, 2021, 10:56:23pm UTC +ALTAIR_FORK_EPOCH: 74240 # Oct 27, 2021, 10:56:23am UTC # Merge MERGE_FORK_VERSION: 0x02000000 MERGE_FORK_EPOCH: 18446744073709551615 From af5eef37b200129105c8430cc1e610c80b171ed3 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 24 Sep 2021 12:29:34 -0600 Subject: [PATCH 23/26] fix am/pm issue --- specs/altair/fork.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/altair/fork.md b/specs/altair/fork.md index 739da80a5..6228022b8 100644 --- a/specs/altair/fork.md +++ b/specs/altair/fork.md @@ -24,7 +24,7 @@ Warning: this configuration is not definitive. | Name | Value | | - | - | | `ALTAIR_FORK_VERSION` | `Version('0x01000000')` | -| `ALTAIR_FORK_EPOCH` | `Epoch(74240)` (Oct 27, 2021, 10:56:23pm UTC) | +| `ALTAIR_FORK_EPOCH` | `Epoch(74240)` (Oct 27, 2021, 10:56:23am UTC) | ## Fork to Altair From 8a76a4143dfd7d58f8d48a289d9e270e7fc1eadb Mon Sep 17 00:00:00 2001 From: Mark Mackey Date: Fri, 24 Sep 2021 14:15:42 -0500 Subject: [PATCH 24/26] Fixed Validation Conditions (Function Renamed) --- specs/merge/p2p-interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/merge/p2p-interface.md b/specs/merge/p2p-interface.md index e101c8ff3..954c1b008 100644 --- a/specs/merge/p2p-interface.md +++ b/specs/merge/p2p-interface.md @@ -71,7 +71,7 @@ Alias `block = signed_beacon_block.message`, `execution_payload = block.body.exe - If the execution is enabled for the block -- i.e. `is_execution_enabled(state, block.body)` then validate the following: - _[REJECT]_ The block's execution payload timestamp is correct with respect to the slot - -- i.e. `execution_payload.timestamp == compute_time_at_slot(state, block.slot)`. + -- i.e. `execution_payload.timestamp == compute_timestamp_at_slot(state, block.slot)`. - _[REJECT]_ Gas used is less than the gas limit -- i.e. `execution_payload.gas_used <= execution_payload.gas_limit`. - _[REJECT]_ The execution payload block hash is not equal to the parent hash -- From e7317e228346d2eb5f3d7414b20f9485b2fa687c Mon Sep 17 00:00:00 2001 From: Etan Kissling Date: Fri, 24 Sep 2021 11:10:57 +0200 Subject: [PATCH 25/26] merkle proof test generator Building merkle proofs is required functionality for implementing light client sync. Although the spec currently only defines a function to verify merkle proofs (`is_valid_merkle_branch`) there are still a few PySpec unit tests that produce merkle proofs. This patch adds a new generator to extract test vectors from those static unit tests, so that light client implementations can validate their merkle proof logic. --- .../test/altair/unittests/test_helpers.py | 8 +++++ tests/formats/merkle/README.md | 36 +++++++++++++++++++ tests/generators/merkle/README.md | 6 ++++ tests/generators/merkle/__init__.py | 0 tests/generators/merkle/main.py | 14 ++++++++ tests/generators/merkle/requirements.txt | 2 ++ 6 files changed, 66 insertions(+) create mode 100644 tests/formats/merkle/README.md create mode 100644 tests/generators/merkle/README.md create mode 100644 tests/generators/merkle/__init__.py create mode 100644 tests/generators/merkle/main.py create mode 100644 tests/generators/merkle/requirements.txt diff --git a/tests/core/pyspec/eth2spec/test/altair/unittests/test_helpers.py b/tests/core/pyspec/eth2spec/test/altair/unittests/test_helpers.py index 137539113..7eefc458b 100644 --- a/tests/core/pyspec/eth2spec/test/altair/unittests/test_helpers.py +++ b/tests/core/pyspec/eth2spec/test/altair/unittests/test_helpers.py @@ -9,7 +9,11 @@ from eth2spec.test.helpers.merkle import build_proof @with_phases([ALTAIR]) @spec_state_test def test_next_sync_committee_tree(spec, state): + yield "state", state + yield "leaf", state.next_sync_committee.hash_tree_root() + yield "leaf_index", "meta", spec.NEXT_SYNC_COMMITTEE_INDEX next_sync_committee_branch = build_proof(state.get_backing(), spec.NEXT_SYNC_COMMITTEE_INDEX) + yield "proof", next_sync_committee_branch assert spec.is_valid_merkle_branch( leaf=state.next_sync_committee.hash_tree_root(), branch=next_sync_committee_branch, @@ -22,7 +26,11 @@ def test_next_sync_committee_tree(spec, state): @with_phases([ALTAIR]) @spec_state_test def test_finality_root_tree(spec, state): + yield "state", state + yield "leaf", state.finalized_checkpoint.root + yield "leaf_index", "meta", spec.FINALIZED_ROOT_INDEX finality_branch = build_proof(state.get_backing(), spec.FINALIZED_ROOT_INDEX) + yield "proof", finality_branch assert spec.is_valid_merkle_branch( leaf=state.finalized_checkpoint.root, branch=finality_branch, diff --git a/tests/formats/merkle/README.md b/tests/formats/merkle/README.md new file mode 100644 index 000000000..a1810d667 --- /dev/null +++ b/tests/formats/merkle/README.md @@ -0,0 +1,36 @@ +# Merkle tests + +This series of tests provides reference test vectors for validating correct +generation and verification of merkle proofs based on static data. + +## Test case format + +### `meta.yaml` + +```yaml +leaf_index: int -- Generalized leaf index, verifying against the proof. +proof_count: int -- Amount of proof elements. +``` + +### `state.ssz_snappy` + +An SSZ-snappy encoded `BeaconState` object from which other data is generated. + +### `leaf.ssz_snappy` + +An SSZ-snappy encoded `Bytes32` reflecting the merkle root of `leaf_index` at +the given `state`. + +### `proof_.ssz_snappy` + +A series of files, with `` in range `[0, proof_count)`. Each file is an +SSZ-snappy encoded `Bytes32` and represents one element of the merkle proof for +`leaf_index` at the given `state`. + +## Condition + +A test-runner can implement the following assertions: +- Check that `is_valid_merkle_branch` confirms `leaf` at `leaf_index` to verify + against `has_tree_root(state)` and `proof`. +- If the implementation supports generating merkle proofs, check that the + self-generated proof matches the `proof` provided with the test. diff --git a/tests/generators/merkle/README.md b/tests/generators/merkle/README.md new file mode 100644 index 000000000..a19a67d9e --- /dev/null +++ b/tests/generators/merkle/README.md @@ -0,0 +1,6 @@ +# Merkle + +The purpose of this test-generator is to provide test-vectors for validating the +correct merkleization of objects and corresponding merkle proofs. + +Test-format documentation can be found [here](../../formats/merkle/README.md). diff --git a/tests/generators/merkle/__init__.py b/tests/generators/merkle/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/generators/merkle/main.py b/tests/generators/merkle/main.py new file mode 100644 index 000000000..53cb59de9 --- /dev/null +++ b/tests/generators/merkle/main.py @@ -0,0 +1,14 @@ +from eth2spec.test.helpers.constants import ALTAIR +from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators + + +if __name__ == "__main__": + altair_mods = {key: 'eth2spec.test.altair.unittests.test_' + key for key in [ + 'helpers', + ]} + + all_mods = { + ALTAIR: altair_mods + } + + run_state_test_generators(runner_name="merkle", all_mods=all_mods) diff --git a/tests/generators/merkle/requirements.txt b/tests/generators/merkle/requirements.txt new file mode 100644 index 000000000..182248686 --- /dev/null +++ b/tests/generators/merkle/requirements.txt @@ -0,0 +1,2 @@ +pytest>=4.4 +../../../[generator] From bd8c97896511680a750afb53919bb57c67e710e1 Mon Sep 17 00:00:00 2001 From: protolambda Date: Mon, 27 Sep 2021 18:07:59 +0200 Subject: [PATCH 26/26] move merkle tests, output proof.yaml, update format --- .../eth2spec/test/altair/merkle/__init__.py | 0 .../test_single_proof.py} | 21 +++++++----- tests/formats/merkle/README.md | 34 ++----------------- tests/formats/merkle/single_proof.md | 28 +++++++++++++++ tests/generators/merkle/main.py | 4 +-- 5 files changed, 46 insertions(+), 41 deletions(-) create mode 100644 tests/core/pyspec/eth2spec/test/altair/merkle/__init__.py rename tests/core/pyspec/eth2spec/test/altair/{unittests/test_helpers.py => merkle/test_single_proof.py} (66%) create mode 100644 tests/formats/merkle/single_proof.md diff --git a/tests/core/pyspec/eth2spec/test/altair/merkle/__init__.py b/tests/core/pyspec/eth2spec/test/altair/merkle/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/core/pyspec/eth2spec/test/altair/unittests/test_helpers.py b/tests/core/pyspec/eth2spec/test/altair/merkle/test_single_proof.py similarity index 66% rename from tests/core/pyspec/eth2spec/test/altair/unittests/test_helpers.py rename to tests/core/pyspec/eth2spec/test/altair/merkle/test_single_proof.py index 7eefc458b..f9aa68add 100644 --- a/tests/core/pyspec/eth2spec/test/altair/unittests/test_helpers.py +++ b/tests/core/pyspec/eth2spec/test/altair/merkle/test_single_proof.py @@ -8,12 +8,14 @@ from eth2spec.test.helpers.merkle import build_proof @with_phases([ALTAIR]) @spec_state_test -def test_next_sync_committee_tree(spec, state): +def test_next_sync_committee_merkle_proof(spec, state): yield "state", state - yield "leaf", state.next_sync_committee.hash_tree_root() - yield "leaf_index", "meta", spec.NEXT_SYNC_COMMITTEE_INDEX next_sync_committee_branch = build_proof(state.get_backing(), spec.NEXT_SYNC_COMMITTEE_INDEX) - yield "proof", next_sync_committee_branch + yield "proof", { + "leaf": "0x" + state.next_sync_committee.hash_tree_root().hex(), + "leaf_index": spec.NEXT_SYNC_COMMITTEE_INDEX, + "branch": ['0x' + root.hex() for root in next_sync_committee_branch] + } assert spec.is_valid_merkle_branch( leaf=state.next_sync_committee.hash_tree_root(), branch=next_sync_committee_branch, @@ -25,12 +27,15 @@ def test_next_sync_committee_tree(spec, state): @with_phases([ALTAIR]) @spec_state_test -def test_finality_root_tree(spec, state): +def test_finality_root_merkle_proof(spec, state): yield "state", state - yield "leaf", state.finalized_checkpoint.root - yield "leaf_index", "meta", spec.FINALIZED_ROOT_INDEX finality_branch = build_proof(state.get_backing(), spec.FINALIZED_ROOT_INDEX) - yield "proof", finality_branch + yield "proof", { + "leaf": "0x" + state.finalized_checkpoint.root.hex(), + "leaf_index": spec.FINALIZED_ROOT_INDEX, + "branch": ['0x' + root.hex() for root in finality_branch] + } + assert spec.is_valid_merkle_branch( leaf=state.finalized_checkpoint.root, branch=finality_branch, diff --git a/tests/formats/merkle/README.md b/tests/formats/merkle/README.md index a1810d667..c0f0a205b 100644 --- a/tests/formats/merkle/README.md +++ b/tests/formats/merkle/README.md @@ -3,34 +3,6 @@ This series of tests provides reference test vectors for validating correct generation and verification of merkle proofs based on static data. -## Test case format - -### `meta.yaml` - -```yaml -leaf_index: int -- Generalized leaf index, verifying against the proof. -proof_count: int -- Amount of proof elements. -``` - -### `state.ssz_snappy` - -An SSZ-snappy encoded `BeaconState` object from which other data is generated. - -### `leaf.ssz_snappy` - -An SSZ-snappy encoded `Bytes32` reflecting the merkle root of `leaf_index` at -the given `state`. - -### `proof_.ssz_snappy` - -A series of files, with `` in range `[0, proof_count)`. Each file is an -SSZ-snappy encoded `Bytes32` and represents one element of the merkle proof for -`leaf_index` at the given `state`. - -## Condition - -A test-runner can implement the following assertions: -- Check that `is_valid_merkle_branch` confirms `leaf` at `leaf_index` to verify - against `has_tree_root(state)` and `proof`. -- If the implementation supports generating merkle proofs, check that the - self-generated proof matches the `proof` provided with the test. +Handlers: +- `single_proof`: see [Single leaf proof test format](./single_proof.md) +- Different types of merkle proofs may be supported in the future. diff --git a/tests/formats/merkle/single_proof.md b/tests/formats/merkle/single_proof.md new file mode 100644 index 000000000..65fe7c988 --- /dev/null +++ b/tests/formats/merkle/single_proof.md @@ -0,0 +1,28 @@ +# Single leaf merkle proof tests + +This series of tests provides reference test vectors for validating correct +generation and verification of merkle proofs based on static data. + +## Test case format + +### `state.ssz_snappy` + +An SSZ-snappy encoded `BeaconState` object from which other data is generated. + +### `proof.yaml` + +A proof of the leaf value (a merkle root) at generalized-index `leaf_index` in the given `state`. + +```yaml +leaf: Bytes32 # string, hex encoded, with 0x prefix +leaf_index: int # integer, decimal +branch: list of Bytes32 # list, each element is a string, hex encoded, with 0x prefix +``` + +## Condition + +A test-runner can implement the following assertions: +- Check that `is_valid_merkle_branch` confirms `leaf` at `leaf_index` to verify + against `has_tree_root(state)` and `proof`. +- If the implementation supports generating merkle proofs, check that the + self-generated proof matches the `proof` provided with the test. diff --git a/tests/generators/merkle/main.py b/tests/generators/merkle/main.py index 53cb59de9..7203c5f19 100644 --- a/tests/generators/merkle/main.py +++ b/tests/generators/merkle/main.py @@ -3,8 +3,8 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators if __name__ == "__main__": - altair_mods = {key: 'eth2spec.test.altair.unittests.test_' + key for key in [ - 'helpers', + altair_mods = {key: 'eth2spec.test.altair.merkle.test_' + key for key in [ + 'single_proof', ]} all_mods = {