From 6ba1f22404181408259d363b7ed311208816d6f8 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 1 Sep 2021 18:42:58 +0600 Subject: [PATCH 01/17] Set ExecutionPayload.random to the previous randao_mix --- specs/merge/beacon-chain.md | 8 ++++---- specs/merge/validator.md | 7 +------ specs/sharding/beacon-chain.md | 4 ++-- tests/core/pyspec/eth2spec/test/helpers/block.py | 3 +-- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index ed12d3eb6..ee5f59aa8 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -246,15 +246,17 @@ The above function is accessed through the `EXECUTION_ENGINE` module which insta ### Block processing +*Note*: The call to the `process_execution_payload` must happen before the call to `process_randao` as the former depends on the `randao_mix` computed with the reveal of the previous block. + ```python def process_block(state: BeaconState, block: BeaconBlock) -> None: process_block_header(state, block) + if is_execution_enabled(state, block.body): + process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [New in Merge] process_randao(state, block.body) 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): - process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [New in Merge] ``` ### Execution payload processing @@ -284,8 +286,6 @@ def is_valid_gas_limit(payload: ExecutionPayload, parent: ExecutionPayloadHeader #### `process_execution_payload` -*Note:* This function depends on `process_randao` function call as it retrieves the most recent randao mix from the `state`. Implementations that are considering parallel processing of execution payload with respect to beacon chain state transition function should work around this dependency. - ```python def process_execution_payload(state: BeaconState, payload: ExecutionPayload, execution_engine: ExecutionEngine) -> None: # Verify consistency of the parent hash, block number, random, base fee per gas and gas limit diff --git a/specs/merge/validator.md b/specs/merge/validator.md index efeeca061..e71ee74a8 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -75,17 +75,12 @@ def get_pow_block_at_total_difficulty(total_difficulty: uint256, pow_chain: Sequ return None -def compute_randao_mix(state: BeaconState, randao_reveal: BLSSignature) -> Bytes32: - epoch = get_current_epoch(state) - return xor(get_randao_mix(state, epoch), hash(randao_reveal)) - - def produce_execution_payload(state: BeaconState, parent_hash: Hash32, randao_reveal: BLSSignature, execution_engine: ExecutionEngine) -> ExecutionPayload: timestamp = compute_timestamp_at_slot(state, state.slot) - randao_mix = compute_randao_mix(state, randao_reveal) + randao_mix = get_randao_mix(state, get_current_epoch(state)) return execution_engine.assemble_block(parent_hash, timestamp, randao_mix) diff --git a/specs/sharding/beacon-chain.md b/specs/sharding/beacon-chain.md index f54394275..2c8e8f20b 100644 --- a/specs/sharding/beacon-chain.md +++ b/specs/sharding/beacon-chain.md @@ -552,12 +552,12 @@ def compute_committee_index_from_shard(state: BeaconState, slot: Slot, shard: Sh ```python def process_block(state: BeaconState, block: BeaconBlock) -> None: process_block_header(state, block) + # is_execution_enabled is omitted, execution is enabled by default. + process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) process_randao(state, block.body) process_eth1_data(state, block.body) process_operations(state, block.body) # [Modified in Sharding] process_sync_aggregate(state, block.body.sync_aggregate) - # is_execution_enabled is omitted, execution is enabled by default. - process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) ``` #### Operations diff --git a/tests/core/pyspec/eth2spec/test/helpers/block.py b/tests/core/pyspec/eth2spec/test/helpers/block.py index b8f7c4bcb..78b90b165 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/block.py +++ b/tests/core/pyspec/eth2spec/test/helpers/block.py @@ -99,8 +99,7 @@ def build_empty_block(spec, state, slot=None): empty_block.body.sync_aggregate.sync_committee_signature = spec.G2_POINT_AT_INFINITY if is_post_merge(spec): - randao_mix = spec.compute_randao_mix(state, empty_block.body.randao_reveal) - empty_block.body.execution_payload = build_empty_execution_payload(spec, state, randao_mix) + empty_block.body.execution_payload = build_empty_execution_payload(spec, state) return empty_block From 02057cb13e5086d259b62d7b3c8ad27effc307dd Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 1 Sep 2021 20:59:16 +0600 Subject: [PATCH 02/17] Fix spelling --- 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 ee5f59aa8..2e7d26954 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -246,7 +246,7 @@ The above function is accessed through the `EXECUTION_ENGINE` module which insta ### Block processing -*Note*: The call to the `process_execution_payload` must happen before the call to `process_randao` as the former depends on the `randao_mix` computed with the reveal of the previous block. +*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. ```python def process_block(state: BeaconState, block: BeaconBlock) -> None: From 960a49afc9629d96f853242f5fe4a268e0ddeeb3 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Fri, 10 Sep 2021 16:07:26 +0600 Subject: [PATCH 03/17] Verify terminal PoW block after call to state_transition --- setup.py | 3 +-- specs/merge/fork-choice.md | 21 +++++++++------------ 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/setup.py b/setup.py index 281e292dc..6e55cfdde 100644 --- a/setup.py +++ b/setup.py @@ -509,8 +509,7 @@ ExecutionState = Any def get_pow_block(hash: Bytes32) -> PowBlock: - return PowBlock(block_hash=hash, parent_hash=Bytes32(), is_valid=True, is_processed=True, - total_difficulty=uint256(0), difficulty=uint256(0)) + return PowBlock(block_hash=hash, parent_hash=Bytes32(), total_difficulty=uint256(0), difficulty=uint256(0)) def get_execution_state(execution_state_root: Bytes32) -> ExecutionState: diff --git a/specs/merge/fork-choice.md b/specs/merge/fork-choice.md index de82b17fa..84be0f1ce 100644 --- a/specs/merge/fork-choice.md +++ b/specs/merge/fork-choice.md @@ -83,8 +83,6 @@ class TransitionStore(object): class PowBlock(object): block_hash: Hash32 parent_hash: Hash32 - is_processed: boolean - is_valid: boolean total_difficulty: uint256 difficulty: uint256 ``` @@ -93,7 +91,7 @@ class PowBlock(object): Let `get_pow_block(block_hash: Hash32) -> PowBlock` be the function that given the hash of the PoW block returns its data. -*Note*: The `eth_getBlockByHash` JSON-RPC method does not distinguish invalid blocks from blocks that haven't been processed yet. Either extending this existing method or implementing a new one is required. +*Note*: The `eth_getBlockByHash` JSON-RPC method may be used to pull this information from an execution client. ### `is_valid_terminal_pow_block` @@ -103,7 +101,7 @@ Used by fork-choice handler, `on_block`. def is_valid_terminal_pow_block(transition_store: TransitionStore, block: PowBlock, parent: PowBlock) -> bool: is_total_difficulty_reached = block.total_difficulty >= transition_store.terminal_total_difficulty is_parent_total_difficulty_valid = parent.total_difficulty < transition_store.terminal_total_difficulty - return block.is_valid and is_total_difficulty_reached and is_parent_total_difficulty_valid + return is_total_difficulty_reached and is_parent_total_difficulty_valid ``` ## Updated fork-choice handlers @@ -128,17 +126,16 @@ def on_block(store: Store, signed_block: SignedBeaconBlock, transition_store: Tr # Check block is a descendant of the finalized block at the checkpoint finalized slot assert get_ancestor(store, block.parent_root, finalized_slot) == store.finalized_checkpoint.root - # [New in Merge] - if (transition_store is not None) and is_merge_block(pre_state, block.body): - # Delay consideration of block until PoW block is processed by the PoW node - pow_block = get_pow_block(block.body.execution_payload.parent_hash) - pow_parent = get_pow_block(pow_block.parent_hash) - assert pow_block.is_processed - assert is_valid_terminal_pow_block(transition_store, pow_block, pow_parent) - # Check the block is valid and compute the post-state state = pre_state.copy() state_transition(state, signed_block, True) + + # [New in Merge] + if (transition_store is not None) and is_merge_block(pre_state, block.body): + pow_block = get_pow_block(block.body.execution_payload.parent_hash) + pow_parent = get_pow_block(pow_block.parent_hash) + assert is_valid_terminal_pow_block(transition_store, pow_block, pow_parent) + # Add new block to the store store.blocks[hash_tree_root(block)] = block # Add new state for this block to the store From 559ca86e2a6eefa1dc4aa4e48b2deede55f5d268 Mon Sep 17 00:00:00 2001 From: Etan Kissling Date: Fri, 10 Sep 2021 16:56:27 +0200 Subject: [PATCH 04/17] "is build" -> "is built" typo Corrects a typo in a Makefile comment. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9f32b4568..977141078 100644 --- a/Makefile +++ b/Makefile @@ -173,7 +173,7 @@ define run_generator echo "generator $(1) finished" endef -# The tests dir itself is simply build by creating the directory (recursively creating deeper directories if necessary) +# The tests dir itself is simply built by creating the directory (recursively creating deeper directories if necessary) $(TEST_VECTOR_DIR): $(info creating test output directory, for generators: ${GENERATOR_TARGETS}) mkdir -p $@ From 899bde08734c3ebe72340226cab808fb563c4f84 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Mon, 13 Sep 2021 06:31:11 -0700 Subject: [PATCH 05/17] Rename client_settings.md to client-settings.md --- specs/merge/{client_settings.md => client-settings.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename specs/merge/{client_settings.md => client-settings.md} (100%) diff --git a/specs/merge/client_settings.md b/specs/merge/client-settings.md similarity index 100% rename from specs/merge/client_settings.md rename to specs/merge/client-settings.md From 370b9e86e31e966a7f105e1b32018d3602d0dec4 Mon Sep 17 00:00:00 2001 From: Etan Kissling Date: Wed, 15 Sep 2021 21:30:16 +0200 Subject: [PATCH 06/17] pass sync committee sig consistently in tests There are three defined unit tests for the light client sync protocol. They all follow a similar structure. However, there is an inconcistency how they pass the slot to compute_aggregate_sync_committee_signature. In one instance it is passed as `block.slot`. In the other two cases it is passed as `block_header.slot`. As the `block_header` is created from the `block`, they share the same value. This patch makes the way how the slot is passed consistent across all of the test cases. --- .../pyspec/eth2spec/test/altair/unittests/test_sync_protocol.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/altair/unittests/test_sync_protocol.py b/tests/core/pyspec/eth2spec/test/altair/unittests/test_sync_protocol.py index a3cf8b7ca..c69957de5 100644 --- a/tests/core/pyspec/eth2spec/test/altair/unittests/test_sync_protocol.py +++ b/tests/core/pyspec/eth2spec/test/altair/unittests/test_sync_protocol.py @@ -52,7 +52,7 @@ def test_process_light_client_update_not_updated(spec, state): sync_committee_signature = compute_aggregate_sync_committee_signature( spec, state, - block.slot, + block_header.slot, committee, ) next_sync_committee_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX))] From 1f486c1b32f8b1b502270e8e5621d222dc5fb158 Mon Sep 17 00:00:00 2001 From: ethDreamer <37123614+ethDreamer@users.noreply.github.com> Date: Wed, 15 Sep 2021 18:47:44 -0500 Subject: [PATCH 07/17] fixed client-settings.md link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b5a898d37..9e5d7dc42 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ The merge is still actively in development. The exact specification has not been * [Merge fork](specs/merge/fork.md) * [Fork Choice changes](specs/merge/fork-choice.md) * [Validator additions](specs/merge/validator.md) - * [Client settings](specs/merge/client_settings.md) + * [Client settings](specs/merge/client-settings.md) ### Sharding From 6f78e6a3eeb87276133e87e6e9d21bb0c47518d9 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 16 Sep 2021 11:28:04 +0600 Subject: [PATCH 08/17] Remove randao_reveal from validator.md --- specs/merge/validator.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/specs/merge/validator.md b/specs/merge/validator.md index e71ee74a8..b7a2860fd 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -77,7 +77,6 @@ def get_pow_block_at_total_difficulty(total_difficulty: uint256, pow_chain: Sequ def produce_execution_payload(state: BeaconState, parent_hash: Hash32, - randao_reveal: BLSSignature, execution_engine: ExecutionEngine) -> ExecutionPayload: timestamp = compute_timestamp_at_slot(state, state.slot) randao_mix = get_randao_mix(state, get_current_epoch(state)) @@ -86,7 +85,6 @@ def produce_execution_payload(state: BeaconState, def get_execution_payload(state: BeaconState, transition_store: TransitionStore, - randao_reveal: BLSSignature, execution_engine: ExecutionEngine, pow_chain: Sequence[PowBlock]) -> ExecutionPayload: if not is_merge_complete(state): @@ -96,9 +94,9 @@ def get_execution_payload(state: BeaconState, return ExecutionPayload() else: # Signify merge via producing on top of the last PoW block - return produce_execution_payload(state, terminal_pow_block.block_hash, randao_reveal, execution_engine) + return produce_execution_payload(state, terminal_pow_block.block_hash, execution_engine) # Post-merge, normal payload parent_hash = state.latest_execution_payload_header.block_hash - return produce_execution_payload(state, parent_hash, randao_reveal, execution_engine) + return produce_execution_payload(state, parent_hash, execution_engine) ``` From 26c78b540a2479a909c4543f0642aca27d3d7d95 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Fri, 17 Sep 2021 16:01:15 +0600 Subject: [PATCH 09/17] Fix test_blocks#test_parent_from_same_slot --- .../pyspec/eth2spec/test/phase0/sanity/test_blocks.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py index 6866a86ae..8ff6bd731 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py @@ -143,6 +143,9 @@ def process_and_sign_block_without_header_validations(spec, state, block): state_root=spec.Bytes32(), body_root=block.body.hash_tree_root(), ) + if is_post_merge(spec): + if spec.is_execution_enabled(state, block.body): + spec.process_execution_payload(state, block.body.execution_payload, spec.EXECUTION_ENGINE) # Perform rest of process_block transitions spec.process_randao(state, block.body) @@ -150,9 +153,6 @@ def process_and_sign_block_without_header_validations(spec, state, block): spec.process_operations(state, block.body) if is_post_altair(spec): spec.process_sync_aggregate(state, block.body.sync_aggregate) - if is_post_merge(spec): - if spec.is_execution_enabled(state, block.body): - spec.process_execution_payload(state, block.body.execution_payload, spec.EXECUTION_ENGINE) # Insert post-state rot block.state_root = state.hash_tree_root() @@ -196,8 +196,7 @@ def test_parent_from_same_slot(spec, state): child_block.parent_root = state.latest_block_header.hash_tree_root() if is_post_merge(spec): - randao_mix = spec.compute_randao_mix(state, child_block.body.randao_reveal) - child_block.body.execution_payload = build_empty_execution_payload(spec, state, randao_mix) + child_block.body.execution_payload = build_empty_execution_payload(spec, state) # Show that normal path through transition fails failed_state = state.copy() From d0889b9001e006173dfe3679196efd5abf7fd7cb Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Fri, 17 Sep 2021 16:20:25 +0600 Subject: [PATCH 10/17] Hardcode terminal total difficulty --- configs/mainnet.yaml | 3 --- configs/minimal.yaml | 3 --- specs/merge/beacon-chain.md | 7 +++++++ specs/merge/client-settings.md | 9 ++------- specs/merge/fork-choice.md | 21 ++++++-------------- specs/merge/fork.md | 35 ---------------------------------- specs/merge/validator.md | 5 ++--- 7 files changed, 17 insertions(+), 66 deletions(-) diff --git a/configs/mainnet.yaml b/configs/mainnet.yaml index dd5b394af..aa1858b69 100644 --- a/configs/mainnet.yaml +++ b/configs/mainnet.yaml @@ -31,9 +31,6 @@ MERGE_FORK_EPOCH: 18446744073709551615 SHARDING_FORK_VERSION: 0x03000000 SHARDING_FORK_EPOCH: 18446744073709551615 -# TBD, 2**32 is a placeholder. Merge transition approach is in active R&D. -MIN_ANCHOR_POW_BLOCK_DIFFICULTY: 4294967296 - # Time parameters # --------------------------------------------------------------- diff --git a/configs/minimal.yaml b/configs/minimal.yaml index b067f222f..9f5cdbe32 100644 --- a/configs/minimal.yaml +++ b/configs/minimal.yaml @@ -30,9 +30,6 @@ MERGE_FORK_EPOCH: 18446744073709551615 SHARDING_FORK_VERSION: 0x03000001 SHARDING_FORK_EPOCH: 18446744073709551615 -# TBD, 2**32 is a placeholder. Merge transition approach is in active R&D. -MIN_ANCHOR_POW_BLOCK_DIFFICULTY: 4294967296 - # Time parameters # --------------------------------------------------------------- diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 2e7d26954..75a667b34 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -14,6 +14,7 @@ - [Execution](#execution) - [Configuration](#configuration) - [Genesis testing settings](#genesis-testing-settings) + - [Transition](#transition) - [Containers](#containers) - [Extended containers](#extended-containers) - [`BeaconBlockBody`](#beaconblockbody) @@ -76,6 +77,12 @@ This patch adds transaction execution to the beacon chain as part of the Merge f | `GENESIS_GAS_LIMIT` | `uint64(30000000)` (= 30,000,000) | | `GENESIS_BASE_FEE_PER_GAS` | `Bytes32('0x00ca9a3b00000000000000000000000000000000000000000000000000000000')` (= 1,000,000,000) | +### Transition settings + +| Name | Value | +| - | - | +| `TERMINAL_TOTAL_DIFFICULTY` | `uint256(2**256 - 1)` | + ## Containers ### Extended containers diff --git a/specs/merge/client-settings.md b/specs/merge/client-settings.md index a8ca633ff..64b2b20e6 100644 --- a/specs/merge/client-settings.md +++ b/specs/merge/client-settings.md @@ -15,12 +15,7 @@ This document specifies configurable settings that clients must implement for th ### Override terminal total difficulty -To coordinate manual overrides to [`terminal_total_difficulty`](fork-choice.md#transitionstore), clients -must provide `--terminal-total-difficulty-override` as a configurable setting. +To coordinate manual overrides to [`TERMINAL_TOTAL_DIFFICULTY`](./beacon-chain.md#Transition-settings) parameter, clients must provide `--terminal-total-difficulty-override` as a configurable setting. The value provided by this setting must take precedence over pre-configured `TERMINAL_TOTAL_DIFFICULTY` parameter. -If `TransitionStore` has already [been initialized](./fork.md#initializing-transition-store), this alters the previously initialized value of -`TransitionStore.terminal_total_difficulty`, otherwise this setting initializes `TransitionStore` with the specified, bypassing `compute_terminal_total_difficulty` and the use of an `anchor_pow_block`. -`terminal_total_difficulty`. +Except under exceptional scenarios, this setting is expected to not be used. Sufficient warning to the user about this exceptional configurable setting should be provided. -Except under exceptional scenarios, this setting is expected to not be used, and `terminal_total_difficulty` will operate with [default functionality](./fork.md#initializing-transition-store). Sufficient warning to the user about this exceptional configurable setting should be provided. -[here](fork.md#initializing-transition-store). diff --git a/specs/merge/fork-choice.md b/specs/merge/fork-choice.md index 84be0f1ce..8051ab3eb 100644 --- a/specs/merge/fork-choice.md +++ b/specs/merge/fork-choice.md @@ -13,7 +13,6 @@ - [`set_head`](#set_head) - [`finalize_block`](#finalize_block) - [Helpers](#helpers) - - [`TransitionStore`](#transitionstore) - [`PowBlock`](#powblock) - [`get_pow_block`](#get_pow_block) - [`is_valid_terminal_pow_block`](#is_valid_terminal_pow_block) @@ -68,14 +67,6 @@ def finalize_block(self: ExecutionEngine, block_hash: Hash32) -> bool: ## Helpers -### `TransitionStore` - -```python -@dataclass -class TransitionStore(object): - terminal_total_difficulty: uint256 -``` - ### `PowBlock` ```python @@ -98,9 +89,9 @@ Let `get_pow_block(block_hash: Hash32) -> PowBlock` be the function that given t Used by fork-choice handler, `on_block`. ```python -def is_valid_terminal_pow_block(transition_store: TransitionStore, block: PowBlock, parent: PowBlock) -> bool: - is_total_difficulty_reached = block.total_difficulty >= transition_store.terminal_total_difficulty - is_parent_total_difficulty_valid = parent.total_difficulty < transition_store.terminal_total_difficulty +def is_valid_terminal_pow_block(block: PowBlock, parent: PowBlock) -> bool: + is_total_difficulty_reached = block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY + is_parent_total_difficulty_valid = parent.total_difficulty < TERMINAL_TOTAL_DIFFICULTY return is_total_difficulty_reached and is_parent_total_difficulty_valid ``` @@ -111,7 +102,7 @@ def is_valid_terminal_pow_block(transition_store: TransitionStore, block: PowBlo *Note*: The only modification is the addition of the verification of transition block conditions. ```python -def on_block(store: Store, signed_block: SignedBeaconBlock, transition_store: TransitionStore=None) -> None: +def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: block = signed_block.message # Parent block must be known assert block.parent_root in store.block_states @@ -131,10 +122,10 @@ def on_block(store: Store, signed_block: SignedBeaconBlock, transition_store: Tr state_transition(state, signed_block, True) # [New in Merge] - if (transition_store is not None) and is_merge_block(pre_state, block.body): + if is_merge_block(pre_state, block.body): pow_block = get_pow_block(block.body.execution_payload.parent_hash) pow_parent = get_pow_block(pow_block.parent_hash) - assert is_valid_terminal_pow_block(transition_store, pow_block, pow_parent) + assert is_valid_terminal_pow_block(pow_block, pow_parent) # Add new block to the store store.blocks[hash_tree_root(block)] = block diff --git a/specs/merge/fork.md b/specs/merge/fork.md index f2547758d..eb0ca91c6 100644 --- a/specs/merge/fork.md +++ b/specs/merge/fork.md @@ -12,7 +12,6 @@ - [Fork to Merge](#fork-to-merge) - [Fork trigger](#fork-trigger) - [Upgrading the state](#upgrading-the-state) - - [Initializing transition store](#initializing-transition-store) @@ -28,8 +27,6 @@ Warning: this configuration is not definitive. | - | - | | `MERGE_FORK_VERSION` | `Version('0x02000000')` | | `MERGE_FORK_EPOCH` | `Epoch(18446744073709551615)` **TBD** | -| `MIN_ANCHOR_POW_BLOCK_DIFFICULTY` | **TBD** | -| `TARGET_SECONDS_TO_MERGE` | `uint64(7 * 86400)` = (604,800) | ## Fork to Merge @@ -37,8 +34,6 @@ Warning: this configuration is not definitive. 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 `MERGE_FORK_EPOCH`. -Since the Merge transition process relies on `Eth1Data` in the beacon state we do want to make sure that this data is fresh. This is achieved by forcing `MERGE_FORK_EPOCH` to point to eth1 voting period boundary, i.e. `MERGE_FORK_EPOCH` should satisfy the following condition `MERGE_FORK_EPOCH % EPOCHS_PER_ETH1_VOTING_PERIOD == 0`. - Note that for the pure Merge networks, we don't apply `upgrade_to_merge` since it starts with Merge version logic. ### Upgrading the state @@ -100,33 +95,3 @@ def upgrade_to_merge(pre: altair.BeaconState) -> BeaconState: return post ``` - -### Initializing transition store - -If `state.slot % SLOTS_PER_EPOCH == 0`, `compute_epoch_at_slot(state.slot) == MERGE_FORK_EPOCH`, and the transition store has not already been initialized, a transition store is initialized to be further utilized by the transition process of the Merge. - -Transition store initialization occurs after the state has been modified by corresponding `upgrade_to_merge` function. - -```python -def compute_terminal_total_difficulty(anchor_pow_block: PowBlock) -> uint256: - seconds_per_voting_period = EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH * SECONDS_PER_SLOT - pow_blocks_per_voting_period = seconds_per_voting_period // SECONDS_PER_ETH1_BLOCK - pow_blocks_to_merge = TARGET_SECONDS_TO_MERGE // SECONDS_PER_ETH1_BLOCK - pow_blocks_after_anchor_block = ETH1_FOLLOW_DISTANCE + pow_blocks_per_voting_period + pow_blocks_to_merge - anchor_difficulty = max(MIN_ANCHOR_POW_BLOCK_DIFFICULTY, anchor_pow_block.difficulty) - - return anchor_pow_block.total_difficulty + anchor_difficulty * pow_blocks_after_anchor_block - - -def get_transition_store(anchor_pow_block: PowBlock) -> TransitionStore: - terminal_total_difficulty = compute_terminal_total_difficulty(anchor_pow_block) - return TransitionStore(terminal_total_difficulty=terminal_total_difficulty) - - -def initialize_transition_store(state: BeaconState) -> TransitionStore: - pow_block = get_pow_block(state.eth1_data.block_hash) - return get_transition_store(pow_block) -``` - -*Note*: Transition store can also be initialized at client startup by [overriding terminal total -difficulty](client_settings.md#override-terminal-total-difficulty). diff --git a/specs/merge/validator.md b/specs/merge/validator.md index b7a2860fd..645e1967b 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -62,7 +62,7 @@ All validator responsibilities remain unchanged other than those noted below. Na ##### Execution Payload -* Set `block.body.execution_payload = get_execution_payload(state, transition_store, execution_engine, pow_chain)` where: +* Set `block.body.execution_payload = get_execution_payload(state, execution_engine, pow_chain)` where: ```python def get_pow_block_at_total_difficulty(total_difficulty: uint256, pow_chain: Sequence[PowBlock]) -> Optional[PowBlock]: @@ -84,11 +84,10 @@ def produce_execution_payload(state: BeaconState, def get_execution_payload(state: BeaconState, - transition_store: TransitionStore, execution_engine: ExecutionEngine, pow_chain: Sequence[PowBlock]) -> ExecutionPayload: if not is_merge_complete(state): - terminal_pow_block = get_pow_block_at_total_difficulty(transition_store.terminal_total_difficulty, pow_chain) + 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() From a48ea83ab80f270e729c3d57b68d38080889d4fe Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Fri, 17 Sep 2021 16:54:06 +0600 Subject: [PATCH 11/17] Fix toc --- 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 75a667b34..a9eea2f4a 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -14,7 +14,7 @@ - [Execution](#execution) - [Configuration](#configuration) - [Genesis testing settings](#genesis-testing-settings) - - [Transition](#transition) + - [Transition settings](#transition-settings) - [Containers](#containers) - [Extended containers](#extended-containers) - [`BeaconBlockBody`](#beaconblockbody) From c2084ad5b010de955a2b58430134678fb8e738df Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Fri, 17 Sep 2021 18:23:21 +0600 Subject: [PATCH 12/17] Bring on extra_data field --- specs/merge/beacon-chain.md | 4 ++++ tests/core/pyspec/eth2spec/test/helpers/execution_payload.py | 2 ++ 2 files changed, 6 insertions(+) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index ed12d3eb6..a2a37e215 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -64,6 +64,7 @@ This patch adds transaction execution to the beacon chain as part of the Merge f | `BYTES_PER_LOGS_BLOOM` | `uint64(2**8)` (= 256) | | `GAS_LIMIT_DENOMINATOR` | `uint64(2**10)` (= 1,024) | | `MIN_GAS_LIMIT` | `uint64(5000)` (= 5,000) | +| `MAX_EXTRA_DATA_BYTES` | `2**5` (= 32) | ## Configuration @@ -159,6 +160,7 @@ class ExecutionPayload(Container): gas_limit: uint64 gas_used: uint64 timestamp: uint64 + extra_data: ByteList[MAX_EXTRA_DATA_BYTES] base_fee_per_gas: Bytes32 # base fee introduced in EIP-1559, little-endian serialized # Extra payload fields block_hash: Hash32 # Hash of execution block @@ -180,6 +182,7 @@ class ExecutionPayloadHeader(Container): gas_limit: uint64 gas_used: uint64 timestamp: uint64 + extra_data: ByteList[MAX_EXTRA_DATA_BYTES] base_fee_per_gas: Bytes32 # Extra payload fields block_hash: Hash32 # Hash of execution block @@ -310,6 +313,7 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe gas_limit=payload.gas_limit, gas_used=payload.gas_used, timestamp=payload.timestamp, + extra_data=payload.extra_data, base_fee_per_gas=payload.base_fee_per_gas, block_hash=payload.block_hash, transactions_root=hash_tree_root(payload.transactions), diff --git a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py index 43be965a5..6126346a9 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py @@ -20,6 +20,7 @@ def build_empty_execution_payload(spec, state, randao_mix=None): gas_limit=latest.gas_limit, # retain same limit gas_used=0, # empty block, 0 gas timestamp=timestamp, + extra_data=spec.ByteList[spec.MAX_EXTRA_DATA_BYTES](), base_fee_per_gas=latest.base_fee_per_gas, # retain same base_fee block_hash=spec.Hash32(), transactions=empty_txs, @@ -42,6 +43,7 @@ def get_execution_payload_header(spec, execution_payload): gas_limit=execution_payload.gas_limit, gas_used=execution_payload.gas_used, timestamp=execution_payload.timestamp, + extra_data=execution_payload.extra_data, base_fee_per_gas=execution_payload.base_fee_per_gas, block_hash=execution_payload.block_hash, transactions_root=spec.hash_tree_root(execution_payload.transactions) From 3ef13561e68ee3522e481a587cf3ad3b702849e0 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 17 Sep 2021 11:00:32 -0600 Subject: [PATCH 13/17] ensure random is validated for all payloads including transition --- specs/merge/beacon-chain.md | 6 ++-- .../test_process_execution_payload.py | 28 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index a2a37e215..4a9fb8a5f 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -291,12 +291,14 @@ def is_valid_gas_limit(payload: ExecutionPayload, parent: ExecutionPayloadHeader ```python def process_execution_payload(state: BeaconState, payload: ExecutionPayload, execution_engine: ExecutionEngine) -> None: - # Verify consistency of the parent hash, block number, random, base fee per gas and gas limit + # Verify consistency of the parent hash, block number, base fee per gas and gas limit + # with respect to the previous execution payload header if is_merge_complete(state): assert payload.parent_hash == state.latest_execution_payload_header.block_hash assert payload.block_number == state.latest_execution_payload_header.block_number + uint64(1) - assert payload.random == get_randao_mix(state, get_current_epoch(state)) assert is_valid_gas_limit(payload, state.latest_execution_payload_header) + # Verify random + assert payload.random == get_randao_mix(state, get_current_epoch(state)) # Verify timestamp assert payload.timestamp == compute_timestamp_at_slot(state, state.slot) # Verify the execution payload is valid 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..9c5ed6712 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 @@ -144,6 +144,34 @@ def test_bad_parent_hash_regular_payload(spec, state): yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) +@with_merge_and_later +@spec_state_test +def test_bad_random_first_payload(spec, state): + # pre-state + state = build_state_with_incomplete_transition(spec, state) + next_slot(spec, state) + + # execution payload + execution_payload = build_empty_execution_payload(spec, state) + execution_payload.random = b'\x42' * 32 + + yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) + + +@with_merge_and_later +@spec_state_test +def test_bad_random_regular_payload(spec, state): + # pre-state + state = build_state_with_complete_transition(spec, state) + next_slot(spec, state) + + # execution payload + execution_payload = build_empty_execution_payload(spec, state) + execution_payload.random = b'\x04' * 32 + + yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) + + @with_merge_and_later @spec_state_test def test_bad_number_regular_payload(spec, state): From 9ca8c592c5b7074b8fa2cea6cf14b63bea46f360 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Sat, 18 Sep 2021 12:39:54 +0600 Subject: [PATCH 14/17] Add TBD for TTD and add the value to the configs --- configs/mainnet.yaml | 6 ++++++ configs/minimal.yaml | 6 ++++++ specs/merge/beacon-chain.md | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/configs/mainnet.yaml b/configs/mainnet.yaml index aa1858b69..6f2f582fa 100644 --- a/configs/mainnet.yaml +++ b/configs/mainnet.yaml @@ -3,6 +3,12 @@ # Extends the mainnet preset PRESET_BASE: 'mainnet' +# Transition +# --------------------------------------------------------------- +# TBD, 2**256-1 is a placeholder +TERMINAL_TOTAL_DIFFICULTY: 115792089237316195423570985008687907853269984665640564039457584007913129639935 + + # Genesis # --------------------------------------------------------------- # `2**14` (= 16,384) diff --git a/configs/minimal.yaml b/configs/minimal.yaml index 9f5cdbe32..8da3260f5 100644 --- a/configs/minimal.yaml +++ b/configs/minimal.yaml @@ -3,6 +3,12 @@ # Extends the minimal preset PRESET_BASE: 'minimal' +# Transition +# --------------------------------------------------------------- +# TBD, 2**256-1 is a placeholder +TERMINAL_TOTAL_DIFFICULTY: 115792089237316195423570985008687907853269984665640564039457584007913129639935 + + # Genesis # --------------------------------------------------------------- # [customized] diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index a9eea2f4a..03bf01155 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -81,7 +81,7 @@ This patch adds transaction execution to the beacon chain as part of the Merge f | Name | Value | | - | - | -| `TERMINAL_TOTAL_DIFFICULTY` | `uint256(2**256 - 1)` | +| `TERMINAL_TOTAL_DIFFICULTY` | **TBD** | ## Containers From cb9e65ab85602661d9625af002247f772453581b Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Fri, 17 Sep 2021 18:23:21 +0600 Subject: [PATCH 15/17] Bring on extra_data field --- specs/merge/beacon-chain.md | 4 ++++ tests/core/pyspec/eth2spec/test/helpers/execution_payload.py | 2 ++ 2 files changed, 6 insertions(+) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 03bf01155..4f89908fd 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -65,6 +65,7 @@ This patch adds transaction execution to the beacon chain as part of the Merge f | `BYTES_PER_LOGS_BLOOM` | `uint64(2**8)` (= 256) | | `GAS_LIMIT_DENOMINATOR` | `uint64(2**10)` (= 1,024) | | `MIN_GAS_LIMIT` | `uint64(5000)` (= 5,000) | +| `MAX_EXTRA_DATA_BYTES` | `2**5` (= 32) | ## Configuration @@ -166,6 +167,7 @@ class ExecutionPayload(Container): gas_limit: uint64 gas_used: uint64 timestamp: uint64 + extra_data: ByteList[MAX_EXTRA_DATA_BYTES] base_fee_per_gas: Bytes32 # base fee introduced in EIP-1559, little-endian serialized # Extra payload fields block_hash: Hash32 # Hash of execution block @@ -187,6 +189,7 @@ class ExecutionPayloadHeader(Container): gas_limit: uint64 gas_used: uint64 timestamp: uint64 + extra_data: ByteList[MAX_EXTRA_DATA_BYTES] base_fee_per_gas: Bytes32 # Extra payload fields block_hash: Hash32 # Hash of execution block @@ -317,6 +320,7 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe gas_limit=payload.gas_limit, gas_used=payload.gas_used, timestamp=payload.timestamp, + extra_data=payload.extra_data, base_fee_per_gas=payload.base_fee_per_gas, block_hash=payload.block_hash, transactions_root=hash_tree_root(payload.transactions), diff --git a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py index 43be965a5..6126346a9 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/helpers/execution_payload.py @@ -20,6 +20,7 @@ def build_empty_execution_payload(spec, state, randao_mix=None): gas_limit=latest.gas_limit, # retain same limit gas_used=0, # empty block, 0 gas timestamp=timestamp, + extra_data=spec.ByteList[spec.MAX_EXTRA_DATA_BYTES](), base_fee_per_gas=latest.base_fee_per_gas, # retain same base_fee block_hash=spec.Hash32(), transactions=empty_txs, @@ -42,6 +43,7 @@ def get_execution_payload_header(spec, execution_payload): gas_limit=execution_payload.gas_limit, gas_used=execution_payload.gas_used, timestamp=execution_payload.timestamp, + extra_data=execution_payload.extra_data, base_fee_per_gas=execution_payload.base_fee_per_gas, block_hash=execution_payload.block_hash, transactions_root=spec.hash_tree_root(execution_payload.transactions) From 731bcad3175f36d1d99d1343b49e0584cae1ad8e Mon Sep 17 00:00:00 2001 From: ethDreamer <37123614+ethDreamer@users.noreply.github.com> Date: Wed, 15 Sep 2021 18:47:44 -0500 Subject: [PATCH 16/17] fixed client-settings.md link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b5a898d37..9e5d7dc42 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ The merge is still actively in development. The exact specification has not been * [Merge fork](specs/merge/fork.md) * [Fork Choice changes](specs/merge/fork-choice.md) * [Validator additions](specs/merge/validator.md) - * [Client settings](specs/merge/client_settings.md) + * [Client settings](specs/merge/client-settings.md) ### Sharding From e2af59c8cd5557de68d0dd890f04e337cd58e7d3 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 17 Sep 2021 11:00:32 -0600 Subject: [PATCH 17/17] ensure random is validated for all payloads including transition --- specs/merge/beacon-chain.md | 6 ++-- .../test_process_execution_payload.py | 28 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 4f89908fd..39f457252 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -298,12 +298,14 @@ def is_valid_gas_limit(payload: ExecutionPayload, parent: ExecutionPayloadHeader ```python def process_execution_payload(state: BeaconState, payload: ExecutionPayload, execution_engine: ExecutionEngine) -> None: - # Verify consistency of the parent hash, block number, random, base fee per gas and gas limit + # Verify consistency of the parent hash, block number, base fee per gas and gas limit + # with respect to the previous execution payload header if is_merge_complete(state): assert payload.parent_hash == state.latest_execution_payload_header.block_hash assert payload.block_number == state.latest_execution_payload_header.block_number + uint64(1) - assert payload.random == get_randao_mix(state, get_current_epoch(state)) assert is_valid_gas_limit(payload, state.latest_execution_payload_header) + # Verify random + assert payload.random == get_randao_mix(state, get_current_epoch(state)) # Verify timestamp assert payload.timestamp == compute_timestamp_at_slot(state, state.slot) # Verify the execution payload is valid 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..9c5ed6712 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 @@ -144,6 +144,34 @@ def test_bad_parent_hash_regular_payload(spec, state): yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) +@with_merge_and_later +@spec_state_test +def test_bad_random_first_payload(spec, state): + # pre-state + state = build_state_with_incomplete_transition(spec, state) + next_slot(spec, state) + + # execution payload + execution_payload = build_empty_execution_payload(spec, state) + execution_payload.random = b'\x42' * 32 + + yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) + + +@with_merge_and_later +@spec_state_test +def test_bad_random_regular_payload(spec, state): + # pre-state + state = build_state_with_complete_transition(spec, state) + next_slot(spec, state) + + # execution payload + execution_payload = build_empty_execution_payload(spec, state) + execution_payload.random = b'\x04' * 32 + + yield from run_execution_payload_processing(spec, state, execution_payload, valid=False) + + @with_merge_and_later @spec_state_test def test_bad_number_regular_payload(spec, state):