adjust some helper code for randomized environment
1. randomized block helpers assume most of the validator set is not slashed 2. `randomize_state` helper slashes or exits ~1/2 of the validator set So, adjust helpers to be less aggresive with exits and slashings and to skip elements as needed if we happen to make something by a validator who has been slashed.
This commit is contained in:
parent
6da2c7a916
commit
86643d805a
|
@ -20,16 +20,20 @@ def set_some_new_deposits(spec, state, rng):
|
|||
state.validators[index].activation_eligibility_epoch = spec.get_current_epoch(state)
|
||||
|
||||
|
||||
def exit_random_validators(spec, state, rng):
|
||||
def exit_random_validators(spec, state, rng, fraction=None):
|
||||
if fraction is None:
|
||||
# Exit ~1/2
|
||||
fraction = 0.5
|
||||
|
||||
if spec.get_current_epoch(state) < 5:
|
||||
# Move epochs forward to allow for some validators already exited/withdrawable
|
||||
for _ in range(5):
|
||||
next_epoch(spec, state)
|
||||
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
# Exit ~1/2 of validators
|
||||
for index in spec.get_active_validator_indices(state, current_epoch):
|
||||
if rng.choice([True, False]):
|
||||
sampled = rng.random() < fraction
|
||||
if not sampled:
|
||||
continue
|
||||
|
||||
validator = state.validators[index]
|
||||
|
@ -41,11 +45,15 @@ def exit_random_validators(spec, state, rng):
|
|||
validator.withdrawable_epoch = current_epoch + 1
|
||||
|
||||
|
||||
def slash_random_validators(spec, state, rng):
|
||||
# Slash ~1/2 of validators
|
||||
def slash_random_validators(spec, state, rng, fraction=None):
|
||||
if fraction is None:
|
||||
# Slash ~1/2 of validators
|
||||
fraction = 0.5
|
||||
|
||||
for index in range(len(state.validators)):
|
||||
# slash at least one validator
|
||||
if index == 0 or rng.choice([True, False]):
|
||||
sampled = rng.random() < fraction
|
||||
if index == 0 or sampled:
|
||||
spec.slash_validator(state, index)
|
||||
|
||||
|
||||
|
@ -115,8 +123,8 @@ def randomize_attestation_participation(spec, state, rng=Random(8020)):
|
|||
randomize_epoch_participation(spec, state, spec.get_current_epoch(state), rng)
|
||||
|
||||
|
||||
def randomize_state(spec, state, rng=Random(8020)):
|
||||
def randomize_state(spec, state, rng=Random(8020), exit_fraction=None, slash_fraction=None):
|
||||
set_some_new_deposits(spec, state, rng)
|
||||
exit_random_validators(spec, state, rng)
|
||||
slash_random_validators(spec, state, rng)
|
||||
exit_random_validators(spec, state, rng, fraction=exit_fraction)
|
||||
slash_random_validators(spec, state, rng, fraction=slash_fraction)
|
||||
randomize_attestation_participation(spec, state, rng)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import itertools
|
||||
from random import Random
|
||||
from typing import Callable
|
||||
from tests.core.pyspec.eth2spec.test.context import default_activation_threshold, misc_balances_in_default_range
|
||||
from tests.core.pyspec.eth2spec.test.context import misc_balances_in_default_range, zero_activation_threshold
|
||||
from eth2spec.test.helpers.multi_operations import (
|
||||
build_random_block_from_state,
|
||||
)
|
||||
|
@ -24,7 +24,17 @@ from eth2spec.test.context import (
|
|||
misc_balances,
|
||||
)
|
||||
|
||||
# May need to make several attempts to find a block that does not correspond to a slashed
|
||||
# proposer with the randomization helpers...
|
||||
BLOCK_ATTEMPTS = 32
|
||||
|
||||
# primitives
|
||||
## state
|
||||
|
||||
def _randomize_state(spec, state):
|
||||
return randomize_state(spec, state, exit_fraction=0.1, slash_fraction=0.1)
|
||||
|
||||
|
||||
## epochs
|
||||
|
||||
def _epochs_until_leak(spec):
|
||||
|
@ -57,8 +67,23 @@ def _no_block(_spec, _pre_state, _signed_blocks):
|
|||
return None
|
||||
|
||||
|
||||
def _random_block_for_next_slot(spec, pre_state, _signed_blocks):
|
||||
return build_random_block_from_state(spec, pre_state)
|
||||
def _random_block(spec, state, _signed_blocks):
|
||||
"""
|
||||
Produce a random block.
|
||||
NOTE: this helper may mutate state, as it will attempt
|
||||
to produce a block over ``BLOCK_ATTEMPTS`` slots in order
|
||||
to find a valid block in the event that the proposer has already been slashed.
|
||||
"""
|
||||
block = build_random_block_from_state(spec, state)
|
||||
for _ in range(BLOCK_ATTEMPTS):
|
||||
proposer = state.validators[block.proposer_index]
|
||||
if proposer.slashed:
|
||||
next_slot(spec, state)
|
||||
block = build_random_block_from_state(spec, state)
|
||||
else:
|
||||
return block
|
||||
else:
|
||||
raise AssertionError("could not find a block with an unslashed proposer, check ``state`` input")
|
||||
|
||||
|
||||
## validations
|
||||
|
@ -105,7 +130,7 @@ def _transition_with_random_block(epochs=None, slots=None):
|
|||
number of epochs or slots before applying the random block.
|
||||
"""
|
||||
transition = {
|
||||
"block_producer": _random_block_for_next_slot,
|
||||
"block_producer": _random_block,
|
||||
}
|
||||
if epochs:
|
||||
transition.update(epochs)
|
||||
|
@ -137,7 +162,7 @@ def _randomized_scenario_setup():
|
|||
# NOTE: the block randomization function assumes at least 1 shard committee period
|
||||
# so advance the state before doing anything else.
|
||||
(_skip_epochs(_epochs_for_shard_committee_period), _no_op_validation),
|
||||
(randomize_state, ensure_state_has_validators_across_lifecycle),
|
||||
(_randomize_state, ensure_state_has_validators_across_lifecycle),
|
||||
)
|
||||
|
||||
|
||||
|
@ -272,7 +297,7 @@ def _iter_temporal(spec, callable_or_int):
|
|||
|
||||
@pytest_generate_tests_adapter
|
||||
@with_all_phases
|
||||
@with_custom_state(balances_fn=misc_balances_in_default_range, threshold_fn=default_activation_threshold)
|
||||
@with_custom_state(balances_fn=misc_balances_in_default_range, threshold_fn=zero_activation_threshold)
|
||||
@spec_test
|
||||
@single_phase
|
||||
@always_bls
|
||||
|
|
Loading…
Reference in New Issue