Repository: ethereum/consensus-specs

Issue:
Proposer index edge cases to test
So thanks to Prysm testnet, an under-tested proposer property was found: the proposer index and shuffling can both be correct, but if the active validators don't match regular indices there can still be a problem. And it only is off after the first inactive validator index in the system, so some tests could look like they test shuffling well (or indirectly as by rewards or otherwise), but just missed it.

TLDR: todo, add test cases for:
- `proposer index > len(active validators)` (already have proposals, but not this edge case)
- `proposer index > any slashed index` (already have slashings, but not this edge case).

Larger validator numbers move the proposer index to be more random and more likely to be bigger than those other numbers, while the current smaller validator sets in tests are generally only 512 validators. (and increasing that would increase the total test size by approx. the same factor).
Test coverage: attester slashings with duplicated attesting indices
While updating to v0.9.1 I noticed that the spec allows validator indices to be duplicated in `IndexedAttestation`s. In Lighthouse our function to check the sorting of the indices was also (incorrectly) checking for duplicates, which wasn't picked up by any of our tests or the spec tests. I think it would be good to have a test vector with an attester slashing containing duplicated indices, if that is indeed the intended behaviour. For the signature, it's sufficient to add the signatures of the duplicated validators to the aggregate multiple times. Concretely, this attester slashing passes validation (pardon the Rust):

```rust
let sig1 = Signature::new(&data1.tree_hash_root(), domain, &keypairs[0].sk);
let sig2 = Signature::new(&data2.tree_hash_root(), domain, &keypairs[0].sk);

let mut agg1 = AggregateSignature::new();
agg1.add(&sig1);
agg1.add(&sig1);

let mut agg2 = AggregateSignature::new();
agg2.add(&sig2);
agg2.add(&sig2);

let attestation_1 = IndexedAttestation {
    attesting_indices: vec![0, 0].into(),
    data: data1,
    signature: agg1,
};

let attestation_2 = IndexedAttestation {
    attesting_indices: vec![0, 0].into(),
    data: data2,
    signature: agg2,
};

let attester_slashing = AttesterSlashing {
    attestation_1,
    attestation_2,
};
```


Discussion:

Resolving patch:
diff --git a/deposit_contract/contracts/validator_registration.v.py b/deposit_contract/contracts/validator_registration.v.py
--- a/deposit_contract/contracts/validator_registration.v.py
+++ b/deposit_contract/contracts/validator_registration.v.py
@@ -1,4 +1,4 @@
-# Vyper target 0.1.0b12
+# Vyper target 0.1.0b13
 MIN_DEPOSIT_AMOUNT: constant(uint256) = 1000000000  # Gwei
 DEPOSIT_CONTRACT_TREE_DEPTH: constant(uint256) = 32
 MAX_DEPOSIT_COUNT: constant(uint256) = 4294967295 # 2**DEPOSIT_CONTRACT_TREE_DEPTH - 1
@@ -75,7 +75,7 @@ def deposit(pubkey: bytes[PUBKEY_LENGTH],
     deposit_amount: uint256 = msg.value / as_wei_value(1, "gwei")
     assert deposit_amount >= MIN_DEPOSIT_AMOUNT
 
-    # Length checks to facilitate formal verification (see https://github.com/ethereum/eth2.0-specs/pull/1362/files#r320361859)
+    # Length checks for safety
     assert len(pubkey) == PUBKEY_LENGTH
     assert len(withdrawal_credentials) == WITHDRAWAL_CREDENTIALS_LENGTH
     assert len(signature) == SIGNATURE_LENGTH
diff --git a/scripts/build_spec.py b/scripts/build_spec.py
--- a/scripts/build_spec.py
+++ b/scripts/build_spec.py
@@ -19,10 +19,7 @@
     field,
 )
 
-from eth2spec.utils.ssz.ssz_impl import (
-    hash_tree_root,
-    signing_root,
-)
+from eth2spec.utils.ssz.ssz_impl import hash_tree_root
 from eth2spec.utils.ssz.ssz_typing import (
     boolean, Container, List, Vector, uint64,
     Bytes1, Bytes4, Bytes8, Bytes32, Bytes48, Bytes96, Bitlist, Bitvector,
@@ -50,7 +47,6 @@
 
 from eth2spec.utils.ssz.ssz_impl import (
     hash_tree_root,
-    signing_root,
     is_zero,
 )
 from eth2spec.utils.ssz.ssz_typing import (
@@ -86,7 +82,7 @@ def get_eth1_data(distance: uint64) -> Bytes32:
     return hash(distance)
 
 
-def hash(x: bytes) -> Bytes32:
+def hash(x: bytes) -> Bytes32:  # type: ignore
     if x not in hash_cache:
         hash_cache[x] = Bytes32(_hash(x))
     return hash_cache[x]


Test patch:
diff --git a/specs/test_formats/ssz_generic/README.md b/specs/test_formats/ssz_generic/README.md
--- a/specs/test_formats/ssz_generic/README.md
+++ b/specs/test_formats/ssz_generic/README.md
@@ -37,12 +37,11 @@ Valid has 3 parts: `meta.yaml`, `serialized.ssz`, `value.yaml`
 
 ### `meta.yaml`
 
-Valid ssz objects can have a hash-tree-root, and for some types also a signing-root.
+Valid ssz objects can have a hash-tree-root.
 The expected roots are encoded into the metadata yaml:
 
 ```yaml
 root: Bytes32             -- Hash-tree-root of the object
-signing_root: Bytes32     -- Signing-root of the object 
 ```
 
 The `Bytes32` is encoded as a string, hexadecimal encoding, prefixed with `0x`.
@@ -62,7 +61,6 @@ The conditions are the same for each type:
 - Encoding: After encoding the given `value` object, the output should match `serialized`.
 - Decoding: After decoding the given `serialized` bytes, it should match the `value` object. 
 - Hash-tree-root: the root should match the root declared in the metadata.
-- Signing-root: if present in metadata, the signing root of the object should match the container.
 
 ## `invalid`
 
diff --git a/specs/test_formats/ssz_static/core.md b/specs/test_formats/ssz_static/core.md
--- a/specs/test_formats/ssz_static/core.md
+++ b/specs/test_formats/ssz_static/core.md
@@ -24,8 +24,6 @@ The output parts are: `roots.yaml`, `serialized.ssz`, `value.yaml`
 
 ```yaml
 root: bytes32         -- string, hash-tree-root of the value, hex encoded, with prefix 0x
-signing_root: bytes32 -- string, signing-root of the value, hex encoded, with prefix 0x. 
-                           *Optional*, present if type is a container and ends with a ``signature`` field.
 ```
 
 ### `serialized.ssz`
@@ -47,11 +45,9 @@ A test-runner can implement the following assertions:
     - Serialization in 2 steps: deserialize `serialized`, then serialize the result, 
        and verify if the bytes match the original `serialized`.
 - Hash-tree-root: After parsing the `value` (or deserializing `serialized`), Hash-tree-root it: the output should match `root`
-    - Optionally also check `signing_root`, if present.
 
 
 ## References
 
 **`serialized`**—[SSZ serialization](../../simple-serialize.md#serialization)   
 **`root`**—[hash_tree_root](../../simple-serialize.md#merkleization) function  
-**`signing_root`**—[signing_root](../../simple-serialize.md#self-signed-containers) function
diff --git a/test_generators/ssz_generic/ssz_test_case.py b/test_generators/ssz_generic/ssz_test_case.py
--- a/test_generators/ssz_generic/ssz_test_case.py
+++ b/test_generators/ssz_generic/ssz_test_case.py
@@ -1,4 +1,4 @@
-from eth2spec.utils.ssz.ssz_impl import serialize, hash_tree_root, signing_root
+from eth2spec.utils.ssz.ssz_impl import serialize, hash_tree_root
 from eth2spec.debug.encode import encode
 from eth2spec.utils.ssz.ssz_typing import SSZValue, Container
 from typing import Callable
@@ -10,8 +10,6 @@ def case_fn():
         yield "value", "data", encode(value)
         yield "serialized", "ssz", serialize(value)
         yield "root", "meta", '0x' + hash_tree_root(value).hex()
-        if isinstance(value, Container):
-            yield "signing_root", "meta", '0x' + signing_root(value).hex()
     return case_fn
 
 
diff --git a/test_generators/ssz_static/main.py b/test_generators/ssz_static/main.py
--- a/test_generators/ssz_static/main.py
+++ b/test_generators/ssz_static/main.py
@@ -7,7 +7,6 @@
 from eth2spec.utils.ssz.ssz_typing import Container
 from eth2spec.utils.ssz.ssz_impl import (
     hash_tree_root,
-    signing_root,
     serialize,
 )
 from gen_base import gen_runner, gen_typing
@@ -24,8 +23,6 @@ def create_test_case(rng: Random, typ, mode: random_value.RandomizationMode, cha
     roots_data = {
         "root": '0x' + hash_tree_root(value).hex()
     }
-    if isinstance(value, Container) and hasattr(value, "signature"):
-        roots_data["signing_root"] = '0x' + signing_root(value).hex()
     yield "roots", "data", roots_data
 
 
diff --git a/test_libs/pyspec/eth2spec/test/fork_choice/test_get_head.py b/test_libs/pyspec/eth2spec/test/fork_choice/test_get_head.py
--- a/test_libs/pyspec/eth2spec/test/fork_choice/test_get_head.py
+++ b/test_libs/pyspec/eth2spec/test/fork_choice/test_get_head.py
@@ -1,22 +1,26 @@
 from eth2spec.test.context import with_all_phases, spec_state_test
 from eth2spec.test.helpers.attestations import get_valid_attestation
 from eth2spec.test.helpers.block import build_empty_block_for_next_slot
-from eth2spec.test.helpers.state import state_transition_and_sign_block
+from eth2spec.test.helpers.state import (
+    next_epoch,
+    next_epoch_with_attestations,
+    state_transition_and_sign_block,
+)
 
 
-def add_block_to_store(spec, store, block):
-    pre_state = store.block_states[block.parent_root]
-    block_time = pre_state.genesis_time + block.slot * spec.SECONDS_PER_SLOT
+def add_block_to_store(spec, store, signed_block):
+    pre_state = store.block_states[signed_block.message.parent_root]
+    block_time = pre_state.genesis_time + signed_block.message.slot * spec.SECONDS_PER_SLOT
 
     if store.time < block_time:
         spec.on_tick(store, block_time)
 
-    spec.on_block(store, block)
+    spec.on_block(store, signed_block)
 
 
 def add_attestation_to_store(spec, store, attestation):
     parent_block = store.blocks[attestation.data.beacon_block_root]
-    pre_state = store.block_states[spec.signing_root(parent_block)]
+    pre_state = store.block_states[spec.hash_tree_root(parent_block)]
     block_time = pre_state.genesis_time + parent_block.slot * spec.SECONDS_PER_SLOT
     next_epoch_time = block_time + spec.SLOTS_PER_EPOCH * spec.SECONDS_PER_SLOT
 
@@ -32,7 +36,7 @@ def test_genesis(spec, state):
     # Initialization
     store = spec.get_genesis_store(state)
     genesis_block = spec.BeaconBlock(state_root=state.hash_tree_root())
-    assert spec.get_head(store) == spec.signing_root(genesis_block)
+    assert spec.get_head(store) == spec.hash_tree_root(genesis_block)
 
 
 @with_all_phases
@@ -41,19 +45,19 @@ def test_chain_no_attestations(spec, state):
     # Initialization
     store = spec.get_genesis_store(state)
     genesis_block = spec.BeaconBlock(state_root=state.hash_tree_root())
-    assert spec.get_head(store) == spec.signing_root(genesis_block)
+    assert spec.get_head(store) == spec.hash_tree_root(genesis_block)
 
     # On receiving a block of `GENESIS_SLOT + 1` slot
     block_1 = build_empty_block_for_next_slot(spec, state)
-    state_transition_and_sign_block(spec, state, block_1)
-    add_block_to_store(spec, store, block_1)
+    signed_block_1 = state_transition_and_sign_block(spec, state, block_1)
+    add_block_to_store(spec, store, signed_block_1)
 
     # On receiving a block of next epoch
     block_2 = build_empty_block_for_next_slot(spec, state)
-    state_transition_and_sign_block(spec, state, block_2)
-    add_block_to_store(spec, store, block_2)
+    signed_block_2 = state_transition_and_sign_block(spec, state, block_2)
+    add_block_to_store(spec, store, signed_block_2)
 
-    assert spec.get_head(store) == spec.signing_root(block_2)
+    assert spec.get_head(store) == spec.hash_tree_root(block_2)
 
 
 @with_all_phases
@@ -64,22 +68,22 @@ def test_split_tie_breaker_no_attestations(spec, state):
     # Initialization
     store = spec.get_genesis_store(state)
     genesis_block = spec.BeaconBlock(state_root=state.hash_tree_root())
-    assert spec.get_head(store) == spec.signing_root(genesis_block)
+    assert spec.get_head(store) == spec.hash_tree_root(genesis_block)
 
     # block at slot 1
     block_1_state = genesis_state.copy()
     block_1 = build_empty_block_for_next_slot(spec, block_1_state)
-    state_transition_and_sign_block(spec, block_1_state, block_1)
-    add_block_to_store(spec, store, block_1)
+    signed_block_1 = state_transition_and_sign_block(spec, block_1_state, block_1)
+    add_block_to_store(spec, store, signed_block_1)
 
     # additional block at slot 1
     block_2_state = genesis_state.copy()
     block_2 = build_empty_block_for_next_slot(spec, block_2_state)
     block_2.body.graffiti = b'\x42' * 32
-    state_transition_and_sign_block(spec, block_2_state, block_2)
-    add_block_to_store(spec, store, block_2)
+    signed_block_2 = state_transition_and_sign_block(spec, block_2_state, block_2)
+    add_block_to_store(spec, store, signed_block_2)
 
-    highest_root = max(spec.signing_root(block_1), spec.signing_root(block_2))
+    highest_root = max(spec.hash_tree_root(block_1), spec.hash_tree_root(block_2))
 
     assert spec.get_head(store) == highest_root
 
@@ -92,23 +96,99 @@ def test_shorter_chain_but_heavier_weight(spec, state):
     # Initialization
     store = spec.get_genesis_store(state)
     genesis_block = spec.BeaconBlock(state_root=state.hash_tree_root())
-    assert spec.get_head(store) == spec.signing_root(genesis_block)
+    assert spec.get_head(store) == spec.hash_tree_root(genesis_block)
 
     # build longer tree
     long_state = genesis_state.copy()
     for i in range(3):
         long_block = build_empty_block_for_next_slot(spec, long_state)
-        state_transition_and_sign_block(spec, long_state, long_block)
-        add_block_to_store(spec, store, long_block)
+        signed_long_block = state_transition_and_sign_block(spec, long_state, long_block)
+        add_block_to_store(spec, store, signed_long_block)
 
     # build short tree
     short_state = genesis_state.copy()
     short_block = build_empty_block_for_next_slot(spec, short_state)
     short_block.body.graffiti = b'\x42' * 32
-    state_transition_and_sign_block(spec, short_state, short_block)
-    add_block_to_store(spec, store, short_block)
+    signed_short_block = state_transition_and_sign_block(spec, short_state, short_block)
+    add_block_to_store(spec, store, signed_short_block)
 
     short_attestation = get_valid_attestation(spec, short_state, short_block.slot, signed=True)
     add_attestation_to_store(spec, store, short_attestation)
 
-    assert spec.get_head(store) == spec.signing_root(short_block)
+    assert spec.get_head(store) == spec.hash_tree_root(short_block)
+
+
+@with_all_phases
+@spec_state_test
+def test_filtered_block_tree(spec, state):
+    # Initialization
+    genesis_state_root = state.hash_tree_root()
+    store = spec.get_genesis_store(state)
+    genesis_block = spec.BeaconBlock(state_root=genesis_state_root)
+
+    # transition state past initial couple of epochs
+    next_epoch(spec, state)
+    next_epoch(spec, state)
+
+    assert spec.get_head(store) == spec.hash_tree_root(genesis_block)
+
+    # fill in attestations for entire epoch, justifying the recent epoch
+    prev_state, signed_blocks, state = next_epoch_with_attestations(spec, state, True, False)
+    attestations = [
+        attestation for signed_block in signed_blocks
+        for attestation in signed_block.message.body.attestations
+    ]
+    assert state.current_justified_checkpoint.epoch > prev_state.current_justified_checkpoint.epoch
+
+    # tick time forward and add blocks and attestations to store
+    current_time = state.slot * spec.SECONDS_PER_SLOT + store.genesis_time
+    spec.on_tick(store, current_time)
+    for signed_block in signed_blocks:
+        spec.on_block(store, signed_block)
+    for attestation in attestations:
+        spec.on_attestation(store, attestation)
+
+    assert store.justified_checkpoint == state.current_justified_checkpoint
+
+    # the last block in the branch should be the head
+    expected_head_root = spec.hash_tree_root(signed_blocks[-1].message)
+    assert spec.get_head(store) == expected_head_root
+
+    #
+    # create branch containing the justified block but not containing enough on
+    # chain votes to justify that block
+    #
+
+    # build a chain without attestations off of previous justified block
+    non_viable_state = store.block_states[store.justified_checkpoint.root].copy()
+
+    # ensure that next wave of votes are for future epoch
+    next_epoch(spec, non_viable_state)
+    next_epoch(spec, non_viable_state)
+    next_epoch(spec, non_viable_state)
+    assert spec.get_current_epoch(non_viable_state) > store.justified_checkpoint.epoch
+
+    # create rogue block that will be attested to in this non-viable branch
+    rogue_block = build_empty_block_for_next_slot(spec, non_viable_state)
+    signed_rogue_block = state_transition_and_sign_block(spec, non_viable_state, rogue_block)
+
+    # create an epoch's worth of attestations for the rogue block
+    next_epoch(spec, non_viable_state)
+    attestations = []
+    for i in range(spec.SLOTS_PER_EPOCH):
+        slot = rogue_block.slot + i
+        for index in range(spec.get_committee_count_at_slot(non_viable_state, slot)):
+            attestation = get_valid_attestation(spec, non_viable_state, rogue_block.slot + i, index)
+            attestations.append(attestation)
+
+    # tick time forward to be able to include up to the latest attestation
+    current_time = (attestations[-1].data.slot + 1) * spec.SECONDS_PER_SLOT + store.genesis_time
+    spec.on_tick(store, current_time)
+
+    # include rogue block and associated attestations in the store
+    spec.on_block(store, signed_rogue_block)
+    for attestation in attestations:
+        spec.on_attestation(store, attestation)
+
+    # ensure that get_head still returns the head from the previous branch
+    assert spec.get_head(store) == expected_head_root
diff --git a/test_libs/pyspec/eth2spec/test/fork_choice/test_on_attestation.py b/test_libs/pyspec/eth2spec/test/fork_choice/test_on_attestation.py
--- a/test_libs/pyspec/eth2spec/test/fork_choice/test_on_attestation.py
+++ b/test_libs/pyspec/eth2spec/test/fork_choice/test_on_attestation.py
@@ -31,12 +31,12 @@ def test_on_attestation_current_epoch(spec, state):
     spec.on_tick(store, store.time + spec.SECONDS_PER_SLOT * 2)
 
     block = build_empty_block_for_next_slot(spec, state)
-    state_transition_and_sign_block(spec, state, block)
+    signed_block = state_transition_and_sign_block(spec, state, block)
 
     # store block in store
-    spec.on_block(store, block)
+    spec.on_block(store, signed_block)
 
-    attestation = get_valid_attestation(spec, state, slot=block.slot)
+    attestation = get_valid_attestation(spec, state, slot=block.slot, signed=True)
     assert attestation.data.target.epoch == spec.GENESIS_EPOCH
     assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == spec.GENESIS_EPOCH
 
@@ -50,12 +50,12 @@ def test_on_attestation_previous_epoch(spec, state):
     spec.on_tick(store, store.time + spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH)
 
     block = build_empty_block_for_next_slot(spec, state)
-    state_transition_and_sign_block(spec, state, block)
+    signed_block = state_transition_and_sign_block(spec, state, block)
 
     # store block in store
-    spec.on_block(store, block)
+    spec.on_block(store, signed_block)
 
-    attestation = get_valid_attestation(spec, state, slot=block.slot)
+    attestation = get_valid_attestation(spec, state, slot=block.slot, signed=True)
     assert attestation.data.target.epoch == spec.GENESIS_EPOCH
     assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == spec.GENESIS_EPOCH + 1
 
@@ -73,17 +73,40 @@ def test_on_attestation_past_epoch(spec, state):
 
     # create and store block from 3 epochs ago
     block = build_empty_block_for_next_slot(spec, state)
-    state_transition_and_sign_block(spec, state, block)
-    spec.on_block(store, block)
+    signed_block = state_transition_and_sign_block(spec, state, block)
+    spec.on_block(store, signed_block)
 
     # create attestation for past block
-    attestation = get_valid_attestation(spec, state, slot=state.slot)
+    attestation = get_valid_attestation(spec, state, slot=state.slot, signed=True)
     assert attestation.data.target.epoch == spec.GENESIS_EPOCH
     assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == spec.GENESIS_EPOCH + 2
 
     run_on_attestation(spec, state, store, attestation, False)
 
 
+@with_all_phases
+@spec_state_test
+def test_on_attestation_mismatched_target_and_slot(spec, state):
+    store = spec.get_genesis_store(state)
+    spec.on_tick(store, store.time + spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH)
+
+    block = build_empty_block_for_next_slot(spec, state)
+    signed_block = state_transition_and_sign_block(spec, state, block)
+
+    # store block in store
+    spec.on_block(store, signed_block)
+
+    attestation = get_valid_attestation(spec, state, slot=block.slot)
+    attestation.data.target.epoch += 1
+    sign_attestation(spec, state, attestation)
+
+    assert attestation.data.target.epoch == spec.GENESIS_EPOCH + 1
+    assert spec.compute_epoch_at_slot(attestation.data.slot) == spec.GENESIS_EPOCH
+    assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == spec.GENESIS_EPOCH + 1
+
+    run_on_attestation(spec, state, store, attestation, False)
+
+
 @with_all_phases
 @spec_state_test
 def test_on_attestation_target_not_in_store(spec, state):
@@ -99,8 +122,8 @@ def test_on_attestation_target_not_in_store(spec, state):
 
     # do not add target block to store
 
-    attestation = get_valid_attestation(spec, state, slot=target_block.slot)
-    assert attestation.data.target.root == target_block.signing_root()
+    attestation = get_valid_attestation(spec, state, slot=target_block.slot, signed=True)
+    assert attestation.data.target.root == target_block.hash_tree_root()
 
     run_on_attestation(spec, state, store, attestation, False)
 
@@ -116,19 +139,19 @@ def test_on_attestation_beacon_block_not_in_store(spec, state):
     transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH - 1)
 
     target_block = build_empty_block_for_next_slot(spec, state)
-    state_transition_and_sign_block(spec, state, target_block)
+    signed_target_block = state_transition_and_sign_block(spec, state, target_block)
 
     # store target in store
-    spec.on_block(store, target_block)
+    spec.on_block(store, signed_target_block)
 
     head_block = build_empty_block_for_next_slot(spec, state)
     state_transition_and_sign_block(spec, state, head_block)
 
     # do not add head block to store
 
-    attestation = get_valid_attestation(spec, state, slot=head_block.slot)
-    assert attestation.data.target.root == target_block.signing_root()
-    assert attestation.data.beacon_block_root == head_block.signing_root()
+    attestation = get_valid_attestation(spec, state, slot=head_block.slot, signed=True)
+    assert attestation.data.target.root == target_block.hash_tree_root()
+    assert attestation.data.beacon_block_root == head_block.hash_tree_root()
 
     run_on_attestation(spec, state, store, attestation, False)
 
@@ -141,15 +164,15 @@ def test_on_attestation_future_epoch(spec, state):
     spec.on_tick(store, time)
 
     block = build_empty_block_for_next_slot(spec, state)
-    state_transition_and_sign_block(spec, state, block)
+    signed_block = state_transition_and_sign_block(spec, state, block)
 
     # store block in store
-    spec.on_block(store, block)
+    spec.on_block(store, signed_block)
 
     # move state forward but not store
     state.slot = block.slot + spec.SLOTS_PER_EPOCH
 
-    attestation = get_valid_attestation(spec, state, slot=state.slot)
+    attestation = get_valid_attestation(spec, state, slot=state.slot, signed=True)
     run_on_attestation(spec, state, store, attestation, False)
 
 
@@ -161,13 +184,13 @@ def test_on_attestation_future_block(spec, state):
     spec.on_tick(store, time)
 
     block = build_empty_block_for_next_slot(spec, state)
-    state_transition_and_sign_block(spec, state, block)
+    signed_block = state_transition_and_sign_block(spec, state, block)
 
-    spec.on_block(store, block)
+    spec.on_block(store, signed_block)
 
     # attestation for slot immediately prior to the block being attested to
     attestation = get_valid_attestation(spec, state, slot=block.slot - 1, signed=False)
-    attestation.data.beacon_block_root = block.signing_root()
+    attestation.data.beacon_block_root = block.hash_tree_root()
     sign_attestation(spec, state, attestation)
 
     run_on_attestation(spec, state, store, attestation, False)
@@ -181,11 +204,11 @@ def test_on_attestation_same_slot(spec, state):
     spec.on_tick(store, time)
 
     block = build_empty_block_for_next_slot(spec, state)
-    state_transition_and_sign_block(spec, state, block)
+    signed_block = state_transition_and_sign_block(spec, state, block)
 
-    spec.on_block(store, block)
+    spec.on_block(store, signed_block)
 
-    attestation = get_valid_attestation(spec, state, slot=block.slot)
+    attestation = get_valid_attestation(spec, state, slot=block.slot, signed=True)
     run_on_attestation(spec, state, store, attestation, False)
 
 
@@ -197,11 +220,11 @@ def test_on_attestation_invalid_attestation(spec, state):
     spec.on_tick(store, time)
 
     block = build_empty_block_for_next_slot(spec, state)
-    state_transition_and_sign_block(spec, state, block)
+    signed_block = state_transition_and_sign_block(spec, state, block)
 
-    spec.on_block(store, block)
+    spec.on_block(store, signed_block)
 
-    attestation = get_valid_attestation(spec, state, slot=block.slot)
+    attestation = get_valid_attestation(spec, state, slot=block.slot, signed=True)
     # make invalid by using an invalid committee index
     attestation.data.index = spec.MAX_COMMITTEES_PER_SLOT * spec.SLOTS_PER_EPOCH
 
diff --git a/test_libs/pyspec/eth2spec/test/fork_choice/test_on_block.py b/test_libs/pyspec/eth2spec/test/fork_choice/test_on_block.py
--- a/test_libs/pyspec/eth2spec/test/fork_choice/test_on_block.py
+++ b/test_libs/pyspec/eth2spec/test/fork_choice/test_on_block.py
@@ -1,33 +1,35 @@
 from copy import deepcopy
-from eth2spec.utils.ssz.ssz_impl import signing_root
+from eth2spec.utils.ssz.ssz_impl import hash_tree_root
 
 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
+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
 
 
-def run_on_block(spec, store, block, valid=True):
+def run_on_block(spec, store, signed_block, valid=True):
     if not valid:
         try:
-            spec.on_block(store, block)
+            spec.on_block(store, signed_block)
         except AssertionError:
             return
         else:
             assert False
 
-    spec.on_block(store, block)
-    assert store.blocks[signing_root(block)] == block
+    spec.on_block(store, signed_block)
+    assert store.blocks[hash_tree_root(signed_block.message)] == signed_block.message
 
 
 def apply_next_epoch_with_attestations(spec, state, store):
-    _, new_blocks, post_state = next_epoch_with_attestations(spec, state, True, False)
-    for block in new_blocks:
-        block_root = signing_root(block)
+    _, new_signed_blocks, post_state = next_epoch_with_attestations(spec, state, True, False)
+    for signed_block in new_signed_blocks:
+        block = signed_block.message
+        block_root = hash_tree_root(block)
         store.blocks[block_root] = block
         store.block_states[block_root] = post_state
-        last_block = block
+        last_signed_block = signed_block
     spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT)
-    return post_state, store, last_block
+    return post_state, store, last_signed_block
 
 
 @with_all_phases
@@ -41,16 +43,15 @@ def test_basic(spec, state):
 
     # On receiving a block of `GENESIS_SLOT + 1` slot
     block = build_empty_block_for_next_slot(spec, state)
-    state_transition_and_sign_block(spec, state, block)
-    run_on_block(spec, store, block)
+    signed_block = state_transition_and_sign_block(spec, state, block)
+    run_on_block(spec, store, signed_block)
 
     # On receiving a block of next epoch
     store.time = time + spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH
-    block = build_empty_block_for_next_slot(spec, state)
-    block.slot += spec.SLOTS_PER_EPOCH
-    state_transition_and_sign_block(spec, state, block)
+    block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
+    signed_block = state_transition_and_sign_block(spec, state, block)
 
-    run_on_block(spec, store, block)
+    run_on_block(spec, store, signed_block)
 
     # TODO: add tests for justified_root and finalized_root
 
@@ -65,10 +66,10 @@ def test_on_block_checkpoints(spec, state):
 
     next_epoch(spec, state)
     spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT)
-    state, store, last_block = apply_next_epoch_with_attestations(spec, state, store)
+    state, store, last_signed_block = apply_next_epoch_with_attestations(spec, state, store)
     next_epoch(spec, state)
     spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT)
-    last_block_root = signing_root(last_block)
+    last_block_root = hash_tree_root(last_signed_block.message)
 
     # Mock the finalized_checkpoint
     fin_state = store.block_states[last_block_root]
@@ -77,8 +78,8 @@ def test_on_block_checkpoints(spec, state):
     )
 
     block = build_empty_block_for_next_slot(spec, fin_state)
-    state_transition_and_sign_block(spec, deepcopy(fin_state), block)
-    run_on_block(spec, store, block)
+    signed_block = state_transition_and_sign_block(spec, deepcopy(fin_state), block)
+    run_on_block(spec, store, signed_block)
 
 
 @with_all_phases
@@ -91,8 +92,8 @@ def test_on_block_future_block(spec, state):
 
     # Fail receiving block of `GENESIS_SLOT + 1` slot
     block = build_empty_block_for_next_slot(spec, state)
-    state_transition_and_sign_block(spec, state, block)
-    run_on_block(spec, store, block, False)
+    signed_block = state_transition_and_sign_block(spec, state, block)
+    run_on_block(spec, store, signed_block, False)
 
 
 @with_all_phases
@@ -105,14 +106,14 @@ def test_on_block_bad_parent_root(spec, state):
 
     # Fail receiving block of `GENESIS_SLOT + 1` slot
     block = build_empty_block_for_next_slot(spec, state)
-    spec.state_transition(state, block)
+    transition_unsigned_block(spec, state, block)
     block.state_root = state.hash_tree_root()
 
     block.parent_root = b'\x45' * 32
 
-    sign_block(spec, state, block)
+    signed_block = sign_block(spec, state, block)
 
-    run_on_block(spec, store, block, False)
+    run_on_block(spec, store, signed_block, False)
 
 
 @with_all_phases
@@ -130,8 +131,8 @@ def test_on_block_before_finalized(spec, state):
 
     # Fail receiving block of `GENESIS_SLOT + 1` slot
     block = build_empty_block_for_next_slot(spec, state)
-    state_transition_and_sign_block(spec, state, block)
-    run_on_block(spec, store, block, False)
+    signed_block = state_transition_and_sign_block(spec, state, block)
+    run_on_block(spec, store, signed_block, False)
 
 
 @with_all_phases
@@ -144,10 +145,10 @@ def test_on_block_update_justified_checkpoint_within_safe_slots(spec, state):
 
     next_epoch(spec, state)
     spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT)
-    state, store, last_block = apply_next_epoch_with_attestations(spec, state, store)
+    state, store, last_signed_block = apply_next_epoch_with_attestations(spec, state, store)
     next_epoch(spec, state)
     spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT)
-    last_block_root = signing_root(last_block)
+    last_block_root = hash_tree_root(last_signed_block.message)
 
     # Mock the justified checkpoint
     just_state = store.block_states[last_block_root]
@@ -158,16 +159,16 @@ def test_on_block_update_justified_checkpoint_within_safe_slots(spec, state):
     just_state.current_justified_checkpoint = new_justified
 
     block = build_empty_block_for_next_slot(spec, just_state)
-    state_transition_and_sign_block(spec, deepcopy(just_state), block)
+    signed_block = state_transition_and_sign_block(spec, deepcopy(just_state), block)
     assert spec.get_current_slot(store) % spec.SLOTS_PER_EPOCH < spec.SAFE_SLOTS_TO_UPDATE_JUSTIFIED
-    run_on_block(spec, store, block)
+    run_on_block(spec, store, signed_block)
 
     assert store.justified_checkpoint == new_justified
 
 
 @with_all_phases
 @spec_state_test
-def test_on_block_outside_safe_slots_and_old_block(spec, state):
+def test_on_block_outside_safe_slots_and_multiple_better_justified(spec, state):
     # Initialization
     store = spec.get_genesis_store(state)
     time = 100
@@ -175,10 +176,10 @@ def test_on_block_outside_safe_slots_and_old_block(spec, state):
 
     next_epoch(spec, state)
     spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT)
-    state, store, last_block = apply_next_epoch_with_attestations(spec, state, store)
+    state, store, last_signed_block = apply_next_epoch_with_attestations(spec, state, store)
     next_epoch(spec, state)
     spec.on_tick(store, store.time + state.slot * spec.SECONDS_PER_SLOT)
-    last_block_root = signing_root(last_block)
+    last_block_root = hash_tree_root(last_signed_block.message)
 
     # Mock justified block in store
     just_block = build_empty_block_for_next_slot(spec, state)
@@ -186,20 +187,30 @@ def test_on_block_outside_safe_slots_and_old_block(spec, state):
     just_block.slot = spec.compute_start_slot_at_epoch(store.justified_checkpoint.epoch)
     store.blocks[just_block.hash_tree_root()] = just_block
 
-    # Mock the justified checkpoint
-    just_state = store.block_states[last_block_root]
-    new_justified = spec.Checkpoint(
-        epoch=just_state.current_justified_checkpoint.epoch + 1,
-        root=just_block.hash_tree_root(),
-    )
-    just_state.current_justified_checkpoint = new_justified
-
-    block = build_empty_block_for_next_slot(spec, just_state)
-    state_transition_and_sign_block(spec, deepcopy(just_state), block)
-
+    # Step time past safe slots
     spec.on_tick(store, store.time + spec.SAFE_SLOTS_TO_UPDATE_JUSTIFIED * spec.SECONDS_PER_SLOT)
     assert spec.get_current_slot(store) % spec.SLOTS_PER_EPOCH >= spec.SAFE_SLOTS_TO_UPDATE_JUSTIFIED
-    run_on_block(spec, store, block)
 
-    assert store.justified_checkpoint != new_justified
-    assert store.best_justified_checkpoint == new_justified
+    previously_justified = store.justified_checkpoint
+
+    # Add a series of new blocks with "better" justifications
+    best_justified_checkpoint = spec.Checkpoint(epoch=0)
+    for i in range(3, 0, -1):
+        just_state = store.block_states[last_block_root]
+        new_justified = spec.Checkpoint(
+            epoch=previously_justified.epoch + i,
+            root=just_block.hash_tree_root(),
+        )
+        if new_justified.epoch > best_justified_checkpoint.epoch:
+            best_justified_checkpoint = new_justified
+
+        just_state.current_justified_checkpoint = new_justified
+
+        block = build_empty_block_for_next_slot(spec, just_state)
+        signed_block = state_transition_and_sign_block(spec, deepcopy(just_state), block)
+
+        run_on_block(spec, store, signed_block)
+
+    assert store.justified_checkpoint == previously_justified
+    # ensure the best from the series was stored
+    assert store.best_justified_checkpoint == best_justified_checkpoint
diff --git a/test_libs/pyspec/eth2spec/test/helpers/attestations.py b/test_libs/pyspec/eth2spec/test/helpers/attestations.py
--- a/test_libs/pyspec/eth2spec/test/helpers/attestations.py
+++ b/test_libs/pyspec/eth2spec/test/helpers/attestations.py
@@ -1,6 +1,7 @@
 from typing import List
 
-from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block
+from eth2spec.test.helpers.block import build_empty_block_for_next_slot, transition_unsigned_block, \
+    build_empty_block
 from eth2spec.test.helpers.keys import privkeys
 from eth2spec.utils.bls import bls_sign, bls_aggregate_signatures
 from eth2spec.utils.ssz.ssz_typing import Bitlist
@@ -122,10 +123,8 @@ def fill_aggregate_attestation(spec, state, attestation, signed=False):
 
 
 def add_attestations_to_state(spec, state, attestations, slot):
-    block = build_empty_block_for_next_slot(spec, state)
-    block.slot = slot
+    block = build_empty_block(spec, state, slot)
     for attestation in attestations:
         block.body.attestations.append(attestation)
     spec.process_slots(state, block.slot)
-    sign_block(spec, state, block)
-    spec.state_transition(state, block)
+    transition_unsigned_block(spec, state, block)
diff --git a/test_libs/pyspec/eth2spec/test/helpers/block.py b/test_libs/pyspec/eth2spec/test/helpers/block.py
--- a/test_libs/pyspec/eth2spec/test/helpers/block.py
+++ b/test_libs/pyspec/eth2spec/test/helpers/block.py
@@ -2,26 +2,30 @@
 
 from eth2spec.test.helpers.keys import privkeys
 from eth2spec.utils.bls import bls_sign, only_with_bls
-from eth2spec.utils.ssz.ssz_impl import signing_root, hash_tree_root
+from eth2spec.utils.ssz.ssz_impl import hash_tree_root
 
 
-# Fully ignore the function if BLS is off, beacon-proposer index calculation is slow.
-@only_with_bls()
-def sign_block(spec, state, block, proposer_index=None):
-    assert state.slot <= block.slot
-
+def get_proposer_index_maybe(spec, state, slot, proposer_index=None):
     if proposer_index is None:
-        if block.slot == state.slot:
+        assert state.slot <= slot
+        if slot == state.slot:
             proposer_index = spec.get_beacon_proposer_index(state)
         else:
-            if spec.compute_epoch_at_slot(state.slot) + 1 > spec.compute_epoch_at_slot(block.slot):
+            if spec.compute_epoch_at_slot(state.slot) + 1 > spec.compute_epoch_at_slot(slot):
                 print("warning: block slot far away, and no proposer index manually given."
                       " Signing block is slow due to transition for proposer index calculation.")
             # use stub state to get proposer index of future slot
             stub_state = deepcopy(state)
-            spec.process_slots(stub_state, block.slot)
+            spec.process_slots(stub_state, slot)
             proposer_index = spec.get_beacon_proposer_index(stub_state)
+    return proposer_index
+
 
+@only_with_bls()
+def apply_randao_reveal(spec, state, block, proposer_index=None):
+    assert state.slot <= block.slot
+
+    proposer_index = get_proposer_index_maybe(spec, state, block.slot, proposer_index)
     privkey = privkeys[proposer_index]
 
     block.body.randao_reveal = bls_sign(
@@ -33,8 +37,18 @@ def sign_block(spec, state, block, proposer_index=None):
             domain_type=spec.DOMAIN_RANDAO,
         )
     )
-    block.signature = bls_sign(
-        message_hash=signing_root(block),
+
+
+# Fully ignore the function if BLS is off, beacon-proposer index calculation is slow.
+@only_with_bls()
+def apply_sig(spec, state, signed_block, proposer_index=None):
+    block = signed_block.message
+
+    proposer_index = get_proposer_index_maybe(spec, state, block.slot, proposer_index)
+    privkey = privkeys[proposer_index]
+
+    signed_block.signature = bls_sign(
+        message_hash=hash_tree_root(block),
         privkey=privkey,
         domain=spec.get_domain(
             state,
@@ -42,17 +56,26 @@ def sign_block(spec, state, block, proposer_index=None):
             spec.compute_epoch_at_slot(block.slot)))
 
 
+def sign_block(spec, state, block, proposer_index=None):
+    signed_block = spec.SignedBeaconBlock(message=block)
+    apply_sig(spec, state, signed_block, proposer_index)
+    return signed_block
+
+
+def transition_unsigned_block(spec, state, block):
+    spec.process_slots(state, block.slot)
+    spec.process_block(state, block)
+
+
 def apply_empty_block(spec, state):
     """
     Transition via an empty block (on current slot, assuming no block has been applied yet).
-    :return: the empty block that triggered the transition.
     """
-    block = build_empty_block(spec, state, signed=True)
-    spec.state_transition(state, block)
-    return block
+    block = build_empty_block(spec, state)
+    transition_unsigned_block(spec, state, block)
 
 
-def build_empty_block(spec, state, slot=None, signed=False):
+def build_empty_block(spec, state, slot=None):
     if slot is None:
         slot = state.slot
     empty_block = spec.BeaconBlock()
@@ -61,13 +84,10 @@ def build_empty_block(spec, state, slot=None, signed=False):
     previous_block_header = deepcopy(state.latest_block_header)
     if previous_block_header.state_root == spec.Root():
         previous_block_header.state_root = state.hash_tree_root()
-    empty_block.parent_root = signing_root(previous_block_header)
-
-    if signed:
-        sign_block(spec, state, empty_block)
-
+    empty_block.parent_root = hash_tree_root(previous_block_header)
+    apply_randao_reveal(spec, state, empty_block)
     return empty_block
 
 
-def build_empty_block_for_next_slot(spec, state, signed=False):
-    return build_empty_block(spec, state, state.slot + 1, signed=signed)
+def build_empty_block_for_next_slot(spec, state):
+    return build_empty_block(spec, state, state.slot + 1)
diff --git a/test_libs/pyspec/eth2spec/test/helpers/block_header.py b/test_libs/pyspec/eth2spec/test/helpers/block_header.py
--- a/test_libs/pyspec/eth2spec/test/helpers/block_header.py
+++ b/test_libs/pyspec/eth2spec/test/helpers/block_header.py
@@ -1,5 +1,5 @@
 from eth2spec.utils.bls import bls_sign
-from eth2spec.utils.ssz.ssz_impl import signing_root
+from eth2spec.utils.ssz.ssz_impl import hash_tree_root
 
 
 def sign_block_header(spec, state, header, privkey):
@@ -7,8 +7,8 @@ def sign_block_header(spec, state, header, privkey):
         state=state,
         domain_type=spec.DOMAIN_BEACON_PROPOSER,
     )
-    header.signature = bls_sign(
-        message_hash=signing_root(header),
+    return spec.SignedBeaconBlockHeader(message=header, signature=bls_sign(
+        message_hash=hash_tree_root(header),
         privkey=privkey,
         domain=domain,
-    )
+    ))
diff --git a/test_libs/pyspec/eth2spec/test/helpers/deposits.py b/test_libs/pyspec/eth2spec/test/helpers/deposits.py
--- a/test_libs/pyspec/eth2spec/test/helpers/deposits.py
+++ b/test_libs/pyspec/eth2spec/test/helpers/deposits.py
@@ -1,7 +1,7 @@
 from eth2spec.test.helpers.keys import pubkeys, privkeys
 from eth2spec.utils.bls import bls_sign
 from eth2spec.utils.merkle_minimal import calc_merkle_tree_from_leaves, get_merkle_proof
-from eth2spec.utils.ssz.ssz_impl import signing_root, hash_tree_root
+from eth2spec.utils.ssz.ssz_impl import hash_tree_root
 from eth2spec.utils.ssz.ssz_typing import List
 
 
@@ -26,8 +26,12 @@ def sign_deposit_data(spec, deposit_data, privkey, state=None):
             spec.DOMAIN_DEPOSIT,
         )
 
+    deposit_message = spec.DepositMessage(
+        pubkey=deposit_data.pubkey,
+        withdrawal_credentials=deposit_data.withdrawal_credentials,
+        amount=deposit_data.amount)
     signature = bls_sign(
-        message_hash=signing_root(deposit_data),
+        message_hash=hash_tree_root(deposit_message),
         privkey=privkey,
         domain=domain,
     )
diff --git a/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_block.py b/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_block.py
--- a/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_block.py
+++ b/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_block.py
@@ -6,7 +6,7 @@
     only_with_bls,
 )
 from eth2spec.utils.ssz.ssz_impl import (
-    signing_root,
+    hash_tree_root,
 )
 
 from .attestations import (
@@ -22,7 +22,7 @@ def sign_shard_block(spec, beacon_state, shard_state, block, proposer_index=None
     privkey = privkeys[proposer_index]
 
     block.signature = bls_sign(
-        message_hash=signing_root(block),
+        message_hash=hash_tree_root(block),
         privkey=privkey,
         domain=spec.get_domain(
             beacon_state,
@@ -44,12 +44,12 @@ def build_empty_shard_block(spec,
     previous_beacon_header = deepcopy(beacon_state.latest_block_header)
     if previous_beacon_header.state_root == spec.Bytes32():
         previous_beacon_header.state_root = beacon_state.hash_tree_root()
-    beacon_block_root = spec.signing_root(previous_beacon_header)
+    beacon_block_root = hash_tree_root(previous_beacon_header)
 
     previous_block_header = deepcopy(shard_state.latest_block_header)
     if previous_block_header.state_root == spec.Bytes32():
         previous_block_header.state_root = shard_state.hash_tree_root()
-    parent_root = signing_root(previous_block_header)
+    parent_root = hash_tree_root(previous_block_header)
 
     block = spec.ShardBlock(
         shard=shard_state.shard,
diff --git a/test_libs/pyspec/eth2spec/test/helpers/proposer_slashings.py b/test_libs/pyspec/eth2spec/test/helpers/proposer_slashings.py
--- a/test_libs/pyspec/eth2spec/test/helpers/proposer_slashings.py
+++ b/test_libs/pyspec/eth2spec/test/helpers/proposer_slashings.py
@@ -20,12 +20,16 @@ def get_valid_proposer_slashing(spec, state, signed_1=False, signed_2=False):
     header_2.parent_root = b'\x99' * 32
 
     if signed_1:
-        sign_block_header(spec, state, header_1, privkey)
+        signed_header_1 = sign_block_header(spec, state, header_1, privkey)
+    else:
+        signed_header_1 = spec.SignedBeaconBlockHeader(message=header_1)
     if signed_2:
-        sign_block_header(spec, state, header_2, privkey)
+        signed_header_2 = sign_block_header(spec, state, header_2, privkey)
+    else:
+        signed_header_2 = spec.SignedBeaconBlockHeader(message=header_2)
 
     return spec.ProposerSlashing(
         proposer_index=validator_index,
-        header_1=header_1,
-        header_2=header_2,
+        signed_header_1=signed_header_1,
+        signed_header_2=signed_header_2,
     )
diff --git a/test_libs/pyspec/eth2spec/test/helpers/state.py b/test_libs/pyspec/eth2spec/test/helpers/state.py
--- a/test_libs/pyspec/eth2spec/test/helpers/state.py
+++ b/test_libs/pyspec/eth2spec/test/helpers/state.py
@@ -1,6 +1,8 @@
 from copy import deepcopy
+
+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
+from eth2spec.test.helpers.block import sign_block, build_empty_block_for_next_slot, transition_unsigned_block
 
 
 def get_balance(state, index):
@@ -40,14 +42,17 @@ def get_state_root(spec, state, slot) -> bytes:
     return state.state_roots[slot % spec.SLOTS_PER_HISTORICAL_ROOT]
 
 
-def state_transition_and_sign_block(spec, state, block):
+def state_transition_and_sign_block(spec, state, block, expect_fail=False):
     """
     State transition via the provided ``block``
-    then package the block with the state root and signature.
+    then package the block with the correct state root and signature.
     """
-    spec.state_transition(state, block)
+    if expect_fail:
+        expect_assertion_error(lambda: transition_unsigned_block(spec, state, block))
+    else:
+        transition_unsigned_block(spec, state, block)
     block.state_root = state.hash_tree_root()
-    sign_block(spec, state, block)
+    return sign_block(spec, state, block)
 
 
 def next_epoch_with_attestations(spec,
@@ -57,7 +62,7 @@ def next_epoch_with_attestations(spec,
     assert state.slot % spec.SLOTS_PER_EPOCH == 0
 
     post_state = deepcopy(state)
-    blocks = []
+    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:
@@ -65,17 +70,17 @@ def next_epoch_with_attestations(spec,
             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)
+                    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)
+                prev_attestation = get_valid_attestation(spec, post_state, slot_to_attest, index=index, signed=True)
                 block.body.attestations.append(prev_attestation)
 
-        state_transition_and_sign_block(spec, post_state, block)
-        blocks.append(block)
+        signed_block = state_transition_and_sign_block(spec, post_state, block)
+        signed_blocks.append(signed_block)
 
-    return state, blocks, post_state
+    return state, signed_blocks, post_state
diff --git a/test_libs/pyspec/eth2spec/test/helpers/voluntary_exits.py b/test_libs/pyspec/eth2spec/test/helpers/voluntary_exits.py
--- a/test_libs/pyspec/eth2spec/test/helpers/voluntary_exits.py
+++ b/test_libs/pyspec/eth2spec/test/helpers/voluntary_exits.py
@@ -1,24 +1,17 @@
 from eth2spec.utils.bls import bls_sign
-from eth2spec.utils.ssz.ssz_impl import signing_root
-
-
-def build_voluntary_exit(spec, state, epoch, validator_index, privkey, signed=False):
-    voluntary_exit = spec.VoluntaryExit(
-        epoch=epoch,
-        validator_index=validator_index,
-    )
-    if signed:
-        sign_voluntary_exit(spec, state, voluntary_exit, privkey)
-    return voluntary_exit
+from eth2spec.utils.ssz.ssz_impl import hash_tree_root
 
 
 def sign_voluntary_exit(spec, state, voluntary_exit, privkey):
-    voluntary_exit.signature = bls_sign(
-        message_hash=signing_root(voluntary_exit),
-        privkey=privkey,
-        domain=spec.get_domain(
-            state=state,
-            domain_type=spec.DOMAIN_VOLUNTARY_EXIT,
-            message_epoch=voluntary_exit.epoch,
+    return spec.SignedVoluntaryExit(
+        message=voluntary_exit,
+        signature=bls_sign(
+            message_hash=hash_tree_root(voluntary_exit),
+            privkey=privkey,
+            domain=spec.get_domain(
+                state=state,
+                domain_type=spec.DOMAIN_VOLUNTARY_EXIT,
+                message_epoch=voluntary_exit.epoch,
+            )
         )
     )
diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py
--- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py
+++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py
@@ -177,6 +177,20 @@ def test_invalid_index(spec, state):
     yield from run_attestation_processing(spec, state, attestation, False)
 
 
+@with_all_phases
+@spec_state_test
+def test_mismatched_target_and_slot(spec, state):
+    next_epoch(spec, state)
+    next_epoch(spec, state)
+
+    attestation = get_valid_attestation(spec, state)
+    attestation.data.slot = attestation.data.slot - spec.SLOTS_PER_EPOCH
+
+    sign_attestation(spec, state, attestation)
+
+    yield from run_attestation_processing(spec, state, attestation, False)
+
+
 @with_all_phases
 @spec_state_test
 def test_old_target_epoch(spec, state):
diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py
--- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py
+++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py
@@ -252,6 +252,76 @@ def test_att2_bad_replaced_index(spec, state):
     yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
 
 
+@with_all_phases
+@spec_state_test
+@always_bls
+def test_att1_duplicate_index_normal_signed(spec, state):
+    attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=True)
+
+    indices = attester_slashing.attestation_1.attesting_indices
+    indices.pop(1)  # remove an index, make room for the additional duplicate index.
+    attester_slashing.attestation_1.attesting_indices = sorted(indices)
+
+    # sign it, the signature will be valid for a single occurence. If the transition accidentally ignores the duplicate.
+    sign_indexed_attestation(spec, state, attester_slashing.attestation_1)
+
+    indices.append(indices[0])  # add one of the indices a second time
+    attester_slashing.attestation_1.attesting_indices = sorted(indices)
+
+    # it will just appear normal, unless the double index is spotted
+    yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
+
+
+@with_all_phases
+@spec_state_test
+@always_bls
+def test_att2_duplicate_index_normal_signed(spec, state):
+    attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=False)
+
+    indices = attester_slashing.attestation_2.attesting_indices
+    indices.pop(2)  # remove an index, make room for the additional duplicate index.
+    attester_slashing.attestation_2.attesting_indices = sorted(indices)
+
+    # sign it, the signature will be valid for a single occurence. If the transition accidentally ignores the duplicate.
+    sign_indexed_attestation(spec, state, attester_slashing.attestation_2)
+
+    indices.append(indices[1])  # add one of the indices a second time
+    attester_slashing.attestation_2.attesting_indices = sorted(indices)
+
+    # it will just appear normal, unless the double index is spotted
+    yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
+
+
+@with_all_phases
+@spec_state_test
+@always_bls
+def test_att1_duplicate_index_double_signed(spec, state):
+    attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=True)
+
+    indices = attester_slashing.attestation_1.attesting_indices
+    indices.pop(1)  # remove an index, make room for the additional duplicate index.
+    indices.append(indices[2])  # add one of the indices a second time
+    attester_slashing.attestation_1.attesting_indices = sorted(indices)
+    sign_indexed_attestation(spec, state, attester_slashing.attestation_1)  # will have one attester signing it double
+
+    yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
+
+
+@with_all_phases
+@spec_state_test
+@always_bls
+def test_att2_duplicate_index_double_signed(spec, state):
+    attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=False)
+
+    indices = attester_slashing.attestation_2.attesting_indices
+    indices.pop(1)  # remove an index, make room for the additional duplicate index.
+    indices.append(indices[2])  # add one of the indices a second time
+    attester_slashing.attestation_2.attesting_indices = sorted(indices)
+    sign_indexed_attestation(spec, state, attester_slashing.attestation_2)  # will have one attester signing it double
+
+    yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
+
+
 @with_all_phases
 @spec_state_test
 def test_unsorted_att_1(spec, state):
diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_block_header.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_block_header.py
--- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_block_header.py
+++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_block_header.py
@@ -1,10 +1,7 @@
 from copy import deepcopy
 
-from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls, with_all_phases
-from eth2spec.test.helpers.block import (
-    build_empty_block_for_next_slot,
-    sign_block
-)
+from eth2spec.test.context import spec_state_test, expect_assertion_error, with_all_phases
+from eth2spec.test.helpers.block import build_empty_block_for_next_slot
 from eth2spec.test.helpers.state import next_slot
 
 
@@ -37,16 +34,8 @@ def run_block_header_processing(spec, state, block, valid=True):
 @with_all_phases
 @spec_state_test
 def test_success_block_header(spec, state):
-    block = build_empty_block_for_next_slot(spec, state, signed=True)
-    yield from run_block_header_processing(spec, state, block)
-
-
-@with_all_phases
-@spec_state_test
-@always_bls
-def test_invalid_sig_block_header(spec, state):
     block = build_empty_block_for_next_slot(spec, state)
-    yield from run_block_header_processing(spec, state, block, valid=False)
+    yield from run_block_header_processing(spec, state, block)
 
 
 @with_all_phases
@@ -54,7 +43,6 @@ def test_invalid_sig_block_header(spec, state):
 def test_invalid_slot_block_header(spec, state):
     block = build_empty_block_for_next_slot(spec, state)
     block.slot = state.slot + 2  # invalid slot
-    sign_block(spec, state, block)
 
     yield from run_block_header_processing(spec, state, block, valid=False)
 
@@ -64,7 +52,6 @@ def test_invalid_slot_block_header(spec, state):
 def test_invalid_parent_root(spec, state):
     block = build_empty_block_for_next_slot(spec, state)
     block.parent_root = b'\12' * 32  # invalid prev root
-    sign_block(spec, state, block)
 
     yield from run_block_header_processing(spec, state, block, valid=False)
 
@@ -80,6 +67,6 @@ def test_proposer_slashed(spec, state):
     # set proposer to slashed
     state.validators[proposer_index].slashed = True
 
-    block = build_empty_block_for_next_slot(spec, state, signed=True)
+    block = build_empty_block_for_next_slot(spec, state)
 
     yield from run_block_header_processing(spec, state, block, valid=False)
diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_proposer_slashing.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_proposer_slashing.py
--- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_proposer_slashing.py
+++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_proposer_slashing.py
@@ -88,8 +88,10 @@ def test_epochs_are_different(spec, state):
     proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=False)
 
     # set slots to be in different epochs
-    proposer_slashing.header_2.slot += spec.SLOTS_PER_EPOCH
-    sign_block_header(spec, state, proposer_slashing.header_2, privkeys[proposer_slashing.proposer_index])
+    header_2 = proposer_slashing.signed_header_2.message
+    header_2.slot += spec.SLOTS_PER_EPOCH
+    proposer_slashing.signed_header_2 = sign_block_header(
+        spec, state, header_2, privkeys[proposer_slashing.proposer_index])
 
     yield from run_proposer_slashing_processing(spec, state, proposer_slashing, False)
 
@@ -100,7 +102,7 @@ def test_headers_are_same(spec, state):
     proposer_slashing = get_valid_proposer_slashing(spec, state, signed_1=True, signed_2=False)
 
     # set headers to be the same
-    proposer_slashing.header_2 = proposer_slashing.header_1
+    proposer_slashing.signed_header_2 = proposer_slashing.signed_header_1
 
     yield from run_proposer_slashing_processing(spec, state, proposer_slashing, False)
 
diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_voluntary_exit.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_voluntary_exit.py
--- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_voluntary_exit.py
+++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_voluntary_exit.py
@@ -1,9 +1,9 @@
 from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls, with_all_phases
 from eth2spec.test.helpers.keys import pubkey_to_privkey
-from eth2spec.test.helpers.voluntary_exits import build_voluntary_exit, sign_voluntary_exit
+from eth2spec.test.helpers.voluntary_exits import sign_voluntary_exit
 
 
-def run_voluntary_exit_processing(spec, state, voluntary_exit, valid=True):
+def run_voluntary_exit_processing(spec, state, signed_voluntary_exit, valid=True):
     """
     Run ``process_voluntary_exit``, yielding:
       - pre-state ('pre')
@@ -11,19 +11,19 @@ def run_voluntary_exit_processing(spec, state, voluntary_exit, valid=True):
       - post-state ('post').
     If ``valid == False``, run expecting ``AssertionError``
     """
-    validator_index = voluntary_exit.validator_index
+    validator_index = signed_voluntary_exit.message.validator_index
 
     yield 'pre', state
-    yield 'voluntary_exit', voluntary_exit
+    yield 'voluntary_exit', signed_voluntary_exit
 
     if not valid:
-        expect_assertion_error(lambda: spec.process_voluntary_exit(state, voluntary_exit))
+        expect_assertion_error(lambda: spec.process_voluntary_exit(state, signed_voluntary_exit))
         yield 'post', None
         return
 
     pre_exit_epoch = state.validators[validator_index].exit_epoch
 
-    spec.process_voluntary_exit(state, voluntary_exit)
+    spec.process_voluntary_exit(state, signed_voluntary_exit)
 
     yield 'post', state
 
@@ -41,9 +41,10 @@ def test_success(spec, state):
     validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
     privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
 
-    voluntary_exit = build_voluntary_exit(spec, state, current_epoch, validator_index, privkey, signed=True)
+    signed_voluntary_exit = sign_voluntary_exit(
+        spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
 
-    yield from run_voluntary_exit_processing(spec, state, voluntary_exit)
+    yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit)
 
 
 @with_all_phases
@@ -55,11 +56,14 @@ def test_invalid_signature(spec, state):
 
     current_epoch = spec.get_current_epoch(state)
     validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
-    privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
 
-    voluntary_exit = build_voluntary_exit(spec, state, current_epoch, validator_index, privkey)
+    voluntary_exit = spec.VoluntaryExit(
+        epoch=current_epoch,
+        validator_index=validator_index,
+    )
+    signed_voluntary_exit = sign_voluntary_exit(spec, state, voluntary_exit, 12345)
 
-    yield from run_voluntary_exit_processing(spec, state, voluntary_exit, False)
+    yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, False)
 
 
 @with_all_phases
@@ -77,14 +81,11 @@ def test_success_exit_queue(spec, state):
     exit_queue = []
     for index in initial_indices:
         privkey = pubkey_to_privkey[state.validators[index].pubkey]
-        exit_queue.append(build_voluntary_exit(
-            spec,
-            state,
-            current_epoch,
-            index,
-            privkey,
-            signed=True,
-        ))
+
+        signed_voluntary_exit = sign_voluntary_exit(
+            spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=index), privkey)
+
+        exit_queue.append(signed_voluntary_exit)
 
     # Now run all the exits
     for voluntary_exit in exit_queue:
@@ -95,18 +96,13 @@ def test_success_exit_queue(spec, state):
     # exit an additional validator
     validator_index = spec.get_active_validator_indices(state, current_epoch)[-1]
     privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
-    voluntary_exit = build_voluntary_exit(
-        spec,
-        state,
-        current_epoch,
-        validator_index,
-        privkey,
-        signed=True,
-    )
+
+    signed_voluntary_exit = sign_voluntary_exit(
+        spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
 
     # This is the interesting part of the test: on a pre-state with a full exit queue,
     #  when processing an additional exit, it results in an exit in a later epoch
-    yield from run_voluntary_exit_processing(spec, state, voluntary_exit)
+    yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit)
 
     assert (
         state.validators[validator_index].exit_epoch ==
@@ -124,18 +120,13 @@ def test_validator_exit_in_future(spec, state):
     validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
     privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
 
-    voluntary_exit = build_voluntary_exit(
-        spec,
-        state,
-        current_epoch,
-        validator_index,
-        privkey,
-        signed=False,
+    voluntary_exit = spec.VoluntaryExit(
+        epoch=current_epoch + 1,
+        validator_index=validator_index,
     )
-    voluntary_exit.epoch += 1
-    sign_voluntary_exit(spec, state, voluntary_exit, privkey)
+    signed_voluntary_exit = sign_voluntary_exit(spec, state, voluntary_exit, privkey)
 
-    yield from run_voluntary_exit_processing(spec, state, voluntary_exit, False)
+    yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, False)
 
 
 @with_all_phases
@@ -148,18 +139,13 @@ def test_validator_invalid_validator_index(spec, state):
     validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
     privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
 
-    voluntary_exit = build_voluntary_exit(
-        spec,
-        state,
-        current_epoch,
-        validator_index,
-        privkey,
-        signed=False,
+    voluntary_exit = spec.VoluntaryExit(
+        epoch=current_epoch,
+        validator_index=len(state.validators),
     )
-    voluntary_exit.validator_index = len(state.validators)
-    sign_voluntary_exit(spec, state, voluntary_exit, privkey)
+    signed_voluntary_exit = sign_voluntary_exit(spec, state, voluntary_exit, privkey)
 
-    yield from run_voluntary_exit_processing(spec, state, voluntary_exit, False)
+    yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, False)
 
 
 @with_all_phases
@@ -171,17 +157,10 @@ def test_validator_not_active(spec, state):
 
     state.validators[validator_index].activation_epoch = spec.FAR_FUTURE_EPOCH
 
-    # build and test voluntary exit
-    voluntary_exit = build_voluntary_exit(
-        spec,
-        state,
-        current_epoch,
-        validator_index,
-        privkey,
-        signed=True,
-    )
+    signed_voluntary_exit = sign_voluntary_exit(
+        spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
 
-    yield from run_voluntary_exit_processing(spec, state, voluntary_exit, False)
+    yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, False)
 
 
 @with_all_phases
@@ -197,16 +176,10 @@ def test_validator_already_exited(spec, state):
     # but validator already has exited
     state.validators[validator_index].exit_epoch = current_epoch + 2
 
-    voluntary_exit = build_voluntary_exit(
-        spec,
-        state,
-        current_epoch,
-        validator_index,
-        privkey,
-        signed=True,
-    )
+    signed_voluntary_exit = sign_voluntary_exit(
+        spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
 
-    yield from run_voluntary_exit_processing(spec, state, voluntary_exit, False)
+    yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, False)
 
 
 @with_all_phases
@@ -216,18 +189,12 @@ def test_validator_not_active_long_enough(spec, state):
     validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
     privkey = pubkey_to_privkey[state.validators[validator_index].pubkey]
 
-    voluntary_exit = build_voluntary_exit(
-        spec,
-        state,
-        current_epoch,
-        validator_index,
-        privkey,
-        signed=True,
-    )
+    signed_voluntary_exit = sign_voluntary_exit(
+        spec, state, spec.VoluntaryExit(epoch=current_epoch, validator_index=validator_index), privkey)
 
     assert (
         current_epoch - state.validators[validator_index].activation_epoch <
         spec.PERSISTENT_COMMITTEE_PERIOD
     )
 
-    yield from run_voluntary_exit_processing(spec, state, voluntary_exit, False)
+    yield from run_voluntary_exit_processing(spec, state, signed_voluntary_exit, False)
diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py
--- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py
+++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py
@@ -17,24 +17,80 @@ def mock_deposit(spec, state, index):
 
 @with_all_phases
 @spec_state_test
-def test_activation(spec, state):
+def test_add_to_activation_queue(spec, state):
+    # move past first two irregular epochs wrt finality
+    next_epoch(spec, state)
+    next_epoch(spec, state)
+
+    index = 0
+    mock_deposit(spec, state, index)
+
+    yield from run_process_registry_updates(spec, state)
+
+    # validator moved into queue
+    assert state.validators[index].activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH
+    assert state.validators[index].activation_epoch == spec.FAR_FUTURE_EPOCH
+    assert not spec.is_active_validator(state.validators[index], spec.get_current_epoch(state))
+
+
+@with_all_phases
+@spec_state_test
+def test_activation_queue_to_activated_if_finalized(spec, state):
+    # move past first two irregular epochs wrt finality
+    next_epoch(spec, state)
+    next_epoch(spec, state)
+
     index = 0
     mock_deposit(spec, state, index)
 
-    for _ in range(spec.MAX_SEED_LOOKAHEAD + 1):
-        next_epoch(spec, state)
+    # mock validator as having been in queue since latest finalized
+    state.finalized_checkpoint.epoch = spec.get_current_epoch(state) - 1
+    state.validators[index].activation_eligibility_epoch = state.finalized_checkpoint.epoch
+
+    assert not spec.is_active_validator(state.validators[index], spec.get_current_epoch(state))
 
     yield from run_process_registry_updates(spec, state)
 
+    # validator activated for future epoch
     assert state.validators[index].activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH
     assert state.validators[index].activation_epoch != spec.FAR_FUTURE_EPOCH
-    assert spec.is_active_validator(state.validators[index], spec.get_current_epoch(state))
+    assert not spec.is_active_validator(state.validators[index], spec.get_current_epoch(state))
+    assert spec.is_active_validator(
+        state.validators[index],
+        spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
+    )
+
+
+@with_all_phases
+@spec_state_test
+def test_activation_queue_no_activation_no_finality(spec, state):
+    # move past first two irregular epochs wrt finality
+    next_epoch(spec, state)
+    next_epoch(spec, state)
+
+    index = 0
+    mock_deposit(spec, state, index)
+
+    # mock validator as having been in queue only after latest finalized
+    state.finalized_checkpoint.epoch = spec.get_current_epoch(state) - 1
+    state.validators[index].activation_eligibility_epoch = state.finalized_checkpoint.epoch + 1
+
+    assert not spec.is_active_validator(state.validators[index], spec.get_current_epoch(state))
+
+    yield from run_process_registry_updates(spec, state)
+
+    # validator not activated
+    assert state.validators[index].activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH
+    assert state.validators[index].activation_epoch == spec.FAR_FUTURE_EPOCH
 
 
 @with_all_phases
 @spec_state_test
 def test_activation_queue_sorting(spec, state):
-    mock_activations = 10
+    churn_limit = spec.get_validator_churn_limit(state)
+
+    # try to activate more than the per-epoch churn linmit
+    mock_activations = churn_limit * 2
 
     epoch = spec.get_current_epoch(state)
     for i in range(mock_activations):
@@ -44,9 +100,9 @@ def test_activation_queue_sorting(spec, state):
     # give the last priority over the others
     state.validators[mock_activations - 1].activation_eligibility_epoch = epoch
 
-    # make sure we are hitting the churn
-    churn_limit = spec.get_validator_churn_limit(state)
-    assert mock_activations > churn_limit
+    # move state forward and finalize to allow for activations
+    state.slot += spec.SLOTS_PER_EPOCH * 3
+    state.finalized_checkpoint.epoch = epoch + 1
 
     yield from run_process_registry_updates(spec, state)
 
@@ -63,6 +119,38 @@ def test_activation_queue_sorting(spec, state):
     assert state.validators[churn_limit - 2].activation_epoch != spec.FAR_FUTURE_EPOCH
 
 
+@with_all_phases
+@spec_state_test
+def test_activation_queue_efficiency(spec, state):
+    churn_limit = spec.get_validator_churn_limit(state)
+    mock_activations = churn_limit * 2
+
+    epoch = spec.get_current_epoch(state)
+    for i in range(mock_activations):
+        mock_deposit(spec, state, i)
+        state.validators[i].activation_eligibility_epoch = epoch + 1
+
+    # move state forward and finalize to allow for activations
+    state.slot += spec.SLOTS_PER_EPOCH * 3
+    state.finalized_checkpoint.epoch = epoch + 1
+
+    # Run first registry update. Do not yield test vectors
+    for _ in run_process_registry_updates(spec, state):
+        pass
+
+    # Half should churn in first run of registry update
+    for i in range(mock_activations):
+        if i < mock_activations // 2:
+            assert state.validators[i].activation_epoch < spec.FAR_FUTURE_EPOCH
+        else:
+            assert state.validators[i].activation_epoch == spec.FAR_FUTURE_EPOCH
+
+    # Second half should churn in second run of registry update
+    yield from run_process_registry_updates(spec, state)
+    for i in range(mock_activations):
+        assert state.validators[i].activation_epoch < spec.FAR_FUTURE_EPOCH
+
+
 @with_all_phases
 @spec_state_test
 def test_ejection(spec, state):
@@ -73,13 +161,87 @@ def test_ejection(spec, state):
     # Mock an ejection
     state.validators[index].effective_balance = spec.EJECTION_BALANCE
 
-    for _ in range(spec.MAX_SEED_LOOKAHEAD + 1):
-        next_epoch(spec, state)
-
     yield from run_process_registry_updates(spec, state)
 
     assert state.validators[index].exit_epoch != spec.FAR_FUTURE_EPOCH
+    assert spec.is_active_validator(state.validators[index], spec.get_current_epoch(state))
     assert not spec.is_active_validator(
         state.validators[index],
-        spec.get_current_epoch(state),
+        spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
+    )
+
+
+@with_all_phases
+@spec_state_test
+def test_ejection_past_churn_limit(spec, state):
+    churn_limit = spec.get_validator_churn_limit(state)
+
+    # try to eject more than per-epoch churn limit
+    mock_ejections = churn_limit * 3
+
+    for i in range(mock_ejections):
+        state.validators[i].effective_balance = spec.EJECTION_BALANCE
+
+    expected_ejection_epoch = spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
+
+    yield from run_process_registry_updates(spec, state)
+
+    for i in range(mock_ejections):
+        # first third ejected in normal speed
+        if i < mock_ejections // 3:
+            assert state.validators[i].exit_epoch == expected_ejection_epoch
+        # second thirdgets delayed by 1 epoch
+        elif mock_ejections // 3 <= i < mock_ejections * 2 // 3:
+            assert state.validators[i].exit_epoch == expected_ejection_epoch + 1
+        # second thirdgets delayed by 2 epochs
+        else:
+            assert state.validators[i].exit_epoch == expected_ejection_epoch + 2
+
+
+@with_all_phases
+@spec_state_test
+def test_activation_queue_activation_and_ejection(spec, state):
+    # move past first two irregular epochs wrt finality
+    next_epoch(spec, state)
+    next_epoch(spec, state)
+
+    # ready for entrance into activation queue
+    activation_queue_index = 0
+    mock_deposit(spec, state, activation_queue_index)
+
+    # ready for activation
+    activation_index = 1
+    mock_deposit(spec, state, activation_index)
+    state.finalized_checkpoint.epoch = spec.get_current_epoch(state) - 1
+    state.validators[activation_index].activation_eligibility_epoch = state.finalized_checkpoint.epoch
+
+    # ready for ejection
+    ejection_index = 2
+    state.validators[ejection_index].effective_balance = spec.EJECTION_BALANCE
+
+    yield from run_process_registry_updates(spec, state)
+
+    # validator moved into activation queue
+    validator = state.validators[activation_queue_index]
+    assert validator.activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH
+    assert validator.activation_epoch == spec.FAR_FUTURE_EPOCH
+    assert not spec.is_active_validator(validator, spec.get_current_epoch(state))
+
+    # validator activated for future epoch
+    validator = state.validators[activation_index]
+    assert validator.activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH
+    assert validator.activation_epoch != spec.FAR_FUTURE_EPOCH
+    assert not spec.is_active_validator(validator, spec.get_current_epoch(state))
+    assert spec.is_active_validator(
+        validator,
+        spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
+    )
+
+    # validator ejected for future epoch
+    validator = state.validators[ejection_index]
+    assert validator.exit_epoch != spec.FAR_FUTURE_EPOCH
+    assert spec.is_active_validator(validator, spec.get_current_epoch(state))
+    assert not spec.is_active_validator(
+        validator,
+        spec.compute_activation_exit_epoch(spec.get_current_epoch(state))
     )
diff --git a/test_libs/pyspec/eth2spec/test/phase_1/sanity/test_shard_blocks.py b/test_libs/pyspec/eth2spec/test/phase_1/sanity/test_shard_blocks.py
--- a/test_libs/pyspec/eth2spec/test/phase_1/sanity/test_shard_blocks.py
+++ b/test_libs/pyspec/eth2spec/test/phase_1/sanity/test_shard_blocks.py
@@ -140,7 +140,7 @@ def test_skipped_slots(spec, state):
     assert shard_state.slot == block.slot
     latest_block_header = deepcopy(shard_state.latest_block_header)
     latest_block_header.state_root = shard_state.hash_tree_root()
-    assert latest_block_header.signing_root() == block.signing_root()
+    assert latest_block_header.hash_tree_root() == block.hash_tree_root()
 
 
 @with_all_phases_except(['phase0'])
diff --git a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py
--- a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py
+++ b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py
@@ -1,17 +1,18 @@
 from copy import deepcopy
 
-from eth2spec.utils.ssz.ssz_impl import signing_root
+from eth2spec.utils.ssz.ssz_impl import hash_tree_root
 from eth2spec.utils.bls import bls_sign
 
-from eth2spec.test.helpers.state import get_balance, state_transition_and_sign_block
-from eth2spec.test.helpers.block import build_empty_block_for_next_slot, build_empty_block, sign_block
+from eth2spec.test.helpers.state import get_balance, state_transition_and_sign_block, next_slot
+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
 from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing
 from eth2spec.test.helpers.proposer_slashings import get_valid_proposer_slashing
 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
+from eth2spec.test.context import spec_state_test, with_all_phases, expect_assertion_error, always_bls
 
 
 @with_all_phases
@@ -20,13 +21,18 @@ def test_prev_slot_block_transition(spec, state):
     # Go to clean slot
     spec.process_slots(state, state.slot + 1)
     # Make a block for it
-    block = build_empty_block(spec, state, slot=state.slot, signed=True)
+    block = build_empty_block(spec, state, slot=state.slot)
+    proposer_index = spec.get_beacon_proposer_index(state)
     # Transition to next slot, above block will not be invalid on top of new state.
     spec.process_slots(state, state.slot + 1)
 
     yield 'pre', state
-    expect_assertion_error(lambda: state_transition_and_sign_block(spec, state, block))
-    yield 'blocks', [block]
+    # State is beyond block slot, but the block can still be realistic when invalid.
+    # Try the transition, and update the state root to where it is halted. Then sign with the supposed proposer.
+    expect_assertion_error(lambda: transition_unsigned_block(spec, state, block))
+    block.state_root = state.hash_tree_root()
+    signed_block = sign_block(spec, state, block, proposer_index=proposer_index)
+    yield 'blocks', [signed_block]
     yield 'post', None
 
 
@@ -36,13 +42,13 @@ def test_same_slot_block_transition(spec, state):
     # Same slot on top of pre-state, but move out of slot 0 first.
     spec.process_slots(state, state.slot + 1)
 
-    block = build_empty_block(spec, state, slot=state.slot, signed=True)
+    block = build_empty_block(spec, state, slot=state.slot)
 
     yield 'pre', state
 
-    state_transition_and_sign_block(spec, state, block)
+    signed_block = state_transition_and_sign_block(spec, state, block)
 
-    yield 'blocks', [block]
+    yield 'blocks', [signed_block]
     yield 'post', state
 
 
@@ -54,15 +60,15 @@ def test_empty_block_transition(spec, state):
 
     yield 'pre', state
 
-    block = build_empty_block_for_next_slot(spec, state, signed=True)
+    block = build_empty_block_for_next_slot(spec, state)
 
-    state_transition_and_sign_block(spec, state, block)
+    signed_block = state_transition_and_sign_block(spec, state, block)
 
-    yield 'blocks', [block]
+    yield 'blocks', [signed_block]
     yield 'post', state
 
     assert len(state.eth1_data_votes) == pre_eth1_votes + 1
-    assert spec.get_block_root_at_slot(state, pre_slot) == block.parent_root
+    assert spec.get_block_root_at_slot(state, pre_slot) == signed_block.message.parent_root
     assert spec.get_randao_mix(state, spec.get_current_epoch(state)) != spec.Bytes32()
 
 
@@ -73,28 +79,58 @@ def test_invalid_state_root(spec, state):
 
     block = build_empty_block_for_next_slot(spec, state)
     block.state_root = b"\xaa" * 32
-    sign_block(spec, state, block)
+    signed_block = sign_block(spec, state, block)
 
-    expect_assertion_error(
-        lambda: spec.state_transition(state, block, validate_state_root=True))
+    expect_assertion_error(lambda: spec.state_transition(state, signed_block))
 
     yield 'blocks', [block]
     yield 'post', None
 
 
+@with_all_phases
+@spec_state_test
+@always_bls
+def test_zero_block_sig(spec, state):
+    block = build_empty_block_for_next_slot(spec, state)
+    invalid_signed_block = spec.SignedBeaconBlock(message=block)
+    expect_assertion_error(lambda: spec.state_transition(state, invalid_signed_block))
+
+    yield 'blocks', [invalid_signed_block]
+    yield 'post', None
+
+
+@with_all_phases
+@spec_state_test
+@always_bls
+def test_invalid_block_sig(spec, state):
+    block = build_empty_block_for_next_slot(spec, state)
+    invalid_signed_block = spec.SignedBeaconBlock(
+        message=block,
+        signature=bls_sign(
+            message_hash=hash_tree_root(block),
+            privkey=123456,
+            domain=spec.get_domain(
+                state,
+                spec.DOMAIN_BEACON_PROPOSER,
+                spec.compute_epoch_at_slot(block.slot)))
+    )
+    expect_assertion_error(lambda: spec.state_transition(state, invalid_signed_block))
+
+    yield 'blocks', [invalid_signed_block]
+    yield 'post', None
+
+
 @with_all_phases
 @spec_state_test
 def test_skipped_slots(spec, state):
     pre_slot = state.slot
     yield 'pre', state
 
-    block = build_empty_block_for_next_slot(spec, state)
-    block.slot += 3
-    sign_block(spec, state, block)
+    block = build_empty_block(spec, state, state.slot + 4)
 
-    state_transition_and_sign_block(spec, state, block)
+    signed_block = state_transition_and_sign_block(spec, state, block)
 
-    yield 'blocks', [block]
+    yield 'blocks', [signed_block]
     yield 'post', state
 
     assert state.slot == block.slot
@@ -109,13 +145,11 @@ def test_empty_epoch_transition(spec, state):
     pre_slot = state.slot
     yield 'pre', state
 
-    block = build_empty_block_for_next_slot(spec, state)
-    block.slot += spec.SLOTS_PER_EPOCH
-    sign_block(spec, state, block)
+    block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
 
-    state_transition_and_sign_block(spec, state, block)
+    signed_block = state_transition_and_sign_block(spec, state, block)
 
-    yield 'blocks', [block]
+    yield 'blocks', [signed_block]
     yield 'post', state
 
     assert state.slot == block.slot
@@ -136,10 +170,10 @@ def test_empty_epoch_transition_not_finalizing(spec, state):
     yield 'pre', state
 
     spec.process_slots(state, state.slot + (spec.SLOTS_PER_EPOCH * 5))
-    block = build_empty_block_for_next_slot(spec, state, signed=True)
-    state_transition_and_sign_block(spec, state, block)
+    block = build_empty_block_for_next_slot(spec, state)
+    signed_block = state_transition_and_sign_block(spec, state, block)
 
-    yield 'blocks', [block]
+    yield 'blocks', [signed_block]
     yield 'post', state
 
     assert state.slot == block.slot
@@ -165,11 +199,10 @@ def test_proposer_slashing(spec, state):
     #
     block = build_empty_block_for_next_slot(spec, state)
     block.body.proposer_slashings.append(proposer_slashing)
-    sign_block(spec, state, block)
 
-    state_transition_and_sign_block(spec, state, block)
+    signed_block = state_transition_and_sign_block(spec, state, block)
 
-    yield 'blocks', [block]
+    yield 'blocks', [signed_block]
     yield 'post', state
 
     # check if slashed
@@ -199,11 +232,10 @@ def test_attester_slashing(spec, state):
     #
     block = build_empty_block_for_next_slot(spec, state)
     block.body.attester_slashings.append(attester_slashing)
-    sign_block(spec, state, block)
 
-    state_transition_and_sign_block(spec, state, block)
+    signed_block = state_transition_and_sign_block(spec, state, block)
 
-    yield 'blocks', [block]
+    yield 'blocks', [signed_block]
     yield 'post', state
 
     slashed_validator = state.validators[validator_index]
@@ -221,6 +253,58 @@ def test_attester_slashing(spec, state):
     )
 
 
+@with_all_phases
+@spec_state_test
+def test_proposer_after_inactive_index(spec, state):
+    # disable some low validator index to check after for
+    inactive_index = 10
+    state.validators[inactive_index].exit_epoch = spec.get_current_epoch(state)
+
+    # skip forward, get brand new proposers
+    state.slot = spec.SLOTS_PER_EPOCH * 2
+    block = build_empty_block_for_next_slot(spec, state)
+    state_transition_and_sign_block(spec, state, block)
+
+    while True:
+        next_slot(spec, state)
+        proposer_index = spec.get_beacon_proposer_index(state)
+        if proposer_index > inactive_index:
+            # found a proposer that has a higher index than a disabled validator
+            yield 'pre', state
+            # test if the proposer can be recognized correctly after the inactive validator
+            signed_block = state_transition_and_sign_block(spec, state, build_empty_block(spec, state))
+            yield 'blocks', [signed_block]
+            yield 'post', state
+            break
+
+
+@with_all_phases
+@spec_state_test
+def test_high_proposer_index(spec, state):
+    # disable a good amount of validators to make the active count lower, for a faster test
+    current_epoch = spec.get_current_epoch(state)
+    for i in range(len(state.validators) // 3):
+        state.validators[i].exit_epoch = current_epoch
+
+    # skip forward, get brand new proposers
+    state.slot = spec.SLOTS_PER_EPOCH * 2
+    block = build_empty_block_for_next_slot(spec, state)
+    state_transition_and_sign_block(spec, state, block)
+
+    active_count = len(spec.get_active_validator_indices(state, current_epoch))
+    while True:
+        next_slot(spec, state)
+        proposer_index = spec.get_beacon_proposer_index(state)
+        if proposer_index >= active_count:
+            # found a proposer that has a higher index than the active validator count
+            yield 'pre', state
+            # test if the proposer can be recognized correctly, even while it has a high index.
+            signed_block = state_transition_and_sign_block(spec, state, build_empty_block(spec, state))
+            yield 'blocks', [signed_block]
+            yield 'post', state
+            break
+
+
 @with_all_phases
 @spec_state_test
 def test_expected_deposit_in_block(spec, state):
@@ -229,17 +313,9 @@ def test_expected_deposit_in_block(spec, state):
     yield 'pre', state
 
     block = build_empty_block_for_next_slot(spec, state)
-    sign_block(spec, state, block)
-    bad = False
-    try:
-        state_transition_and_sign_block(spec, state, block)
-        bad = True
-    except AssertionError:
-        pass
-    if bad:
-        raise AssertionError("expected deposit was not enforced")
+    signed_block = state_transition_and_sign_block(spec, state, block, expect_fail=True)
 
-    yield 'blocks', [block]
+    yield 'blocks', [signed_block]
     yield 'post', None
 
 
@@ -257,11 +333,9 @@ def test_deposit_in_block(spec, state):
 
     block = build_empty_block_for_next_slot(spec, state)
     block.body.deposits.append(deposit)
-    sign_block(spec, state, block)
+    signed_block = state_transition_and_sign_block(spec, state, block)
 
-    state_transition_and_sign_block(spec, state, block)
-
-    yield 'blocks', [block]
+    yield 'blocks', [signed_block]
     yield 'post', state
 
     assert len(state.validators) == initial_registry_len + 1
@@ -285,11 +359,10 @@ def test_deposit_top_up(spec, state):
 
     block = build_empty_block_for_next_slot(spec, state)
     block.body.deposits.append(deposit)
-    sign_block(spec, state, block)
 
-    state_transition_and_sign_block(spec, state, block)
+    signed_block = state_transition_and_sign_block(spec, state, block)
 
-    yield 'blocks', [block]
+    yield 'blocks', [signed_block]
     yield 'post', state
 
     assert len(state.validators) == initial_registry_len
@@ -308,23 +381,19 @@ 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_for_next_slot(spec, state)
-    attestation_block.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
+    attestation_block = build_empty_block(spec, state, state.slot + 1 + spec.MIN_ATTESTATION_INCLUSION_DELAY)
     attestation_block.body.attestations.append(attestation)
-    sign_block(spec, state, attestation_block)
-    state_transition_and_sign_block(spec, state, attestation_block)
+    signed_attestation_block = state_transition_and_sign_block(spec, state, attestation_block)
 
     assert len(state.current_epoch_attestations) == pre_current_attestations_len + 1
 
     # Epoch transition should move to previous_epoch_attestations
     pre_current_attestations_root = spec.hash_tree_root(state.current_epoch_attestations)
 
-    epoch_block = build_empty_block_for_next_slot(spec, state)
-    epoch_block.slot += spec.SLOTS_PER_EPOCH
-    sign_block(spec, state, epoch_block)
-    state_transition_and_sign_block(spec, state, epoch_block)
+    epoch_block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
+    signed_epoch_block = state_transition_and_sign_block(spec, state, epoch_block)
 
-    yield 'blocks', [attestation_block, epoch_block]
+    yield 'blocks', [signed_attestation_block, signed_epoch_block]
     yield 'post', state
 
     assert len(state.current_epoch_attestations) == 0
@@ -348,30 +417,30 @@ def test_voluntary_exit(spec, state):
         epoch=spec.get_current_epoch(state),
         validator_index=validator_index,
     )
-    voluntary_exit.signature = bls_sign(
-        message_hash=signing_root(voluntary_exit),
-        privkey=privkeys[validator_index],
-        domain=spec.get_domain(
-            state=state,
-            domain_type=spec.DOMAIN_VOLUNTARY_EXIT,
+    signed_voluntary_exit = spec.SignedVoluntaryExit(
+        message=voluntary_exit,
+        signature=bls_sign(
+            message_hash=hash_tree_root(voluntary_exit),
+            privkey=privkeys[validator_index],
+            domain=spec.get_domain(
+                state=state,
+                domain_type=spec.DOMAIN_VOLUNTARY_EXIT,
+            )
         )
     )
 
     # Add to state via block transition
     initiate_exit_block = build_empty_block_for_next_slot(spec, state)
-    initiate_exit_block.body.voluntary_exits.append(voluntary_exit)
-    sign_block(spec, state, initiate_exit_block)
-    state_transition_and_sign_block(spec, state, initiate_exit_block)
+    initiate_exit_block.body.voluntary_exits.append(signed_voluntary_exit)
+    signed_initiate_exit_block = state_transition_and_sign_block(spec, state, initiate_exit_block)
 
     assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
 
     # Process within epoch transition
-    exit_block = build_empty_block_for_next_slot(spec, state)
-    exit_block.slot += spec.SLOTS_PER_EPOCH
-    sign_block(spec, state, exit_block)
-    state_transition_and_sign_block(spec, state, exit_block)
+    exit_block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
+    signed_exit_block = state_transition_and_sign_block(spec, state, exit_block)
 
-    yield 'blocks', [initiate_exit_block, exit_block]
+    yield 'blocks', [signed_initiate_exit_block, signed_exit_block]
     yield 'post', state
 
     assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
@@ -391,12 +460,10 @@ def test_balance_driven_status_transitions(spec, state):
     yield 'pre', state
 
     # trigger epoch transition
-    block = build_empty_block_for_next_slot(spec, state)
-    block.slot += spec.SLOTS_PER_EPOCH
-    sign_block(spec, state, block)
-    state_transition_and_sign_block(spec, state, block)
+    block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
+    signed_block = state_transition_and_sign_block(spec, state, block)
 
-    yield 'blocks', [block]
+    yield 'blocks', [signed_block]
     yield 'post', state
 
     assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
@@ -410,11 +477,10 @@ def test_historical_batch(spec, state):
 
     yield 'pre', state
 
-    block = build_empty_block_for_next_slot(spec, state, signed=True)
-    sign_block(spec, state, block)
-    state_transition_and_sign_block(spec, state, block)
+    block = build_empty_block_for_next_slot(spec, state)
+    signed_block = state_transition_and_sign_block(spec, state, block)
 
-    yield 'blocks', [block]
+    yield 'blocks', [signed_block]
     yield 'post', state
 
     assert state.slot == block.slot
@@ -430,7 +496,6 @@ def test_eth1_data_votes_consensus(spec, state):
         return
 
     offset_block = build_empty_block(spec, state, slot=spec.SLOTS_PER_ETH1_VOTING_PERIOD - 1)
-    sign_block(spec, state, offset_block)
     state_transition_and_sign_block(spec, state, offset_block)
     yield 'pre', state
 
@@ -444,9 +509,8 @@ def test_eth1_data_votes_consensus(spec, state):
         block = build_empty_block_for_next_slot(spec, state)
         # wait for over 50% for A, then start voting B
         block.body.eth1_data.block_hash = b if i * 2 > spec.SLOTS_PER_ETH1_VOTING_PERIOD else a
-        sign_block(spec, state, block)
-        state_transition_and_sign_block(spec, state, block)
-        blocks.append(block)
+        signed_block = state_transition_and_sign_block(spec, state, block)
+        blocks.append(signed_block)
 
     assert len(state.eth1_data_votes) == spec.SLOTS_PER_ETH1_VOTING_PERIOD
     assert state.eth1_data.block_hash == a
@@ -454,9 +518,8 @@ def test_eth1_data_votes_consensus(spec, state):
     # transition to next eth1 voting period
     block = build_empty_block_for_next_slot(spec, state)
     block.body.eth1_data.block_hash = c
-    sign_block(spec, state, block)
-    state_transition_and_sign_block(spec, state, block)
-    blocks.append(block)
+    signed_block = state_transition_and_sign_block(spec, state, block)
+    blocks.append(signed_block)
 
     yield 'blocks', blocks
     yield 'post', state
@@ -477,7 +540,6 @@ def test_eth1_data_votes_no_consensus(spec, state):
     pre_eth1_hash = state.eth1_data.block_hash
 
     offset_block = build_empty_block(spec, state, slot=spec.SLOTS_PER_ETH1_VOTING_PERIOD - 1)
-    sign_block(spec, state, offset_block)
     state_transition_and_sign_block(spec, state, offset_block)
     yield 'pre', state
 
@@ -490,9 +552,8 @@ def test_eth1_data_votes_no_consensus(spec, state):
         block = build_empty_block_for_next_slot(spec, state)
         # wait for precisely 50% for A, then start voting B for other 50%
         block.body.eth1_data.block_hash = b if i * 2 >= spec.SLOTS_PER_ETH1_VOTING_PERIOD else a
-        sign_block(spec, state, block)
-        state_transition_and_sign_block(spec, state, block)
-        blocks.append(block)
+        signed_block = state_transition_and_sign_block(spec, state, block)
+        blocks.append(signed_block)
 
     assert len(state.eth1_data_votes) == spec.SLOTS_PER_ETH1_VOTING_PERIOD
     assert state.eth1_data.block_hash == pre_eth1_hash
diff --git a/test_libs/pyspec/eth2spec/utils/ssz/ssz_impl.py b/test_libs/pyspec/eth2spec/utils/ssz/ssz_impl.py
--- a/test_libs/pyspec/eth2spec/utils/ssz/ssz_impl.py
+++ b/test_libs/pyspec/eth2spec/utils/ssz/ssz_impl.py
@@ -155,10 +155,3 @@ def hash_tree_root(obj: SSZValue):
         return mix_in_length(merkleize_chunks(leaves, limit=chunk_count(obj.type())), len(obj))
     else:
         return merkleize_chunks(leaves)
-
-
-def signing_root(obj: Container):
-    # ignore last field
-    fields = [field for field in obj][:-1]
-    leaves = [hash_tree_root(f) for f in fields]
-    return merkleize_chunks(chunkify(b''.join(leaves)))
diff --git a/test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py b/test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py
--- a/test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py
+++ b/test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py
@@ -156,10 +156,6 @@ def hash_tree_root(self):
         from .ssz_impl import hash_tree_root
         return hash_tree_root(self)
 
-    def signing_root(self):
-        from .ssz_impl import signing_root
-        return signing_root(self)
-
     def __setattr__(self, name, value):
         if name not in self.__class__.__annotations__:
             raise AttributeError("Cannot change non-existing SSZ-container attribute")
diff --git a/test_libs/pyspec/requirements-testing.txt b/test_libs/pyspec/requirements-testing.txt
--- a/test_libs/pyspec/requirements-testing.txt
+++ b/test_libs/pyspec/requirements-testing.txt
@@ -2,6 +2,6 @@
 pytest>=4.4
 ../config_helpers
 flake8==3.7.7
-mypy==0.701
+mypy==0.750
 pytest-cov
 pytest-xdist