Repository: ethereum/consensus-specs

Issue:
is verifying max number of indices necessary
in method is_valid_indexed_attestation(), there is
`if not len(indices) <= MAX_VALIDATORS_PER_COMMITTEE:
      return False`
But since we defined the length of the indices already in struct IndexedAttestation as MAX_VALIDATORS_PER_COMMITTEE, which means when create a instance of the IndexedAttestation, the length is guaranteed to be MAX_VALIDATORS_PER_COMMITTEE.

But if we assume that the construction of the struct may give it a variant-length, we might need check the other fields as well, e.g.,  some fields in struct BeaconBlockBody.


Discussion:

Resolving patch:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -499,7 +499,7 @@ def run(self):
         "pycryptodome==3.9.4",
         "py_ecc==2.0.0",
         "dataclasses==0.6",
-        "remerkleable==0.1.12",
+        "remerkleable==0.1.13",
         "ruamel.yaml==0.16.5",
         "lru-dict==1.1.6"
     ]


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 @@
-0.11.1
\ No newline at end of file
+0.11.2
\ No newline at end of file
diff --git a/tests/core/pyspec/eth2spec/config/config_util.py b/tests/core/pyspec/eth2spec/config/config_util.py
--- a/tests/core/pyspec/eth2spec/config/config_util.py
+++ b/tests/core/pyspec/eth2spec/config/config_util.py
@@ -8,13 +8,18 @@
 
 # Access to overwrite spec constants based on configuration
 # This is called by the spec module after declaring its globals, and applies the loaded presets.
-def apply_constants_config(spec_globals: Dict[str, Any]) -> None:
+def apply_constants_config(spec_globals: Dict[str, Any], warn_if_unknown: bool = False) -> None:
     global config
     for k, v in config.items():
-        if k.startswith('DOMAIN_'):
-            spec_globals[k] = spec_globals['DomainType'](v)  # domain types are defined as bytes in the configs
+        # the spec should have default values for everything, if not, the config key is invalid.
+        if k in spec_globals:
+            # Keep the same type as the default value indicates (which may be an SSZ basic type subclass, e.g. 'Gwei')
+            spec_globals[k] = spec_globals[k].__class__(v)
         else:
-            spec_globals[k] = v
+            # Note: Phase 0 spec will not know the phase 1 config values.
+            # Yet, during debugging you can enable explicit warnings.
+            if warn_if_unknown:
+                print(f"WARNING: unknown config key: '{k}' with value: '{v}'")
 
 
 # Load presets from a file, and then prepares the global config setting. This does not apply the config.
@@ -36,7 +41,8 @@ def load_config_file(configs_dir, presets_name) -> Dict[str, Any]:
     out = dict()
     for k, v in loaded.items():
         if isinstance(v, list):
-            out[k] = v
+            # Clean up integer values. YAML parser renders lists of ints as list of str
+            out[k] = [int(item) if item.isdigit() else item for item in v]
         elif isinstance(v, str) and v.startswith("0x"):
             out[k] = bytes.fromhex(v[2:])
         else:
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
@@ -7,7 +7,7 @@
 from .utils import vector_test, with_meta_tags
 
 from random import Random
-from typing import Any, Callable, Sequence, TypedDict, Protocol
+from typing import Any, Callable, NewType, Sequence, TypedDict, Protocol
 
 from importlib import reload
 
@@ -19,25 +19,33 @@ def reload_specs():
 
 # Some of the Spec module functionality is exposed here to deal with phase-specific changes.
 
+SpecForkName = NewType("SpecForkName", str)
+
+PHASE0 = SpecForkName('phase0')
+PHASE1 = SpecForkName('phase1')
+ALL_PHASES = (PHASE0, PHASE1)
+
 # TODO: currently phases are defined as python modules.
 # It would be better if they would be more well-defined interfaces for stronger typing.
+
+
 class Spec(Protocol):
     version: str
 
 
-class Phase0(Spec):
+class SpecPhase0(Spec):
     ...
 
 
-class Phase1(Spec):
+class SpecPhase1(Spec):
     def upgrade_to_phase1(self, state: spec_phase0.BeaconState) -> spec_phase1.BeaconState:
         ...
 
 
 # add transfer, bridge, etc. as the spec evolves
 class SpecForks(TypedDict, total=False):
-    phase0: Phase0
-    phase1: Phase1
+    PHASE0: SpecPhase0
+    PHASE1: SpecPhase1
 
 
 def with_custom_state(balances_fn: Callable[[Any], Sequence[int]],
@@ -45,16 +53,19 @@ def with_custom_state(balances_fn: Callable[[Any], Sequence[int]],
     def deco(fn):
         def entry(*args, spec: Spec, phases: SpecForks, **kw):
             try:
-                p0 = phases["phase0"]
+                p0 = phases[PHASE0]
                 balances = balances_fn(p0)
                 activation_threshold = threshold_fn(p0)
 
                 state = create_genesis_state(spec=p0, validator_balances=balances,
                                              activation_threshold=activation_threshold)
-                if spec.fork == 'phase1':
+                if spec.fork == PHASE1:
                     # TODO: instead of upgrading a test phase0 genesis state we can also write a phase1 state helper.
                     # Decide based on performance/consistency results later.
-                    state = phases["phase1"].upgrade_to_phase1(state)
+                    state = phases[PHASE1].upgrade_to_phase1(state)
+                    # Shard state slot must lag behind BeaconState slot by at least 1
+                    # Will handle this more elegantly with fork mechanics
+                    spec.process_slots(state, state.slot + 1)
 
                 kw['state'] = state
             except KeyError:
@@ -217,14 +228,11 @@ def entry(*args, **kw):
     return entry
 
 
-all_phases = ['phase0', 'phase1']
-
-
 def with_all_phases(fn):
     """
     A decorator for running a test with every phase
     """
-    return with_phases(all_phases)(fn)
+    return with_phases(ALL_PHASES)(fn)
 
 
 def with_all_phases_except(exclusion_phases):
@@ -232,7 +240,7 @@ def with_all_phases_except(exclusion_phases):
     A decorator factory for running a tests with every phase except the ones listed
     """
     def decorator(fn):
-        return with_phases([phase for phase in all_phases if phase not in exclusion_phases])(fn)
+        return with_phases([phase for phase in ALL_PHASES if phase not in exclusion_phases])(fn)
     return decorator
 
 
@@ -258,18 +266,18 @@ def wrapper(*args, **kw):
 
             # TODO: test state is dependent on phase0 but is immediately transitioned to phase1.
             #  A new state-creation helper for phase 1 may be in place, and then phase1+ tests can run without phase0
-            available_phases.add('phase0')
+            available_phases.add(PHASE0)
 
             phase_dir = {}
-            if 'phase0' in available_phases:
-                phase_dir['phase0'] = spec_phase0
-            if 'phase1' in available_phases:
-                phase_dir['phase1'] = spec_phase1
+            if PHASE0 in available_phases:
+                phase_dir[PHASE0] = spec_phase0
+            if PHASE1 in available_phases:
+                phase_dir[PHASE1] = spec_phase1
 
             # return is ignored whenever multiple phases are ran. If
-            if 'phase0' in run_phases:
+            if PHASE0 in run_phases:
                 ret = fn(spec=spec_phase0, phases=phase_dir, *args, **kw)
-            if 'phase1' in run_phases:
+            if PHASE1 in run_phases:
                 ret = fn(spec=spec_phase1, phases=phase_dir, *args, **kw)
             return ret
         return wrapper
diff --git a/tests/core/pyspec/eth2spec/test/fork_choice/test_get_head.py b/tests/core/pyspec/eth2spec/test/fork_choice/test_get_head.py
--- a/tests/core/pyspec/eth2spec/test/fork_choice/test_get_head.py
+++ b/tests/core/pyspec/eth2spec/test/fork_choice/test_get_head.py
@@ -1,9 +1,8 @@
 from eth2spec.test.context import with_all_phases, spec_state_test
-from eth2spec.test.helpers.attestations import get_valid_attestation
+from eth2spec.test.helpers.attestations import get_valid_attestation, next_epoch_with_attestations
 from eth2spec.test.helpers.block import build_empty_block_for_next_slot
 from eth2spec.test.helpers.state import (
     next_epoch,
-    next_epoch_with_attestations,
     state_transition_and_sign_block,
 )
 
diff --git a/tests/core/pyspec/eth2spec/test/fork_choice/test_on_attestation.py b/tests/core/pyspec/eth2spec/test/fork_choice/test_on_attestation.py
--- a/tests/core/pyspec/eth2spec/test/fork_choice/test_on_attestation.py
+++ b/tests/core/pyspec/eth2spec/test/fork_choice/test_on_attestation.py
@@ -1,7 +1,7 @@
-from eth2spec.test.context import with_all_phases, spec_state_test
+from eth2spec.test.context import PHASE0, with_all_phases, spec_state_test
 from eth2spec.test.helpers.block import build_empty_block_for_next_slot
 from eth2spec.test.helpers.attestations import get_valid_attestation, sign_attestation
-from eth2spec.test.helpers.state import transition_to, state_transition_and_sign_block
+from eth2spec.test.helpers.state import transition_to, state_transition_and_sign_block, next_epoch
 
 
 def run_on_attestation(spec, state, store, attestation, valid=True):
@@ -16,7 +16,7 @@ def run_on_attestation(spec, state, store, attestation, valid=True):
     indexed_attestation = spec.get_indexed_attestation(state, attestation)
     spec.on_attestation(store, attestation)
 
-    if spec.fork == 'phase0':
+    if spec.fork == PHASE0:
         sample_index = indexed_attestation.attesting_indices[0]
     else:
         attesting_indices = [
@@ -120,11 +120,12 @@ def test_on_attestation_mismatched_target_and_slot(spec, state):
 @spec_state_test
 def test_on_attestation_target_not_in_store(spec, state):
     store = spec.get_forkchoice_store(state)
-    time = spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH
+    time = store.time + spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH
     spec.on_tick(store, time)
 
     # move to immediately before next epoch to make block new target
-    transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH - 1)
+    next_epoch = spec.get_current_epoch(state) + 1
+    transition_to(spec, state, spec.compute_start_slot_at_epoch(next_epoch) - 1)
 
     target_block = build_empty_block_for_next_slot(spec, state)
     state_transition_and_sign_block(spec, state, target_block)
@@ -141,11 +142,12 @@ def test_on_attestation_target_not_in_store(spec, state):
 @spec_state_test
 def test_on_attestation_beacon_block_not_in_store(spec, state):
     store = spec.get_forkchoice_store(state)
-    time = spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH
+    time = store.time + spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH
     spec.on_tick(store, time)
 
     # move to immediately before next epoch to make block new target
-    transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH - 1)
+    next_epoch = spec.get_current_epoch(state) + 1
+    transition_to(spec, state, spec.compute_start_slot_at_epoch(next_epoch) - 1)
 
     target_block = build_empty_block_for_next_slot(spec, state)
     signed_target_block = state_transition_and_sign_block(spec, state, target_block)
@@ -169,7 +171,7 @@ def test_on_attestation_beacon_block_not_in_store(spec, state):
 @spec_state_test
 def test_on_attestation_future_epoch(spec, state):
     store = spec.get_forkchoice_store(state)
-    time = 3 * spec.SECONDS_PER_SLOT
+    time = store.time + 3 * spec.SECONDS_PER_SLOT
     spec.on_tick(store, time)
 
     block = build_empty_block_for_next_slot(spec, state)
@@ -179,7 +181,7 @@ def test_on_attestation_future_epoch(spec, state):
     spec.on_block(store, signed_block)
 
     # move state forward but not store
-    state.slot = block.slot + spec.SLOTS_PER_EPOCH
+    next_epoch(spec, state)
 
     attestation = get_valid_attestation(spec, state, slot=state.slot, signed=True)
     run_on_attestation(spec, state, store, attestation, False)
@@ -189,7 +191,7 @@ def test_on_attestation_future_epoch(spec, state):
 @spec_state_test
 def test_on_attestation_future_block(spec, state):
     store = spec.get_forkchoice_store(state)
-    time = spec.SECONDS_PER_SLOT * 5
+    time = store.time + spec.SECONDS_PER_SLOT * 5
     spec.on_tick(store, time)
 
     block = build_empty_block_for_next_slot(spec, state)
@@ -209,7 +211,7 @@ def test_on_attestation_future_block(spec, state):
 @spec_state_test
 def test_on_attestation_same_slot(spec, state):
     store = spec.get_forkchoice_store(state)
-    time = 1 * spec.SECONDS_PER_SLOT
+    time = store.time + spec.SECONDS_PER_SLOT
     spec.on_tick(store, time)
 
     block = build_empty_block_for_next_slot(spec, state)
@@ -225,7 +227,7 @@ def test_on_attestation_same_slot(spec, state):
 @spec_state_test
 def test_on_attestation_invalid_attestation(spec, state):
     store = spec.get_forkchoice_store(state)
-    time = 3 * spec.SECONDS_PER_SLOT
+    time = store.time + 3 * spec.SECONDS_PER_SLOT
     spec.on_tick(store, time)
 
     block = build_empty_block_for_next_slot(spec, state)
diff --git a/tests/core/pyspec/eth2spec/test/fork_choice/test_on_block.py b/tests/core/pyspec/eth2spec/test/fork_choice/test_on_block.py
--- a/tests/core/pyspec/eth2spec/test/fork_choice/test_on_block.py
+++ b/tests/core/pyspec/eth2spec/test/fork_choice/test_on_block.py
@@ -4,7 +4,8 @@
 from eth2spec.test.context import with_all_phases, spec_state_test
 from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block, transition_unsigned_block, \
     build_empty_block
-from eth2spec.test.helpers.state import next_epoch, next_epoch_with_attestations, state_transition_and_sign_block
+from eth2spec.test.helpers.attestations import next_epoch_with_attestations
+from eth2spec.test.helpers.state import next_epoch, state_transition_and_sign_block
 
 
 def run_on_block(spec, store, signed_block, valid=True):
@@ -159,6 +160,7 @@ def test_on_block_finalized_skip_slots(spec, state):
 @spec_state_test
 def test_on_block_finalized_skip_slots_not_in_skip_chain(spec, state):
     # Initialization
+    next_epoch(spec, state)
     store = spec.get_forkchoice_store(state)
 
     store.finalized_checkpoint = spec.Checkpoint(
diff --git a/tests/core/pyspec/eth2spec/test/fork_choice/test_on_tick.py b/tests/core/pyspec/eth2spec/test/fork_choice/test_on_tick.py
--- a/tests/core/pyspec/eth2spec/test/fork_choice/test_on_tick.py
+++ b/tests/core/pyspec/eth2spec/test/fork_choice/test_on_tick.py
@@ -27,14 +27,16 @@ def test_basic(spec, state):
 @spec_state_test
 def test_update_justified_single(spec, state):
     store = spec.get_forkchoice_store(state)
-    seconds_per_epoch = spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH
+    next_epoch = spec.get_current_epoch(state) + 1
+    next_epoch_start_slot = spec.compute_start_slot_at_epoch(next_epoch)
+    seconds_until_next_epoch = next_epoch_start_slot * spec.SECONDS_PER_SLOT - store.time
 
     store.best_justified_checkpoint = spec.Checkpoint(
         epoch=store.justified_checkpoint.epoch + 1,
         root=b'\x55' * 32,
     )
 
-    run_on_tick(spec, store, store.time + seconds_per_epoch, True)
+    run_on_tick(spec, store, store.time + seconds_until_next_epoch, True)
 
 
 @with_all_phases
diff --git a/tests/core/pyspec/eth2spec/test/genesis/test_initialization.py b/tests/core/pyspec/eth2spec/test/genesis/test_initialization.py
--- a/tests/core/pyspec/eth2spec/test/genesis/test_initialization.py
+++ b/tests/core/pyspec/eth2spec/test/genesis/test_initialization.py
@@ -1,10 +1,10 @@
-from eth2spec.test.context import spec_test, with_phases, single_phase
+from eth2spec.test.context import PHASE0, spec_test, with_phases, single_phase
 from eth2spec.test.helpers.deposits import (
     prepare_genesis_deposits,
 )
 
 
-@with_phases(['phase0'])
+@with_phases(([PHASE0]))
 @spec_test
 @single_phase
 def test_initialize_beacon_state_from_eth1(spec):
@@ -32,7 +32,7 @@ def test_initialize_beacon_state_from_eth1(spec):
     yield 'state', state
 
 
-@with_phases(['phase0'])
+@with_phases([PHASE0])
 @spec_test
 @single_phase
 def test_initialize_beacon_state_some_small_balances(spec):
diff --git a/tests/core/pyspec/eth2spec/test/genesis/test_validity.py b/tests/core/pyspec/eth2spec/test/genesis/test_validity.py
--- a/tests/core/pyspec/eth2spec/test/genesis/test_validity.py
+++ b/tests/core/pyspec/eth2spec/test/genesis/test_validity.py
@@ -1,4 +1,4 @@
-from eth2spec.test.context import spec_test, with_phases, single_phase
+from eth2spec.test.context import PHASE0, spec_test, with_phases, single_phase
 from eth2spec.test.helpers.deposits import (
     prepare_genesis_deposits,
 )
@@ -25,7 +25,7 @@ def run_is_valid_genesis_state(spec, state, valid=True):
     assert is_valid == valid
 
 
-@with_phases(['phase0'])
+@with_phases([PHASE0])
 @spec_test
 @single_phase
 def test_is_valid_genesis_state_true(spec):
@@ -34,7 +34,7 @@ def test_is_valid_genesis_state_true(spec):
     yield from run_is_valid_genesis_state(spec, state, valid=True)
 
 
-@with_phases(['phase0'])
+@with_phases([PHASE0])
 @spec_test
 @single_phase
 def test_is_valid_genesis_state_false_invalid_timestamp(spec):
@@ -44,7 +44,7 @@ def test_is_valid_genesis_state_false_invalid_timestamp(spec):
     yield from run_is_valid_genesis_state(spec, state, valid=False)
 
 
-@with_phases(['phase0'])
+@with_phases([PHASE0])
 @spec_test
 @single_phase
 def test_is_valid_genesis_state_true_more_balance(spec):
@@ -55,7 +55,7 @@ def test_is_valid_genesis_state_true_more_balance(spec):
 
 
 # TODO: not part of the genesis function yet. Erroneously merged.
-# @with_phases(['phase0'])
+# @with_phases([PHASE0])
 # @spec_test
 # def test_is_valid_genesis_state_false_not_enough_balance(spec):
 #     state = create_valid_beacon_state(spec)
@@ -64,7 +64,7 @@ def test_is_valid_genesis_state_true_more_balance(spec):
 #     yield from run_is_valid_genesis_state(spec, state, valid=False)
 
 
-@with_phases(['phase0'])
+@with_phases([PHASE0])
 @spec_test
 @single_phase
 def test_is_valid_genesis_state_true_one_more_validator(spec):
@@ -78,7 +78,7 @@ def test_is_valid_genesis_state_true_one_more_validator(spec):
     yield from run_is_valid_genesis_state(spec, state, valid=True)
 
 
-@with_phases(['phase0'])
+@with_phases([PHASE0])
 @spec_test
 @single_phase
 def test_is_valid_genesis_state_false_not_enough_validator(spec):
diff --git a/tests/core/pyspec/eth2spec/test/helpers/attestations.py b/tests/core/pyspec/eth2spec/test/helpers/attestations.py
--- a/tests/core/pyspec/eth2spec/test/helpers/attestations.py
+++ b/tests/core/pyspec/eth2spec/test/helpers/attestations.py
@@ -1,12 +1,48 @@
 from typing import List
 
-from eth2spec.test.helpers.block import build_empty_block_for_next_slot, transition_unsigned_block, \
-    build_empty_block
+from eth2spec.test.context import expect_assertion_error, PHASE0
+from eth2spec.test.helpers.state import state_transition_and_sign_block
+from eth2spec.test.helpers.block import build_empty_block_for_next_slot
 from eth2spec.test.helpers.keys import privkeys
 from eth2spec.utils import bls
 from eth2spec.utils.ssz.ssz_typing import Bitlist
 
 
+def run_attestation_processing(spec, state, attestation, valid=True):
+    """
+    Run ``process_attestation``, yielding:
+      - pre-state ('pre')
+      - attestation ('attestation')
+      - post-state ('post').
+    If ``valid == False``, run expecting ``AssertionError``
+    """
+    # yield pre-state
+    yield 'pre', state
+
+    yield 'attestation', attestation
+
+    # If the attestation is invalid, processing is aborted, and there is no post-state.
+    if not valid:
+        expect_assertion_error(lambda: spec.process_attestation(state, attestation))
+        yield 'post', None
+        return
+
+    current_epoch_count = len(state.current_epoch_attestations)
+    previous_epoch_count = len(state.previous_epoch_attestations)
+
+    # process attestation
+    spec.process_attestation(state, attestation)
+
+    # Make sure the attestation has been processed
+    if attestation.data.target.epoch == spec.get_current_epoch(state):
+        assert len(state.current_epoch_attestations) == current_epoch_count + 1
+    else:
+        assert len(state.previous_epoch_attestations) == previous_epoch_count + 1
+
+    # yield post-state
+    yield 'post', state
+
+
 def build_attestation_data(spec, state, slot, index):
     assert state.slot >= slot
 
@@ -39,7 +75,45 @@ def build_attestation_data(spec, state, slot, index):
     )
 
 
-def get_valid_attestation(spec, state, slot=None, index=None, empty=False, signed=False):
+def convert_to_valid_on_time_attestation(spec, state, attestation, signed=False):
+    shard = spec.get_shard(state, attestation)
+    offset_slots = spec.compute_offset_slots(spec.get_latest_slot_for_shard(state, shard), state.slot + 1)
+    for offset_slot in offset_slots:
+        attestation.custody_bits_blocks.append(
+            Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE]([0 for _ in attestation.aggregation_bits])
+        )
+
+    if signed:
+        sign_attestation(spec, state, attestation)
+
+    return attestation
+
+
+def get_valid_on_time_attestation(spec, state, slot=None, index=None, signed=False):
+    '''
+    Construct on-time attestation for next slot
+    '''
+    if slot is None:
+        slot = state.slot
+    if index is None:
+        index = 0
+
+    return get_valid_attestation(spec, state, slot=slot, index=index, signed=signed, on_time=True)
+
+
+def get_valid_late_attestation(spec, state, slot=None, index=None, signed=False):
+    '''
+    Construct on-time attestation for next slot
+    '''
+    if slot is None:
+        slot = state.slot
+    if index is None:
+        index = 0
+
+    return get_valid_attestation(spec, state, slot=slot, index=index, signed=signed, on_time=False)
+
+
+def get_valid_attestation(spec, state, slot=None, index=None, empty=False, signed=False, on_time=True):
     if slot is None:
         slot = state.slot
     if index is None:
@@ -63,6 +137,10 @@ def get_valid_attestation(spec, state, slot=None, index=None, empty=False, signe
         fill_aggregate_attestation(spec, state, attestation)
     if signed:
         sign_attestation(spec, state, attestation)
+
+    if spec.fork == 'phase1' and on_time:
+        attestation = convert_to_valid_on_time_attestation(spec, state, attestation, signed)
+
     return attestation
 
 
@@ -78,12 +156,11 @@ def sign_aggregate_attestation(spec, state, attestation_data, participants: List
                 privkey
             )
         )
-    # TODO: we should try signing custody bits if spec.fork == 'phase1'
     return bls.Aggregate(signatures)
 
 
 def sign_indexed_attestation(spec, state, indexed_attestation):
-    if spec.fork == 'phase0':
+    if spec.fork == PHASE0:
         participants = indexed_attestation.attesting_indices
         data = indexed_attestation.data
         indexed_attestation.signature = sign_aggregate_attestation(spec, state, data, participants)
@@ -96,7 +173,47 @@ def sign_indexed_attestation(spec, state, indexed_attestation):
         indexed_attestation.attestation.signature = sign_aggregate_attestation(spec, state, data, participants)
 
 
+def sign_on_time_attestation(spec, state, attestation):
+    if not any(attestation.custody_bits_blocks):
+        sign_attestation(spec, state, attestation)
+        return
+
+    committee = spec.get_beacon_committee(state, attestation.data.slot, attestation.data.index)
+    signatures = []
+    for block_index, custody_bits in enumerate(attestation.custody_bits_blocks):
+        for participant, abit, cbit in zip(committee, attestation.aggregation_bits, custody_bits):
+            if not abit:
+                continue
+            signatures.append(get_attestation_custody_signature(
+                spec,
+                state,
+                attestation.data,
+                block_index,
+                cbit,
+                privkeys[participant]
+            ))
+
+    attestation.signature = bls.Aggregate(signatures)
+
+
+def get_attestation_custody_signature(spec, state, attestation_data, block_index, bit, privkey):
+    domain = spec.get_domain(state, spec.DOMAIN_BEACON_ATTESTER, attestation_data.target.epoch)
+    signing_root = spec.compute_signing_root(
+        spec.AttestationCustodyBitWrapper(
+            attestation_data_root=attestation_data.hash_tree_root(),
+            block_index=block_index,
+            bit=bit,
+        ),
+        domain,
+    )
+    return bls.Sign(privkey, signing_root)
+
+
 def sign_attestation(spec, state, attestation):
+    if spec.fork == 'phase1' and any(attestation.custody_bits_blocks):
+        sign_on_time_attestation(spec, state, attestation)
+        return
+
     participants = spec.get_attesting_indices(
         state,
         attestation.data,
@@ -113,7 +230,6 @@ def get_attestation_signature(spec, state, attestation_data, privkey):
 
 
 def fill_aggregate_attestation(spec, state, attestation, signed=False):
-
     beacon_committee = spec.get_beacon_committee(
         state,
         attestation.data.slot,
@@ -127,8 +243,38 @@ def fill_aggregate_attestation(spec, state, attestation, signed=False):
 
 
 def add_attestations_to_state(spec, state, attestations, slot):
-    block = build_empty_block(spec, state, slot)
+    spec.process_slots(state, slot)
     for attestation in attestations:
-        block.body.attestations.append(attestation)
-    spec.process_slots(state, block.slot)
-    transition_unsigned_block(spec, state, block)
+        spec.process_attestation(state, attestation)
+
+
+def next_epoch_with_attestations(spec,
+                                 state,
+                                 fill_cur_epoch,
+                                 fill_prev_epoch):
+    assert state.slot % spec.SLOTS_PER_EPOCH == 0
+
+    post_state = state.copy()
+    signed_blocks = []
+    for _ in range(spec.SLOTS_PER_EPOCH):
+        block = build_empty_block_for_next_slot(spec, post_state)
+        if fill_cur_epoch and post_state.slot >= spec.MIN_ATTESTATION_INCLUSION_DELAY:
+            slot_to_attest = post_state.slot - spec.MIN_ATTESTATION_INCLUSION_DELAY + 1
+            committees_per_slot = spec.get_committee_count_at_slot(state, slot_to_attest)
+            if slot_to_attest >= spec.compute_start_slot_at_epoch(spec.get_current_epoch(post_state)):
+                for index in range(committees_per_slot):
+                    cur_attestation = get_valid_attestation(spec, post_state, slot_to_attest, index=index, signed=True)
+                    block.body.attestations.append(cur_attestation)
+
+        if fill_prev_epoch:
+            slot_to_attest = post_state.slot - spec.SLOTS_PER_EPOCH + 1
+            committees_per_slot = spec.get_committee_count_at_slot(state, slot_to_attest)
+            for index in range(committees_per_slot):
+                prev_attestation = get_valid_attestation(
+                    spec, post_state, slot_to_attest, index=index, signed=True, on_time=False)
+                block.body.attestations.append(prev_attestation)
+
+        signed_block = state_transition_and_sign_block(spec, post_state, block)
+        signed_blocks.append(signed_block)
+
+    return state, signed_blocks, post_state
diff --git a/tests/core/pyspec/eth2spec/test/helpers/attester_slashings.py b/tests/core/pyspec/eth2spec/test/helpers/attester_slashings.py
--- a/tests/core/pyspec/eth2spec/test/helpers/attester_slashings.py
+++ b/tests/core/pyspec/eth2spec/test/helpers/attester_slashings.py
@@ -1,3 +1,4 @@
+from eth2spec.test.context import PHASE1
 from eth2spec.test.helpers.attestations import get_valid_attestation, sign_attestation
 
 
@@ -20,7 +21,7 @@ def get_indexed_attestation_participants(spec, indexed_att):
     """
     Wrapper around index-attestation to return the list of participant indices, regardless of spec phase.
     """
-    if spec.fork == "phase1":
+    if spec.fork == PHASE1:
         return list(spec.get_indices_from_committee(
             indexed_att.committee,
             indexed_att.attestation.aggregation_bits,
@@ -33,21 +34,21 @@ def set_indexed_attestation_participants(spec, indexed_att, participants):
     """
     Wrapper around index-attestation to return the list of participant indices, regardless of spec phase.
     """
-    if spec.fork == "phase1":
+    if spec.fork == PHASE1:
         indexed_att.attestation.aggregation_bits = [bool(i in participants) for i in indexed_att.committee]
     else:
         indexed_att.attesting_indices = participants
 
 
 def get_attestation_1_data(spec, att_slashing):
-    if spec.fork == "phase1":
+    if spec.fork == PHASE1:
         return att_slashing.attestation_1.attestation.data
     else:
         return att_slashing.attestation_1.data
 
 
 def get_attestation_2_data(spec, att_slashing):
-    if spec.fork == "phase1":
+    if spec.fork == PHASE1:
         return att_slashing.attestation_2.attestation.data
     else:
         return att_slashing.attestation_2.data
diff --git a/tests/core/pyspec/eth2spec/test/helpers/keys.py b/tests/core/pyspec/eth2spec/test/helpers/keys.py
--- a/tests/core/pyspec/eth2spec/test/helpers/keys.py
+++ b/tests/core/pyspec/eth2spec/test/helpers/keys.py
@@ -1,6 +1,6 @@
 from py_ecc.bls import G2ProofOfPossession as bls
 from eth2spec.phase0 import spec
 
-privkeys = [i + 1 for i in range(spec.SLOTS_PER_EPOCH * 16)]
+privkeys = [i + 1 for i in range(spec.SLOTS_PER_EPOCH * 256)]
 pubkeys = [bls.PrivToPub(privkey) for privkey in privkeys]
 pubkey_to_privkey = {pubkey: privkey for privkey, pubkey in zip(privkeys, pubkeys)}
diff --git a/tests/core/pyspec/eth2spec/test/helpers/phase1/attestations.py b/tests/core/pyspec/eth2spec/test/helpers/phase1/attestations.py
--- a/tests/core/pyspec/eth2spec/test/helpers/phase1/attestations.py
+++ b/tests/core/pyspec/eth2spec/test/helpers/phase1/attestations.py
@@ -1,30 +1,63 @@
-from eth2spec.test.helpers.keys import privkeys
+from eth2spec.utils.ssz.ssz_typing import Bitlist
 from eth2spec.utils import bls
 
+from eth2spec.test.helpers.keys import privkeys
+import eth2spec.test.helpers.attestations as phase0_attestations
+
+
+def get_valid_on_time_attestation(spec, state, index=None, signed=False):
+    '''
+    Construct on-time attestation for next slot
+    '''
+    if index is None:
+        index = 0
+
+    attestation = phase0_attestations.get_valid_attestation(spec, state, state.slot, index, False)
+    shard = spec.get_shard(state, attestation)
+    offset_slots = spec.compute_offset_slots(spec.get_latest_slot_for_shard(state, shard), state.slot + 1)
+
+    for _ in offset_slots:
+        attestation.custody_bits_blocks.append(
+            Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE]([0 for _ in attestation.aggregation_bits])
+        )
 
-def sign_shard_attestation(spec, beacon_state, shard_state, block, participants):
+    if signed:
+        sign_attestation(spec, state, attestation)
+
+    return attestation
+
+
+def sign_attestation(spec, state, attestation):
+    if not any(attestation.custody_bits_blocks):
+        phase0_attestations.sign_attestation(spec, state, attestation)
+        return
+
+    committee = spec.get_beacon_committee(state, attestation.data.slot, attestation.data.index)
     signatures = []
-    message_hash = spec.ShardAttestationData(
-        slot=block.slot,
-        parent_root=block.parent_root,
-    ).hash_tree_root()
-    block_epoch = spec.compute_epoch_of_shard_slot(block.slot)
-    for validator_index in participants:
-        privkey = privkeys[validator_index]
-        signatures.append(
-            get_attestation_signature(
+    for block_index, custody_bits in enumerate(attestation.custody_bits_blocks):
+        for participant, abit, cbit in zip(committee, attestation.aggregation_bits, custody_bits):
+            if not abit:
+                continue
+            signatures.append(get_attestation_custody_signature(
                 spec,
-                beacon_state,
-                shard_state,
-                message_hash,
-                block_epoch,
-                privkey,
-            )
-        )
-    return bls.Aggregate(signatures)
+                state,
+                attestation.data,
+                block_index,
+                cbit,
+                privkeys[participant]
+            ))
+
+    attestation.signature = bls.Aggregate(signatures)
 
 
-def get_attestation_signature(spec, beacon_state, shard_state, message_hash, block_epoch, privkey):
-    domain = spec.get_domain(beacon_state, spec.DOMAIN_SHARD_ATTESTER, block_epoch)
-    signing_root = spec.compute_signing_root(message_hash, domain)
+def get_attestation_custody_signature(spec, state, attestation_data, block_index, bit, privkey):
+    domain = spec.get_domain(state, spec.DOMAIN_BEACON_ATTESTER, attestation_data.target.epoch)
+    signing_root = spec.compute_signing_root(
+        spec.AttestationCustodyBitWrapper(
+            attestation_data.hash_tree_root(),
+            block_index,
+            bit,
+        ),
+        domain,
+    )
     return bls.Sign(privkey, signing_root)
diff --git a/tests/core/pyspec/eth2spec/test/helpers/state.py b/tests/core/pyspec/eth2spec/test/helpers/state.py
--- a/tests/core/pyspec/eth2spec/test/helpers/state.py
+++ b/tests/core/pyspec/eth2spec/test/helpers/state.py
@@ -1,6 +1,5 @@
 from eth2spec.test.context import expect_assertion_error
-from eth2spec.test.helpers.attestations import get_valid_attestation
-from eth2spec.test.helpers.block import sign_block, build_empty_block_for_next_slot, transition_unsigned_block
+from eth2spec.test.helpers.block import sign_block, transition_unsigned_block
 
 
 def get_balance(state, index):
@@ -14,6 +13,13 @@ def next_slot(spec, state):
     spec.process_slots(state, state.slot + 1)
 
 
+def next_slots(spec, state, slots):
+    """
+    Transition given slots forward.
+    """
+    spec.process_slots(state, state.slot + slots)
+
+
 def transition_to(spec, state, slot):
     """
     Transition to ``slot``.
@@ -51,34 +57,3 @@ def state_transition_and_sign_block(spec, state, block, expect_fail=False):
         transition_unsigned_block(spec, state, block)
     block.state_root = state.hash_tree_root()
     return sign_block(spec, state, block)
-
-
-def next_epoch_with_attestations(spec,
-                                 state,
-                                 fill_cur_epoch,
-                                 fill_prev_epoch):
-    assert state.slot % spec.SLOTS_PER_EPOCH == 0
-
-    post_state = state.copy()
-    signed_blocks = []
-    for _ in range(spec.SLOTS_PER_EPOCH):
-        block = build_empty_block_for_next_slot(spec, post_state)
-        if fill_cur_epoch and post_state.slot >= spec.MIN_ATTESTATION_INCLUSION_DELAY:
-            slot_to_attest = post_state.slot - spec.MIN_ATTESTATION_INCLUSION_DELAY + 1
-            committees_per_slot = spec.get_committee_count_at_slot(state, slot_to_attest)
-            if slot_to_attest >= spec.compute_start_slot_at_epoch(spec.get_current_epoch(post_state)):
-                for index in range(committees_per_slot):
-                    cur_attestation = get_valid_attestation(spec, post_state, slot_to_attest, index=index, signed=True)
-                    block.body.attestations.append(cur_attestation)
-
-        if fill_prev_epoch:
-            slot_to_attest = post_state.slot - spec.SLOTS_PER_EPOCH + 1
-            committees_per_slot = spec.get_committee_count_at_slot(state, slot_to_attest)
-            for index in range(committees_per_slot):
-                prev_attestation = get_valid_attestation(spec, post_state, slot_to_attest, index=index, signed=True)
-                block.body.attestations.append(prev_attestation)
-
-        signed_block = state_transition_and_sign_block(spec, post_state, block)
-        signed_blocks.append(signed_block)
-
-    return state, signed_blocks, post_state
diff --git a/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py b/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py
--- a/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py
+++ b/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py
@@ -1,6 +1,5 @@
 from eth2spec.test.context import (
     spec_state_test,
-    expect_assertion_error,
     always_bls, never_bls,
     with_all_phases,
     spec_test,
@@ -8,57 +7,26 @@
     with_custom_state,
     single_phase)
 from eth2spec.test.helpers.attestations import (
+    run_attestation_processing,
     get_valid_attestation,
     sign_aggregate_attestation,
     sign_attestation,
 )
 from eth2spec.test.helpers.state import (
+    next_slot,
+    next_slots,
     next_epoch,
+    transition_to,
 )
 from eth2spec.test.helpers.block import apply_empty_block
 from eth2spec.utils.ssz.ssz_typing import Bitlist
 
 
-def run_attestation_processing(spec, state, attestation, valid=True):
-    """
-    Run ``process_attestation``, yielding:
-      - pre-state ('pre')
-      - attestation ('attestation')
-      - post-state ('post').
-    If ``valid == False``, run expecting ``AssertionError``
-    """
-    # yield pre-state
-    yield 'pre', state
-
-    yield 'attestation', attestation
-
-    # If the attestation is invalid, processing is aborted, and there is no post-state.
-    if not valid:
-        expect_assertion_error(lambda: spec.process_attestation(state, attestation))
-        yield 'post', None
-        return
-
-    current_epoch_count = len(state.current_epoch_attestations)
-    previous_epoch_count = len(state.previous_epoch_attestations)
-
-    # process attestation
-    spec.process_attestation(state, attestation)
-
-    # Make sure the attestation has been processed
-    if attestation.data.target.epoch == spec.get_current_epoch(state):
-        assert len(state.current_epoch_attestations) == current_epoch_count + 1
-    else:
-        assert len(state.previous_epoch_attestations) == previous_epoch_count + 1
-
-    # yield post-state
-    yield 'post', state
-
-
 @with_all_phases
 @spec_state_test
 def test_success(spec, state):
     attestation = get_valid_attestation(spec, state, signed=True)
-    state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
+    next_slots(spec, state, spec.MIN_ATTESTATION_INCLUSION_DELAY)
 
     yield from run_attestation_processing(spec, state, attestation)
 
@@ -68,9 +36,9 @@ def test_success(spec, state):
 @with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.EJECTION_BALANCE)
 @single_phase
 def test_success_multi_proposer_index_iterations(spec, state):
-    state.slot += spec.SLOTS_PER_EPOCH * 2
+    next_slots(spec, state, spec.SLOTS_PER_EPOCH * 2)
     attestation = get_valid_attestation(spec, state, signed=True)
-    state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
+    next_slots(spec, state, spec.MIN_ATTESTATION_INCLUSION_DELAY)
 
     yield from run_attestation_processing(spec, state, attestation)
 
@@ -78,8 +46,8 @@ def test_success_multi_proposer_index_iterations(spec, state):
 @with_all_phases
 @spec_state_test
 def test_success_previous_epoch(spec, state):
-    attestation = get_valid_attestation(spec, state, signed=True)
-    state.slot = spec.SLOTS_PER_EPOCH - 1
+    attestation = get_valid_attestation(spec, state, signed=True, on_time=False)
+    transition_to(spec, state, spec.SLOTS_PER_EPOCH - 1)
     next_epoch(spec, state)
     apply_empty_block(spec, state)
 
@@ -91,7 +59,7 @@ def test_success_previous_epoch(spec, state):
 @always_bls
 def test_invalid_attestation_signature(spec, state):
     attestation = get_valid_attestation(spec, state)
-    state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
+    next_slots(spec, state, spec.MIN_ATTESTATION_INCLUSION_DELAY)
 
     yield from run_attestation_processing(spec, state, attestation, False)
 
@@ -108,10 +76,10 @@ def test_before_inclusion_delay(spec, state):
 @with_all_phases
 @spec_state_test
 def test_after_epoch_slots(spec, state):
-    attestation = get_valid_attestation(spec, state, signed=True)
-    state.slot = spec.SLOTS_PER_EPOCH - 1
+    attestation = get_valid_attestation(spec, state, signed=True, on_time=False)
+
     # increment past latest inclusion slot
-    spec.process_slots(state, state.slot + 2)
+    transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH + 1)
     apply_empty_block(spec, state)
 
     yield from run_attestation_processing(spec, state, attestation, False)
@@ -120,7 +88,7 @@ def test_after_epoch_slots(spec, state):
 @with_all_phases
 @spec_state_test
 def test_old_source_epoch(spec, state):
-    state.slot = spec.SLOTS_PER_EPOCH * 5
+    next_slots(spec, state, spec.SLOTS_PER_EPOCH * 5)
     state.finalized_checkpoint.epoch = 2
     state.previous_justified_checkpoint.epoch = 3
     state.current_justified_checkpoint.epoch = 4
@@ -142,7 +110,7 @@ def test_old_source_epoch(spec, state):
 @always_bls
 def test_wrong_index_for_committee_signature(spec, state):
     attestation = get_valid_attestation(spec, state)
-    state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
+    next_slots(spec, state, spec.MIN_ATTESTATION_INCLUSION_DELAY)
 
     attestation.data.index += 1
 
@@ -153,12 +121,14 @@ def test_wrong_index_for_committee_signature(spec, state):
 @spec_state_test
 @never_bls
 def test_wrong_index_for_slot(spec, state):
-    committees_per_slot = spec.get_committee_count_at_slot(state, state.slot)
-    assert committees_per_slot < spec.MAX_COMMITTEES_PER_SLOT
-    index = committees_per_slot
+    while spec.get_committee_count_at_slot(state, state.slot) >= spec.MAX_COMMITTEES_PER_SLOT:
+        state.validators = state.validators[:len(state.validators) // 2]
+        state.balances = state.balances[:len(state.balances) // 2]
+
+    index = spec.MAX_COMMITTEES_PER_SLOT - 1
 
     attestation = get_valid_attestation(spec, state)
-    state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
+    next_slots(spec, state, spec.MIN_ATTESTATION_INCLUSION_DELAY)
 
     attestation.data.index = index
 
@@ -170,7 +140,7 @@ def test_wrong_index_for_slot(spec, state):
 @never_bls
 def test_invalid_index(spec, state):
     attestation = get_valid_attestation(spec, state)
-    state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
+    next_slots(spec, state, spec.MIN_ATTESTATION_INCLUSION_DELAY)
 
     # off by one (with respect to valid range) on purpose
     attestation.data.index = spec.MAX_COMMITTEES_PER_SLOT
@@ -184,7 +154,7 @@ def test_mismatched_target_and_slot(spec, state):
     next_epoch(spec, state)
     next_epoch(spec, state)
 
-    attestation = get_valid_attestation(spec, state)
+    attestation = get_valid_attestation(spec, state, on_time=False)
     attestation.data.slot = attestation.data.slot - spec.SLOTS_PER_EPOCH
 
     sign_attestation(spec, state, attestation)
@@ -197,9 +167,9 @@ def test_mismatched_target_and_slot(spec, state):
 def test_old_target_epoch(spec, state):
     assert spec.MIN_ATTESTATION_INCLUSION_DELAY < spec.SLOTS_PER_EPOCH * 2
 
-    attestation = get_valid_attestation(spec, state, signed=True)
+    attestation = get_valid_attestation(spec, state, signed=True, on_time=False)
 
-    state.slot = spec.SLOTS_PER_EPOCH * 2  # target epoch will be too old to handle
+    next_slots(spec, state, spec.SLOTS_PER_EPOCH * 2)  # target epoch will be too old to handle
 
     yield from run_attestation_processing(spec, state, attestation, False)
 
@@ -221,7 +191,7 @@ def test_future_target_epoch(spec, state):
     # manually add signature for correct participants
     attestation.signature = sign_aggregate_attestation(spec, state, attestation.data, participants)
 
-    state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
+    next_slots(spec, state, spec.MIN_ATTESTATION_INCLUSION_DELAY)
 
     yield from run_attestation_processing(spec, state, attestation, False)
 
@@ -230,7 +200,7 @@ def test_future_target_epoch(spec, state):
 @spec_state_test
 def test_new_source_epoch(spec, state):
     attestation = get_valid_attestation(spec, state)
-    state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
+    next_slots(spec, state, spec.MIN_ATTESTATION_INCLUSION_DELAY)
 
     attestation.data.source.epoch += 1
 
@@ -243,7 +213,7 @@ def test_new_source_epoch(spec, state):
 @spec_state_test
 def test_source_root_is_target_root(spec, state):
     attestation = get_valid_attestation(spec, state)
-    state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
+    next_slots(spec, state, spec.MIN_ATTESTATION_INCLUSION_DELAY)
 
     attestation.data.source.root = attestation.data.target.root
 
@@ -255,14 +225,15 @@ def test_source_root_is_target_root(spec, state):
 @with_all_phases
 @spec_state_test
 def test_invalid_current_source_root(spec, state):
-    state.slot = spec.SLOTS_PER_EPOCH * 5
+    next_slots(spec, state, spec.SLOTS_PER_EPOCH * 5)
+
     state.finalized_checkpoint.epoch = 2
 
     state.previous_justified_checkpoint = spec.Checkpoint(epoch=3, root=b'\x01' * 32)
     state.current_justified_checkpoint = spec.Checkpoint(epoch=4, root=b'\x32' * 32)
 
-    attestation = get_valid_attestation(spec, state, slot=(spec.SLOTS_PER_EPOCH * 3) + 1)
-    state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
+    attestation = get_valid_attestation(spec, state, slot=(spec.SLOTS_PER_EPOCH * 3) + 1, on_time=False)
+    next_slots(spec, state, spec.MIN_ATTESTATION_INCLUSION_DELAY)
 
     # Test logic sanity checks:
     assert state.current_justified_checkpoint.root != state.previous_justified_checkpoint.root
@@ -280,7 +251,7 @@ def test_invalid_current_source_root(spec, state):
 @spec_state_test
 def test_bad_source_root(spec, state):
     attestation = get_valid_attestation(spec, state)
-    state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
+    next_slots(spec, state, spec.MIN_ATTESTATION_INCLUSION_DELAY)
 
     attestation.data.source.root = b'\x42' * 32
 
@@ -292,8 +263,9 @@ def test_bad_source_root(spec, state):
 @with_all_phases
 @spec_state_test
 def test_empty_aggregation_bits(spec, state):
+    next_slot(spec, state)
     attestation = get_valid_attestation(spec, state, empty=True)
-    state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
+    next_slots(spec, state, spec.MIN_ATTESTATION_INCLUSION_DELAY)
 
     assert attestation.aggregation_bits == Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE](
         *([0b0] * len(attestation.aggregation_bits)))
@@ -305,7 +277,7 @@ def test_empty_aggregation_bits(spec, state):
 @spec_state_test
 def test_too_many_aggregation_bits(spec, state):
     attestation = get_valid_attestation(spec, state, signed=True)
-    state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
+    next_slots(spec, state, spec.MIN_ATTESTATION_INCLUSION_DELAY)
 
     # one too many bits
     attestation.aggregation_bits.append(0b0)
@@ -317,7 +289,7 @@ def test_too_many_aggregation_bits(spec, state):
 @spec_state_test
 def test_too_few_aggregation_bits(spec, state):
     attestation = get_valid_attestation(spec, state)
-    state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
+    next_slots(spec, state, spec.MIN_ATTESTATION_INCLUSION_DELAY)
 
     attestation.aggregation_bits = Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE](
         *([0b1] + [0b0] * (len(attestation.aggregation_bits) - 1)))
diff --git a/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py b/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py
--- a/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py
+++ b/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py
@@ -1,4 +1,7 @@
-from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls, with_all_phases, with_phases
+from eth2spec.test.context import (
+    PHASE0, PHASE1,
+    spec_state_test, expect_assertion_error, always_bls, with_all_phases, with_phases
+)
 from eth2spec.test.helpers.attestations import sign_indexed_attestation
 from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing, \
     get_indexed_attestation_participants, get_attestation_2_data, get_attestation_1_data
@@ -161,7 +164,7 @@ def test_same_data(spec, state):
 
     indexed_att_1 = attester_slashing.attestation_1
     att_2_data = get_attestation_2_data(spec, attester_slashing)
-    if spec.fork == 'phase1':
+    if spec.fork == PHASE1:
         indexed_att_1.attestation.data = att_2_data
     else:
         indexed_att_1.data = att_2_data
@@ -199,7 +202,7 @@ def test_participants_already_slashed(spec, state):
 # Some of the following tests are phase0 only: phase 1 lists participants with bitfields instead of index list.
 
 
-@with_phases(['phase0'])
+@with_phases([PHASE0])
 @spec_state_test
 @always_bls
 def test_att1_bad_extra_index(spec, state):
@@ -215,7 +218,7 @@ def test_att1_bad_extra_index(spec, state):
     yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
 
 
-@with_phases(['phase0'])
+@with_phases([PHASE0])
 @spec_state_test
 @always_bls
 def test_att1_bad_replaced_index(spec, state):
@@ -231,7 +234,7 @@ def test_att1_bad_replaced_index(spec, state):
     yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
 
 
-@with_phases(['phase0'])
+@with_phases([PHASE0])
 @spec_state_test
 @always_bls
 def test_att2_bad_extra_index(spec, state):
@@ -247,7 +250,7 @@ def test_att2_bad_extra_index(spec, state):
     yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
 
 
-@with_phases(['phase0'])
+@with_phases([PHASE0])
 @spec_state_test
 @always_bls
 def test_att2_bad_replaced_index(spec, state):
@@ -263,7 +266,7 @@ def test_att2_bad_replaced_index(spec, state):
     yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
 
 
-@with_phases(['phase0'])
+@with_phases([PHASE0])
 @spec_state_test
 @always_bls
 def test_att1_duplicate_index_normal_signed(spec, state):
@@ -283,7 +286,7 @@ def test_att1_duplicate_index_normal_signed(spec, state):
     yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
 
 
-@with_phases(['phase0'])
+@with_phases([PHASE0])
 @spec_state_test
 @always_bls
 def test_att2_duplicate_index_normal_signed(spec, state):
@@ -303,7 +306,7 @@ def test_att2_duplicate_index_normal_signed(spec, state):
     yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
 
 
-@with_phases(['phase0'])
+@with_phases([PHASE0])
 @spec_state_test
 @always_bls
 def test_att1_duplicate_index_double_signed(spec, state):
@@ -318,7 +321,7 @@ def test_att1_duplicate_index_double_signed(spec, state):
     yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
 
 
-@with_phases(['phase0'])
+@with_phases([PHASE0])
 @spec_state_test
 @always_bls
 def test_att2_duplicate_index_double_signed(spec, state):
@@ -333,7 +336,7 @@ def test_att2_duplicate_index_double_signed(spec, state):
     yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
 
 
-@with_phases(['phase0'])
+@with_phases([PHASE0])
 @spec_state_test
 def test_unsorted_att_1(spec, state):
     attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=True)
@@ -346,7 +349,7 @@ def test_unsorted_att_1(spec, state):
     yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
 
 
-@with_phases(['phase0'])
+@with_phases([PHASE0])
 @spec_state_test
 def test_unsorted_att_2(spec, state):
     attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=False)
diff --git a/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_proposer_slashing.py b/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_proposer_slashing.py
--- a/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_proposer_slashing.py
+++ b/tests/core/pyspec/eth2spec/test/phase_0/block_processing/test_process_proposer_slashing.py
@@ -2,7 +2,7 @@
 from eth2spec.test.helpers.block_header import sign_block_header
 from eth2spec.test.helpers.keys import privkeys
 from eth2spec.test.helpers.proposer_slashings import get_valid_proposer_slashing
-from eth2spec.test.helpers.state import get_balance
+from eth2spec.test.helpers.state import get_balance, next_epoch
 
 
 def run_proposer_slashing_processing(spec, state, proposer_slashing, valid=True):
@@ -152,7 +152,7 @@ def test_proposer_is_withdrawn(spec, state):
     proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=True)
 
     # move 1 epoch into future, to allow for past withdrawable epoch
-    state.slot += spec.SLOTS_PER_EPOCH
+    next_epoch(spec, state)
     # set proposer withdrawable_epoch in past
     current_epoch = spec.get_current_epoch(state)
     proposer_index = proposer_slashing.signed_header_1.message.proposer_index
diff --git a/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_final_updates.py b/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_final_updates.py
--- a/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_final_updates.py
+++ b/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_final_updates.py
@@ -2,6 +2,7 @@
 from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import (
     run_epoch_processing_with, run_epoch_processing_to
 )
+from eth2spec.test.helpers.state import transition_to
 
 
 def run_process_final_updates(spec, state):
@@ -13,7 +14,8 @@ def run_process_final_updates(spec, state):
 def test_eth1_vote_no_reset(spec, state):
     assert spec.EPOCHS_PER_ETH1_VOTING_PERIOD > 1
     # skip ahead to the end of the epoch
-    state.slot = spec.SLOTS_PER_EPOCH - 1
+    transition_to(spec, state, spec.SLOTS_PER_EPOCH - 1)
+
     for i in range(state.slot + 1):  # add a vote for each skipped slot.
         state.eth1_data_votes.append(
             spec.Eth1Data(deposit_root=b'\xaa' * 32,
diff --git a/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py b/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py
--- a/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py
+++ b/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py
@@ -2,6 +2,7 @@
 from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import (
     run_epoch_processing_with
 )
+from eth2spec.test.helpers.state import transition_to
 
 
 def run_process_just_and_fin(spec, state):
@@ -23,7 +24,7 @@ def add_mock_attestations(spec, state, epoch, source, target, sufficient_support
         raise Exception(f"cannot include attestations in epoch ${epoch} from epoch ${current_epoch}")
 
     total_balance = spec.get_total_active_balance(state)
-    remaining_balance = total_balance * 2 // 3
+    remaining_balance = int(total_balance * 2 // 3)  # can become negative
 
     start_slot = spec.compute_start_slot_at_epoch(epoch)
     for slot in range(start_slot, start_slot + spec.SLOTS_PER_EPOCH):
@@ -41,14 +42,15 @@ def add_mock_attestations(spec, state, epoch, source, target, sufficient_support
             aggregation_bits = [0] * len(committee)
             for v in range(len(committee) * 2 // 3 + 1):
                 if remaining_balance > 0:
-                    remaining_balance -= state.validators[v].effective_balance
+                    remaining_balance -= int(state.validators[v].effective_balance)
                     aggregation_bits[v] = 1
                 else:
                     break
 
-            # remove just one attester to make the marginal support insufficient
+            # remove 1/5th of attesters so that support is insufficient
             if not sufficient_support:
-                aggregation_bits[aggregation_bits.index(1)] = 0
+                for i in range(max(len(committee) // 5, 1)):
+                    aggregation_bits[i] = 0
 
             attestations.append(spec.PendingAttestation(
                 aggregation_bits=aggregation_bits,
@@ -81,7 +83,7 @@ def put_checkpoints_in_block_roots(spec, state, checkpoints):
 
 def finalize_on_234(spec, state, epoch, sufficient_support):
     assert epoch > 4
-    state.slot = (spec.SLOTS_PER_EPOCH * epoch) - 1  # skip ahead to just before epoch
+    transition_to(spec, state, spec.SLOTS_PER_EPOCH * epoch - 1)  # skip ahead to just before epoch
 
     # 43210 -- epochs ago
     # 3210x -- justification bitfield indices
@@ -116,7 +118,7 @@ def finalize_on_234(spec, state, epoch, sufficient_support):
 
 def finalize_on_23(spec, state, epoch, sufficient_support):
     assert epoch > 3
-    state.slot = (spec.SLOTS_PER_EPOCH * epoch) - 1  # skip ahead to just before epoch
+    transition_to(spec, state, spec.SLOTS_PER_EPOCH * epoch - 1)  # skip ahead to just before epoch
 
     # 43210 -- epochs ago
     # 210xx  -- justification bitfield indices (pre shift)
@@ -194,7 +196,7 @@ def finalize_on_123(spec, state, epoch, sufficient_support):
 
 def finalize_on_12(spec, state, epoch, sufficient_support, messed_up_target):
     assert epoch > 2
-    state.slot = (spec.SLOTS_PER_EPOCH * epoch) - 1  # skip ahead to just before epoch
+    transition_to(spec, state, spec.SLOTS_PER_EPOCH * epoch - 1)  # skip ahead to just before epoch
 
     # 43210 -- epochs ago
     # 210xx  -- justification bitfield indices (pre shift)
diff --git a/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py b/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py
--- a/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py
+++ b/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py
@@ -1,4 +1,4 @@
-from eth2spec.test.helpers.state import next_epoch
+from eth2spec.test.helpers.state import next_epoch, next_slots
 from eth2spec.test.context import spec_state_test, with_all_phases
 from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_epoch_processing_with
 
@@ -101,7 +101,7 @@ def test_activation_queue_sorting(spec, state):
     state.validators[mock_activations - 1].activation_eligibility_epoch = epoch
 
     # move state forward and finalize to allow for activations
-    state.slot += spec.SLOTS_PER_EPOCH * 3
+    next_slots(spec, state, spec.SLOTS_PER_EPOCH * 3)
     state.finalized_checkpoint.epoch = epoch + 1
 
     yield from run_process_registry_updates(spec, state)
@@ -113,10 +113,10 @@ def test_activation_queue_sorting(spec, state):
     # the second last is at the end of the queue, and did not make the churn,
     #  hence is not assigned an activation_epoch yet.
     assert state.validators[mock_activations - 2].activation_epoch == spec.FAR_FUTURE_EPOCH
-    # the one at churn_limit - 1 did not make it, it was out-prioritized
-    assert state.validators[churn_limit - 1].activation_epoch == spec.FAR_FUTURE_EPOCH
+    # the one at churn_limit did not make it, it was out-prioritized
+    assert state.validators[churn_limit].activation_epoch == spec.FAR_FUTURE_EPOCH
     # but the the one in front of the above did
-    assert state.validators[churn_limit - 2].activation_epoch != spec.FAR_FUTURE_EPOCH
+    assert state.validators[churn_limit - 1].activation_epoch != spec.FAR_FUTURE_EPOCH
 
 
 @with_all_phases
@@ -131,7 +131,8 @@ def test_activation_queue_efficiency(spec, state):
         state.validators[i].activation_eligibility_epoch = epoch + 1
 
     # move state forward and finalize to allow for activations
-    state.slot += spec.SLOTS_PER_EPOCH * 3
+    next_slots(spec, state, spec.SLOTS_PER_EPOCH * 3)
+
     state.finalized_checkpoint.epoch = epoch + 1
 
     # Run first registry update. Do not yield test vectors
diff --git a/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_rewards_and_penalties.py b/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_rewards_and_penalties.py
--- a/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_rewards_and_penalties.py
+++ b/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_rewards_and_penalties.py
@@ -1,8 +1,9 @@
 from eth2spec.test.context import (
-    spec_state_test, with_all_phases, spec_test,
-    misc_balances, with_custom_state,
-    low_single_balance, zero_activation_threshold,
-    single_phase,
+    spec_state_test, spec_test,
+    with_all_phases, with_phases, single_phase,
+    with_custom_state,
+    zero_activation_threshold,
+    misc_balances, low_single_balance,
 )
 from eth2spec.test.helpers.state import (
     next_epoch,
@@ -20,7 +21,34 @@ def run_process_rewards_and_penalties(spec, state):
     yield from run_epoch_processing_with(spec, state, 'process_rewards_and_penalties')
 
 
-@with_all_phases
+def prepare_state_with_full_attestations(spec, state, empty=False):
+    # Go to start of next epoch to ensure can have full participation
+    next_epoch(spec, state)
+
+    start_slot = state.slot
+    start_epoch = spec.get_current_epoch(state)
+    next_epoch_start_slot = spec.compute_start_slot_at_epoch(start_epoch + 1)
+    attestations = []
+    for _ in range(spec.SLOTS_PER_EPOCH + spec.MIN_ATTESTATION_INCLUSION_DELAY):
+        # create an attestation for each index in each slot in epoch
+        if state.slot < next_epoch_start_slot:
+            for committee_index in range(spec.get_committee_count_at_slot(state, state.slot)):
+                attestation = get_valid_attestation(spec, state, index=committee_index, empty=empty, signed=True)
+                attestations.append(attestation)
+        # fill each created slot in state after inclusion delay
+        if state.slot >= start_slot + spec.MIN_ATTESTATION_INCLUSION_DELAY:
+            inclusion_slot = state.slot - spec.MIN_ATTESTATION_INCLUSION_DELAY
+            include_attestations = [att for att in attestations if att.data.slot == inclusion_slot]
+            add_attestations_to_state(spec, state, include_attestations, state.slot)
+        next_slot(spec, state)
+
+    assert state.slot == next_epoch_start_slot + spec.MIN_ATTESTATION_INCLUSION_DELAY
+    assert len(state.previous_epoch_attestations) == len(attestations)
+
+    return attestations
+
+
+@with_phases(['phase0'])
 @spec_state_test
 def test_genesis_epoch_no_attestations_no_penalties(spec, state):
     pre_state = state.copy()
@@ -33,7 +61,7 @@ def test_genesis_epoch_no_attestations_no_penalties(spec, state):
         assert state.balances[index] == pre_state.balances[index]
 
 
-@with_all_phases
+@with_phases(['phase0'])
 @spec_state_test
 def test_genesis_epoch_full_attestations_no_rewards(spec, state):
     attestations = []
@@ -59,25 +87,6 @@ def test_genesis_epoch_full_attestations_no_rewards(spec, state):
         assert state.balances[index] == pre_state.balances[index]
 
 
-def prepare_state_with_full_attestations(spec, state, empty=False):
-    attestations = []
-    for slot in range(spec.SLOTS_PER_EPOCH + spec.MIN_ATTESTATION_INCLUSION_DELAY):
-        # create an attestation for each slot in epoch
-        if slot < spec.SLOTS_PER_EPOCH:
-            attestation = get_valid_attestation(spec, state, empty=empty, signed=True)
-            attestations.append(attestation)
-        # fill each created slot in state after inclusion delay
-        if slot - spec.MIN_ATTESTATION_INCLUSION_DELAY >= 0:
-            include_att = attestations[slot - spec.MIN_ATTESTATION_INCLUSION_DELAY]
-            add_attestations_to_state(spec, state, [include_att], state.slot)
-        next_slot(spec, state)
-
-    assert spec.compute_epoch_at_slot(state.slot) == spec.GENESIS_EPOCH + 1
-    assert len(state.previous_epoch_attestations) == spec.SLOTS_PER_EPOCH
-
-    return attestations
-
-
 @with_all_phases
 @spec_state_test
 def test_full_attestations(spec, state):
@@ -88,7 +97,7 @@ def test_full_attestations(spec, state):
     yield from run_process_rewards_and_penalties(spec, state)
 
     attesting_indices = spec.get_unslashed_attesting_indices(state, attestations)
-    assert len(attesting_indices) > 0
+    assert len(attesting_indices) == len(pre_state.validators)
     for index in range(len(pre_state.validators)):
         if index in attesting_indices:
             assert state.balances[index] > pre_state.balances[index]
@@ -168,6 +177,7 @@ def test_full_attestations_one_validaor_one_gwei(spec, state):
 @with_all_phases
 @spec_state_test
 def test_no_attestations_all_penalties(spec, state):
+    # Move to next epoch to ensure rewards/penalties are processed
     next_epoch(spec, state)
     pre_state = state.copy()
 
@@ -237,26 +247,14 @@ def test_duplicate_attestation(spec, state):
 @spec_state_test
 # Case when some eligible attestations are slashed. Modifies attesting_balance and consequently rewards/penalties.
 def test_attestations_some_slashed(spec, state):
-    attestations = []
-    for slot in range(spec.SLOTS_PER_EPOCH + spec.MIN_ATTESTATION_INCLUSION_DELAY):
-        # create an attestation for each slot in epoch
-        if slot < spec.SLOTS_PER_EPOCH:
-            attestation = get_valid_attestation(spec, state, signed=True)
-            attestations.append(attestation)
-        # fill each created slot in state after inclusion delay
-        if slot - spec.MIN_ATTESTATION_INCLUSION_DELAY >= 0:
-            include_att = attestations[slot - spec.MIN_ATTESTATION_INCLUSION_DELAY]
-            add_attestations_to_state(spec, state, [include_att], state.slot)
-        next_slot(spec, state)
-
+    attestations = prepare_state_with_full_attestations(spec, state)
     attesting_indices_before_slashings = list(spec.get_unslashed_attesting_indices(state, attestations))
 
     # Slash maximum amount of validators allowed per epoch.
     for i in range(spec.MIN_PER_EPOCH_CHURN_LIMIT):
         spec.slash_validator(state, attesting_indices_before_slashings[i])
 
-    assert spec.compute_epoch_at_slot(state.slot) == spec.GENESIS_EPOCH + 1
-    assert len(state.previous_epoch_attestations) == spec.SLOTS_PER_EPOCH
+    assert len(state.previous_epoch_attestations) == len(attestations)
 
     pre_state = state.copy()
 
@@ -267,6 +265,8 @@ def test_attestations_some_slashed(spec, state):
     assert len(attesting_indices_before_slashings) - len(attesting_indices) == spec.MIN_PER_EPOCH_CHURN_LIMIT
     for index in range(len(pre_state.validators)):
         if index in attesting_indices:
+            # non-slashed attester should gain reward
             assert state.balances[index] > pre_state.balances[index]
         else:
+            # Slashed non-proposer attester should have penalty
             assert state.balances[index] < pre_state.balances[index]
diff --git a/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_slashings.py b/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_slashings.py
--- a/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_slashings.py
+++ b/tests/core/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_slashings.py
@@ -2,6 +2,7 @@
 from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import (
     run_epoch_processing_with, run_epoch_processing_to
 )
+from eth2spec.test.helpers.state import next_epoch
 
 
 def run_process_slashings(spec, state):
@@ -79,7 +80,7 @@ def test_small_penalty(spec, state):
 @spec_state_test
 def test_scaled_penalties(spec, state):
     # skip to next epoch
-    state.slot = spec.SLOTS_PER_EPOCH
+    next_epoch(spec, state)
 
     # Also mock some previous slashings, so that we test to have the delta in the penalties computation.
     base = spec.EJECTION_BALANCE
diff --git a/tests/core/pyspec/eth2spec/test/phase_1/block_processing/test_process_attestation.py b/tests/core/pyspec/eth2spec/test/phase_1/block_processing/test_process_attestation.py
new file mode 100644
--- /dev/null
+++ b/tests/core/pyspec/eth2spec/test/phase_1/block_processing/test_process_attestation.py
@@ -0,0 +1,46 @@
+from eth2spec.test.context import (
+    with_all_phases_except,
+    spec_state_test,
+    always_bls,
+)
+from eth2spec.test.helpers.state import transition_to
+from eth2spec.test.helpers.attestations import (
+    run_attestation_processing,
+    get_valid_late_attestation,
+    get_valid_on_time_attestation,
+)
+
+
+@with_all_phases_except(['phase0'])
+@spec_state_test
+@always_bls
+def test_on_time_success(spec, state):
+    attestation = get_valid_on_time_attestation(spec, state, signed=True)
+
+    transition_to(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY)
+
+    yield from run_attestation_processing(spec, state, attestation)
+
+
+@with_all_phases_except(['phase0'])
+@spec_state_test
+@always_bls
+def test_on_time_empty_custody_bits_blocks(spec, state):
+    attestation = get_valid_late_attestation(spec, state, signed=True)
+
+    assert not any(attestation.custody_bits_blocks)
+
+    transition_to(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY)
+
+    yield from run_attestation_processing(spec, state, attestation, False)
+
+
+@with_all_phases_except(['phase0'])
+@spec_state_test
+@always_bls
+def test_late_with_custody_bits_blocks(spec, state):
+    attestation = get_valid_on_time_attestation(spec, state, signed=True)
+
+    transition_to(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY + 1)
+
+    yield from run_attestation_processing(spec, state, attestation, False)
diff --git a/tests/core/pyspec/eth2spec/test/phase_1/block_processing/test_process_custody_key_reveal.py b/tests/core/pyspec/eth2spec/test/phase_1/block_processing/test_process_custody_key_reveal.py
--- a/tests/core/pyspec/eth2spec/test/phase_1/block_processing/test_process_custody_key_reveal.py
+++ b/tests/core/pyspec/eth2spec/test/phase_1/block_processing/test_process_custody_key_reveal.py
@@ -1,5 +1,6 @@
 from eth2spec.test.helpers.custody import get_valid_custody_key_reveal
 from eth2spec.test.context import (
+    PHASE0,
     with_all_phases_except,
     spec_state_test,
     expect_assertion_error,
@@ -54,7 +55,7 @@ def run_custody_key_reveal_processing(spec, state, custody_key_reveal, valid=Tru
     yield 'post', state
 
 
-@with_all_phases_except(['phase0'])
+@with_all_phases_except([PHASE0])
 @spec_state_test
 @always_bls
 def test_success(spec, state):
@@ -64,7 +65,7 @@ def test_success(spec, state):
     yield from run_custody_key_reveal_processing(spec, state, custody_key_reveal)
 
 
-@with_all_phases_except(['phase0'])
+@with_all_phases_except([PHASE0])
 @spec_state_test
 @always_bls
 def test_reveal_too_early(spec, state):
@@ -73,7 +74,7 @@ def test_reveal_too_early(spec, state):
     yield from run_custody_key_reveal_processing(spec, state, custody_key_reveal, False)
 
 
-@with_all_phases_except(['phase0'])
+@with_all_phases_except([PHASE0])
 @spec_state_test
 @always_bls
 def test_wrong_period(spec, state):
@@ -82,7 +83,7 @@ def test_wrong_period(spec, state):
     yield from run_custody_key_reveal_processing(spec, state, custody_key_reveal, False)
 
 
-@with_all_phases_except(['phase0'])
+@with_all_phases_except([PHASE0])
 @spec_state_test
 @always_bls
 def test_late_reveal(spec, state):
@@ -92,7 +93,7 @@ def test_late_reveal(spec, state):
     yield from run_custody_key_reveal_processing(spec, state, custody_key_reveal)
 
 
-@with_all_phases_except(['phase0'])
+@with_all_phases_except([PHASE0])
 @spec_state_test
 @always_bls
 def test_double_reveal(spec, state):
@@ -104,7 +105,7 @@ def test_double_reveal(spec, state):
     yield from run_custody_key_reveal_processing(spec, state, custody_key_reveal, False)
 
 
-@with_all_phases_except(['phase0'])
+@with_all_phases_except([PHASE0])
 @spec_state_test
 @always_bls
 def test_max_decrement(spec, state):
diff --git a/tests/core/pyspec/eth2spec/test/phase_1/block_processing/test_process_early_derived_secret_reveal.py b/tests/core/pyspec/eth2spec/test/phase_1/block_processing/test_process_early_derived_secret_reveal.py
--- a/tests/core/pyspec/eth2spec/test/phase_1/block_processing/test_process_early_derived_secret_reveal.py
+++ b/tests/core/pyspec/eth2spec/test/phase_1/block_processing/test_process_early_derived_secret_reveal.py
@@ -2,6 +2,7 @@
 from eth2spec.test.helpers.block import apply_empty_block
 from eth2spec.test.helpers.state import next_epoch, get_balance
 from eth2spec.test.context import (
+    PHASE0,
     with_all_phases_except,
     spec_state_test,
     expect_assertion_error,
@@ -41,7 +42,7 @@ def run_early_derived_secret_reveal_processing(spec, state, randao_key_reveal, v
     yield 'post', state
 
 
-@with_all_phases_except(['phase0'])
+@with_all_phases_except([PHASE0])
 @spec_state_test
 @always_bls
 def test_success(spec, state):
@@ -50,7 +51,7 @@ def test_success(spec, state):
     yield from run_early_derived_secret_reveal_processing(spec, state, randao_key_reveal)
 
 
-@with_all_phases_except(['phase0'])
+@with_all_phases_except([PHASE0])
 @spec_state_test
 @never_bls
 def test_reveal_from_current_epoch(spec, state):
@@ -59,7 +60,7 @@ def test_reveal_from_current_epoch(spec, state):
     yield from run_early_derived_secret_reveal_processing(spec, state, randao_key_reveal, False)
 
 
-@with_all_phases_except(['phase0'])
+@with_all_phases_except([PHASE0])
 @spec_state_test
 @never_bls
 def test_reveal_from_past_epoch(spec, state):
@@ -70,7 +71,7 @@ def test_reveal_from_past_epoch(spec, state):
     yield from run_early_derived_secret_reveal_processing(spec, state, randao_key_reveal, False)
 
 
-@with_all_phases_except(['phase0'])
+@with_all_phases_except([PHASE0])
 @spec_state_test
 @always_bls
 def test_reveal_with_custody_padding(spec, state):
@@ -82,7 +83,7 @@ def test_reveal_with_custody_padding(spec, state):
     yield from run_early_derived_secret_reveal_processing(spec, state, randao_key_reveal, True)
 
 
-@with_all_phases_except(['phase0'])
+@with_all_phases_except([PHASE0])
 @spec_state_test
 @always_bls
 def test_reveal_with_custody_padding_minus_one(spec, state):
@@ -94,7 +95,7 @@ def test_reveal_with_custody_padding_minus_one(spec, state):
     yield from run_early_derived_secret_reveal_processing(spec, state, randao_key_reveal, True)
 
 
-@with_all_phases_except(['phase0'])
+@with_all_phases_except([PHASE0])
 @spec_state_test
 @never_bls
 def test_double_reveal(spec, state):
@@ -115,7 +116,7 @@ def test_double_reveal(spec, state):
     yield from run_early_derived_secret_reveal_processing(spec, state, randao_key_reveal2, False)
 
 
-@with_all_phases_except(['phase0'])
+@with_all_phases_except([PHASE0])
 @spec_state_test
 @never_bls
 def test_revealer_is_slashed(spec, state):
@@ -125,7 +126,7 @@ def test_revealer_is_slashed(spec, state):
     yield from run_early_derived_secret_reveal_processing(spec, state, randao_key_reveal, False)
 
 
-@with_all_phases_except(['phase0'])
+@with_all_phases_except([PHASE0])
 @spec_state_test
 @never_bls
 def test_far_future_epoch(spec, state):
diff --git a/tests/core/pyspec/eth2spec/test/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/sanity/test_blocks.py
--- a/tests/core/pyspec/eth2spec/test/sanity/test_blocks.py
+++ b/tests/core/pyspec/eth2spec/test/sanity/test_blocks.py
@@ -2,7 +2,7 @@
 
 from eth2spec.utils import bls
 
-from eth2spec.test.helpers.state import get_balance, state_transition_and_sign_block, next_slot
+from eth2spec.test.helpers.state import get_balance, state_transition_and_sign_block, next_slot, next_epoch
 from eth2spec.test.helpers.block import build_empty_block_for_next_slot, build_empty_block, sign_block, \
     transition_unsigned_block
 from eth2spec.test.helpers.keys import privkeys, pubkeys
@@ -11,7 +11,7 @@
 from eth2spec.test.helpers.attestations import get_valid_attestation
 from eth2spec.test.helpers.deposits import prepare_state_and_deposit
 
-from eth2spec.test.context import spec_state_test, with_all_phases, expect_assertion_error, always_bls
+from eth2spec.test.context import spec_state_test, with_all_phases, expect_assertion_error, always_bls, with_phases
 
 
 @with_all_phases
@@ -303,7 +303,8 @@ def test_proposer_after_inactive_index(spec, state):
     state.validators[inactive_index].exit_epoch = spec.get_current_epoch(state)
 
     # skip forward, get brand new proposers
-    state.slot = spec.SLOTS_PER_EPOCH * 2
+    next_epoch(spec, state)
+    next_epoch(spec, state)
     block = build_empty_block_for_next_slot(spec, state)
     state_transition_and_sign_block(spec, state, block)
 
@@ -415,7 +416,7 @@ def test_deposit_top_up(spec, state):
 @with_all_phases
 @spec_state_test
 def test_attestation(spec, state):
-    state.slot = spec.SLOTS_PER_EPOCH
+    next_epoch(spec, state)
 
     yield 'pre', state
 
@@ -423,7 +424,7 @@ def test_attestation(spec, state):
 
     # Add to state via block transition
     pre_current_attestations_len = len(state.current_epoch_attestations)
-    attestation_block = build_empty_block(spec, state, state.slot + 1 + spec.MIN_ATTESTATION_INCLUSION_DELAY)
+    attestation_block = build_empty_block(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY)
     attestation_block.body.attestations.append(attestation)
     signed_attestation_block = state_transition_and_sign_block(spec, state, attestation_block)
 
@@ -442,7 +443,9 @@ def test_attestation(spec, state):
     assert spec.hash_tree_root(state.previous_epoch_attestations) == pre_current_attestations_root
 
 
-@with_all_phases
+# In phase1 a committee is computed for PERSISTENT_COMMITTEE_PERIOD slots ago,
+# exceeding the minimal-config randao mixes memory size.
+@with_phases(['phase0'])
 @spec_state_test
 def test_voluntary_exit(spec, state):
     validator_index = spec.get_active_validator_indices(
diff --git a/tests/core/pyspec/eth2spec/test/test_finality.py b/tests/core/pyspec/eth2spec/test/test_finality.py
--- a/tests/core/pyspec/eth2spec/test/test_finality.py
+++ b/tests/core/pyspec/eth2spec/test/test_finality.py
@@ -1,5 +1,6 @@
-from eth2spec.test.context import spec_state_test, never_bls, with_all_phases
-from eth2spec.test.helpers.state import next_epoch, next_epoch_with_attestations
+from eth2spec.test.context import spec_state_test, never_bls, with_all_phases, with_phases
+from eth2spec.test.helpers.state import next_epoch
+from eth2spec.test.helpers.attestations import next_epoch_with_attestations
 from eth2spec.test.helpers.block import apply_empty_block
 
 
@@ -28,7 +29,7 @@ def check_finality(spec,
         assert state.finalized_checkpoint == prev_state.finalized_checkpoint
 
 
-@with_all_phases
+@with_phases(["phase0"])
 @spec_state_test
 @never_bls
 def test_finality_no_updates_at_genesis(spec, state):
diff --git a/tests/formats/operations/README.md b/tests/formats/operations/README.md
--- a/tests/formats/operations/README.md
+++ b/tests/formats/operations/README.md
@@ -18,11 +18,11 @@ A YAML-encoded `BeaconState`, the state before applying the operation.
 
 Also available as `pre.ssz`.
 
-### `<operation-name>.yaml`
+### `<input-name>.yaml`
 
 A YAML-encoded operation object, e.g. a `ProposerSlashing`, or `Deposit`.
 
-Also available as `<operation-name>.ssz`.
+Also available as `<input-name>.ssz`.
 
 ### `post.yaml`
 
@@ -39,14 +39,14 @@ This excludes the other parts of the block-transition.
 
 Operations:
 
-| *`operation-name`*      | *`operation-object`* | *`input name`*       | *`processing call`*                                    |
-|-------------------------|----------------------|----------------------|--------------------------------------------------------|
-| `attestation`           | `Attestation`        | `attestation`        | `process_attestation(state, attestation)`              |
-| `attester_slashing`     | `AttesterSlashing`   | `attester_slashing`  | `process_attester_slashing(state, attester_slashing)`  |
-| `block_header`          | `Block`              | **`block`**          | `process_block_header(state, block)`                   |
-| `deposit`               | `Deposit`            | `deposit`            | `process_deposit(state, deposit)`                      |
-| `proposer_slashing`     | `ProposerSlashing`   | `proposer_slashing`  | `process_proposer_slashing(state, proposer_slashing)`  |
-| `voluntary_exit`        | `VoluntaryExit`      | `voluntary_exit`     | `process_voluntary_exit(state, voluntary_exit)`        |
+| *`operation-name`*      | *`operation-object`*  | *`input name`*       | *`processing call`*                                    |
+|-------------------------|-----------------------|----------------------|--------------------------------------------------------|
+| `attestation`           | `Attestation`         | `attestation`        | `process_attestation(state, attestation)`              |
+| `attester_slashing`     | `AttesterSlashing`    | `attester_slashing`  | `process_attester_slashing(state, attester_slashing)`  |
+| `block_header`          | `BeaconBlock`         | **`block`**          | `process_block_header(state, block)`                   |
+| `deposit`               | `Deposit`             | `deposit`            | `process_deposit(state, deposit)`                      |
+| `proposer_slashing`     | `ProposerSlashing`    | `proposer_slashing`  | `process_proposer_slashing(state, proposer_slashing)`  |
+| `voluntary_exit`        | `SignedVoluntaryExit` | `voluntary_exit`     | `process_voluntary_exit(state, voluntary_exit)`        |
 
 Note that `block_header` is not strictly an operation (and is a full `Block`), but processed in the same manner, and hence included here.
 
diff --git a/tests/formats/sanity/blocks.md b/tests/formats/sanity/blocks.md
--- a/tests/formats/sanity/blocks.md
+++ b/tests/formats/sanity/blocks.md
@@ -25,7 +25,7 @@ Also available as `pre.ssz`.
 A series of files, with `<index>` in range `[0, blocks_count)`. Blocks need to be processed in order,
  following the main transition function (i.e. process slot and epoch transitions in between blocks as normal)
 
-Each file is a YAML-encoded `BeaconBlock`.
+Each file is a YAML-encoded `SignedBeaconBlock`.
 
 Each block is also available as `blocks_<index>.ssz`
 
diff --git a/tests/generators/bls/main.py b/tests/generators/bls/main.py
--- a/tests/generators/bls/main.py
+++ b/tests/generators/bls/main.py
@@ -13,6 +13,7 @@
 from py_ecc import bls
 from hashlib import sha256
 
+from eth2spec.test.context import PHASE0
 
 def hash(x):
     return sha256(x).digest()
@@ -202,7 +203,7 @@ def cases_fn() -> Iterable[gen_typing.TestCase]:
             print(data)
             (case_name, case_content) = data
             yield gen_typing.TestCase(
-                fork_name='phase0',
+                fork_name=PHASE0,
                 runner_name='bls',
                 handler_name=handler_name,
                 suite_name='small',
diff --git a/tests/generators/epoch_processing/README.md b/tests/generators/epoch_processing/README.md
--- a/tests/generators/epoch_processing/README.md
+++ b/tests/generators/epoch_processing/README.md
@@ -5,7 +5,7 @@ Epoch processing covers the sub-transitions during an epoch change.
 An epoch-processing test-runner can consume these sub-transition test-suites,
  and handle different kinds of epoch sub-transitions by processing the cases using the specified test handler.
 
-Information on the format of the tests can be found in the [epoch-processing test formats documentation](../../specs/test_formats/epoch_processing/README.md).
+Information on the format of the tests can be found in the [epoch-processing test formats documentation](../../formats/epoch_processing/README.md).
 
  
 
diff --git a/tests/generators/epoch_processing/main.py b/tests/generators/epoch_processing/main.py
--- a/tests/generators/epoch_processing/main.py
+++ b/tests/generators/epoch_processing/main.py
@@ -13,6 +13,7 @@
 from gen_from_tests.gen import generate_from_tests
 from importlib import reload
 from eth2spec.config import config_util
+from eth2spec.test.context import PHASE0
 
 
 def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typing.TestProvider:
@@ -28,7 +29,7 @@ def cases_fn() -> Iterable[gen_typing.TestCase]:
             runner_name='epoch_processing',
             handler_name=handler_name,
             src=tests_src,
-            fork_name='phase0'
+            fork_name=PHASE0,
         )
 
     return gen_typing.TestProvider(prepare=prepare_fn, make_cases=cases_fn)
diff --git a/tests/generators/genesis/main.py b/tests/generators/genesis/main.py
--- a/tests/generators/genesis/main.py
+++ b/tests/generators/genesis/main.py
@@ -1,5 +1,6 @@
 from typing import Iterable
 
+from eth2spec.test.context import PHASE0
 from eth2spec.test.genesis import test_initialization, test_validity
 
 from gen_base import gen_runner, gen_typing
@@ -21,7 +22,7 @@ def cases_fn() -> Iterable[gen_typing.TestCase]:
             runner_name='genesis',
             handler_name=handler_name,
             src=tests_src,
-            fork_name='phase0'
+            fork_name=PHASE0,
         )
 
     return gen_typing.TestProvider(prepare=prepare_fn, make_cases=cases_fn)
diff --git a/tests/generators/operations/README.md b/tests/generators/operations/README.md
--- a/tests/generators/operations/README.md
+++ b/tests/generators/operations/README.md
@@ -6,7 +6,7 @@ Operations (or "transactions" in previous spec iterations),
 An operation test-runner can consume these operation test-suites,
  and handle different kinds of operations by processing the cases using the specified test handler.
 
-Information on the format of the tests can be found in the [operations test formats documentation](../../specs/test_formats/operations/README.md).
+Information on the format of the tests can be found in the [operations test formats documentation](../../formats/operations/README.md).
 
  
 
diff --git a/tests/generators/operations/main.py b/tests/generators/operations/main.py
--- a/tests/generators/operations/main.py
+++ b/tests/generators/operations/main.py
@@ -15,6 +15,7 @@
 from eth2spec.config import config_util
 from eth2spec.phase0 import spec as spec_phase0
 from eth2spec.phase1 import spec as spec_phase1
+from eth2spec.test.context import PHASE0
 
 
 def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typing.TestProvider:
@@ -30,7 +31,7 @@ def cases_fn() -> Iterable[gen_typing.TestCase]:
             runner_name='operations',
             handler_name=handler_name,
             src=tests_src,
-            fork_name='phase0'
+            fork_name=PHASE0,
         )
 
     return gen_typing.TestProvider(prepare=prepare_fn, make_cases=cases_fn)
diff --git a/tests/generators/sanity/README.md b/tests/generators/sanity/README.md
--- a/tests/generators/sanity/README.md
+++ b/tests/generators/sanity/README.md
@@ -2,7 +2,7 @@
 
 Sanity tests cover regular state-transitions in a common block-list format, to ensure the basics work.
 
-Information on the format of the tests can be found in the [sanity test formats documentation](../../specs/test_formats/sanity/README.md).
+Information on the format of the tests can be found in the [sanity test formats documentation](../../formats/sanity/README.md).
 
  
 
diff --git a/tests/generators/sanity/main.py b/tests/generators/sanity/main.py
--- a/tests/generators/sanity/main.py
+++ b/tests/generators/sanity/main.py
@@ -4,6 +4,7 @@
 from gen_base import gen_runner, gen_typing
 from gen_from_tests.gen import generate_from_tests
 
+from eth2spec.test.context import PHASE0
 from eth2spec.test.sanity import test_blocks, test_slots
 from eth2spec.config import config_util
 from eth2spec.phase0 import spec as spec_phase0
@@ -23,7 +24,7 @@ def cases_fn() -> Iterable[gen_typing.TestCase]:
             runner_name='sanity',
             handler_name=handler_name,
             src=tests_src,
-            fork_name='phase0'
+            fork_name=PHASE0,
         )
 
     return gen_typing.TestProvider(prepare=prepare_fn, make_cases=cases_fn)
diff --git a/tests/generators/shuffling/main.py b/tests/generators/shuffling/main.py
--- a/tests/generators/shuffling/main.py
+++ b/tests/generators/shuffling/main.py
@@ -6,6 +6,7 @@
 
 from eth2spec.config import config_util
 from eth2spec.phase0 import spec as spec
+from eth2spec.test.context import PHASE0
 
 
 def shuffling_case_fn(seed, count):
@@ -37,7 +38,7 @@ def prepare_fn(configs_path: str) -> str:
     def cases_fn() -> Iterable[gen_typing.TestCase]:
         for (case_name, case_fn) in shuffling_test_cases():
             yield gen_typing.TestCase(
-                fork_name='phase0',
+                fork_name=PHASE0,
                 runner_name='shuffling',
                 handler_name='core',
                 suite_name='shuffle',
diff --git a/tests/generators/ssz_generic/main.py b/tests/generators/ssz_generic/main.py
--- a/tests/generators/ssz_generic/main.py
+++ b/tests/generators/ssz_generic/main.py
@@ -6,6 +6,7 @@
 import ssz_boolean
 import ssz_uints
 import ssz_container
+from eth2spec.test.context import PHASE0
 
 
 def create_provider(handler_name: str, suite_name: str, case_maker) -> gen_typing.TestProvider:
@@ -16,7 +17,7 @@ def prepare_fn(configs_path: str) -> str:
     def cases_fn() -> Iterable[gen_typing.TestCase]:
         for (case_name, case_fn) in case_maker():
             yield gen_typing.TestCase(
-                fork_name='phase0',
+                fork_name=PHASE0,
                 runner_name='ssz_generic',
                 handler_name=handler_name,
                 suite_name=suite_name,
diff --git a/tests/generators/ssz_static/README.md b/tests/generators/ssz_static/README.md
--- a/tests/generators/ssz_static/README.md
+++ b/tests/generators/ssz_static/README.md
@@ -3,4 +3,4 @@
 The purpose of this test-generator is to provide test-vectors for the most important applications of SSZ:
  the serialization and hashing of Eth2 data types.
 
-Test-format documentation can be found [here](../../specs/test_formats/ssz_static/README.md).
+Test-format documentation can be found [here](../../formats/ssz_static/README.md).
diff --git a/tests/generators/ssz_static/main.py b/tests/generators/ssz_static/main.py
--- a/tests/generators/ssz_static/main.py
+++ b/tests/generators/ssz_static/main.py
@@ -8,6 +8,7 @@
 from eth2spec.debug import random_value, encode
 from eth2spec.config import config_util
 from eth2spec.phase0 import spec
+from eth2spec.test.context import PHASE0
 from eth2spec.utils.ssz.ssz_typing import Container
 from eth2spec.utils.ssz.ssz_impl import (
     hash_tree_root,
@@ -44,7 +45,7 @@ def ssz_static_cases(seed: int, name, ssz_type, mode: random_value.Randomization
 
     for i in range(count):
         yield gen_typing.TestCase(
-            fork_name='phase0',
+            fork_name=PHASE0,
             runner_name='ssz_static',
             handler_name=name,
             suite_name=f"ssz_{random_mode_name}{'_chaos' if chaos else ''}",