diff --git a/test_libs/pyspec/eth2spec/test/context.py b/test_libs/pyspec/eth2spec/test/context.py index 05e3706e6..c1c085b16 100644 --- a/test_libs/pyspec/eth2spec/test/context.py +++ b/test_libs/pyspec/eth2spec/test/context.py @@ -2,7 +2,7 @@ from eth2spec.phase0 import spec as spec_phase0 from eth2spec.phase1 import spec as spec_phase1 from eth2spec.utils import bls -from .helpers.genesis import create_genesis_state +from .helpers.genesis import create_genesis_state, create_genesis_state_misc_balances from .utils import vector_test, with_meta_tags @@ -29,6 +29,18 @@ def with_state_low_balance(fn): return entry +def with_state_misc_balances(fn): + def entry(*args, **kw): + try: + validator_balances = [spec_phase0.MAX_EFFECTIVE_BALANCE] * (spec_phase0.SLOTS_PER_EPOCH * 8) + [ + spec_phase0.MIN_DEPOSIT_AMOUNT] * spec_phase0.SLOTS_PER_EPOCH + kw['state'] = create_genesis_state_misc_balances(spec=kw['spec'], validator_balances=validator_balances) + except KeyError: + raise TypeError('Spec decorator must come within state decorator to inject spec into state.') + return fn(*args, **kw) + return entry + + # BLS is turned off by default *for performance purposes during TESTING*. # The runner of the test can indicate the preferred setting (test generators prefer BLS to be ON). # - Some tests are marked as BLS-requiring, and ignore this setting. @@ -57,6 +69,10 @@ def spec_state_low_balance_test(fn): return spec_test(with_state_low_balance(fn)) +def spec_state_misc_balances_test(fn): + return spec_test(with_state_misc_balances(fn)) + + def expect_assertion_error(fn): bad = False try: diff --git a/test_libs/pyspec/eth2spec/test/genesis/test_initialization.py b/test_libs/pyspec/eth2spec/test/genesis/test_initialization.py index 6c9a46ed3..462065bb9 100644 --- a/test_libs/pyspec/eth2spec/test/genesis/test_initialization.py +++ b/test_libs/pyspec/eth2spec/test/genesis/test_initialization.py @@ -36,7 +36,7 @@ def test_initialize_beacon_state_from_eth1(spec): 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) + 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, @@ -64,4 +64,4 @@ def test_initialize_beacon_state_some_small_balances(spec): assert spec.get_total_active_balance(state) == main_deposit_count * spec.MAX_EFFECTIVE_BALANCE # yield state - yield 'state', state \ No newline at end of file + yield 'state', state diff --git a/test_libs/pyspec/eth2spec/test/helpers/genesis.py b/test_libs/pyspec/eth2spec/test/helpers/genesis.py index 19fd65829..06322d3a1 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/genesis.py +++ b/test_libs/pyspec/eth2spec/test/helpers/genesis.py @@ -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): @@ -52,3 +53,39 @@ def create_genesis_state(spec, num_validators, validator_balance): state.compact_committees_roots[index] = genesis_compact_committees_root return state + + +def create_genesis_state_misc_balances(spec, validator_balances): + deposit_root = b'\x42' * 32 + + state = spec.BeaconState( + genesis_time=0, + eth1_deposit_index=len(validator_balances), + eth1_data=spec.Eth1Data( + deposit_root=deposit_root, + deposit_count=len(validator_balances), + block_hash=spec.Hash(), + ), + latest_block_header=spec.BeaconBlockHeader(body_root=spec.hash_tree_root(spec.BeaconBlockBody())), + ) + + # We "hack" in the initial validators, + # as it is much faster than creating and processing genesis deposits for every single test case. + 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.EJECTION_BALANCE: + validator.activation_eligibility_epoch = spec.GENESIS_EPOCH + validator.activation_epoch = spec.GENESIS_EPOCH + + genesis_active_index_root = hash_tree_root(List[spec.ValidatorIndex, spec.VALIDATOR_REGISTRY_LIMIT]( + spec.get_active_validator_indices(state, spec.GENESIS_EPOCH))) + genesis_compact_committees_root = hash_tree_root(List[spec.ValidatorIndex, spec.VALIDATOR_REGISTRY_LIMIT]( + spec.get_active_validator_indices(state, spec.GENESIS_EPOCH))) + for index in range(spec.EPOCHS_PER_HISTORICAL_VECTOR): + state.active_index_roots[index] = genesis_active_index_root + state.compact_committees_roots[index] = genesis_compact_committees_root + + return state diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_rewards_and_penalties.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_rewards_and_penalties.py index 0d2547436..857ad8bb0 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_rewards_and_penalties.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_rewards_and_penalties.py @@ -1,6 +1,6 @@ from copy import deepcopy -from eth2spec.test.context import spec_state_test, with_all_phases +from eth2spec.test.context import spec_state_test, spec_state_misc_balances_test, with_all_phases from eth2spec.test.helpers.state import ( next_epoch, next_slot, @@ -86,6 +86,40 @@ def test_full_attestations(spec, state): assert state.balances[index] < pre_state.balances[index] +@with_all_phases +@spec_state_misc_balances_test +def test_full_attestations_misc_balances(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) + + 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) != 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):