cleanup code duplication, and build new context util for state customization

This commit is contained in:
protolambda 2019-10-23 16:33:48 +08:00
parent baded82247
commit e8a3eac55e
No known key found for this signature in database
GPG Key ID: EC89FDBB2B4C7623
4 changed files with 69 additions and 98 deletions

View File

@ -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:

View File

@ -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

View File

@ -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)

View File

@ -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)