From b530e017619b79b7a8c4d9105177c3425d5d9770 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Mon, 20 Sep 2021 20:57:45 +0600 Subject: [PATCH 01/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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 11840ce51ff69614d19747e546e8c1fa351a1a37 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Fri, 24 Sep 2021 16:31:09 +0600 Subject: [PATCH 14/14] 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`