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)
|
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:
|
if spec.get_current_epoch(state) < 5:
|
||||||
# Move epochs forward to allow for some validators already exited/withdrawable
|
# Move epochs forward to allow for some validators already exited/withdrawable
|
||||||
for _ in range(5):
|
for _ in range(5):
|
||||||
next_epoch(spec, state)
|
next_epoch(spec, state)
|
||||||
|
|
||||||
current_epoch = spec.get_current_epoch(state)
|
current_epoch = spec.get_current_epoch(state)
|
||||||
# Exit ~1/2 of validators
|
|
||||||
for index in spec.get_active_validator_indices(state, current_epoch):
|
for index in spec.get_active_validator_indices(state, current_epoch):
|
||||||
if rng.choice([True, False]):
|
sampled = rng.random() < fraction
|
||||||
|
if not sampled:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
validator = state.validators[index]
|
validator = state.validators[index]
|
||||||
|
@ -41,11 +45,15 @@ def exit_random_validators(spec, state, rng):
|
||||||
validator.withdrawable_epoch = current_epoch + 1
|
validator.withdrawable_epoch = current_epoch + 1
|
||||||
|
|
||||||
|
|
||||||
def slash_random_validators(spec, state, rng):
|
def slash_random_validators(spec, state, rng, fraction=None):
|
||||||
|
if fraction is None:
|
||||||
# Slash ~1/2 of validators
|
# Slash ~1/2 of validators
|
||||||
|
fraction = 0.5
|
||||||
|
|
||||||
for index in range(len(state.validators)):
|
for index in range(len(state.validators)):
|
||||||
# slash at least one validator
|
# 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)
|
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)
|
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)
|
set_some_new_deposits(spec, state, rng)
|
||||||
exit_random_validators(spec, state, rng)
|
exit_random_validators(spec, state, rng, fraction=exit_fraction)
|
||||||
slash_random_validators(spec, state, rng)
|
slash_random_validators(spec, state, rng, fraction=slash_fraction)
|
||||||
randomize_attestation_participation(spec, state, rng)
|
randomize_attestation_participation(spec, state, rng)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import itertools
|
import itertools
|
||||||
from random import Random
|
from random import Random
|
||||||
from typing import Callable
|
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 (
|
from eth2spec.test.helpers.multi_operations import (
|
||||||
build_random_block_from_state,
|
build_random_block_from_state,
|
||||||
)
|
)
|
||||||
|
@ -24,7 +24,17 @@ from eth2spec.test.context import (
|
||||||
misc_balances,
|
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
|
# primitives
|
||||||
|
## state
|
||||||
|
|
||||||
|
def _randomize_state(spec, state):
|
||||||
|
return randomize_state(spec, state, exit_fraction=0.1, slash_fraction=0.1)
|
||||||
|
|
||||||
|
|
||||||
## epochs
|
## epochs
|
||||||
|
|
||||||
def _epochs_until_leak(spec):
|
def _epochs_until_leak(spec):
|
||||||
|
@ -57,8 +67,23 @@ def _no_block(_spec, _pre_state, _signed_blocks):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _random_block_for_next_slot(spec, pre_state, _signed_blocks):
|
def _random_block(spec, state, _signed_blocks):
|
||||||
return build_random_block_from_state(spec, pre_state)
|
"""
|
||||||
|
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
|
## validations
|
||||||
|
@ -105,7 +130,7 @@ def _transition_with_random_block(epochs=None, slots=None):
|
||||||
number of epochs or slots before applying the random block.
|
number of epochs or slots before applying the random block.
|
||||||
"""
|
"""
|
||||||
transition = {
|
transition = {
|
||||||
"block_producer": _random_block_for_next_slot,
|
"block_producer": _random_block,
|
||||||
}
|
}
|
||||||
if epochs:
|
if epochs:
|
||||||
transition.update(epochs)
|
transition.update(epochs)
|
||||||
|
@ -137,7 +162,7 @@ def _randomized_scenario_setup():
|
||||||
# NOTE: the block randomization function assumes at least 1 shard committee period
|
# NOTE: the block randomization function assumes at least 1 shard committee period
|
||||||
# so advance the state before doing anything else.
|
# so advance the state before doing anything else.
|
||||||
(_skip_epochs(_epochs_for_shard_committee_period), _no_op_validation),
|
(_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
|
@pytest_generate_tests_adapter
|
||||||
@with_all_phases
|
@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
|
@spec_test
|
||||||
@single_phase
|
@single_phase
|
||||||
@always_bls
|
@always_bls
|
||||||
|
|
Loading…
Reference in New Issue