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:
commit
07e86ea074
|
@ -7,6 +7,7 @@ from eth2spec.test.phase_0.epoch_processing import (
|
|||
test_process_final_updates,
|
||||
test_process_justification_and_finalization,
|
||||
test_process_registry_updates,
|
||||
test_process_rewards_and_penalties,
|
||||
test_process_slashings
|
||||
)
|
||||
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('registry_updates', test_process_registry_updates, 'minimal'),
|
||||
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, 'mainnet'),
|
||||
])
|
||||
|
|
|
@ -6,15 +6,67 @@ from .helpers.genesis import create_genesis_state
|
|||
|
||||
from .utils import vector_test, with_meta_tags
|
||||
|
||||
from typing import Any, Callable, Sequence
|
||||
|
||||
def with_state(fn):
|
||||
def entry(*args, **kw):
|
||||
try:
|
||||
kw['state'] = create_genesis_state(spec=kw['spec'], num_validators=spec_phase0.SLOTS_PER_EPOCH * 8)
|
||||
except KeyError:
|
||||
raise TypeError('Spec decorator must come within state decorator to inject spec into state.')
|
||||
return fn(*args, **kw)
|
||||
return entry
|
||||
|
||||
def with_custom_state(balances_fn: Callable[[Any], Sequence[int]],
|
||||
threshold_fn: Callable[[Any], int]):
|
||||
def deco(fn):
|
||||
def entry(*args, **kw):
|
||||
try:
|
||||
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:
|
||||
raise TypeError('Spec decorator must come within state decorator to inject spec into state.')
|
||||
return fn(*args, **kw)
|
||||
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*.
|
||||
|
|
|
@ -8,7 +8,7 @@ from eth2spec.test.helpers.deposits import (
|
|||
@spec_test
|
||||
def test_initialize_beacon_state_from_eth1(spec):
|
||||
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_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_count == deposit_count
|
||||
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', state
|
||||
|
|
|
@ -6,7 +6,7 @@ from eth2spec.test.helpers.deposits import (
|
|||
|
||||
def create_valid_beacon_state(spec):
|
||||
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_timestamp = spec.MIN_GENESIS_TIME
|
||||
|
@ -65,7 +65,7 @@ def test_is_valid_genesis_state_true_more_balance(spec):
|
|||
@spec_test
|
||||
def test_is_valid_genesis_state_true_one_more_validator(spec):
|
||||
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_timestamp = spec.MIN_GENESIS_TIME
|
||||
|
@ -78,7 +78,7 @@ def test_is_valid_genesis_state_true_one_more_validator(spec):
|
|||
@spec_test
|
||||
def test_is_valid_genesis_state_false_not_enough_validator(spec):
|
||||
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_timestamp = spec.MIN_GENESIS_TIME
|
||||
|
|
|
@ -55,8 +55,9 @@ def build_deposit(spec,
|
|||
return deposit, root, deposit_data_list
|
||||
|
||||
|
||||
def prepare_genesis_deposits(spec, genesis_validator_count, amount, signed=False):
|
||||
deposit_data_list = []
|
||||
def prepare_genesis_deposits(spec, genesis_validator_count, amount, signed=False, deposit_data_list=None):
|
||||
if deposit_data_list is None:
|
||||
deposit_data_list = []
|
||||
genesis_deposits = []
|
||||
for validator_index in range(genesis_validator_count):
|
||||
pubkey = pubkeys[validator_index]
|
||||
|
@ -75,7 +76,7 @@ def prepare_genesis_deposits(spec, genesis_validator_count, amount, signed=False
|
|||
)
|
||||
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):
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from eth2spec.test.helpers.keys import pubkeys
|
||||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
||||
from eth2spec.utils.ssz.ssz_typing import List
|
||||
import copy
|
||||
|
||||
|
||||
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
|
||||
|
||||
state = spec.BeaconState(
|
||||
genesis_time=0,
|
||||
eth1_deposit_index=num_validators,
|
||||
eth1_deposit_index=len(validator_balances),
|
||||
eth1_data=spec.Eth1Data(
|
||||
deposit_root=deposit_root,
|
||||
deposit_count=num_validators,
|
||||
deposit_count=len(validator_balances),
|
||||
block_hash=spec.Hash(),
|
||||
),
|
||||
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,
|
||||
# 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.validators = [build_mock_validator(spec, i, state.balances[i]) for i in range(num_validators)]
|
||||
state.balances = copy.deepcopy(validator_balances)
|
||||
state.validators = [build_mock_validator(spec, i, state.balances[i]) for i in range(len(validator_balances))]
|
||||
|
||||
# Process genesis activations
|
||||
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_epoch = spec.GENESIS_EPOCH
|
||||
|
||||
|
|
|
@ -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 (
|
||||
get_valid_attestation,
|
||||
sign_aggregate_attestation,
|
||||
|
@ -56,6 +57,17 @@ def test_success(spec, state):
|
|||
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
|
||||
@spec_state_test
|
||||
def test_success_previous_epoch(spec, state):
|
||||
|
|
|
@ -16,7 +16,7 @@ def get_shards_for_slot(spec, state, 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
|
||||
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,
|
||||
))
|
||||
if messed_up_target:
|
||||
attestations[len(attestations) - 1].data.target.root = b'\x99' * 32
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
def finalize_on_12(spec, state, epoch, sufficient_support):
|
||||
def finalize_on_12(spec, state, epoch, sufficient_support, messed_up_target):
|
||||
assert epoch > 2
|
||||
state.slot = (spec.SLOTS_PER_EPOCH * epoch) - 1 # skip ahead to just before epoch
|
||||
|
||||
|
@ -218,13 +220,14 @@ def finalize_on_12(spec, state, epoch, sufficient_support):
|
|||
epoch=epoch - 1,
|
||||
source=c2,
|
||||
target=c1,
|
||||
sufficient_support=sufficient_support)
|
||||
sufficient_support=sufficient_support,
|
||||
messed_up_target=messed_up_target)
|
||||
|
||||
# process!
|
||||
yield from run_process_just_and_fin(spec, state)
|
||||
|
||||
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.finalized_checkpoint == c2 # finalized previous justified epoch
|
||||
else:
|
||||
|
@ -271,10 +274,16 @@ def test_123_poor_support(spec, state):
|
|||
@with_all_phases
|
||||
@spec_state_test
|
||||
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
|
||||
@spec_state_test
|
||||
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)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
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 (
|
||||
next_epoch,
|
||||
next_slot,
|
||||
|
@ -55,9 +56,7 @@ def test_genesis_epoch_full_attestations_no_rewards(spec, state):
|
|||
assert state.balances[index] == pre_state.balances[index]
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_full_attestations(spec, state):
|
||||
def prepare_state_with_full_attestations(spec, state):
|
||||
attestations = []
|
||||
for slot in range(spec.SLOTS_PER_EPOCH + spec.MIN_ATTESTATION_INCLUSION_DELAY):
|
||||
# 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 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)
|
||||
|
||||
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]
|
||||
|
||||
|
||||
@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
|
||||
@spec_state_test
|
||||
def test_no_attestations_all_penalties(spec, state):
|
||||
|
@ -136,3 +165,42 @@ def test_duplicate_attestation(spec, state):
|
|||
for index in participants:
|
||||
assert state.balances[index] < single_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]
|
||||
|
|
Loading…
Reference in New Issue