From d50ffa5f3e55123bf1b10c4d62ac6b235ff77c3c Mon Sep 17 00:00:00 2001 From: Denis Bogdanas Date: Mon, 30 Sep 2019 12:49:32 +0300 Subject: [PATCH 01/11] Added generator for rewards_and_penalties --- test_generators/epoch_processing/main.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test_generators/epoch_processing/main.py b/test_generators/epoch_processing/main.py index f0505ee94..0b6c49fb0 100644 --- a/test_generators/epoch_processing/main.py +++ b/test_generators/epoch_processing/main.py @@ -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,8 @@ 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'), + 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'), ]) From f47e023bf0f651f1058c4fc942a722d919ce3bfa Mon Sep 17 00:00:00 2001 From: Denis Bogdanas Date: Sat, 21 Sep 2019 20:18:32 +0300 Subject: [PATCH 02/11] Test case for get_matching_target_attestations() with some real filtering going on on line `if a.data.target.root == get_block_root(state, epoch)`. Discovered by K coverage tool. --- ..._process_justification_and_finalization.py | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py index 7dcdb42a4..0f2ab2ff7 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py @@ -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,13 @@ 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 +273,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) From 82d41db1b4d5ef8fa7f30e22246cc5647e0d9e24 Mon Sep 17 00:00:00 2001 From: Denis Bogdanas Date: Thu, 26 Sep 2019 22:55:58 +0300 Subject: [PATCH 03/11] Test case for get_beacon_proposer_index(), loop with multiple iterations. --- test_libs/pyspec/eth2spec/test/context.py | 18 +++++++++++++++++- .../pyspec/eth2spec/test/helpers/genesis.py | 6 +++--- .../test_process_attestation.py | 13 ++++++++++++- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/context.py b/test_libs/pyspec/eth2spec/test/context.py index 5a0ddb59d..05e3706e6 100644 --- a/test_libs/pyspec/eth2spec/test/context.py +++ b/test_libs/pyspec/eth2spec/test/context.py @@ -10,7 +10,19 @@ from .utils import vector_test, with_meta_tags 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) + kw['state'] = create_genesis_state(spec=kw['spec'], num_validators=spec_phase0.SLOTS_PER_EPOCH * 8, + validator_balance=spec_phase0.MAX_EFFECTIVE_BALANCE) + except KeyError: + raise TypeError('Spec decorator must come within state decorator to inject spec into state.') + return fn(*args, **kw) + return entry + + +def with_state_low_balance(fn): + def entry(*args, **kw): + try: + kw['state'] = create_genesis_state(spec=kw['spec'], num_validators=spec_phase0.SLOTS_PER_EPOCH * 8, + validator_balance=18 * 10**9) except KeyError: raise TypeError('Spec decorator must come within state decorator to inject spec into state.') return fn(*args, **kw) @@ -41,6 +53,10 @@ def spec_state_test(fn): return spec_test(with_state(fn)) +def spec_state_low_balance_test(fn): + return spec_test(with_state_low_balance(fn)) + + def expect_assertion_error(fn): bad = False try: diff --git a/test_libs/pyspec/eth2spec/test/helpers/genesis.py b/test_libs/pyspec/eth2spec/test/helpers/genesis.py index 11ab76b79..19fd65829 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/genesis.py +++ b/test_libs/pyspec/eth2spec/test/helpers/genesis.py @@ -18,7 +18,7 @@ def build_mock_validator(spec, i: int, balance: int): ) -def create_genesis_state(spec, num_validators): +def create_genesis_state(spec, num_validators, validator_balance): deposit_root = b'\x42' * 32 state = spec.BeaconState( @@ -34,12 +34,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.balances = [validator_balance] * num_validators state.validators = [build_mock_validator(spec, i, state.balances[i]) for i in range(num_validators)] # Process genesis activations for validator in state.validators: - if validator.effective_balance >= spec.MAX_EFFECTIVE_BALANCE: + if validator.effective_balance >= validator_balance: validator.activation_eligibility_epoch = spec.GENESIS_EPOCH validator.activation_epoch = spec.GENESIS_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 index 24ffd940b..a13db1815 100644 --- 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 @@ -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, spec_state_low_balance_test, expect_assertion_error, always_bls, \ + with_all_phases, with_phases from eth2spec.test.helpers.attestations import ( get_valid_attestation, sign_aggregate_attestation, @@ -56,6 +57,16 @@ def test_success(spec, state): yield from run_attestation_processing(spec, state, attestation) +@with_all_phases +@spec_state_low_balance_test +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): From c108d1a3563e97e8b5f28f5645f66c4ddc3fdb6e Mon Sep 17 00:00:00 2001 From: Denis Bogdanas Date: Sun, 6 Oct 2019 14:59:13 +0300 Subject: [PATCH 04/11] test for initialize_beacon_state_from_eth1, case when some small deposits don't contribute to active balance. --- .../test/genesis/test_initialization.py | 39 ++++++++++++++++++- .../eth2spec/test/genesis/test_validity.py | 6 +-- .../pyspec/eth2spec/test/helpers/deposits.py | 7 ++-- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/genesis/test_initialization.py b/test_libs/pyspec/eth2spec/test/genesis/test_initialization.py index 2ff57be74..6c9a46ed3 100644 --- a/test_libs/pyspec/eth2spec/test/genesis/test_initialization.py +++ b/test_libs/pyspec/eth2spec/test/genesis/test_initialization.py @@ -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 \ No newline at end of file diff --git a/test_libs/pyspec/eth2spec/test/genesis/test_validity.py b/test_libs/pyspec/eth2spec/test/genesis/test_validity.py index 07ad3a73c..a003938e7 100644 --- a/test_libs/pyspec/eth2spec/test/genesis/test_validity.py +++ b/test_libs/pyspec/eth2spec/test/genesis/test_validity.py @@ -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 diff --git a/test_libs/pyspec/eth2spec/test/helpers/deposits.py b/test_libs/pyspec/eth2spec/test/helpers/deposits.py index 8dc6b3b58..b3d230eaf 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/deposits.py +++ b/test_libs/pyspec/eth2spec/test/helpers/deposits.py @@ -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): From 1a65570c9b8a1f9847d42b70c738b4f4180f148f Mon Sep 17 00:00:00 2001 From: Denis Bogdanas Date: Sun, 6 Oct 2019 19:55:27 +0300 Subject: [PATCH 05/11] test_process_rewards_and_penalties.py: test for case when eligible_validator_indices in get_attestation_deltas() != state.validators. In this test some validators were just never active. --- test_libs/pyspec/eth2spec/test/context.py | 18 ++++++++- .../test/genesis/test_initialization.py | 4 +- .../pyspec/eth2spec/test/helpers/genesis.py | 37 +++++++++++++++++++ .../test_process_rewards_and_penalties.py | 36 +++++++++++++++++- 4 files changed, 91 insertions(+), 4 deletions(-) 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): From baded822472c45ba136a0c9e8df2c219601f1c16 Mon Sep 17 00:00:00 2001 From: Denis Bogdanas Date: Mon, 7 Oct 2019 13:50:24 +0300 Subject: [PATCH 06/11] test for process_rewards_and_penalties: Case when some eligible attestations are slashed. Modifies attesting_balance and consequently rewards/penalties. --- .../test_process_rewards_and_penalties.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) 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 857ad8bb0..09f4b9eb0 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 @@ -170,3 +170,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 = 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, list(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] From e8a3eac55e493bdc0f693af3ec2e8cf73efd7d7f Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 23 Oct 2019 16:33:48 +0800 Subject: [PATCH 07/11] cleanup code duplication, and build new context util for state customization --- test_libs/pyspec/eth2spec/test/context.py | 87 +++++++++++-------- .../pyspec/eth2spec/test/helpers/genesis.py | 40 +-------- .../test_process_attestation.py | 7 +- .../test_process_rewards_and_penalties.py | 33 +++---- 4 files changed, 69 insertions(+), 98 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/context.py b/test_libs/pyspec/eth2spec/test/context.py index c1c085b16..462c50685 100644 --- a/test_libs/pyspec/eth2spec/test/context.py +++ b/test_libs/pyspec/eth2spec/test/context.py @@ -2,43 +2,62 @@ 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, create_genesis_state_misc_balances +from .helpers.genesis import create_genesis_state from .utils import vector_test, with_meta_tags - -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, - validator_balance=spec_phase0.MAX_EFFECTIVE_BALANCE) - except KeyError: - raise TypeError('Spec decorator must come within state decorator to inject spec into state.') - return fn(*args, **kw) - return entry +from typing import Any, Callable, Sequence -def with_state_low_balance(fn): - def entry(*args, **kw): - try: - kw['state'] = create_genesis_state(spec=kw['spec'], num_validators=spec_phase0.SLOTS_PER_EPOCH * 8, - validator_balance=18 * 10**9) - 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 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 +def default_activation_threshold(spec): + return spec.MAX_EFFECTIVE_BALANCE + + +def default_balances(spec): + 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_validator_balances(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_validator_balances(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*. @@ -65,14 +84,6 @@ def spec_state_test(fn): return spec_test(with_state(fn)) -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/helpers/genesis.py b/test_libs/pyspec/eth2spec/test/helpers/genesis.py index 06322d3a1..65d7efc4f 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/genesis.py +++ b/test_libs/pyspec/eth2spec/test/helpers/genesis.py @@ -19,43 +19,7 @@ def build_mock_validator(spec, i: int, balance: int): ) -def create_genesis_state(spec, num_validators, validator_balance): - deposit_root = b'\x42' * 32 - - state = spec.BeaconState( - genesis_time=0, - eth1_deposit_index=num_validators, - eth1_data=spec.Eth1Data( - deposit_root=deposit_root, - deposit_count=num_validators, - 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 = [validator_balance] * num_validators - state.validators = [build_mock_validator(spec, i, state.balances[i]) for i in range(num_validators)] - - # Process genesis activations - for validator in state.validators: - if validator.effective_balance >= validator_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 - - -def create_genesis_state_misc_balances(spec, validator_balances): +def create_genesis_state(spec, validator_balances, activation_threshold): deposit_root = b'\x42' * 32 state = spec.BeaconState( @@ -76,7 +40,7 @@ def create_genesis_state_misc_balances(spec, validator_balances): # Process genesis activations for validator in state.validators: - if validator.effective_balance > spec.EJECTION_BALANCE: + if validator.effective_balance >= activation_threshold: validator.activation_eligibility_epoch = spec.GENESIS_EPOCH validator.activation_epoch = spec.GENESIS_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 index a13db1815..0445d763e 100644 --- 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 @@ -1,5 +1,5 @@ -from eth2spec.test.context import spec_state_test, spec_state_low_balance_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, @@ -58,7 +58,8 @@ def test_success(spec, state): @with_all_phases -@spec_state_low_balance_test +@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) 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 09f4b9eb0..e8be79aaf 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,7 @@ from copy import deepcopy -from eth2spec.test.context import spec_state_test, spec_state_misc_balances_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) @@ -87,22 +94,10 @@ def test_full_attestations(spec, state): @with_all_phases -@spec_state_misc_balances_test +@spec_test +@with_custom_state(balances_fn=misc_balances, threshold_fn=default_activation_threshold) 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 + attestations = prepare_state_with_full_attestations(spec, state) pre_state = deepcopy(state) From 9deda149da900da8196dd1271dccb205840fe0e7 Mon Sep 17 00:00:00 2001 From: protolambda Date: Thu, 24 Oct 2019 00:00:27 +0800 Subject: [PATCH 08/11] fix list lookup --- .../epoch_processing/test_process_rewards_and_penalties.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 e8be79aaf..b4bcc7425 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 @@ -183,11 +183,11 @@ def test_attestations_some_slashed(spec, state): add_attestations_to_state(spec, state, [include_att], state.slot) next_slot(spec, state) - attesting_indices_before_slashings = spec.get_unslashed_attesting_indices(state, attestations) + 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, list(attesting_indices_before_slashings)[i]) + 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 From 473c42b994ec88ab80584de4efbf1e860c979597 Mon Sep 17 00:00:00 2001 From: protolambda Date: Thu, 24 Oct 2019 00:44:00 +0800 Subject: [PATCH 09/11] temporarily disable a few mainnet tests in generator; too long --- test_generators/epoch_processing/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test_generators/epoch_processing/main.py b/test_generators/epoch_processing/main.py index 0b6c49fb0..d28cb4832 100644 --- a/test_generators/epoch_processing/main.py +++ b/test_generators/epoch_processing/main.py @@ -45,7 +45,8 @@ if __name__ == "__main__": 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'), - create_provider('rewards_and_penalties', test_process_rewards_and_penalties, 'mainnet'), + # 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'), ]) From b2ad6069d439683ebb855d211128b8823249a49f Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 24 Oct 2019 14:55:56 +0800 Subject: [PATCH 10/11] minor nitpick to PR --- .../test_process_justification_and_finalization.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py index 0f2ab2ff7..175ff54c9 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py @@ -220,7 +220,8 @@ def finalize_on_12(spec, state, epoch, sufficient_support, messed_up_target): epoch=epoch - 1, source=c2, target=c1, - sufficient_support=sufficient_support, messed_up_target=messed_up_target) + sufficient_support=sufficient_support, + messed_up_target=messed_up_target) # process! yield from run_process_just_and_fin(spec, state) From 0cc50725edcd514f954ce7830dfa23979ec25068 Mon Sep 17 00:00:00 2001 From: protolambda Date: Thu, 24 Oct 2019 15:31:43 +0800 Subject: [PATCH 11/11] py docs fixes --- test_libs/pyspec/eth2spec/test/context.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/context.py b/test_libs/pyspec/eth2spec/test/context.py index 462c50685..33171c4e1 100644 --- a/test_libs/pyspec/eth2spec/test/context.py +++ b/test_libs/pyspec/eth2spec/test/context.py @@ -29,10 +29,18 @@ def with_custom_state(balances_fn: Callable[[Any], Sequence[int]], 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 @@ -42,7 +50,8 @@ 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_validator_balances(low_balances)` + 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 @@ -53,7 +62,7 @@ def low_balances(spec): def misc_balances(spec): """ Helper method to create a series of balances that includes some misc. balances. - Usage: `@with_validator_balances(misc_balances)` + Usage: `@with_custom_state(balances_fn=misc_balances, ...)` """ num_validators = spec.SLOTS_PER_EPOCH * 8 num_misc_validators = spec.SLOTS_PER_EPOCH