From e8a3eac55e493bdc0f693af3ec2e8cf73efd7d7f Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 23 Oct 2019 16:33:48 +0800 Subject: [PATCH] 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)