Repository: ethereum/consensus-specs

Issue:
Test util slashed-validators and proposer index collision at random
Tracking issue, see https://github.com/ethereum/eth2.0-specs/pull/2520 for full description. The RNG seed was changed to avoid this test util bug, and get test vectors out




Discussion:

Resolving patch:
diff --git a/setup.py b/setup.py
--- 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 @@ def preparations(cls):
     @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 implement_optimizations(cls, functions: Dict[str, str]) -> Dict[str, str]:
 #
 # 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
 '''
 
@@ -509,7 +509,7 @@ def sundry_functions(cls) -> str:
 
 
 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))
 
 
@@ -844,19 +844,15 @@ def finalize_options(self):
         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 @@ def finalize_options(self):
                     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()
@@ -1024,7 +1016,7 @@ def run(self):
         "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,


Test patch:
diff --git a/tests/core/pyspec/eth2spec/VERSION.txt b/tests/core/pyspec/eth2spec/VERSION.txt
--- 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
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
--- 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 @@
     with_presets,
     spec_state_test,
     always_bls,
+    spec_test,
 )
 from eth2spec.utils.hash_function import hash
 
@@ -112,6 +113,47 @@ 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):
+    block = build_empty_block_for_next_slot(spec, state)
+    # 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] * 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_single_participant(spec, state):
+    block = build_empty_block_for_next_slot(spec, state)
+    # Try include a single participant with the special-case signature for no-participants.
+    block.body.sync_aggregate = spec.SyncAggregate(
+        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)
+
+
 @with_altair_and_later
 @spec_state_test
 @always_bls
@@ -534,6 +576,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 +639,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):
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
--- 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(33221))
+    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,
diff --git a/tests/core/pyspec/eth2spec/test/context.py b/tests/core/pyspec/eth2spec/test/context.py
--- a/tests/core/pyspec/eth2spec/test/context.py
+++ b/tests/core/pyspec/eth2spec/test/context.py
@@ -347,10 +347,6 @@ def wrapper(*args, **kw):
                 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 wrapper(*args, spec: Spec, **kw):
 
 
 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
--- 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/constants.py b/tests/core/pyspec/eth2spec/test/helpers/constants.py
--- a/tests/core/pyspec/eth2spec/test/helpers/constants.py
+++ b/tests/core/pyspec/eth2spec/test/helpers/constants.py
@@ -18,12 +18,9 @@
 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/epoch_processing.py b/tests/core/pyspec/eth2spec/test/helpers/epoch_processing.py
--- 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
--- 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
--- a/tests/core/pyspec/eth2spec/test/helpers/merge/fork.py
+++ b/tests/core/pyspec/eth2spec/test/helpers/merge/fork.py
@@ -4,9 +4,6 @@
 
 
 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)
@@ -24,10 +21,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
--- 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.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 @@
 )
 
 
-@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
--- 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,20 +9,17 @@
 )
 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 (
     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=[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 +28,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 +37,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 +46,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,40 +55,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])
-@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=[PHASE0], 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=[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 +64,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 +73,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