From 5582490364031ca014f79084b36ce14a52358899 Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 14 Jul 2021 13:40:34 +0200 Subject: [PATCH 01/19] Apply missing spec_test decorators, to handle generator_mode flag --- .../altair/block_processing/test_process_sync_aggregate.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_aggregate.py b/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_aggregate.py index b9ac8a954..ad285f43c 100644 --- a/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_aggregate.py +++ b/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_aggregate.py @@ -26,6 +26,7 @@ from eth2spec.test.context import ( with_presets, spec_state_test, always_bls, + spec_test, ) from eth2spec.utils.hash_function import hash @@ -534,6 +535,7 @@ def test_random_all_but_one_participating_with_duplicates(spec, state): @with_altair_and_later @with_presets([MAINNET], reason="to create duplicate committee") +@spec_test @with_custom_state(balances_fn=misc_balances, threshold_fn=default_activation_threshold) @single_phase def test_random_misc_balances_and_half_participation_with_duplicates(spec, state): @@ -596,6 +598,7 @@ def test_random_all_but_one_participating_without_duplicates(spec, state): @with_altair_and_later @with_presets([MINIMAL], reason="to create nonduplicate committee") +@spec_test @with_custom_state(balances_fn=misc_balances, threshold_fn=default_activation_threshold) @single_phase def test_random_misc_balances_and_half_participation_without_duplicates(spec, state): From c420968f664d6400ebfadab56ebdc9a3bb66e93a Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 14 Jul 2021 11:08:54 -0600 Subject: [PATCH 02/19] fix last beta.1 mainnet test --- .../altair/epoch_processing/test_process_inactivity_updates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/altair/epoch_processing/test_process_inactivity_updates.py b/tests/core/pyspec/eth2spec/test/altair/epoch_processing/test_process_inactivity_updates.py index fd1bf3c57..634c7bc48 100644 --- a/tests/core/pyspec/eth2spec/test/altair/epoch_processing/test_process_inactivity_updates.py +++ b/tests/core/pyspec/eth2spec/test/altair/epoch_processing/test_process_inactivity_updates.py @@ -218,7 +218,7 @@ def test_some_slashed_zero_scores_full_participation(spec, state): @spec_state_test @leaking() def test_some_slashed_zero_scores_full_participation_leaking(spec, state): - slash_some_validators(spec, state, rng=Random(33221)) + slash_some_validators(spec, state, rng=Random(332243)) yield from run_inactivity_scores_test( spec, state, set_full_participation, zero_inactivity_scores, From a4a050e97af47bde6ee1aa0d1a8facec6c4fed6d Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Fri, 16 Jul 2021 19:16:32 +0600 Subject: [PATCH 03/19] Enforce terminal PoW block to be on the cusp --- setup.py | 2 +- specs/merge/fork-choice.md | 9 ++++++--- specs/merge/validator.md | 3 ++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index fa73e0824..c9b22b1a9 100644 --- a/setup.py +++ b/setup.py @@ -509,7 +509,7 @@ ExecutionState = Any def get_pow_block(hash: Bytes32) -> PowBlock: - return PowBlock(block_hash=hash, is_valid=True, is_processed=True, + return PowBlock(block_hash=hash, parent_hash=Bytes32(), is_valid=True, is_processed=True, total_difficulty=uint256(0), difficulty=uint256(0)) diff --git a/specs/merge/fork-choice.md b/specs/merge/fork-choice.md index d0f327137..e21b54a7e 100644 --- a/specs/merge/fork-choice.md +++ b/specs/merge/fork-choice.md @@ -82,6 +82,7 @@ class TransitionStore(object): @dataclass class PowBlock(object): block_hash: Hash32 + parent_hash: Hash32 is_processed: boolean is_valid: boolean total_difficulty: uint256 @@ -99,9 +100,10 @@ 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) -> bool: +def is_valid_terminal_pow_block(transition_store: TransitionStore, block: PowBlock, parent: PowBlock) -> bool: is_total_difficulty_reached = block.total_difficulty >= transition_store.transition_total_difficulty - return block.is_valid and is_total_difficulty_reached + is_parent_total_difficulty_valid = parent.total_difficulty < transition_store.transition_total_difficulty + return block.is_valid and is_total_difficulty_reached and is_parent_total_difficulty_valid ``` ## Updated fork-choice handlers @@ -130,8 +132,9 @@ def on_block(store: Store, signed_block: SignedBeaconBlock, transition_store: Tr if (transition_store is not None) and is_merge_block(pre_state, block): # 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) + 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() diff --git a/specs/merge/validator.md b/specs/merge/validator.md index 97e6a1deb..be8f8b274 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -90,7 +90,8 @@ def get_execution_payload(state: BeaconState, execution_engine: ExecutionEngine) -> ExecutionPayload: if not is_merge_complete(state): pow_block = get_pow_chain_head() - if not is_valid_terminal_pow_block(transition_store, pow_block): + pow_parent = get_pow_block(pow_block.parent_hash) + if not is_valid_terminal_pow_block(transition_store, pow_block, pow_parent): # Pre-merge, empty payload return ExecutionPayload() else: From f668b2b43342e3fe0c93c24f91357a3f62111fc9 Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Sat, 17 Jul 2021 16:26:18 +1000 Subject: [PATCH 04/19] Add tests for SyncAggregate with no participants and all zero signature. --- .../test_process_sync_aggregate.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_aggregate.py b/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_aggregate.py index ad285f43c..effd01047 100644 --- a/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_aggregate.py +++ b/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_aggregate.py @@ -113,6 +113,21 @@ def test_invalid_signature_missing_participant(spec, state): yield from run_sync_committee_processing(spec, state, block, expect_exception=True) +@with_altair_and_later +@spec_state_test +@always_bls +def test_invalid_signature_no_participants(spec, state): + committee_indices = compute_committee_indices(spec, state, state.current_sync_committee) + + block = build_empty_block_for_next_slot(spec, state) + # Exclude one participant whose signature was included. + block.body.sync_aggregate = spec.SyncAggregate( + sync_committee_bits=[False for _ in committee_indices], + sync_committee_signature=b'\x00' * 96 + ) + yield from run_sync_committee_processing(spec, state, block, expect_exception=True) + + @with_altair_and_later @spec_state_test @always_bls From add00ad2e090baba86971f3b68b13f9ecd15c72c Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Sat, 17 Jul 2021 12:33:06 +0600 Subject: [PATCH 05/19] Replace get_pow_chain_head with get_pow_block_at_total_difficulty in validator.md --- setup.py | 1 + specs/merge/validator.md | 28 +++++++++++++++++----------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/setup.py b/setup.py index c9b22b1a9..69ae05666 100644 --- a/setup.py +++ b/setup.py @@ -496,6 +496,7 @@ class MergeSpecBuilder(Phase0SpecBuilder): from typing import Protocol from eth2spec.phase0 import {preset_name} as phase0 from eth2spec.utils.ssz.ssz_typing import Bytes20, ByteList, ByteVector, uint256, Union +from typing import Union as PyUnion ''' @classmethod diff --git a/specs/merge/validator.md b/specs/merge/validator.md index be8f8b274..22569887e 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -19,7 +19,6 @@ - [Block proposal](#block-proposal) - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) - [Execution Payload](#execution-payload) - - [`get_pow_chain_head`](#get_pow_chain_head) @@ -63,13 +62,20 @@ All validator responsibilities remain unchanged other than those noted below. Na ##### Execution Payload -###### `get_pow_chain_head` - -Let `get_pow_chain_head() -> PowBlock` be the function that returns the head of the PoW chain. The body of the function is implementation specific. - -* Set `block.body.execution_payload = get_execution_payload(state, transition_store, execution_engine)` where: +* Set `block.body.execution_payload = get_execution_payload(state, transition_store, execution_engine, pow_chain)` where: ```python +def get_pow_block_at_total_difficulty(total_difficulty: uint256, + pow_chain: Sequence[PowBlock]) -> PyUnion[PowBlock, None]: + # `pow_chain` abstractly represents all blocks in the PoW chain + for block in pow_chain: + parent = get_pow_block(block.parent_hash) + if block.total_difficulty >= total_difficulty and parent.total_difficulty < total_difficulty: + return block + + 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)) @@ -87,16 +93,16 @@ def produce_execution_payload(state: BeaconState, def get_execution_payload(state: BeaconState, transition_store: TransitionStore, randao_reveal: BLSSignature, - execution_engine: ExecutionEngine) -> ExecutionPayload: + execution_engine: ExecutionEngine, + pow_chain: Sequence[PowBlock]) -> ExecutionPayload: if not is_merge_complete(state): - pow_block = get_pow_chain_head() - pow_parent = get_pow_block(pow_block.parent_hash) - if not is_valid_terminal_pow_block(transition_store, pow_block, pow_parent): + terminal_pow_block = get_pow_block_at_total_difficulty(transition_store.transition_total_difficulty, pow_chain) + if terminal_pow_block is None: # Pre-merge, empty payload return ExecutionPayload() else: # Signify merge via producing on top of the last PoW block - return produce_execution_payload(state, pow_block.block_hash, randao_reveal, execution_engine) + return produce_execution_payload(state, terminal_pow_block.block_hash, randao_reveal, execution_engine) # Post-merge, normal payload parent_hash = state.latest_execution_payload_header.block_hash From 11d54af89d382ea14de42593f87eaf82890c3d49 Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Sat, 17 Jul 2021 16:34:41 +1000 Subject: [PATCH 06/19] Add test to confirm infinite signature is invalid when there are participants. --- .../test_process_sync_aggregate.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_aggregate.py b/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_aggregate.py index effd01047..2557715e4 100644 --- a/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_aggregate.py +++ b/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_aggregate.py @@ -128,6 +128,21 @@ def test_invalid_signature_no_participants(spec, state): yield from run_sync_committee_processing(spec, state, block, expect_exception=True) +@with_altair_and_later +@spec_state_test +@always_bls +def test_invalid_signature_infinite_signature_with_participants(spec, state): + committee_indices = compute_committee_indices(spec, state, state.current_sync_committee) + + block = build_empty_block_for_next_slot(spec, state) + # Exclude one participant whose signature was included. + block.body.sync_aggregate = spec.SyncAggregate( + sync_committee_bits=[True for _ in committee_indices], + sync_committee_signature=spec.G2_POINT_AT_INFINITY + ) + yield from run_sync_committee_processing(spec, state, block, expect_exception=True) + + @with_altair_and_later @spec_state_test @always_bls From f16cfe7c3a5933876d0e2b2006a8d5a57b9f3d03 Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 17 Jul 2021 14:46:25 +0200 Subject: [PATCH 07/19] update sync aggregate tests --- .../test_process_sync_aggregate.py | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_aggregate.py b/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_aggregate.py index 2557715e4..e4176ee58 100644 --- a/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_aggregate.py +++ b/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_aggregate.py @@ -117,27 +117,38 @@ def test_invalid_signature_missing_participant(spec, state): @spec_state_test @always_bls def test_invalid_signature_no_participants(spec, state): - committee_indices = compute_committee_indices(spec, state, state.current_sync_committee) - block = build_empty_block_for_next_slot(spec, state) - # Exclude one participant whose signature was included. + # No participants is an allowed case, but needs a specific signature, not the full-zeroed signature. block.body.sync_aggregate = spec.SyncAggregate( - sync_committee_bits=[False for _ in committee_indices], + sync_committee_bits=[False] * len(block.body.sync_aggregate.sync_committee_bits), sync_committee_signature=b'\x00' * 96 ) yield from run_sync_committee_processing(spec, state, block, expect_exception=True) +# No-participants, with valid signature, is tested in test_sync_committee_rewards_empty_participants already. + + +@with_altair_and_later +@spec_state_test +@always_bls +def test_invalid_signature_infinite_signature_with_all_participants(spec, state): + block = build_empty_block_for_next_slot(spec, state) + # Include all participants, try the special-case signature for no-participants + block.body.sync_aggregate = spec.SyncAggregate( + sync_committee_bits=[True] * len(block.body.sync_aggregate.sync_committee_bits), + sync_committee_signature=spec.G2_POINT_AT_INFINITY + ) + yield from run_sync_committee_processing(spec, state, block, expect_exception=True) + @with_altair_and_later @spec_state_test @always_bls -def test_invalid_signature_infinite_signature_with_participants(spec, state): - committee_indices = compute_committee_indices(spec, state, state.current_sync_committee) - +def test_invalid_signature_infinite_signature_with_single_participant(spec, state): block = build_empty_block_for_next_slot(spec, state) - # Exclude one participant whose signature was included. + # Try include a single participant with the special-case signature for no-participants. block.body.sync_aggregate = spec.SyncAggregate( - sync_committee_bits=[True for _ in committee_indices], + sync_committee_bits=[True] + ([False] * (len(block.body.sync_aggregate.sync_committee_bits) - 1)), sync_committee_signature=spec.G2_POINT_AT_INFINITY ) yield from run_sync_committee_processing(spec, state, block, expect_exception=True) From 5ad36bd3d5272eff047cfcd42cc1e7f01b22168e Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 17 Jul 2021 15:55:38 +0200 Subject: [PATCH 08/19] Update remerkleable to v0.1.22: list lookup speed improvement --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index fa73e0824..1b9cbefbb 100644 --- a/setup.py +++ b/setup.py @@ -1024,7 +1024,7 @@ setup( "py_ecc==5.2.0", "milagro_bls_binding==1.6.3", "dataclasses==0.6", - "remerkleable==0.1.21", + "remerkleable==0.1.22", RUAMEL_YAML_VERSION, "lru-dict==1.1.6", MARKO_VERSION, From 1e484b8d98fc55afbe686e2e4e635d6b3f1d19ef Mon Sep 17 00:00:00 2001 From: Piotr Chromiec Date: Mon, 19 Jul 2021 14:48:30 +0200 Subject: [PATCH 09/19] README typo fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d1a0b2876..f5ac598b6 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ This repository hosts the current Eth2 specifications. Discussions about design [![GitHub release](https://img.shields.io/github/v/release/ethereum/eth2.0-specs)](https://github.com/ethereum/eth2.0-specs/releases/) [![PyPI version](https://badge.fury.io/py/eth2spec.svg)](https://badge.fury.io/py/eth2spec) -Core specifications for Eth2 clients be found in [specs](specs/). These are divided into features. +Core specifications for Eth2 clients can be found in [specs](specs/). These are divided into features. Features are researched and developed in parallel, and then consolidated into sequential upgrades when ready. The current features are: From 758b828ecb5520f6821cdb57d60be0c8bb982ffa Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Mon, 19 Jul 2021 14:19:44 -0700 Subject: [PATCH 10/19] Update slashing helper to avoid proposer. Fixes #2521. --- .../test_process_inactivity_updates.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/altair/epoch_processing/test_process_inactivity_updates.py b/tests/core/pyspec/eth2spec/test/altair/epoch_processing/test_process_inactivity_updates.py index 634c7bc48..f7d2fa9c8 100644 --- a/tests/core/pyspec/eth2spec/test/altair/epoch_processing/test_process_inactivity_updates.py +++ b/tests/core/pyspec/eth2spec/test/altair/epoch_processing/test_process_inactivity_updates.py @@ -195,17 +195,24 @@ def test_random_inactivity_scores_full_participation_leaking(spec, state): assert spec.is_in_inactivity_leak(state) -def slash_some_validators(spec, state, rng=Random(40404040)): +def slash_some_validators_for_inactivity_scores_test(spec, state, rng=Random(40404040)): + # ``run_inactivity_scores_test`` runs at the next epoch from `state`. + # We retrieve the proposer of this future state to avoid + # accidentally slashing that validator + future_state = state.copy() + next_epoch_via_block(spec, future_state) + + proposer_index = spec.get_beacon_proposer_index(future_state) # Slash ~1/4 of validaors for validator_index in range(len(state.validators)): - if rng.choice(range(4)) == 0: + if rng.choice(range(4)) == 0 and validator_index != proposer_index: spec.slash_validator(state, validator_index) @with_altair_and_later @spec_state_test def test_some_slashed_zero_scores_full_participation(spec, state): - slash_some_validators(spec, state, rng=Random(33429)) + slash_some_validators_for_inactivity_scores_test(spec, state, rng=Random(33429)) yield from run_inactivity_scores_test( spec, state, set_full_participation, zero_inactivity_scores, @@ -218,7 +225,7 @@ def test_some_slashed_zero_scores_full_participation(spec, state): @spec_state_test @leaking() def test_some_slashed_zero_scores_full_participation_leaking(spec, state): - slash_some_validators(spec, state, rng=Random(332243)) + slash_some_validators_for_inactivity_scores_test(spec, state, rng=Random(332243)) yield from run_inactivity_scores_test( spec, state, set_full_participation, zero_inactivity_scores, @@ -239,7 +246,7 @@ def test_some_slashed_zero_scores_full_participation_leaking(spec, state): @spec_state_test def test_some_slashed_full_random(spec, state): rng = Random(1010222) - slash_some_validators(spec, state, rng=rng) + slash_some_validators_for_inactivity_scores_test(spec, state, rng=rng) yield from run_inactivity_scores_test( spec, state, randomize_attestation_participation, randomize_inactivity_scores, rng=rng, @@ -251,7 +258,7 @@ def test_some_slashed_full_random(spec, state): @leaking() def test_some_slashed_full_random_leaking(spec, state): rng = Random(1102233) - slash_some_validators(spec, state, rng=rng) + slash_some_validators_for_inactivity_scores_test(spec, state, rng=rng) yield from run_inactivity_scores_test( spec, state, randomize_previous_epoch_participation, randomize_inactivity_scores, rng=rng, From 65f6aa1b33b84a94b613a76fc9c6f3122e0e0344 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 20 Jul 2021 17:37:52 +0600 Subject: [PATCH 11/19] Replace PyUnion with Optional --- setup.py | 1 - specs/merge/validator.md | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 69ae05666..c9b22b1a9 100644 --- a/setup.py +++ b/setup.py @@ -496,7 +496,6 @@ class MergeSpecBuilder(Phase0SpecBuilder): from typing import Protocol from eth2spec.phase0 import {preset_name} as phase0 from eth2spec.utils.ssz.ssz_typing import Bytes20, ByteList, ByteVector, uint256, Union -from typing import Union as PyUnion ''' @classmethod diff --git a/specs/merge/validator.md b/specs/merge/validator.md index 22569887e..2fd7bcc22 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -65,8 +65,7 @@ All validator responsibilities remain unchanged other than those noted below. Na * Set `block.body.execution_payload = get_execution_payload(state, transition_store, execution_engine, pow_chain)` where: ```python -def get_pow_block_at_total_difficulty(total_difficulty: uint256, - pow_chain: Sequence[PowBlock]) -> PyUnion[PowBlock, None]: +def get_pow_block_at_total_difficulty(total_difficulty: uint256, pow_chain: Sequence[PowBlock]) -> Optional[PowBlock]: # `pow_chain` abstractly represents all blocks in the PoW chain for block in pow_chain: parent = get_pow_block(block.parent_hash) From 02a9fc460e4bbb806e7b18ae80e0bbaac810651b Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 21 Jul 2021 11:03:19 -0600 Subject: [PATCH 12/19] require aggregation bits to have at least one participant in sync contributions --- specs/altair/p2p-interface.md | 2 ++ specs/altair/validator.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/specs/altair/p2p-interface.md b/specs/altair/p2p-interface.md index e5e391ff5..ba8d09c04 100644 --- a/specs/altair/p2p-interface.md +++ b/specs/altair/p2p-interface.md @@ -139,6 +139,8 @@ def get_sync_subcommittee_pubkeys(state: BeaconState, subcommittee_index: uint64 - _[IGNORE]_ The contribution's slot is for the current slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance), i.e. `contribution.slot == current_slot`. - _[REJECT]_ The subcommittee index is in the allowed range, i.e. `contribution.subcommittee_index < SYNC_COMMITTEE_SUBNET_COUNT`. +- _[REJECT]_ The contribution has participants -- + that is, `len(set(bit for bit in contribution.aggregation_bits if bit == True)) >= 1`. - _[REJECT]_ `contribution_and_proof.selection_proof` selects the validator as an aggregator for the slot -- i.e. `is_sync_committee_aggregator(contribution_and_proof.selection_proof)` returns `True`. - _[REJECT]_ The aggregator's validator index is in the declared subcommittee of the current sync committee -- i.e. `state.validators[contribution_and_proof.aggregator_index].pubkey in get_sync_subcommittee_pubkeys(state, contribution.subcommittee_index)`. diff --git a/specs/altair/validator.md b/specs/altair/validator.md index 8742900bc..badef2de5 100644 --- a/specs/altair/validator.md +++ b/specs/altair/validator.md @@ -354,7 +354,7 @@ def is_sync_committee_aggregator(signature: BLSSignature) -> bool: If a validator is selected to aggregate the `SyncCommitteeMessage`s produced on a subnet during a given `slot`, they construct an aggregated `SyncCommitteeContribution`. -Given all of the (valid) collected `sync_committee_messages: Set[SyncCommitteeMessage]` from the `sync_committee_{subnet_id}` gossip during the selected `slot` with an equivalent `beacon_block_root` to that of the aggregator, the aggregator creates a `contribution: SyncCommitteeContribution` with the following fields: +Collect all of the (valid) `sync_committee_messages: Set[SyncCommitteeMessage]` from the `sync_committee_{subnet_id}` gossip during the selected `slot` with an equivalent `beacon_block_root` to that of the aggregator. If `len(sync_committee_messages) > 0`, the aggregator creates a `contribution: SyncCommitteeContribution` with the following fields: ###### Slot From f7a2a973acd9005a64abbf5f164d6b879cb1d6d9 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 21 Jul 2021 15:51:59 -0600 Subject: [PATCH 13/19] Update specs/altair/p2p-interface.md Co-authored-by: Alex Stokes --- specs/altair/p2p-interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/altair/p2p-interface.md b/specs/altair/p2p-interface.md index ba8d09c04..6820fd10a 100644 --- a/specs/altair/p2p-interface.md +++ b/specs/altair/p2p-interface.md @@ -140,7 +140,7 @@ def get_sync_subcommittee_pubkeys(state: BeaconState, subcommittee_index: uint64 - _[IGNORE]_ The contribution's slot is for the current slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance), i.e. `contribution.slot == current_slot`. - _[REJECT]_ The subcommittee index is in the allowed range, i.e. `contribution.subcommittee_index < SYNC_COMMITTEE_SUBNET_COUNT`. - _[REJECT]_ The contribution has participants -- - that is, `len(set(bit for bit in contribution.aggregation_bits if bit == True)) >= 1`. + that is, `any(contribution.aggregation_bits)`. - _[REJECT]_ `contribution_and_proof.selection_proof` selects the validator as an aggregator for the slot -- i.e. `is_sync_committee_aggregator(contribution_and_proof.selection_proof)` returns `True`. - _[REJECT]_ The aggregator's validator index is in the declared subcommittee of the current sync committee -- i.e. `state.validators[contribution_and_proof.aggregator_index].pubkey in get_sync_subcommittee_pubkeys(state, contribution.subcommittee_index)`. From 17fad2dea2ee7e0747d346cf600590721b9b9c59 Mon Sep 17 00:00:00 2001 From: protolambda Date: Thu, 22 Jul 2021 16:36:41 +0200 Subject: [PATCH 14/19] rebase The Merge onto Altair base functionality --- setup.py | 28 ++++----- specs/merge/beacon-chain.md | 58 +++++++++++++++++-- specs/merge/fork.md | 21 ++++--- specs/merge/validator.md | 8 +-- specs/sharding/beacon-chain.md | 21 +++---- specs/sharding/p2p-interface.md | 3 +- tests/core/pyspec/eth2spec/test/context.py | 20 ++----- .../eth2spec/test/helpers/block_processing.py | 1 + .../eth2spec/test/helpers/epoch_processing.py | 2 +- .../pyspec/eth2spec/test/helpers/genesis.py | 14 +++-- .../eth2spec/test/helpers/merge/fork.py | 8 ++- .../test/merge/fork/test_merge_fork_basic.py | 16 ++--- .../test/merge/fork/test_merge_fork_random.py | 20 +++---- 13 files changed, 132 insertions(+), 88 deletions(-) diff --git a/setup.py b/setup.py index 552c69740..4c6969a26 100644 --- a/setup.py +++ b/setup.py @@ -447,7 +447,7 @@ class AltairSpecBuilder(Phase0SpecBuilder): @classmethod def imports(cls, preset_name: str) -> str: return super().imports(preset_name) + '\n' + f''' -from typing import NewType, Union +from typing import NewType, Union as PyUnion from eth2spec.phase0 import {preset_name} as phase0 from eth2spec.utils.ssz.ssz_typing import Path @@ -463,7 +463,7 @@ GeneralizedIndex = NewType('GeneralizedIndex', int) @classmethod def sundry_functions(cls) -> str: return super().sundry_functions() + '\n\n' + ''' -def get_generalized_index(ssz_class: Any, *path: Sequence[Union[int, SSZVariableName]]) -> GeneralizedIndex: +def get_generalized_index(ssz_class: Any, *path: Sequence[PyUnion[int, SSZVariableName]]) -> GeneralizedIndex: ssz_path = Path(ssz_class) for item in path: ssz_path = ssz_path / item @@ -487,14 +487,14 @@ def get_generalized_index(ssz_class: Any, *path: Sequence[Union[int, SSZVariable # # MergeSpecBuilder # -class MergeSpecBuilder(Phase0SpecBuilder): +class MergeSpecBuilder(AltairSpecBuilder): fork: str = MERGE @classmethod def imports(cls, preset_name: str): return super().imports(preset_name) + f''' from typing import Protocol -from eth2spec.phase0 import {preset_name} as phase0 +from eth2spec.altair import {preset_name} as altair from eth2spec.utils.ssz.ssz_typing import Bytes20, ByteList, ByteVector, uint256, Union ''' @@ -844,19 +844,15 @@ class PySpecCommand(Command): if len(self.md_doc_paths) == 0: print("no paths were specified, using default markdown file paths for pyspec" " build (spec fork: %s)" % self.spec_fork) - if self.spec_fork == PHASE0: + if self.spec_fork in (PHASE0, ALTAIR, MERGE): self.md_doc_paths = """ specs/phase0/beacon-chain.md specs/phase0/fork-choice.md specs/phase0/validator.md specs/phase0/weak-subjectivity.md """ - elif self.spec_fork == ALTAIR: - self.md_doc_paths = """ - specs/phase0/beacon-chain.md - specs/phase0/fork-choice.md - specs/phase0/validator.md - specs/phase0/weak-subjectivity.md + if self.spec_fork in (ALTAIR, MERGE): + self.md_doc_paths += """ specs/altair/beacon-chain.md specs/altair/bls.md specs/altair/fork.md @@ -864,18 +860,14 @@ class PySpecCommand(Command): specs/altair/p2p-interface.md specs/altair/sync-protocol.md """ - elif self.spec_fork == MERGE: - self.md_doc_paths = """ - specs/phase0/beacon-chain.md - specs/phase0/fork-choice.md - specs/phase0/validator.md - specs/phase0/weak-subjectivity.md + if self.spec_fork == MERGE: + self.md_doc_paths += """ specs/merge/beacon-chain.md specs/merge/fork.md specs/merge/fork-choice.md specs/merge/validator.md """ - else: + if len(self.md_doc_paths) == 0: raise Exception('no markdown files specified, and spec fork "%s" is unknown', self.spec_fork) self.parsed_md_doc_paths = self.md_doc_paths.split() diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index 59ec3ce48..aa2e3b334 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -1,7 +1,5 @@ # Ethereum 2.0 The Merge -**Warning**: This document is currently based on [Phase 0](../phase0/beacon-chain.md) and will be rebased on [Altair](../altair/beacon-chain.md). - **Notice**: This document is a work-in-progress for researchers and implementers. ## Table of contents @@ -69,7 +67,17 @@ This patch adds transaction execution to the beacon chain as part of the Merge f #### `BeaconBlockBody` ```python -class BeaconBlockBody(phase0.BeaconBlockBody): +class BeaconBlockBody(Container): + randao_reveal: BLSSignature + eth1_data: Eth1Data # Eth1 data vote + graffiti: Bytes32 # Arbitrary data + # Operations + proposer_slashings: List[ProposerSlashing, MAX_PROPOSER_SLASHINGS] + attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS] + attestations: List[Attestation, MAX_ATTESTATIONS] + deposits: List[Deposit, MAX_DEPOSITS] + voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS] + sync_aggregate: SyncAggregate # Execution execution_payload: ExecutionPayload # [New in Merge] ``` @@ -77,7 +85,41 @@ class BeaconBlockBody(phase0.BeaconBlockBody): #### `BeaconState` ```python -class BeaconState(phase0.BeaconState): +class BeaconState(altair.BeaconState): + # Versioning + genesis_time: uint64 + genesis_validators_root: Root + slot: Slot + fork: Fork + # History + latest_block_header: BeaconBlockHeader + block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] + state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] + historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] + # Eth1 + eth1_data: Eth1Data + eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH] + eth1_deposit_index: uint64 + # Registry + validators: List[Validator, VALIDATOR_REGISTRY_LIMIT] + balances: List[Gwei, VALIDATOR_REGISTRY_LIMIT] + # Randomness + randao_mixes: Vector[Bytes32, EPOCHS_PER_HISTORICAL_VECTOR] + # Slashings + slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] # Per-epoch sums of slashed effective balances + # Participation + previous_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] + current_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] + # Finality + justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] # Bit set for every recent justified epoch + previous_justified_checkpoint: Checkpoint + current_justified_checkpoint: Checkpoint + finalized_checkpoint: Checkpoint + # Inactivity + inactivity_scores: List[uint64, VALIDATOR_REGISTRY_LIMIT] + # Sync + current_sync_committee: SyncCommittee + next_sync_committee: SyncCommittee # Execution latest_execution_payload_header: ExecutionPayloadHeader # [New in Merge] ``` @@ -190,6 +232,7 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: 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] ``` @@ -232,7 +275,7 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe *Note*: The function `initialize_beacon_state_from_eth1` is modified for pure Merge testing only. -*Note*: The function `initialize_beacon_state_from_eth1` is modified to use `MERGE_FORK_VERSION` and initialize `latest_execution_payload_header`. +*Note*: The function `initialize_beacon_state_from_eth1` is modified: (1) using `MERGE_FORK_VERSION` as the current fork version, (2) utilizing the Merge `BeaconBlockBody` when constructing the initial `latest_block_header`, and (3) initialize `latest_execution_payload_header`. ```python def initialize_beacon_state_from_eth1(eth1_block_hash: Bytes32, @@ -269,6 +312,11 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Bytes32, # Set genesis validators root for domain separation and chain versioning state.genesis_validators_root = hash_tree_root(state.validators) + # Fill in sync committees + # Note: A duplicate committee is assigned for the current and next committee at genesis + state.current_sync_committee = get_next_sync_committee(state) + state.next_sync_committee = get_next_sync_committee(state) + # [New in Merge] Initialize the execution payload header (with block number set to 0) state.latest_execution_payload_header.block_hash = eth1_block_hash state.latest_execution_payload_header.timestamp = eth1_timestamp diff --git a/specs/merge/fork.md b/specs/merge/fork.md index 1f2ea7fff..5c5c40a64 100644 --- a/specs/merge/fork.md +++ b/specs/merge/fork.md @@ -43,15 +43,18 @@ Note that for the pure Merge networks, we don't apply `upgrade_to_merge` since i ### Upgrading the state -If `state.slot % SLOTS_PER_EPOCH == 0` and `compute_epoch_at_slot(state.slot) == MERGE_FORK_EPOCH`, an irregular state change is made to upgrade to Merge. +As with the Phase0-to-Altair upgrade, the `state_transition` is modified to upgrade the `BeaconState`. +The `BeaconState` upgrade runs as part of `process_slots`, slots with missing block proposals do not affect the upgrade time. +If `state.slot % SLOTS_PER_EPOCH == 0` and `compute_epoch_at_slot(state.slot) == MERGE_FORK_EPOCH`, an irregular state change is made to upgrade to Merge. The upgrade occurs after the completion of the inner loop of `process_slots` that sets `state.slot` equal to `MERGE_FORK_EPOCH * SLOTS_PER_EPOCH`. -Care must be taken when transitioning through the fork boundary as implementations will need a modified [state transition function](../phase0/beacon-chain.md#beacon-chain-state-transition-function) that deviates from the Phase 0 document. -In particular, the outer `state_transition` function defined in the Phase 0 document will not expose the precise fork slot to execute the upgrade in the presence of skipped slots at the fork boundary. Instead the logic must be within `process_slots`. + +When multiple upgrades are scheduled for the same epoch (common for test-networks), +all the upgrades run in sequence before resuming the regular state transition. ```python -def upgrade_to_merge(pre: phase0.BeaconState) -> BeaconState: - epoch = phase0.get_current_epoch(pre) +def upgrade_to_merge(pre: altair.BeaconState) -> BeaconState: + epoch = altair.get_current_epoch(pre) post = BeaconState( # Versioning genesis_time=pre.genesis_time, @@ -78,14 +81,16 @@ def upgrade_to_merge(pre: phase0.BeaconState) -> BeaconState: randao_mixes=pre.randao_mixes, # Slashings slashings=pre.slashings, - # Attestations - previous_epoch_attestations=pre.previous_epoch_attestations, - current_epoch_attestations=pre.current_epoch_attestations, + # Participation + previous_epoch_participation=pre.previous_epoch_participation, + current_epoch_participation=pre.current_epoch_participation, # Finality justification_bits=pre.justification_bits, previous_justified_checkpoint=pre.previous_justified_checkpoint, current_justified_checkpoint=pre.current_justified_checkpoint, finalized_checkpoint=pre.finalized_checkpoint, + # Inactivity + inactivity_scores=pre.inactivity_scores, # Execution-layer latest_execution_payload_header=ExecutionPayloadHeader(), ) diff --git a/specs/merge/validator.md b/specs/merge/validator.md index 2fd7bcc22..baf716760 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -1,7 +1,5 @@ # Ethereum 2.0 The Merge -**Warning:** This document is currently based on [Phase 0](../phase0/validator.md) but will be rebased to [Altair](../altair/validator.md) once the latter is shipped. - **Notice**: This document is a work-in-progress for researchers and implementers. ## Table of contents @@ -29,9 +27,11 @@ This document represents the changes to be made in the code of an "honest valida ## Prerequisites -This document is an extension of the [Phase 0 -- Validator](../phase0/validator.md). All behaviors and definitions defined in the Phase 0 doc carry over unless explicitly noted or overridden. +This document is an extension of the [Altair -- Honest Validator](../altair/validator.md) guide. +All behaviors and definitions defined in this document, and documents it extends, carry over unless explicitly noted or overridden. -All terminology, constants, functions, and protocol mechanics defined in the updated Beacon Chain doc of [The Merge](./beacon-chain.md) are requisite for this document and used throughout. Please see related Beacon Chain doc before continuing and use them as a reference throughout. +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. ## Protocols diff --git a/specs/sharding/beacon-chain.md b/specs/sharding/beacon-chain.md index 5a460855b..4e88a8971 100644 --- a/specs/sharding/beacon-chain.md +++ b/specs/sharding/beacon-chain.md @@ -179,7 +179,7 @@ class BeaconBlockBody(merge.BeaconBlockBody): # [extends The Merge block body] ### `BeaconState` ```python -class BeaconState(merge.BeaconState): # [extends The Merge state] +class BeaconState(merge.BeaconState): # [Updated fields] (Warning: this changes with Altair, Sharding will rebase to use participation-flags) previous_epoch_attestations: List[PendingAttestation, MAX_ATTESTATIONS * SLOTS_PER_EPOCH] current_epoch_attestations: List[PendingAttestation, MAX_ATTESTATIONS * SLOTS_PER_EPOCH] @@ -494,9 +494,9 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: process_randao(state, block.body) process_eth1_data(state, block.body) process_operations(state, block.body) # [Modified in Sharding] - # Pre-merge, skip execution payload processing - if is_execution_enabled(state, block): - process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [New in Merge] + 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 @@ -527,7 +527,7 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: ```python def process_attestation(state: BeaconState, attestation: Attestation) -> None: - phase0.process_attestation(state, attestation) + altair.process_attestation(state, attestation) update_pending_shard_work(state, attestation) ``` @@ -681,25 +681,26 @@ This epoch transition overrides the Merge epoch transition: ```python def process_epoch(state: BeaconState) -> None: - # Sharding + # Sharding pre-processing process_pending_shard_confirmations(state) charge_confirmed_shard_fees(state) reset_pending_shard_work(state) - # Phase0 + # Base functionality process_justification_and_finalization(state) + process_inactivity_updates(state) process_rewards_and_penalties(state) process_registry_updates(state) process_slashings(state) - - # Final updates process_eth1_data_reset(state) process_effective_balance_updates(state) process_slashings_reset(state) process_randao_mixes_reset(state) process_historical_roots_update(state) - process_participation_record_updates(state) + process_participation_flag_updates(state) + process_sync_committee_updates(state) + # Sharding post-processing process_shard_epoch_increment(state) ``` diff --git a/specs/sharding/p2p-interface.md b/specs/sharding/p2p-interface.md index 51dbfd5a6..baf9494f2 100644 --- a/specs/sharding/p2p-interface.md +++ b/specs/sharding/p2p-interface.md @@ -29,8 +29,7 @@ ## Introduction -The specification of these changes continues in the same format as the [Phase0](../phase0/p2p-interface.md) and -[Altair](../altair/p2p-interface.md) network specifications, and assumes them as pre-requisite. +The specification of these changes continues in the same format as the network specifications of previous upgrades, and assumes them as pre-requisite. The adjustments and additions for Shards are outlined in this document. ## Constants diff --git a/tests/core/pyspec/eth2spec/test/context.py b/tests/core/pyspec/eth2spec/test/context.py index 362f64abf..7fddc0762 100644 --- a/tests/core/pyspec/eth2spec/test/context.py +++ b/tests/core/pyspec/eth2spec/test/context.py @@ -347,10 +347,6 @@ def with_phases(phases, other_phases=None): preset_name = kw.pop('preset') targets = spec_targets[preset_name] - # TODO: test state is dependent on phase0 but is immediately transitioned to later phases. - # A new state-creation helper for later phases may be in place, and then tests can run without phase0 - available_phases.add(PHASE0) - # Populate all phases for multi-phase tests phase_dir = {} if PHASE0 in available_phases: @@ -433,23 +429,15 @@ def with_config_overrides(config_overrides): def is_post_altair(spec): - if spec.fork == MERGE: # TODO: remove parallel Altair-Merge condition after rebase. - return False - if spec.fork in FORKS_BEFORE_ALTAIR: - return False - return True + return spec.fork not in FORKS_BEFORE_ALTAIR def is_post_merge(spec): - if spec.fork == ALTAIR: # TODO: remove parallel Altair-Merge condition after rebase. - return False - if spec.fork in FORKS_BEFORE_MERGE: - return False - return True + return spec.fork not in FORKS_BEFORE_MERGE -with_altair_and_later = with_phases([ALTAIR]) # TODO: include Merge, but not until Merge work is rebased. -with_merge_and_later = with_phases([MERGE]) +with_altair_and_later = with_phases([ALTAIR, MERGE]) +with_merge_and_later = with_phases([MERGE]) # TODO: include sharding when spec stabilizes. def fork_transition_test(pre_fork_name, post_fork_name, fork_epoch=None): diff --git a/tests/core/pyspec/eth2spec/test/helpers/block_processing.py b/tests/core/pyspec/eth2spec/test/helpers/block_processing.py index d2ec4a111..e82f62ed0 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/block_processing.py +++ b/tests/core/pyspec/eth2spec/test/helpers/block_processing.py @@ -30,6 +30,7 @@ def get_process_calls(spec): # Merge 'process_application_payload': lambda state, block: spec.process_application_payload(state, block.body), + # TODO: add sharding processing functions when spec stabilizes. # Custody Game 'process_custody_game_operations': lambda state, block: spec.process_custody_game_operations(state, block.body), diff --git a/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py b/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py index c783692fc..eed259e81 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py +++ b/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py @@ -28,7 +28,7 @@ def get_process_calls(spec): 'process_participation_record_updates' ), 'process_sync_committee_updates', # altair - 'process_shard_epoch_increment' # sharding + # TODO: add sharding processing functions when spec stabilizes. ] diff --git a/tests/core/pyspec/eth2spec/test/helpers/genesis.py b/tests/core/pyspec/eth2spec/test/helpers/genesis.py index 8617ce9f3..a9eb59f67 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/genesis.py +++ b/tests/core/pyspec/eth2spec/test/helpers/genesis.py @@ -1,7 +1,6 @@ from eth2spec.test.helpers.constants import ( - ALTAIR, - FORKS_BEFORE_ALTAIR, - MERGE, + ALTAIR, MERGE, + FORKS_BEFORE_ALTAIR, FORKS_BEFORE_MERGE, ) from eth2spec.test.helpers.keys import pubkeys @@ -25,11 +24,13 @@ def create_genesis_state(spec, validator_balances, activation_threshold): deposit_root = b'\x42' * 32 eth1_block_hash = b'\xda' * 32 + previous_version = spec.config.GENESIS_FORK_VERSION current_version = spec.config.GENESIS_FORK_VERSION if spec.fork == ALTAIR: current_version = spec.config.ALTAIR_FORK_VERSION elif spec.fork == MERGE: + previous_version = spec.config.ALTAIR_FORK_VERSION current_version = spec.config.MERGE_FORK_VERSION state = spec.BeaconState( @@ -41,7 +42,7 @@ def create_genesis_state(spec, validator_balances, activation_threshold): block_hash=eth1_block_hash, ), fork=spec.Fork( - previous_version=spec.config.GENESIS_FORK_VERSION, + previous_version=previous_version, current_version=current_version, epoch=spec.GENESIS_EPOCH, ), @@ -73,4 +74,9 @@ def create_genesis_state(spec, validator_balances, activation_threshold): state.current_sync_committee = spec.get_next_sync_committee(state) state.next_sync_committee = spec.get_next_sync_committee(state) + if spec.fork not in FORKS_BEFORE_MERGE: + # Initialize the execution payload header (with block number and genesis time set to 0) + state.latest_execution_payload_header.block_hash = eth1_block_hash + state.latest_execution_payload_header.random = eth1_block_hash + return state diff --git a/tests/core/pyspec/eth2spec/test/helpers/merge/fork.py b/tests/core/pyspec/eth2spec/test/helpers/merge/fork.py index 9b7f89366..3a55dc88c 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/merge/fork.py +++ b/tests/core/pyspec/eth2spec/test/helpers/merge/fork.py @@ -24,10 +24,14 @@ def run_fork_test(post_spec, pre_state): 'randao_mixes', # Slashings 'slashings', - # Attestations - 'previous_epoch_attestations', 'current_epoch_attestations', + # Participation + 'previous_epoch_participation', 'current_epoch_participation', # Finality 'justification_bits', 'previous_justified_checkpoint', 'current_justified_checkpoint', 'finalized_checkpoint', + # Inactivity + 'inactivity_scores', + # Sync + 'current_sync_committee', 'next_sync_committee' ] for field in stable_fields: assert getattr(pre_state, field) == getattr(post_state, field) diff --git a/tests/core/pyspec/eth2spec/test/merge/fork/test_merge_fork_basic.py b/tests/core/pyspec/eth2spec/test/merge/fork/test_merge_fork_basic.py index 066a656a8..d92b0015c 100644 --- a/tests/core/pyspec/eth2spec/test/merge/fork/test_merge_fork_basic.py +++ b/tests/core/pyspec/eth2spec/test/merge/fork/test_merge_fork_basic.py @@ -7,7 +7,7 @@ from eth2spec.test.context import ( ) from eth2spec.test.utils import with_meta_tags from eth2spec.test.helpers.constants import ( - PHASE0, MERGE, + ALTAIR, MERGE, MINIMAL, ) from eth2spec.test.helpers.state import ( @@ -20,7 +20,7 @@ from eth2spec.test.helpers.merge.fork import ( ) -@with_phases(phases=[PHASE0], other_phases=[MERGE]) +@with_phases(phases=[ALTAIR], other_phases=[MERGE]) @spec_test @with_state @with_meta_tags(MERGE_FORK_TEST_META_TAGS) @@ -28,7 +28,7 @@ def test_fork_base_state(spec, phases, state): yield from run_fork_test(phases[MERGE], state) -@with_phases(phases=[PHASE0], other_phases=[MERGE]) +@with_phases(phases=[ALTAIR], other_phases=[MERGE]) @spec_test @with_state @with_meta_tags(MERGE_FORK_TEST_META_TAGS) @@ -37,7 +37,7 @@ def test_fork_next_epoch(spec, phases, state): yield from run_fork_test(phases[MERGE], state) -@with_phases(phases=[PHASE0], other_phases=[MERGE]) +@with_phases(phases=[ALTAIR], other_phases=[MERGE]) @spec_test @with_state @with_meta_tags(MERGE_FORK_TEST_META_TAGS) @@ -46,7 +46,7 @@ def test_fork_next_epoch_with_block(spec, phases, state): yield from run_fork_test(phases[MERGE], state) -@with_phases(phases=[PHASE0], other_phases=[MERGE]) +@with_phases(phases=[ALTAIR], other_phases=[MERGE]) @spec_test @with_state @with_meta_tags(MERGE_FORK_TEST_META_TAGS) @@ -56,7 +56,7 @@ def test_fork_many_next_epoch(spec, phases, state): yield from run_fork_test(phases[MERGE], state) -@with_phases(phases=[PHASE0], other_phases=[MERGE]) +@with_phases(phases=[ALTAIR], other_phases=[MERGE]) @with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) @spec_test @with_meta_tags(MERGE_FORK_TEST_META_TAGS) @@ -64,7 +64,7 @@ def test_fork_random_low_balances(spec, phases, state): yield from run_fork_test(phases[MERGE], state) -@with_phases(phases=[PHASE0], other_phases=[MERGE]) +@with_phases(phases=[ALTAIR], other_phases=[MERGE]) @with_custom_state(balances_fn=misc_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) @spec_test @with_meta_tags(MERGE_FORK_TEST_META_TAGS) @@ -72,7 +72,7 @@ def test_fork_random_misc_balances(spec, phases, state): yield from run_fork_test(phases[MERGE], state) -@with_phases(phases=[PHASE0], other_phases=[MERGE]) +@with_phases(phases=[ALTAIR], other_phases=[MERGE]) @with_presets([MINIMAL], reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated") @with_custom_state(balances_fn=large_validator_set, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) diff --git a/tests/core/pyspec/eth2spec/test/merge/fork/test_merge_fork_random.py b/tests/core/pyspec/eth2spec/test/merge/fork/test_merge_fork_random.py index d790acd3a..cabde150f 100644 --- a/tests/core/pyspec/eth2spec/test/merge/fork/test_merge_fork_random.py +++ b/tests/core/pyspec/eth2spec/test/merge/fork/test_merge_fork_random.py @@ -9,7 +9,7 @@ from eth2spec.test.context import ( ) from eth2spec.test.utils import with_meta_tags from eth2spec.test.helpers.constants import ( - PHASE0, MERGE, + ALTAIR, MERGE, MINIMAL, ) from eth2spec.test.helpers.merge.fork import ( @@ -22,7 +22,7 @@ from eth2spec.test.helpers.random import ( ) -@with_phases(phases=[PHASE0], other_phases=[MERGE]) +@with_phases(phases=[ALTAIR], other_phases=[MERGE]) @spec_test @with_state @with_meta_tags(MERGE_FORK_TEST_META_TAGS) @@ -31,7 +31,7 @@ def test_merge_fork_random_0(spec, phases, state): yield from run_fork_test(phases[MERGE], state) -@with_phases(phases=[PHASE0], other_phases=[MERGE]) +@with_phases(phases=[ALTAIR], other_phases=[MERGE]) @spec_test @with_state @with_meta_tags(MERGE_FORK_TEST_META_TAGS) @@ -40,7 +40,7 @@ def test_merge_fork_random_1(spec, phases, state): yield from run_fork_test(phases[MERGE], state) -@with_phases(phases=[PHASE0], other_phases=[MERGE]) +@with_phases(phases=[ALTAIR], other_phases=[MERGE]) @spec_test @with_state @with_meta_tags(MERGE_FORK_TEST_META_TAGS) @@ -49,7 +49,7 @@ def test_merge_fork_random_2(spec, phases, state): yield from run_fork_test(phases[MERGE], state) -@with_phases(phases=[PHASE0], other_phases=[MERGE]) +@with_phases(phases=[ALTAIR], other_phases=[MERGE]) @spec_test @with_state @with_meta_tags(MERGE_FORK_TEST_META_TAGS) @@ -58,7 +58,7 @@ def test_merge_fork_random_3(spec, phases, state): yield from run_fork_test(phases[MERGE], state) -@with_phases(phases=[PHASE0], other_phases=[MERGE]) +@with_phases(phases=[ALTAIR], other_phases=[MERGE]) @spec_test @with_state @with_meta_tags(MERGE_FORK_TEST_META_TAGS) @@ -69,7 +69,7 @@ def test_merge_fork_random_duplicate_attestations(spec, phases, state): yield from run_fork_test(phases[MERGE], state) -@with_phases(phases=[PHASE0], other_phases=[MERGE]) +@with_phases(phases=[ALTAIR], other_phases=[MERGE]) @spec_test @with_state @with_meta_tags(MERGE_FORK_TEST_META_TAGS) @@ -91,7 +91,7 @@ def test_merge_fork_random_mismatched_attestations(spec, phases, state): yield from run_fork_test(phases[MERGE], state_0) -@with_phases(phases=[PHASE0], other_phases=[MERGE]) +@with_phases(phases=[ALTAIR], other_phases=[MERGE]) @spec_test @with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) @with_meta_tags(MERGE_FORK_TEST_META_TAGS) @@ -100,7 +100,7 @@ def test_merge_fork_random_low_balances(spec, phases, state): yield from run_fork_test(phases[MERGE], state) -@with_phases(phases=[PHASE0], other_phases=[MERGE]) +@with_phases(phases=[ALTAIR], other_phases=[MERGE]) @spec_test @with_custom_state(balances_fn=misc_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) @with_meta_tags(MERGE_FORK_TEST_META_TAGS) @@ -109,7 +109,7 @@ def test_merge_fork_random_misc_balances(spec, phases, state): yield from run_fork_test(phases[MERGE], state) -@with_phases(phases=[PHASE0], other_phases=[MERGE]) +@with_phases(phases=[ALTAIR], other_phases=[MERGE]) @with_presets([MINIMAL], reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated") @spec_test From bb0848b6f6d07ff0eaad16c039e9010f93c85514 Mon Sep 17 00:00:00 2001 From: protolambda Date: Thu, 22 Jul 2021 19:58:10 +0200 Subject: [PATCH 15/19] carry over current and previous_sync_committee in Merge state upgrader, review from @djrtwo --- specs/merge/fork.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/specs/merge/fork.md b/specs/merge/fork.md index 5c5c40a64..c89e00cf9 100644 --- a/specs/merge/fork.md +++ b/specs/merge/fork.md @@ -91,6 +91,9 @@ def upgrade_to_merge(pre: altair.BeaconState) -> BeaconState: finalized_checkpoint=pre.finalized_checkpoint, # Inactivity inactivity_scores=pre.inactivity_scores, + # Sync + current_sync_committee=pre.current_sync_committee, + next_sync_committee=pre.next_sync_committee, # Execution-layer latest_execution_payload_header=ExecutionPayloadHeader(), ) From 8318441474c045255deb48e708f6ebfd43edc240 Mon Sep 17 00:00:00 2001 From: protolambda Date: Thu, 22 Jul 2021 19:59:15 +0200 Subject: [PATCH 16/19] Fix remaining merge-rebase-altair tests --- .../pyspec/eth2spec/test/helpers/constants.py | 7 +--- .../eth2spec/test/helpers/merge/fork.py | 3 -- .../test/merge/fork/test_merge_fork_random.py | 38 +------------------ 3 files changed, 3 insertions(+), 45 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/constants.py b/tests/core/pyspec/eth2spec/test/helpers/constants.py index 8f116dc3d..5ab847327 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/constants.py +++ b/tests/core/pyspec/eth2spec/test/helpers/constants.py @@ -18,12 +18,9 @@ DAS = SpecForkName('das') ALL_PHASES = (PHASE0, ALTAIR, MERGE) # The forks that output to the test vectors. TESTGEN_FORKS = (PHASE0, ALTAIR, MERGE) -# TODO: everything runs in parallel to Altair. -# After features are rebased on the Altair fork, this can be reduced to just PHASE0. -FORKS_BEFORE_ALTAIR = (PHASE0, MERGE, SHARDING, CUSTODY_GAME, DAS) -# TODO: when rebasing Merge onto Altair, add ALTAIR to this tuple. -FORKS_BEFORE_MERGE = (PHASE0,) +FORKS_BEFORE_ALTAIR = (PHASE0,) +FORKS_BEFORE_MERGE = (PHASE0, ALTAIR) # # Config diff --git a/tests/core/pyspec/eth2spec/test/helpers/merge/fork.py b/tests/core/pyspec/eth2spec/test/helpers/merge/fork.py index 3a55dc88c..5a45e8565 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/merge/fork.py +++ b/tests/core/pyspec/eth2spec/test/helpers/merge/fork.py @@ -4,9 +4,6 @@ MERGE_FORK_TEST_META_TAGS = { def run_fork_test(post_spec, pre_state): - # Clean up state to be more realistic - pre_state.current_epoch_attestations = [] - yield 'pre', pre_state post_state = post_spec.upgrade_to_merge(pre_state) diff --git a/tests/core/pyspec/eth2spec/test/merge/fork/test_merge_fork_random.py b/tests/core/pyspec/eth2spec/test/merge/fork/test_merge_fork_random.py index cabde150f..20101fac4 100644 --- a/tests/core/pyspec/eth2spec/test/merge/fork/test_merge_fork_random.py +++ b/tests/core/pyspec/eth2spec/test/merge/fork/test_merge_fork_random.py @@ -16,10 +16,7 @@ from eth2spec.test.helpers.merge.fork import ( MERGE_FORK_TEST_META_TAGS, run_fork_test, ) -from eth2spec.test.helpers.random import ( - randomize_state, - randomize_attestation_participation, -) +from eth2spec.test.helpers.random import randomize_state @with_phases(phases=[ALTAIR], other_phases=[MERGE]) @@ -58,39 +55,6 @@ def test_merge_fork_random_3(spec, phases, state): yield from run_fork_test(phases[MERGE], state) -@with_phases(phases=[ALTAIR], other_phases=[MERGE]) -@spec_test -@with_state -@with_meta_tags(MERGE_FORK_TEST_META_TAGS) -def test_merge_fork_random_duplicate_attestations(spec, phases, state): - randomize_state(spec, state, rng=Random(1111)) - # Note: `run_fork_test` empties `current_epoch_attestations` - state.previous_epoch_attestations = state.previous_epoch_attestations + state.previous_epoch_attestations - yield from run_fork_test(phases[MERGE], state) - - -@with_phases(phases=[ALTAIR], other_phases=[MERGE]) -@spec_test -@with_state -@with_meta_tags(MERGE_FORK_TEST_META_TAGS) -def test_merge_fork_random_mismatched_attestations(spec, phases, state): - # Create a random state - randomize_state(spec, state, rng=Random(2222)) - - # Now make two copies - state_0 = state.copy() - state_1 = state.copy() - - # Randomize attestation participation of both - randomize_attestation_participation(spec, state_0, rng=Random(3333)) - randomize_attestation_participation(spec, state_1, rng=Random(4444)) - - # Note: `run_fork_test` empties `current_epoch_attestations` - # Use pending attestations from both random states in a single state for testing - state_0.previous_epoch_attestations = state_0.previous_epoch_attestations + state_1.previous_epoch_attestations - yield from run_fork_test(phases[MERGE], state_0) - - @with_phases(phases=[ALTAIR], other_phases=[MERGE]) @spec_test @with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE) From bf6ad465ce63b05d067dc8207dea96e8958f01b4 Mon Sep 17 00:00:00 2001 From: protolambda Date: Thu, 22 Jul 2021 20:01:50 +0200 Subject: [PATCH 17/19] remove old BeaconState extension --- 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 aa2e3b334..01c54e017 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -85,7 +85,7 @@ class BeaconBlockBody(Container): #### `BeaconState` ```python -class BeaconState(altair.BeaconState): +class BeaconState(Container): # Versioning genesis_time: uint64 genesis_validators_root: Root From 06b8bb1a5785bfd928b0cf907203ef660305cc91 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 21 Jun 2021 20:27:29 +0300 Subject: [PATCH 18/19] Simplify get_start_shard function --- specs/sharding/beacon-chain.md | 33 ++++----------------------------- 1 file changed, 4 insertions(+), 29 deletions(-) diff --git a/specs/sharding/beacon-chain.md b/specs/sharding/beacon-chain.md index 4e88a8971..517892b8c 100644 --- a/specs/sharding/beacon-chain.md +++ b/specs/sharding/beacon-chain.md @@ -58,7 +58,6 @@ - [`process_pending_shard_confirmations`](#process_pending_shard_confirmations) - [`charge_confirmed_shard_fees`](#charge_confirmed_shard_fees) - [`reset_pending_shard_work`](#reset_pending_shard_work) - - [`process_shard_epoch_increment`](#process_shard_epoch_increment) @@ -187,7 +186,6 @@ class BeaconState(merge.BeaconState): # A ring buffer of the latest slots, with information per active shard. shard_buffer: Vector[List[ShardWork, MAX_SHARDS], SHARD_STATE_MEMORY_SLOTS] shard_gasprice: uint64 - current_epoch_start_shard: Shard ``` ## New containers @@ -447,22 +445,10 @@ def get_start_shard(state: BeaconState, slot: Slot) -> Shard: """ Return the start shard at ``slot``. """ - current_epoch_start_slot = compute_start_slot_at_epoch(get_current_epoch(state)) - shard = state.current_epoch_start_shard - if slot > current_epoch_start_slot: - # Current epoch or the next epoch lookahead - for _slot in range(current_epoch_start_slot, slot): - committee_count = get_committee_count_per_slot(state, compute_epoch_at_slot(Slot(_slot))) - active_shard_count = get_active_shard_count(state, compute_epoch_at_slot(Slot(_slot))) - shard = (shard + committee_count) % active_shard_count - elif slot < current_epoch_start_slot: - # Previous epoch - for _slot in list(range(slot, current_epoch_start_slot))[::-1]: - committee_count = get_committee_count_per_slot(state, compute_epoch_at_slot(Slot(_slot))) - active_shard_count = get_active_shard_count(state, compute_epoch_at_slot(Slot(_slot))) - # Ensure positive - shard = (shard + active_shard_count - committee_count) % active_shard_count - return Shard(shard) + epoch = compute_epoch_at_slot(Slot(_slot)) + committee_count = get_committee_count_per_slot(state, epoch) + active_shard_count = get_active_shard_count(state, epoch) + return committee_count * slot % active_shard_count ``` #### `compute_shard_from_committee_index` @@ -699,9 +685,6 @@ def process_epoch(state: BeaconState) -> None: process_historical_roots_update(state) process_participation_flag_updates(state) process_sync_committee_updates(state) - - # Sharding post-processing - process_shard_epoch_increment(state) ``` #### `process_pending_shard_confirmations` @@ -800,11 +783,3 @@ def reset_pending_shard_work(state: BeaconState) -> None: ) # a shard without committee available defaults to SHARD_WORK_UNCONFIRMED. ``` - -#### `process_shard_epoch_increment` - -```python -def process_shard_epoch_increment(state: BeaconState) -> None: - # Update current_epoch_start_shard - state.current_epoch_start_shard = get_start_shard(state, Slot(state.slot + 1)) -``` From 37da2018a3ea860c3661b2d516d923e03a15aa7d Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 23 Jul 2021 08:22:53 -0600 Subject: [PATCH 19/19] bump VERSION.txt to 1.1.0-beta.2 --- 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 854663a0e..fd5a6fed6 100644 --- a/tests/core/pyspec/eth2spec/VERSION.txt +++ b/tests/core/pyspec/eth2spec/VERSION.txt @@ -1 +1 @@ -1.1.0-beta.1 \ No newline at end of file +1.1.0-beta.2 \ No newline at end of file