new process-slashings tests, and epoch processing bugfix with transition-to-excl not working when not yielded from

This commit is contained in:
protolambda 2019-06-26 03:17:46 +02:00
parent 46dc3f39bb
commit 24aa0646c0
No known key found for this signature in database
GPG Key ID: EC89FDBB2B4C7623
5 changed files with 142 additions and 16 deletions

View File

@ -12,13 +12,9 @@ process_calls = [
]
def run_epoch_processing_to(spec, state, process_name: str, exclusive=False):
def run_epoch_processing_to(spec, state, process_name: str):
"""
Run the epoch processing functions up to ``process_name``.
If ``exclusive`` is True, the process itself will not be ran.
If ``exclusive`` is False (default), this function yields:
- pre-state ('pre'), state before calling ``process_name``
- post-state ('post'), state after calling ``process_name``
Processes to the next epoch transition, up to, but not including, the sub-transition named ``process_name``
"""
slot = state.slot + (spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH)
@ -36,7 +32,14 @@ def run_epoch_processing_to(spec, state, process_name: str, exclusive=False):
if hasattr(spec, name):
getattr(spec, name)(state)
if not exclusive:
yield 'pre', state
getattr(spec, process_name)(state)
yield 'post', state
def run_epoch_processing_with(spec, state, process_name: str):
"""
Processes to the next epoch transition, up to and including the sub-transition named ``process_name``
- pre-state ('pre'), state before calling ``process_name``
- post-state ('post'), state after calling ``process_name``
"""
run_epoch_processing_to(spec, state, process_name)
yield 'pre', state
getattr(spec, process_name)(state)
yield 'post', state

View File

@ -12,11 +12,11 @@ from eth2spec.test.helpers.attestations import (
get_valid_attestation,
sign_attestation,
)
from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_epoch_processing_to
from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_epoch_processing_with
def run_process_crosslinks(spec, state):
yield from run_epoch_processing_to(spec, state, 'process_crosslinks')
yield from run_epoch_processing_with(spec, state, 'process_crosslinks')
@with_all_phases

View File

@ -1,9 +1,11 @@
from eth2spec.test.context import spec_state_test, with_all_phases
from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_epoch_processing_to
from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import (
run_epoch_processing_with, run_epoch_processing_to
)
def run_process_final_updates(spec, state):
yield from run_epoch_processing_to(spec, state, 'process_final_updates')
yield from run_epoch_processing_with(spec, state, 'process_final_updates')
@with_all_phases

View File

@ -1,10 +1,10 @@
from eth2spec.test.helpers.state import next_epoch
from eth2spec.test.context import spec_state_test, with_all_phases
from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_epoch_processing_to
from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_epoch_processing_with
def run_process_registry_updates(spec, state):
yield from run_epoch_processing_to(spec, state, 'process_registry_updates')
yield from run_epoch_processing_with(spec, state, 'process_registry_updates')
def mock_deposit(spec, state, index):

View File

@ -0,0 +1,121 @@
from eth2spec.test.context import spec_state_test, with_all_phases
from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import (
run_epoch_processing_with, run_epoch_processing_to
)
def run_process_slashings(spec, state):
yield from run_epoch_processing_with(spec, state, 'process_slashings')
def slash_validators(spec, state, indices, out_epochs):
total_slashed_balance = 0
for i, out_epoch in zip(indices, out_epochs):
v = state.validators[i]
v.slashed = True
spec.initiate_validator_exit(state, i)
v.withdrawable_epoch = out_epoch
total_slashed_balance += v.effective_balance
state.slashed_balances[
spec.get_current_epoch(state) % spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR
] = total_slashed_balance
@with_all_phases
@spec_state_test
def test_max_penalties(spec, state):
slashed_count = (len(state.validators) // 3) + 1
out_epoch = spec.get_current_epoch(state) + (spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR // 2)
slashed_indices = list(range(slashed_count))
slash_validators(spec, state, slashed_indices, [out_epoch] * slashed_count)
total_balance = spec.get_total_active_balance(state)
total_penalties = state.slashed_balances[spec.get_current_epoch(state) % spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR]
assert total_balance // 3 <= total_penalties
yield from run_process_slashings(spec, state)
for i in slashed_indices:
assert state.balances[i] == 0
@with_all_phases
@spec_state_test
def test_min_penalties(spec, state):
# run_epoch_processing_to(spec, state, 'process_slashings', exclusive=True)
# Just the bare minimum for this one validator
pre_balance = state.balances[0] = state.validators[0].effective_balance = spec.EJECTION_BALANCE
# All the other validators get the maximum.
for i in range(1, len(state.validators)):
state.validators[i].effective_balance = state.balances[i] = spec.MAX_EFFECTIVE_BALANCE
out_epoch = spec.get_current_epoch(state) + (spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR // 2)
slash_validators(spec, state, [0], [out_epoch])
total_balance = spec.get_total_active_balance(state)
total_penalties = state.slashed_balances[spec.get_current_epoch(state) % spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR]
# we are testing the minimum here, i.e. get slashed (effective_balance / MIN_SLASHING_PENALTY_QUOTIENT)
assert total_penalties * 3 / total_balance < 1 / spec.MIN_SLASHING_PENALTY_QUOTIENT
yield from run_process_slashings(spec, state)
assert state.balances[0] == pre_balance - (pre_balance // spec.MIN_SLASHING_PENALTY_QUOTIENT)
@with_all_phases
@spec_state_test
def test_scaled_penalties(spec, state):
# skip to next epoch
state.slot = spec.SLOTS_PER_EPOCH
# Also mock some previous slashings, so that we test to have the delta in the penalties computation.
for i in range(spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR):
state.slashed_balances[i] = spec.MAX_EFFECTIVE_BALANCE * 3
# Mock the very last one (which is to be used for the delta balance computation) to be different.
# To enforce the client test runner to correctly get this one from the array, not the others.
prev_penalties = state.slashed_balances[
(spec.get_current_epoch(state) + 1) % spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR
] = spec.MAX_EFFECTIVE_BALANCE * 2
slashed_count = len(state.validators) // 4
assert slashed_count > 10
# make the balances non-uniform.
# Otherwise it would just be a simple 3/4 balance slashing. Test the per-validator scaled penalties.
for i in range(10):
state.validators[i].effective_balance += spec.EFFECTIVE_BALANCE_INCREMENT * 4
state.balances[i] += spec.EFFECTIVE_BALANCE_INCREMENT * 4
total_balance = spec.get_total_active_balance(state)
out_epoch = spec.get_current_epoch(state) + (spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR // 2)
slashed_indices = list(range(slashed_count))
# Process up to the sub-transition, then Hi-jack and get the balances.
# We just want to test the slashings.
# But we are not interested in the other balance changes during the same epoch transition.
run_epoch_processing_to(spec, state, 'process_slashings')
pre_slash_balances = list(state.balances)
slash_validators(spec, state, slashed_indices, [out_epoch] * slashed_count)
yield 'pre', state
spec.process_slashings(state)
yield 'post', state
total_penalties = state.slashed_balances[spec.get_current_epoch(state) % spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR]
total_penalties -= prev_penalties
for i in slashed_indices:
v = state.validators[i]
penalty = v.effective_balance * total_penalties * 3 // total_balance
assert state.balances[i] == pre_slash_balances[i] - penalty