PR feedback. Rework `transition_to_next_epoch_and_append_blocks` a bit
This commit is contained in:
parent
a4e5d50660
commit
162711ea56
|
@ -21,7 +21,8 @@ from eth2spec.test.helpers.random import (
|
|||
#
|
||||
|
||||
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
|
||||
@with_presets([MINIMAL], reason="only test with non-full committee")
|
||||
@with_presets([MINIMAL],
|
||||
reason="only test with enough validators such that at lease one exited index is not in sync committee")
|
||||
def test_transition_with_one_fourth_exiting_validators_exit_post_fork(state,
|
||||
fork_epoch,
|
||||
spec,
|
||||
|
@ -33,7 +34,13 @@ def test_transition_with_one_fourth_exiting_validators_exit_post_fork(state,
|
|||
and are exiting but still active *after* the fork transition.
|
||||
"""
|
||||
exited_indices = exit_random_validators(
|
||||
spec, state, rng=random.Random(5566), fraction=0.25, exit_epoch=10, forward=False)
|
||||
spec,
|
||||
state,
|
||||
rng=random.Random(5566),
|
||||
fraction=0.25,
|
||||
exit_epoch=10,
|
||||
forward=False,
|
||||
)
|
||||
|
||||
transition_until_fork(spec, state, fork_epoch)
|
||||
|
||||
|
@ -85,7 +92,13 @@ def test_transition_with_one_fourth_exiting_validators_exit_at_fork(state,
|
|||
and being exited and inactive *right after* the fork transition.
|
||||
"""
|
||||
exited_indices = exit_random_validators(
|
||||
spec, state, rng=random.Random(5566), fraction=0.25, exit_epoch=fork_epoch, forward=False)
|
||||
spec,
|
||||
state,
|
||||
rng=random.Random(5566),
|
||||
fraction=0.25,
|
||||
exit_epoch=fork_epoch,
|
||||
forward=False,
|
||||
)
|
||||
|
||||
transition_until_fork(spec, state, fork_epoch)
|
||||
|
||||
|
@ -136,11 +149,11 @@ def test_transition_with_non_empty_activation_queue(state, fork_epoch, spec, pos
|
|||
"""
|
||||
transition_until_fork(spec, state, fork_epoch)
|
||||
|
||||
queuing_indices = set_some_new_deposits(spec, state, rng=random.Random(5566))
|
||||
deposited_indices = set_some_new_deposits(spec, state, rng=random.Random(5566))
|
||||
|
||||
assert spec.get_current_epoch(state) < fork_epoch
|
||||
assert len(queuing_indices) > 0
|
||||
for validator_index in queuing_indices:
|
||||
assert len(deposited_indices) > 0
|
||||
for validator_index in deposited_indices:
|
||||
assert not spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
|
||||
yield "pre", state
|
||||
|
|
|
@ -4,7 +4,7 @@ from eth2spec.test.context import (
|
|||
)
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR
|
||||
from eth2spec.test.helpers.fork_transition import (
|
||||
OperetionType,
|
||||
OperationType,
|
||||
run_transition_with_operation,
|
||||
)
|
||||
|
||||
|
@ -26,7 +26,7 @@ def test_transition_with_proposer_slashing_right_after_fork(state, fork_epoch, s
|
|||
post_spec,
|
||||
pre_tag,
|
||||
post_tag,
|
||||
operation_type=OperetionType.PROPOSER_SLASHING,
|
||||
operation_type=OperationType.PROPOSER_SLASHING,
|
||||
operation_at_slot=fork_epoch * spec.SLOTS_PER_EPOCH,
|
||||
)
|
||||
|
||||
|
@ -44,7 +44,7 @@ def test_transition_with_proposer_slashing_right_before_fork(state, fork_epoch,
|
|||
post_spec,
|
||||
pre_tag,
|
||||
post_tag,
|
||||
operation_type=OperetionType.PROPOSER_SLASHING,
|
||||
operation_type=OperationType.PROPOSER_SLASHING,
|
||||
operation_at_slot=fork_epoch * spec.SLOTS_PER_EPOCH - 1,
|
||||
)
|
||||
|
||||
|
@ -67,7 +67,7 @@ def test_transition_with_attester_slashing_right_after_fork(state, fork_epoch, s
|
|||
post_spec,
|
||||
pre_tag,
|
||||
post_tag,
|
||||
operation_type=OperetionType.ATTESTER_SLASHING,
|
||||
operation_type=OperationType.ATTESTER_SLASHING,
|
||||
operation_at_slot=fork_epoch * spec.SLOTS_PER_EPOCH,
|
||||
)
|
||||
|
||||
|
@ -85,7 +85,7 @@ def test_transition_with_attester_slashing_right_before_fork(state, fork_epoch,
|
|||
post_spec,
|
||||
pre_tag,
|
||||
post_tag,
|
||||
operation_type=OperetionType.ATTESTER_SLASHING,
|
||||
operation_type=OperationType.ATTESTER_SLASHING,
|
||||
operation_at_slot=fork_epoch * spec.SLOTS_PER_EPOCH - 1,
|
||||
)
|
||||
|
||||
|
@ -107,7 +107,7 @@ def test_transition_with_deposit_right_after_fork(state, fork_epoch, spec, post_
|
|||
post_spec,
|
||||
pre_tag,
|
||||
post_tag,
|
||||
operation_type=OperetionType.DEPOSIT,
|
||||
operation_type=OperationType.DEPOSIT,
|
||||
operation_at_slot=fork_epoch * spec.SLOTS_PER_EPOCH,
|
||||
)
|
||||
|
||||
|
@ -124,7 +124,7 @@ def test_transition_with_deposit_right_before_fork(state, fork_epoch, spec, post
|
|||
post_spec,
|
||||
pre_tag,
|
||||
post_tag,
|
||||
operation_type=OperetionType.DEPOSIT,
|
||||
operation_type=OperationType.DEPOSIT,
|
||||
operation_at_slot=fork_epoch * spec.SLOTS_PER_EPOCH - 1,
|
||||
)
|
||||
|
||||
|
@ -150,7 +150,7 @@ def test_transition_with_voluntary_exit_right_after_fork(state, fork_epoch, spec
|
|||
post_spec,
|
||||
pre_tag,
|
||||
post_tag,
|
||||
operation_type=OperetionType.VOLUNTARY_EXIT,
|
||||
operation_type=OperationType.VOLUNTARY_EXIT,
|
||||
operation_at_slot=fork_epoch * spec.SLOTS_PER_EPOCH,
|
||||
)
|
||||
|
||||
|
@ -171,6 +171,6 @@ def test_transition_with_voluntary_exit_right_before_fork(state, fork_epoch, spe
|
|||
post_spec,
|
||||
pre_tag,
|
||||
post_tag,
|
||||
operation_type=OperetionType.VOLUNTARY_EXIT,
|
||||
operation_type=OperationType.VOLUNTARY_EXIT,
|
||||
operation_at_slot=fork_epoch * spec.SLOTS_PER_EPOCH - 1,
|
||||
)
|
||||
|
|
|
@ -7,7 +7,7 @@ from eth2spec.test.context import (
|
|||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR
|
||||
from eth2spec.test.helpers.fork_transition import (
|
||||
do_altair_fork,
|
||||
state_transition_across_slots_with_ignoring_proposers,
|
||||
transition_to_next_epoch_and_append_blocks,
|
||||
transition_until_fork,
|
||||
)
|
||||
from eth2spec.test.helpers.random import (
|
||||
|
@ -16,7 +16,8 @@ from eth2spec.test.helpers.random import (
|
|||
|
||||
|
||||
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=1)
|
||||
@with_presets([MINIMAL], reason="only test with non-full committee")
|
||||
@with_presets([MINIMAL],
|
||||
reason="only test with enough validators such that at lease one exited index is not in sync committee")
|
||||
def test_transition_with_one_fourth_slashed_active_validators_pre_fork(state,
|
||||
fork_epoch,
|
||||
spec,
|
||||
|
@ -52,13 +53,16 @@ def test_transition_with_one_fourth_slashed_active_validators_pre_fork(state,
|
|||
assert any(set(slashed_pubkeys).difference(list(state.current_sync_committee.pubkeys)))
|
||||
|
||||
# continue regular state transition with new spec into next epoch
|
||||
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
|
||||
# since the proposer might have been slashed, here we only create blocks with non-slashed proposers
|
||||
blocks = []
|
||||
blocks.extend([
|
||||
post_tag(block) for block in
|
||||
state_transition_across_slots_with_ignoring_proposers(post_spec, state, to_slot, slashed_indices)
|
||||
])
|
||||
transition_to_next_epoch_and_append_blocks(
|
||||
post_spec,
|
||||
state,
|
||||
post_tag,
|
||||
blocks,
|
||||
only_last_block=True,
|
||||
ignoring_proposers=slashed_indices,
|
||||
)
|
||||
|
||||
# check post state
|
||||
for validator in state.validators:
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
from enum import Enum, auto
|
||||
import random
|
||||
|
||||
from eth2spec.test.helpers.attester_slashings import (
|
||||
get_valid_attester_slashing,
|
||||
get_valid_attester_slashing_by_indices,
|
||||
)
|
||||
from eth2spec.test.helpers.attestations import next_slots_with_attestations
|
||||
from eth2spec.test.helpers.block import (
|
||||
|
@ -26,7 +25,7 @@ from eth2spec.test.helpers.voluntary_exits import (
|
|||
)
|
||||
|
||||
|
||||
class OperetionType(Enum):
|
||||
class OperationType(Enum):
|
||||
PROPOSER_SLASHING = auto()
|
||||
ATTESTER_SLASHING = auto()
|
||||
DEPOSIT = auto()
|
||||
|
@ -104,7 +103,11 @@ def state_transition_across_slots(spec, state, to_slot, block_filter=_all_blocks
|
|||
next_slot(spec, state)
|
||||
|
||||
|
||||
def state_transition_across_slots_with_ignoring_proposers(spec, state, to_slot, ignoring_proposers):
|
||||
def state_transition_across_slots_with_ignoring_proposers(spec,
|
||||
state,
|
||||
to_slot,
|
||||
ignoring_proposers,
|
||||
only_last_block=False):
|
||||
"""
|
||||
The slashed validators can't be proposers. Here we ignore the given `ignoring_proposers`
|
||||
and ensure that the result state was computed with a block with slot >= to_slot.
|
||||
|
@ -113,6 +116,10 @@ def state_transition_across_slots_with_ignoring_proposers(spec, state, to_slot,
|
|||
|
||||
found_valid = False
|
||||
while state.slot < to_slot or not found_valid:
|
||||
if state.slot + 1 < to_slot and only_last_block:
|
||||
next_slot(spec, state)
|
||||
continue
|
||||
|
||||
future_state = state.copy()
|
||||
next_slot(spec, future_state)
|
||||
proposer_index = spec.get_beacon_proposer_index(future_state)
|
||||
|
@ -144,20 +151,6 @@ def do_altair_fork(state, spec, post_spec, fork_epoch, with_block=True, operatio
|
|||
return state, None
|
||||
|
||||
|
||||
def set_validators_exit_epoch(spec, state, exit_epoch, rng=random.Random(40404040), fraction=0.25):
|
||||
"""
|
||||
Set some valdiators' `exit_epoch` and `withdrawable_epoch`.
|
||||
"""
|
||||
selected_count = int(len(state.validators) * fraction)
|
||||
selected_indices = rng.sample(range(len(state.validators)), selected_count)
|
||||
for validator_index in selected_indices:
|
||||
state.validators[validator_index].exit_epoch = exit_epoch
|
||||
state.validators[validator_index].withdrawable_epoch = (
|
||||
exit_epoch + spec.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY
|
||||
)
|
||||
return selected_indices
|
||||
|
||||
|
||||
def transition_until_fork(spec, state, fork_epoch):
|
||||
to_slot = fork_epoch * spec.SLOTS_PER_EPOCH - 1
|
||||
transition_to(spec, state, to_slot)
|
||||
|
@ -168,7 +161,12 @@ def _transition_until_fork_minus_one(spec, state, fork_epoch):
|
|||
transition_to(spec, state, to_slot)
|
||||
|
||||
|
||||
def transition_to_next_epoch_and_append_blocks(spec, state, post_tag, blocks, only_last_block=False):
|
||||
def transition_to_next_epoch_and_append_blocks(spec,
|
||||
state,
|
||||
post_tag,
|
||||
blocks,
|
||||
only_last_block=False,
|
||||
ignoring_proposers=None):
|
||||
to_slot = spec.SLOTS_PER_EPOCH + state.slot
|
||||
|
||||
if only_last_block:
|
||||
|
@ -176,9 +174,20 @@ def transition_to_next_epoch_and_append_blocks(spec, state, post_tag, blocks, on
|
|||
else:
|
||||
block_filter = _all_blocks
|
||||
|
||||
if ignoring_proposers is None:
|
||||
result_blocks = state_transition_across_slots(spec, state, to_slot, block_filter=block_filter)
|
||||
else:
|
||||
result_blocks = state_transition_across_slots_with_ignoring_proposers(
|
||||
spec,
|
||||
state,
|
||||
to_slot,
|
||||
ignoring_proposers,
|
||||
only_last_block=only_last_block,
|
||||
)
|
||||
|
||||
blocks.extend([
|
||||
post_tag(block) for block in
|
||||
state_transition_across_slots(spec, state, to_slot, block_filter=block_filter)
|
||||
result_blocks
|
||||
])
|
||||
|
||||
|
||||
|
@ -203,27 +212,34 @@ def run_transition_with_operation(state,
|
|||
elif is_right_before_fork:
|
||||
_transition_until_fork_minus_one(spec, state, fork_epoch)
|
||||
|
||||
is_slashing_operation = operation_type in (OperationType.PROPOSER_SLASHING, OperationType.ATTESTER_SLASHING)
|
||||
# prepare operation
|
||||
selected_validator_index = None
|
||||
if operation_type == OperetionType.PROPOSER_SLASHING:
|
||||
if is_slashing_operation:
|
||||
# avoid slashing the next proposer
|
||||
future_state = state.copy()
|
||||
next_slot(spec, future_state)
|
||||
proposer_index = spec.get_beacon_proposer_index(future_state)
|
||||
selected_validator_index = (proposer_index + 1) % len(state.validators)
|
||||
proposer_slashing = get_valid_proposer_slashing(
|
||||
spec, state, slashed_index=selected_validator_index, signed_1=True, signed_2=True)
|
||||
operation_dict = {'proposer_slashings': [proposer_slashing]}
|
||||
elif operation_type == OperetionType.ATTESTER_SLASHING:
|
||||
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True)
|
||||
operation_dict = {'attester_slashings': [attester_slashing]}
|
||||
elif operation_type == OperetionType.DEPOSIT:
|
||||
if operation_type == OperationType.PROPOSER_SLASHING:
|
||||
proposer_slashing = get_valid_proposer_slashing(
|
||||
spec, state, slashed_index=selected_validator_index, signed_1=True, signed_2=True)
|
||||
operation_dict = {'proposer_slashings': [proposer_slashing]}
|
||||
else:
|
||||
# operation_type == OperationType.ATTESTER_SLASHING:
|
||||
attester_slashing = get_valid_attester_slashing_by_indices(
|
||||
spec, state,
|
||||
[selected_validator_index],
|
||||
signed_1=True, signed_2=True,
|
||||
)
|
||||
operation_dict = {'attester_slashings': [attester_slashing]}
|
||||
elif operation_type == OperationType.DEPOSIT:
|
||||
# create a new deposit
|
||||
selected_validator_index = len(state.validators)
|
||||
amount = spec.MAX_EFFECTIVE_BALANCE
|
||||
deposit = prepare_state_and_deposit(spec, state, selected_validator_index, amount, signed=True)
|
||||
operation_dict = {'deposits': [deposit]}
|
||||
elif operation_type == OperetionType.VOLUNTARY_EXIT:
|
||||
elif operation_type == OperationType.VOLUNTARY_EXIT:
|
||||
selected_validator_index = 0
|
||||
signed_exits = prepare_signed_exits(spec, state, [selected_validator_index])
|
||||
operation_dict = {'voluntary_exits': signed_exits}
|
||||
|
@ -238,22 +254,23 @@ def run_transition_with_operation(state,
|
|||
blocks.append(pre_tag(signed_block))
|
||||
|
||||
def _check_state():
|
||||
if operation_type == OperetionType.PROPOSER_SLASHING:
|
||||
if operation_type == OperationType.PROPOSER_SLASHING:
|
||||
slashed_proposer = state.validators[proposer_slashing.signed_header_1.message.proposer_index]
|
||||
assert slashed_proposer.slashed
|
||||
elif operation_type == OperetionType.ATTESTER_SLASHING:
|
||||
elif operation_type == OperationType.ATTESTER_SLASHING:
|
||||
indices = set(attester_slashing.attestation_1.attesting_indices).intersection(
|
||||
attester_slashing.attestation_2.attesting_indices
|
||||
)
|
||||
assert selected_validator_index in indices
|
||||
assert len(indices) > 0
|
||||
for validator_index in indices:
|
||||
assert state.validators[validator_index].slashed
|
||||
elif operation_type == OperetionType.DEPOSIT:
|
||||
elif operation_type == OperationType.DEPOSIT:
|
||||
assert not post_spec.is_active_validator(
|
||||
state.validators[selected_validator_index],
|
||||
post_spec.get_current_epoch(state)
|
||||
)
|
||||
elif operation_type == OperetionType.VOLUNTARY_EXIT:
|
||||
elif operation_type == OperationType.VOLUNTARY_EXIT:
|
||||
validator = state.validators[selected_validator_index]
|
||||
assert validator.exit_epoch < post_spec.FAR_FUTURE_EPOCH
|
||||
|
||||
|
@ -271,11 +288,21 @@ def run_transition_with_operation(state,
|
|||
_check_state()
|
||||
|
||||
# after the fork
|
||||
if operation_type == OperetionType.DEPOSIT:
|
||||
if operation_type == OperationType.DEPOSIT:
|
||||
_transition_until_active(post_spec, state, post_tag, blocks, selected_validator_index)
|
||||
else:
|
||||
# avoid using the slashed validators as block proposers
|
||||
ignoring_proposers = [selected_validator_index] if is_slashing_operation else None
|
||||
|
||||
# continue regular state transition with new spec into next epoch
|
||||
transition_to_next_epoch_and_append_blocks(post_spec, state, post_tag, blocks, only_last_block=True)
|
||||
transition_to_next_epoch_and_append_blocks(
|
||||
post_spec,
|
||||
state,
|
||||
post_tag,
|
||||
blocks,
|
||||
only_last_block=True,
|
||||
ignoring_proposers=ignoring_proposers,
|
||||
)
|
||||
|
||||
yield "blocks", blocks
|
||||
yield "post", state
|
||||
|
|
|
@ -7,7 +7,7 @@ from eth2spec.test.helpers.state import next_epoch
|
|||
|
||||
|
||||
def set_some_new_deposits(spec, state, rng):
|
||||
queuing_indices = []
|
||||
deposited_indices = []
|
||||
num_validators = len(state.validators)
|
||||
# Set ~1/10 to just recently deposited
|
||||
for index in range(num_validators):
|
||||
|
@ -16,24 +16,21 @@ def set_some_new_deposits(spec, state, rng):
|
|||
continue
|
||||
if rng.randrange(num_validators) < num_validators // 10:
|
||||
mock_deposit(spec, state, index)
|
||||
# Set ~half of selected to eligible for activation
|
||||
if rng.choice([True, False]):
|
||||
# Set ~half of selected to eligible for activation
|
||||
state.validators[index].activation_eligibility_epoch = spec.get_current_epoch(state)
|
||||
else:
|
||||
queuing_indices.append(index)
|
||||
return queuing_indices
|
||||
# The validators that just made a deposit
|
||||
deposited_indices.append(index)
|
||||
return deposited_indices
|
||||
|
||||
|
||||
def exit_random_validators(spec, state, rng, fraction=None, exit_epoch=None, withdrawable_epoch=None, forward=True):
|
||||
def exit_random_validators(spec, state, rng, fraction=0.5, exit_epoch=None, withdrawable_epoch=None, forward=True):
|
||||
"""
|
||||
Set some validators' exit_epoch and withdrawable_epoch.
|
||||
|
||||
If exit_epoch is configured, use the given exit_epoch. Otherwise, randomly set exit_epoch and withdrawable_epoch.
|
||||
"""
|
||||
if fraction is None:
|
||||
# Exit ~1/2
|
||||
fraction = 0.5
|
||||
|
||||
if forward:
|
||||
if spec.get_current_epoch(state) < 5:
|
||||
# Move epochs forward to allow for some validators already exited/withdrawable
|
||||
|
@ -67,11 +64,7 @@ def exit_random_validators(spec, state, rng, fraction=None, exit_epoch=None, wit
|
|||
return exited_indices
|
||||
|
||||
|
||||
def slash_random_validators(spec, state, rng, fraction=None):
|
||||
if fraction is None:
|
||||
# Slash ~1/2 of validators
|
||||
fraction = 0.5
|
||||
|
||||
def slash_random_validators(spec, state, rng, fraction=0.5):
|
||||
slashed_indices = []
|
||||
for index in range(len(state.validators)):
|
||||
# slash at least one validator
|
||||
|
|
Loading…
Reference in New Issue