Merge pull request #1442 from ethereum/k-cov-improv

Tests based on K cov from #1420, updated to get into v08x before release.
This commit is contained in:
Danny Ryan 2019-10-24 15:35:49 +08:00 committed by GitHub
commit 07e86ea074
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 216 additions and 32 deletions

View File

@ -7,6 +7,7 @@ from eth2spec.test.phase_0.epoch_processing import (
test_process_final_updates, test_process_final_updates,
test_process_justification_and_finalization, test_process_justification_and_finalization,
test_process_registry_updates, test_process_registry_updates,
test_process_rewards_and_penalties,
test_process_slashings test_process_slashings
) )
from gen_base import gen_runner, gen_typing from gen_base import gen_runner, gen_typing
@ -43,6 +44,9 @@ if __name__ == "__main__":
create_provider('justification_and_finalization', test_process_justification_and_finalization, 'mainnet'), create_provider('justification_and_finalization', test_process_justification_and_finalization, 'mainnet'),
create_provider('registry_updates', test_process_registry_updates, 'minimal'), create_provider('registry_updates', test_process_registry_updates, 'minimal'),
create_provider('registry_updates', test_process_registry_updates, 'mainnet'), create_provider('registry_updates', test_process_registry_updates, 'mainnet'),
create_provider('rewards_and_penalties', test_process_rewards_and_penalties, 'minimal'),
# runs full epochs filled with data, with uncached ssz. Disabled for now.
# create_provider('rewards_and_penalties', test_process_rewards_and_penalties, 'mainnet'),
create_provider('slashings', test_process_slashings, 'minimal'), create_provider('slashings', test_process_slashings, 'minimal'),
create_provider('slashings', test_process_slashings, 'mainnet'), create_provider('slashings', test_process_slashings, 'mainnet'),
]) ])

View File

@ -6,15 +6,67 @@ from .helpers.genesis import create_genesis_state
from .utils import vector_test, with_meta_tags from .utils import vector_test, with_meta_tags
from typing import Any, Callable, Sequence
def with_state(fn):
def with_custom_state(balances_fn: Callable[[Any], Sequence[int]],
threshold_fn: Callable[[Any], int]):
def deco(fn):
def entry(*args, **kw): def entry(*args, **kw):
try: try:
kw['state'] = create_genesis_state(spec=kw['spec'], num_validators=spec_phase0.SLOTS_PER_EPOCH * 8) spec = kw['spec']
balances = balances_fn(spec)
activation_threshold = threshold_fn(spec)
kw['state'] = create_genesis_state(spec=spec, validator_balances=balances,
activation_threshold=activation_threshold)
except KeyError: except KeyError:
raise TypeError('Spec decorator must come within state decorator to inject spec into state.') raise TypeError('Spec decorator must come within state decorator to inject spec into state.')
return fn(*args, **kw) return fn(*args, **kw)
return entry return entry
return deco
def default_activation_threshold(spec):
"""
Helper method to use the default balance activation threshold for state creation for tests.
Usage: `@with_custom_state(threshold_fn=default_activation_threshold, ...)`
"""
return spec.MAX_EFFECTIVE_BALANCE
def default_balances(spec):
"""
Helper method to create a series of default balances.
Usage: `@with_custom_state(balances_fn=default_balances, ...)`
"""
num_validators = spec.SLOTS_PER_EPOCH * 8
return [spec.MAX_EFFECTIVE_BALANCE] * num_validators
with_state = with_custom_state(default_balances, default_activation_threshold)
def low_balances(spec):
"""
Helper method to create a series of low balances.
Usage: `@with_custom_state(balances_fn=low_balances, ...)`
"""
num_validators = spec.SLOTS_PER_EPOCH * 8
# Technically the balances cannot be this low starting from genesis, but it is useful for testing
low_balance = 18 * 10 ** 9
return [low_balance] * num_validators
def misc_balances(spec):
"""
Helper method to create a series of balances that includes some misc. balances.
Usage: `@with_custom_state(balances_fn=misc_balances, ...)`
"""
num_validators = spec.SLOTS_PER_EPOCH * 8
num_misc_validators = spec.SLOTS_PER_EPOCH
return [spec.MAX_EFFECTIVE_BALANCE] * num_validators + [spec.MIN_DEPOSIT_AMOUNT] * num_misc_validators
# BLS is turned off by default *for performance purposes during TESTING*. # BLS is turned off by default *for performance purposes during TESTING*.

View File

@ -8,7 +8,7 @@ from eth2spec.test.helpers.deposits import (
@spec_test @spec_test
def test_initialize_beacon_state_from_eth1(spec): def test_initialize_beacon_state_from_eth1(spec):
deposit_count = spec.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT deposit_count = spec.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT
deposits, deposit_root = prepare_genesis_deposits(spec, deposit_count, spec.MAX_EFFECTIVE_BALANCE, signed=True) deposits, deposit_root, _ = prepare_genesis_deposits(spec, deposit_count, spec.MAX_EFFECTIVE_BALANCE, signed=True)
eth1_block_hash = b'\x12' * 32 eth1_block_hash = b'\x12' * 32
eth1_timestamp = spec.MIN_GENESIS_TIME eth1_timestamp = spec.MIN_GENESIS_TIME
@ -25,6 +25,43 @@ def test_initialize_beacon_state_from_eth1(spec):
assert state.eth1_data.deposit_root == deposit_root assert state.eth1_data.deposit_root == deposit_root
assert state.eth1_data.deposit_count == deposit_count assert state.eth1_data.deposit_count == deposit_count
assert state.eth1_data.block_hash == eth1_block_hash assert state.eth1_data.block_hash == eth1_block_hash
assert spec.get_total_active_balance(state) == deposit_count * spec.MAX_EFFECTIVE_BALANCE
# yield state
yield 'state', state
@with_phases(['phase0'])
@spec_test
def test_initialize_beacon_state_some_small_balances(spec):
main_deposit_count = spec.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT
main_deposits, _, deposit_data_list = prepare_genesis_deposits(spec, main_deposit_count,
spec.MAX_EFFECTIVE_BALANCE, signed=True)
# For deposits above, and for another deposit_count, add a balance of EFFECTIVE_BALANCE_INCREMENT
small_deposit_count = main_deposit_count * 2
small_deposits, deposit_root, _ = prepare_genesis_deposits(spec, small_deposit_count,
spec.MIN_DEPOSIT_AMOUNT,
signed=True,
deposit_data_list=deposit_data_list)
deposits = main_deposits + small_deposits
eth1_block_hash = b'\x12' * 32
eth1_timestamp = spec.MIN_GENESIS_TIME
yield 'eth1_block_hash', eth1_block_hash
yield 'eth1_timestamp', eth1_timestamp
yield 'deposits', deposits
# initialize beacon_state
state = spec.initialize_beacon_state_from_eth1(eth1_block_hash, eth1_timestamp, deposits)
assert state.genesis_time == eth1_timestamp - eth1_timestamp % spec.SECONDS_PER_DAY + 2 * spec.SECONDS_PER_DAY
assert len(state.validators) == small_deposit_count
assert state.eth1_data.deposit_root == deposit_root
assert state.eth1_data.deposit_count == len(deposits)
assert state.eth1_data.block_hash == eth1_block_hash
# only main deposits participate to the active balance
assert spec.get_total_active_balance(state) == main_deposit_count * spec.MAX_EFFECTIVE_BALANCE
# yield state # yield state
yield 'state', state yield 'state', state

View File

@ -6,7 +6,7 @@ from eth2spec.test.helpers.deposits import (
def create_valid_beacon_state(spec): def create_valid_beacon_state(spec):
deposit_count = spec.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT deposit_count = spec.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT
deposits, _ = prepare_genesis_deposits(spec, deposit_count, spec.MAX_EFFECTIVE_BALANCE, signed=True) deposits, _, _ = prepare_genesis_deposits(spec, deposit_count, spec.MAX_EFFECTIVE_BALANCE, signed=True)
eth1_block_hash = b'\x12' * 32 eth1_block_hash = b'\x12' * 32
eth1_timestamp = spec.MIN_GENESIS_TIME eth1_timestamp = spec.MIN_GENESIS_TIME
@ -65,7 +65,7 @@ def test_is_valid_genesis_state_true_more_balance(spec):
@spec_test @spec_test
def test_is_valid_genesis_state_true_one_more_validator(spec): def test_is_valid_genesis_state_true_one_more_validator(spec):
deposit_count = spec.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT + 1 deposit_count = spec.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT + 1
deposits, _ = prepare_genesis_deposits(spec, deposit_count, spec.MAX_EFFECTIVE_BALANCE, signed=True) deposits, _, _ = prepare_genesis_deposits(spec, deposit_count, spec.MAX_EFFECTIVE_BALANCE, signed=True)
eth1_block_hash = b'\x12' * 32 eth1_block_hash = b'\x12' * 32
eth1_timestamp = spec.MIN_GENESIS_TIME eth1_timestamp = spec.MIN_GENESIS_TIME
@ -78,7 +78,7 @@ def test_is_valid_genesis_state_true_one_more_validator(spec):
@spec_test @spec_test
def test_is_valid_genesis_state_false_not_enough_validator(spec): def test_is_valid_genesis_state_false_not_enough_validator(spec):
deposit_count = spec.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT - 1 deposit_count = spec.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT - 1
deposits, _ = prepare_genesis_deposits(spec, deposit_count, spec.MAX_EFFECTIVE_BALANCE, signed=True) deposits, _, _ = prepare_genesis_deposits(spec, deposit_count, spec.MAX_EFFECTIVE_BALANCE, signed=True)
eth1_block_hash = b'\x12' * 32 eth1_block_hash = b'\x12' * 32
eth1_timestamp = spec.MIN_GENESIS_TIME eth1_timestamp = spec.MIN_GENESIS_TIME

View File

@ -55,7 +55,8 @@ def build_deposit(spec,
return deposit, root, deposit_data_list return deposit, root, deposit_data_list
def prepare_genesis_deposits(spec, genesis_validator_count, amount, signed=False): def prepare_genesis_deposits(spec, genesis_validator_count, amount, signed=False, deposit_data_list=None):
if deposit_data_list is None:
deposit_data_list = [] deposit_data_list = []
genesis_deposits = [] genesis_deposits = []
for validator_index in range(genesis_validator_count): for validator_index in range(genesis_validator_count):
@ -75,7 +76,7 @@ def prepare_genesis_deposits(spec, genesis_validator_count, amount, signed=False
) )
genesis_deposits.append(deposit) genesis_deposits.append(deposit)
return genesis_deposits, root return genesis_deposits, root, deposit_data_list
def prepare_state_and_deposit(spec, state, validator_index, amount, withdrawal_credentials=None, signed=False): def prepare_state_and_deposit(spec, state, validator_index, amount, withdrawal_credentials=None, signed=False):

View File

@ -1,6 +1,7 @@
from eth2spec.test.helpers.keys import pubkeys from eth2spec.test.helpers.keys import pubkeys
from eth2spec.utils.ssz.ssz_impl import hash_tree_root from eth2spec.utils.ssz.ssz_impl import hash_tree_root
from eth2spec.utils.ssz.ssz_typing import List from eth2spec.utils.ssz.ssz_typing import List
import copy
def build_mock_validator(spec, i: int, balance: int): def build_mock_validator(spec, i: int, balance: int):
@ -18,15 +19,15 @@ def build_mock_validator(spec, i: int, balance: int):
) )
def create_genesis_state(spec, num_validators): def create_genesis_state(spec, validator_balances, activation_threshold):
deposit_root = b'\x42' * 32 deposit_root = b'\x42' * 32
state = spec.BeaconState( state = spec.BeaconState(
genesis_time=0, genesis_time=0,
eth1_deposit_index=num_validators, eth1_deposit_index=len(validator_balances),
eth1_data=spec.Eth1Data( eth1_data=spec.Eth1Data(
deposit_root=deposit_root, deposit_root=deposit_root,
deposit_count=num_validators, deposit_count=len(validator_balances),
block_hash=spec.Hash(), block_hash=spec.Hash(),
), ),
latest_block_header=spec.BeaconBlockHeader(body_root=spec.hash_tree_root(spec.BeaconBlockBody())), latest_block_header=spec.BeaconBlockHeader(body_root=spec.hash_tree_root(spec.BeaconBlockBody())),
@ -34,12 +35,12 @@ def create_genesis_state(spec, num_validators):
# We "hack" in the initial validators, # We "hack" in the initial validators,
# as it is much faster than creating and processing genesis deposits for every single test case. # as it is much faster than creating and processing genesis deposits for every single test case.
state.balances = [spec.MAX_EFFECTIVE_BALANCE] * num_validators state.balances = copy.deepcopy(validator_balances)
state.validators = [build_mock_validator(spec, i, state.balances[i]) for i in range(num_validators)] state.validators = [build_mock_validator(spec, i, state.balances[i]) for i in range(len(validator_balances))]
# Process genesis activations # Process genesis activations
for validator in state.validators: for validator in state.validators:
if validator.effective_balance >= spec.MAX_EFFECTIVE_BALANCE: if validator.effective_balance >= activation_threshold:
validator.activation_eligibility_epoch = spec.GENESIS_EPOCH validator.activation_eligibility_epoch = spec.GENESIS_EPOCH
validator.activation_epoch = spec.GENESIS_EPOCH validator.activation_epoch = spec.GENESIS_EPOCH

View File

@ -1,4 +1,5 @@
from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls, with_all_phases, with_phases from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls, \
with_all_phases, with_phases, spec_test, low_balances, with_custom_state
from eth2spec.test.helpers.attestations import ( from eth2spec.test.helpers.attestations import (
get_valid_attestation, get_valid_attestation,
sign_aggregate_attestation, sign_aggregate_attestation,
@ -56,6 +57,17 @@ def test_success(spec, state):
yield from run_attestation_processing(spec, state, attestation) yield from run_attestation_processing(spec, state, attestation)
@with_all_phases
@spec_test
@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.EJECTION_BALANCE)
def test_success_multi_proposer_index_iterations(spec, state):
state.slot += spec.SLOTS_PER_EPOCH * 2
attestation = get_valid_attestation(spec, state, signed=True)
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
yield from run_attestation_processing(spec, state, attestation)
@with_all_phases @with_all_phases
@spec_state_test @spec_state_test
def test_success_previous_epoch(spec, state): def test_success_previous_epoch(spec, state):

View File

@ -16,7 +16,7 @@ def get_shards_for_slot(spec, state, slot):
return [shard + i for i in range(committees_per_slot)] return [shard + i for i in range(committees_per_slot)]
def add_mock_attestations(spec, state, epoch, source, target, sufficient_support=False): def add_mock_attestations(spec, state, epoch, source, target, sufficient_support=False, messed_up_target=False):
# we must be at the end of the epoch # we must be at the end of the epoch
assert (state.slot + 1) % spec.SLOTS_PER_EPOCH == 0 assert (state.slot + 1) % spec.SLOTS_PER_EPOCH == 0
@ -67,6 +67,8 @@ def add_mock_attestations(spec, state, epoch, source, target, sufficient_support
), ),
inclusion_delay=1, inclusion_delay=1,
)) ))
if messed_up_target:
attestations[len(attestations) - 1].data.target.root = b'\x99' * 32
def get_checkpoints(spec, epoch): def get_checkpoints(spec, epoch):
@ -196,7 +198,7 @@ def finalize_on_123(spec, state, epoch, sufficient_support):
assert state.finalized_checkpoint == old_finalized # no new finalized assert state.finalized_checkpoint == old_finalized # no new finalized
def finalize_on_12(spec, state, epoch, sufficient_support): def finalize_on_12(spec, state, epoch, sufficient_support, messed_up_target):
assert epoch > 2 assert epoch > 2
state.slot = (spec.SLOTS_PER_EPOCH * epoch) - 1 # skip ahead to just before epoch state.slot = (spec.SLOTS_PER_EPOCH * epoch) - 1 # skip ahead to just before epoch
@ -218,13 +220,14 @@ def finalize_on_12(spec, state, epoch, sufficient_support):
epoch=epoch - 1, epoch=epoch - 1,
source=c2, source=c2,
target=c1, target=c1,
sufficient_support=sufficient_support) sufficient_support=sufficient_support,
messed_up_target=messed_up_target)
# process! # process!
yield from run_process_just_and_fin(spec, state) yield from run_process_just_and_fin(spec, state)
assert state.previous_justified_checkpoint == c2 # changed to old current assert state.previous_justified_checkpoint == c2 # changed to old current
if sufficient_support: if sufficient_support and not messed_up_target:
assert state.current_justified_checkpoint == c1 # changed to 1st latest assert state.current_justified_checkpoint == c1 # changed to 1st latest
assert state.finalized_checkpoint == c2 # finalized previous justified epoch assert state.finalized_checkpoint == c2 # finalized previous justified epoch
else: else:
@ -271,10 +274,16 @@ def test_123_poor_support(spec, state):
@with_all_phases @with_all_phases
@spec_state_test @spec_state_test
def test_12_ok_support(spec, state): def test_12_ok_support(spec, state):
yield from finalize_on_12(spec, state, 3, True) yield from finalize_on_12(spec, state, 3, True, False)
@with_all_phases
@spec_state_test
def test_12_ok_support_messed_target(spec, state):
yield from finalize_on_12(spec, state, 3, True, True)
@with_all_phases @with_all_phases
@spec_state_test @spec_state_test
def test_12_poor_support(spec, state): def test_12_poor_support(spec, state):
yield from finalize_on_12(spec, state, 3, False) yield from finalize_on_12(spec, state, 3, False, False)

View File

@ -1,6 +1,7 @@
from copy import deepcopy from copy import deepcopy
from eth2spec.test.context import spec_state_test, with_all_phases from eth2spec.test.context import spec_state_test, with_all_phases, spec_test, \
misc_balances, with_custom_state, default_activation_threshold
from eth2spec.test.helpers.state import ( from eth2spec.test.helpers.state import (
next_epoch, next_epoch,
next_slot, next_slot,
@ -55,9 +56,7 @@ def test_genesis_epoch_full_attestations_no_rewards(spec, state):
assert state.balances[index] == pre_state.balances[index] assert state.balances[index] == pre_state.balances[index]
@with_all_phases def prepare_state_with_full_attestations(spec, state):
@spec_state_test
def test_full_attestations(spec, state):
attestations = [] attestations = []
for slot in range(spec.SLOTS_PER_EPOCH + spec.MIN_ATTESTATION_INCLUSION_DELAY): for slot in range(spec.SLOTS_PER_EPOCH + spec.MIN_ATTESTATION_INCLUSION_DELAY):
# create an attestation for each slot in epoch # create an attestation for each slot in epoch
@ -73,6 +72,14 @@ def test_full_attestations(spec, state):
assert spec.compute_epoch_of_slot(state.slot) == spec.GENESIS_EPOCH + 1 assert spec.compute_epoch_of_slot(state.slot) == spec.GENESIS_EPOCH + 1
assert len(state.previous_epoch_attestations) == spec.SLOTS_PER_EPOCH assert len(state.previous_epoch_attestations) == spec.SLOTS_PER_EPOCH
return attestations
@with_all_phases
@spec_state_test
def test_full_attestations(spec, state):
attestations = prepare_state_with_full_attestations(spec, state)
pre_state = deepcopy(state) pre_state = deepcopy(state)
yield from run_process_rewards_and_penalties(spec, state) yield from run_process_rewards_and_penalties(spec, state)
@ -86,6 +93,28 @@ def test_full_attestations(spec, state):
assert state.balances[index] < pre_state.balances[index] assert state.balances[index] < pre_state.balances[index]
@with_all_phases
@spec_test
@with_custom_state(balances_fn=misc_balances, threshold_fn=default_activation_threshold)
def test_full_attestations_misc_balances(spec, state):
attestations = prepare_state_with_full_attestations(spec, state)
pre_state = deepcopy(state)
yield from run_process_rewards_and_penalties(spec, state)
attesting_indices = spec.get_unslashed_attesting_indices(state, attestations)
assert len(attesting_indices) > 0
assert len(attesting_indices) != len(pre_state.validators)
for index in range(len(pre_state.validators)):
if index in attesting_indices:
assert state.balances[index] > pre_state.balances[index]
elif spec.is_active_validator(pre_state.validators[index], spec.compute_epoch_of_slot(state.slot)):
assert state.balances[index] < pre_state.balances[index]
else:
assert state.balances[index] == pre_state.balances[index]
@with_all_phases @with_all_phases
@spec_state_test @spec_state_test
def test_no_attestations_all_penalties(spec, state): def test_no_attestations_all_penalties(spec, state):
@ -136,3 +165,42 @@ def test_duplicate_attestation(spec, state):
for index in participants: for index in participants:
assert state.balances[index] < single_state.balances[index] assert state.balances[index] < single_state.balances[index]
assert single_state.balances[index] == dup_state.balances[index] assert single_state.balances[index] == dup_state.balances[index]
@with_all_phases
@spec_state_test
# Case when some eligible attestations are slashed. Modifies attesting_balance and consequently rewards/penalties.
def test_attestations_some_slashed(spec, state):
attestations = []
for slot in range(spec.SLOTS_PER_EPOCH + spec.MIN_ATTESTATION_INCLUSION_DELAY):
# create an attestation for each slot in epoch
if slot < spec.SLOTS_PER_EPOCH:
attestation = get_valid_attestation(spec, state, signed=True)
attestations.append(attestation)
# fill each created slot in state after inclusion delay
if slot - spec.MIN_ATTESTATION_INCLUSION_DELAY >= 0:
include_att = attestations[slot - spec.MIN_ATTESTATION_INCLUSION_DELAY]
add_attestations_to_state(spec, state, [include_att], state.slot)
next_slot(spec, state)
attesting_indices_before_slashings = list(spec.get_unslashed_attesting_indices(state, attestations))
# Slash maximum amount of validators allowed per epoch.
for i in range(spec.MIN_PER_EPOCH_CHURN_LIMIT):
spec.slash_validator(state, attesting_indices_before_slashings[i])
assert spec.compute_epoch_of_slot(state.slot) == spec.GENESIS_EPOCH + 1
assert len(state.previous_epoch_attestations) == spec.SLOTS_PER_EPOCH
pre_state = deepcopy(state)
yield from run_process_rewards_and_penalties(spec, state)
attesting_indices = spec.get_unslashed_attesting_indices(state, attestations)
assert len(attesting_indices) > 0
assert len(attesting_indices_before_slashings) - len(attesting_indices) == spec.MIN_PER_EPOCH_CHURN_LIMIT
for index in range(len(pre_state.validators)):
if index in attesting_indices:
assert state.balances[index] > pre_state.balances[index]
else:
assert state.balances[index] < pre_state.balances[index]