Merge pull request #2664 from ethereum/new-transition-test-cases
Add new Altair transition tests
This commit is contained in:
commit
bf01e11cae
|
@ -1,7 +1,10 @@
|
|||
from random import Random
|
||||
|
||||
from eth2spec.test.context import spec_state_test, with_altair_and_later
|
||||
from eth2spec.test.helpers.inactivity_scores import randomize_inactivity_scores, zero_inactivity_scores
|
||||
from eth2spec.test.helpers.inactivity_scores import (
|
||||
randomize_inactivity_scores,
|
||||
zero_inactivity_scores,
|
||||
)
|
||||
from eth2spec.test.helpers.state import (
|
||||
next_epoch,
|
||||
next_epoch_via_block,
|
||||
|
|
|
@ -0,0 +1,206 @@
|
|||
import random
|
||||
from eth2spec.test.context import (
|
||||
MINIMAL,
|
||||
fork_transition_test,
|
||||
with_presets,
|
||||
)
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR
|
||||
from eth2spec.test.helpers.fork_transition import (
|
||||
do_altair_fork,
|
||||
transition_until_fork,
|
||||
transition_to_next_epoch_and_append_blocks,
|
||||
)
|
||||
from eth2spec.test.helpers.random import (
|
||||
exit_random_validators,
|
||||
set_some_activations,
|
||||
set_some_new_deposits,
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# Exit
|
||||
#
|
||||
|
||||
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
|
||||
@with_presets([MINIMAL],
|
||||
reason="only test with enough validators such that at least one exited index is not in sync committee")
|
||||
def test_transition_with_one_fourth_exiting_validators_exit_post_fork(state,
|
||||
fork_epoch,
|
||||
spec,
|
||||
post_spec,
|
||||
pre_tag,
|
||||
post_tag):
|
||||
"""
|
||||
1/4 validators initiated voluntary exit before the fork,
|
||||
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,
|
||||
from_epoch=spec.get_current_epoch(state),
|
||||
)
|
||||
|
||||
transition_until_fork(spec, state, fork_epoch)
|
||||
|
||||
# check pre state
|
||||
assert len(exited_indices) > 0
|
||||
for index in exited_indices:
|
||||
validator = state.validators[index]
|
||||
assert not validator.slashed
|
||||
assert fork_epoch < validator.exit_epoch < spec.FAR_FUTURE_EPOCH
|
||||
assert spec.is_active_validator(validator, spec.get_current_epoch(state))
|
||||
assert not spec.is_in_inactivity_leak(state)
|
||||
assert spec.get_current_epoch(state) < fork_epoch
|
||||
|
||||
yield "pre", state
|
||||
|
||||
# irregular state transition to handle fork:
|
||||
blocks = []
|
||||
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
|
||||
blocks.append(post_tag(block))
|
||||
|
||||
# ensure that some of the current sync committee members are exiting
|
||||
exited_pubkeys = [state.validators[index].pubkey for index in exited_indices]
|
||||
assert any(set(exited_pubkeys).intersection(list(state.current_sync_committee.pubkeys)))
|
||||
assert any(set(exited_pubkeys).difference(list(state.current_sync_committee.pubkeys)))
|
||||
|
||||
# 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)
|
||||
|
||||
# check state
|
||||
for index in exited_indices:
|
||||
validator = state.validators[index]
|
||||
assert not validator.slashed
|
||||
assert post_spec.is_active_validator(validator, post_spec.get_current_epoch(state))
|
||||
assert not post_spec.is_in_inactivity_leak(state)
|
||||
|
||||
yield "blocks", blocks
|
||||
yield "post", state
|
||||
|
||||
|
||||
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
|
||||
def test_transition_with_one_fourth_exiting_validators_exit_at_fork(state,
|
||||
fork_epoch,
|
||||
spec,
|
||||
post_spec,
|
||||
pre_tag,
|
||||
post_tag):
|
||||
"""
|
||||
1/4 validators initiated voluntary exit before the fork,
|
||||
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,
|
||||
from_epoch=spec.get_current_epoch(state),
|
||||
)
|
||||
|
||||
transition_until_fork(spec, state, fork_epoch)
|
||||
|
||||
# check pre state
|
||||
assert len(exited_indices) > 0
|
||||
for index in exited_indices:
|
||||
validator = state.validators[index]
|
||||
assert not validator.slashed
|
||||
assert fork_epoch == validator.exit_epoch < spec.FAR_FUTURE_EPOCH
|
||||
assert spec.is_active_validator(validator, spec.get_current_epoch(state))
|
||||
assert not spec.is_in_inactivity_leak(state)
|
||||
assert spec.get_current_epoch(state) < fork_epoch
|
||||
|
||||
yield "pre", state
|
||||
|
||||
# irregular state transition to handle fork:
|
||||
blocks = []
|
||||
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
|
||||
blocks.append(post_tag(block))
|
||||
|
||||
# check post transition state
|
||||
for index in exited_indices:
|
||||
validator = state.validators[index]
|
||||
assert not validator.slashed
|
||||
assert not post_spec.is_active_validator(validator, post_spec.get_current_epoch(state))
|
||||
assert not post_spec.is_in_inactivity_leak(state)
|
||||
|
||||
# ensure that none of the current sync committee members are exited validators
|
||||
exited_pubkeys = [state.validators[index].pubkey for index in exited_indices]
|
||||
assert not any(set(exited_pubkeys).intersection(list(state.current_sync_committee.pubkeys)))
|
||||
|
||||
# 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)
|
||||
|
||||
yield "blocks", blocks
|
||||
yield "post", state
|
||||
|
||||
|
||||
#
|
||||
# Activation
|
||||
#
|
||||
|
||||
|
||||
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
|
||||
def test_transition_with_non_empty_activation_queue(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
|
||||
"""
|
||||
Create some deposits before the transition
|
||||
"""
|
||||
transition_until_fork(spec, state, fork_epoch)
|
||||
|
||||
deposited_indices = set_some_new_deposits(spec, state, rng=random.Random(5566))
|
||||
|
||||
assert spec.get_current_epoch(state) < fork_epoch
|
||||
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
|
||||
|
||||
# irregular state transition to handle fork:
|
||||
blocks = []
|
||||
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
|
||||
blocks.append(post_tag(block))
|
||||
|
||||
# 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)
|
||||
|
||||
yield "blocks", blocks
|
||||
yield "post", state
|
||||
|
||||
|
||||
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
|
||||
def test_transition_with_activation_at_fork_epoch(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
|
||||
"""
|
||||
Create some deposits before the transition
|
||||
"""
|
||||
transition_until_fork(spec, state, fork_epoch)
|
||||
|
||||
selected_indices = set_some_activations(spec, state, rng=random.Random(5566), activation_epoch=fork_epoch)
|
||||
|
||||
assert spec.get_current_epoch(state) < fork_epoch
|
||||
assert len(selected_indices) > 0
|
||||
for validator_index in selected_indices:
|
||||
validator = state.validators[validator_index]
|
||||
assert not spec.is_active_validator(validator, spec.get_current_epoch(state))
|
||||
assert validator.activation_epoch == fork_epoch
|
||||
|
||||
yield "pre", state
|
||||
|
||||
# irregular state transition to handle fork:
|
||||
blocks = []
|
||||
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
|
||||
blocks.append(post_tag(block))
|
||||
|
||||
# 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)
|
||||
|
||||
# now they are active
|
||||
for validator_index in selected_indices:
|
||||
validator = state.validators[validator_index]
|
||||
assert post_spec.is_active_validator(validator, post_spec.get_current_epoch(state))
|
||||
|
||||
yield "blocks", blocks
|
||||
yield "post", state
|
|
@ -0,0 +1,63 @@
|
|||
from eth2spec.test.context import fork_transition_test
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR
|
||||
from eth2spec.test.helpers.fork_transition import (
|
||||
do_altair_fork,
|
||||
transition_until_fork,
|
||||
transition_to_next_epoch_and_append_blocks,
|
||||
)
|
||||
|
||||
|
||||
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=7)
|
||||
def test_transition_with_leaking_pre_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
|
||||
"""
|
||||
Leaking starts at epoch 6 (MIN_EPOCHS_TO_INACTIVITY_PENALTY + 2).
|
||||
The leaking starts before the fork transition in this case.
|
||||
"""
|
||||
transition_until_fork(spec, state, fork_epoch)
|
||||
|
||||
assert spec.is_in_inactivity_leak(state)
|
||||
assert spec.get_current_epoch(state) < fork_epoch
|
||||
|
||||
yield "pre", state
|
||||
|
||||
# irregular state transition to handle fork:
|
||||
blocks = []
|
||||
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
|
||||
blocks.append(post_tag(block))
|
||||
|
||||
# check post transition state
|
||||
assert spec.is_in_inactivity_leak(state)
|
||||
|
||||
# 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)
|
||||
|
||||
yield "blocks", blocks
|
||||
yield "post", state
|
||||
|
||||
|
||||
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=6)
|
||||
def test_transition_with_leaking_at_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
|
||||
"""
|
||||
Leaking starts at epoch 6 (MIN_EPOCHS_TO_INACTIVITY_PENALTY + 2).
|
||||
The leaking starts at the fork transition in this case.
|
||||
"""
|
||||
transition_until_fork(spec, state, fork_epoch)
|
||||
|
||||
assert not spec.is_in_inactivity_leak(state)
|
||||
assert spec.get_current_epoch(state) < fork_epoch
|
||||
|
||||
yield "pre", state
|
||||
|
||||
# irregular state transition to handle fork:
|
||||
blocks = []
|
||||
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
|
||||
blocks.append(post_tag(block))
|
||||
|
||||
# check post transition state
|
||||
assert spec.is_in_inactivity_leak(state)
|
||||
|
||||
# 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)
|
||||
|
||||
yield "blocks", blocks
|
||||
yield "post", state
|
|
@ -0,0 +1,176 @@
|
|||
from eth2spec.test.context import (
|
||||
always_bls,
|
||||
fork_transition_test,
|
||||
)
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR
|
||||
from eth2spec.test.helpers.fork_transition import (
|
||||
OperationType,
|
||||
run_transition_with_operation,
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# PROPOSER_SLASHING
|
||||
#
|
||||
|
||||
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
|
||||
@always_bls
|
||||
def test_transition_with_proposer_slashing_right_after_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
|
||||
"""
|
||||
Create an attester slashing right *after* the transition
|
||||
"""
|
||||
yield from run_transition_with_operation(
|
||||
state,
|
||||
fork_epoch,
|
||||
spec,
|
||||
post_spec,
|
||||
pre_tag,
|
||||
post_tag,
|
||||
operation_type=OperationType.PROPOSER_SLASHING,
|
||||
operation_at_slot=fork_epoch * spec.SLOTS_PER_EPOCH,
|
||||
)
|
||||
|
||||
|
||||
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
|
||||
@always_bls
|
||||
def test_transition_with_proposer_slashing_right_before_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
|
||||
"""
|
||||
Create an attester slashing right *before* the transition
|
||||
"""
|
||||
yield from run_transition_with_operation(
|
||||
state,
|
||||
fork_epoch,
|
||||
spec,
|
||||
post_spec,
|
||||
pre_tag,
|
||||
post_tag,
|
||||
operation_type=OperationType.PROPOSER_SLASHING,
|
||||
operation_at_slot=fork_epoch * spec.SLOTS_PER_EPOCH - 1,
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# ATTESTER_SLASHING
|
||||
#
|
||||
|
||||
|
||||
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
|
||||
@always_bls
|
||||
def test_transition_with_attester_slashing_right_after_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
|
||||
"""
|
||||
Create an attester slashing right *after* the transition
|
||||
"""
|
||||
yield from run_transition_with_operation(
|
||||
state,
|
||||
fork_epoch,
|
||||
spec,
|
||||
post_spec,
|
||||
pre_tag,
|
||||
post_tag,
|
||||
operation_type=OperationType.ATTESTER_SLASHING,
|
||||
operation_at_slot=fork_epoch * spec.SLOTS_PER_EPOCH,
|
||||
)
|
||||
|
||||
|
||||
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
|
||||
@always_bls
|
||||
def test_transition_with_attester_slashing_right_before_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
|
||||
"""
|
||||
Create an attester slashing right *after* the transition
|
||||
"""
|
||||
yield from run_transition_with_operation(
|
||||
state,
|
||||
fork_epoch,
|
||||
spec,
|
||||
post_spec,
|
||||
pre_tag,
|
||||
post_tag,
|
||||
operation_type=OperationType.ATTESTER_SLASHING,
|
||||
operation_at_slot=fork_epoch * spec.SLOTS_PER_EPOCH - 1,
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# DEPOSIT
|
||||
#
|
||||
|
||||
|
||||
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
|
||||
def test_transition_with_deposit_right_after_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
|
||||
"""
|
||||
Create a deposit right *after* the transition
|
||||
"""
|
||||
yield from run_transition_with_operation(
|
||||
state,
|
||||
fork_epoch,
|
||||
spec,
|
||||
post_spec,
|
||||
pre_tag,
|
||||
post_tag,
|
||||
operation_type=OperationType.DEPOSIT,
|
||||
operation_at_slot=fork_epoch * spec.SLOTS_PER_EPOCH,
|
||||
)
|
||||
|
||||
|
||||
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
|
||||
def test_transition_with_deposit_right_before_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
|
||||
"""
|
||||
Create a deposit right *before* the transition
|
||||
"""
|
||||
yield from run_transition_with_operation(
|
||||
state,
|
||||
fork_epoch,
|
||||
spec,
|
||||
post_spec,
|
||||
pre_tag,
|
||||
post_tag,
|
||||
operation_type=OperationType.DEPOSIT,
|
||||
operation_at_slot=fork_epoch * spec.SLOTS_PER_EPOCH - 1,
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# VOLUNTARY_EXIT
|
||||
#
|
||||
|
||||
|
||||
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=260)
|
||||
def test_transition_with_voluntary_exit_right_after_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
|
||||
"""
|
||||
Create a voluntary exit right *after* the transition.
|
||||
fork_epoch=260 because mainnet `SHARD_COMMITTEE_PERIOD` is 256 epochs.
|
||||
"""
|
||||
# Fast forward to the future epoch so that validator can do voluntary exit
|
||||
state.slot = spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
|
||||
|
||||
yield from run_transition_with_operation(
|
||||
state,
|
||||
fork_epoch,
|
||||
spec,
|
||||
post_spec,
|
||||
pre_tag,
|
||||
post_tag,
|
||||
operation_type=OperationType.VOLUNTARY_EXIT,
|
||||
operation_at_slot=fork_epoch * spec.SLOTS_PER_EPOCH,
|
||||
)
|
||||
|
||||
|
||||
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=260)
|
||||
def test_transition_with_voluntary_exit_right_before_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
|
||||
"""
|
||||
Create a voluntary exit right *before* the transition.
|
||||
fork_epoch=260 because mainnet `SHARD_COMMITTEE_PERIOD` is 256 epochs.
|
||||
"""
|
||||
# Fast forward to the future epoch so that validator can do voluntary exit
|
||||
state.slot = spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
|
||||
|
||||
yield from run_transition_with_operation(
|
||||
state,
|
||||
fork_epoch,
|
||||
spec,
|
||||
post_spec,
|
||||
pre_tag,
|
||||
post_tag,
|
||||
operation_type=OperationType.VOLUNTARY_EXIT,
|
||||
operation_at_slot=fork_epoch * spec.SLOTS_PER_EPOCH - 1,
|
||||
)
|
|
@ -0,0 +1,73 @@
|
|||
import random
|
||||
from eth2spec.test.context import (
|
||||
MINIMAL,
|
||||
fork_transition_test,
|
||||
with_presets,
|
||||
)
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR
|
||||
from eth2spec.test.helpers.fork_transition import (
|
||||
do_altair_fork,
|
||||
transition_to_next_epoch_and_append_blocks,
|
||||
transition_until_fork,
|
||||
)
|
||||
from eth2spec.test.helpers.random import (
|
||||
slash_random_validators,
|
||||
)
|
||||
|
||||
|
||||
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=1)
|
||||
@with_presets([MINIMAL],
|
||||
reason="only test with enough validators such that at least one exited index is not in sync committee")
|
||||
def test_transition_with_one_fourth_slashed_active_validators_pre_fork(state,
|
||||
fork_epoch,
|
||||
spec,
|
||||
post_spec,
|
||||
pre_tag,
|
||||
post_tag):
|
||||
"""
|
||||
1/4 validators are slashed but still active at the fork transition.
|
||||
"""
|
||||
# slash 1/4 validators
|
||||
slashed_indices = slash_random_validators(spec, state, rng=random.Random(5566), fraction=0.25)
|
||||
assert len(slashed_indices) > 0
|
||||
|
||||
# check if some validators are slashed but still active
|
||||
for validator_index in slashed_indices:
|
||||
validator = state.validators[validator_index]
|
||||
assert validator.slashed
|
||||
assert spec.is_active_validator(validator, spec.get_current_epoch(state))
|
||||
assert not spec.is_in_inactivity_leak(state)
|
||||
|
||||
transition_until_fork(spec, state, fork_epoch)
|
||||
|
||||
assert spec.get_current_epoch(state) < fork_epoch
|
||||
|
||||
yield "pre", state
|
||||
|
||||
# irregular state transition to handle fork:
|
||||
state, _ = do_altair_fork(state, spec, post_spec, fork_epoch, with_block=False)
|
||||
|
||||
# ensure that some of the current sync committee members are slashed
|
||||
slashed_pubkeys = [state.validators[index].pubkey for index in slashed_indices]
|
||||
assert any(set(slashed_pubkeys).intersection(list(state.current_sync_committee.pubkeys)))
|
||||
assert any(set(slashed_pubkeys).difference(list(state.current_sync_committee.pubkeys)))
|
||||
|
||||
# continue regular state transition with new spec into next epoch
|
||||
# since the proposer might have been slashed, here we only create blocks with non-slashed proposers
|
||||
blocks = []
|
||||
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:
|
||||
assert post_spec.is_active_validator(validator, post_spec.get_current_epoch(state))
|
||||
assert not post_spec.is_in_inactivity_leak(state)
|
||||
|
||||
yield "blocks", blocks
|
||||
yield "post", state
|
|
@ -1,83 +1,18 @@
|
|||
import random
|
||||
from eth2spec.test.context import fork_transition_test
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR
|
||||
from eth2spec.test.helpers.state import state_transition_and_sign_block, next_slot, next_epoch_via_signed_block
|
||||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot, build_empty_block, sign_block
|
||||
from eth2spec.test.helpers.state import (
|
||||
next_epoch_via_signed_block,
|
||||
)
|
||||
from eth2spec.test.helpers.attestations import next_slots_with_attestations
|
||||
|
||||
|
||||
def _state_transition_and_sign_block_at_slot(spec, state):
|
||||
"""
|
||||
Cribbed from ``transition_unsigned_block`` helper
|
||||
where the early parts of the state transition have already
|
||||
been applied to ``state``.
|
||||
|
||||
Used to produce a block during an irregular state transition.
|
||||
"""
|
||||
block = build_empty_block(spec, state)
|
||||
|
||||
assert state.latest_block_header.slot < block.slot
|
||||
assert state.slot == block.slot
|
||||
spec.process_block(state, block)
|
||||
block.state_root = state.hash_tree_root()
|
||||
return sign_block(spec, state, block)
|
||||
|
||||
|
||||
def _all_blocks(_):
|
||||
return True
|
||||
|
||||
|
||||
def _skip_slots(*slots):
|
||||
"""
|
||||
Skip making a block if its slot is
|
||||
passed as an argument to this filter
|
||||
"""
|
||||
def f(state_at_prior_slot):
|
||||
return state_at_prior_slot.slot + 1 not in slots
|
||||
return f
|
||||
|
||||
|
||||
def _no_blocks(_):
|
||||
return False
|
||||
|
||||
|
||||
def _only_at(slot):
|
||||
"""
|
||||
Only produce a block if its slot is ``slot``.
|
||||
"""
|
||||
def f(state_at_prior_slot):
|
||||
return state_at_prior_slot.slot + 1 == slot
|
||||
return f
|
||||
|
||||
|
||||
def _state_transition_across_slots(spec, state, to_slot, block_filter=_all_blocks):
|
||||
assert state.slot < to_slot
|
||||
while state.slot < to_slot:
|
||||
should_make_block = block_filter(state)
|
||||
if should_make_block:
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
yield signed_block
|
||||
else:
|
||||
next_slot(spec, state)
|
||||
|
||||
|
||||
def _do_altair_fork(state, spec, post_spec, fork_epoch, with_block=True):
|
||||
spec.process_slots(state, state.slot + 1)
|
||||
|
||||
assert state.slot % spec.SLOTS_PER_EPOCH == 0
|
||||
assert spec.get_current_epoch(state) == fork_epoch
|
||||
|
||||
state = post_spec.upgrade_to_altair(state)
|
||||
|
||||
assert state.fork.epoch == fork_epoch
|
||||
assert state.fork.previous_version == post_spec.config.GENESIS_FORK_VERSION
|
||||
assert state.fork.current_version == post_spec.config.ALTAIR_FORK_VERSION
|
||||
|
||||
if with_block:
|
||||
return state, _state_transition_and_sign_block_at_slot(post_spec, state)
|
||||
else:
|
||||
return state, None
|
||||
from eth2spec.test.helpers.fork_transition import (
|
||||
do_altair_fork,
|
||||
no_blocks,
|
||||
only_at,
|
||||
skip_slots,
|
||||
state_transition_across_slots,
|
||||
transition_to_next_epoch_and_append_blocks,
|
||||
)
|
||||
|
||||
|
||||
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=2)
|
||||
|
@ -95,19 +30,15 @@ def test_normal_transition(state, fork_epoch, spec, post_spec, pre_tag, post_tag
|
|||
blocks = []
|
||||
blocks.extend([
|
||||
pre_tag(block) for block in
|
||||
_state_transition_across_slots(spec, state, to_slot)
|
||||
state_transition_across_slots(spec, state, to_slot)
|
||||
])
|
||||
|
||||
# irregular state transition to handle fork:
|
||||
state, block = _do_altair_fork(state, spec, post_spec, fork_epoch)
|
||||
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
|
||||
blocks.append(post_tag(block))
|
||||
|
||||
# continue regular state transition with new spec into next epoch
|
||||
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
|
||||
blocks.extend([
|
||||
post_tag(block) for block in
|
||||
_state_transition_across_slots(post_spec, state, to_slot)
|
||||
])
|
||||
transition_to_next_epoch_and_append_blocks(post_spec, state, post_tag, blocks)
|
||||
|
||||
assert state.slot % post_spec.SLOTS_PER_EPOCH == 0
|
||||
assert post_spec.get_current_epoch(state) == fork_epoch + 1
|
||||
|
@ -136,18 +67,14 @@ def test_transition_missing_first_post_block(state, fork_epoch, spec, post_spec,
|
|||
blocks = []
|
||||
blocks.extend([
|
||||
pre_tag(block) for block in
|
||||
_state_transition_across_slots(spec, state, to_slot)
|
||||
state_transition_across_slots(spec, state, to_slot)
|
||||
])
|
||||
|
||||
# irregular state transition to handle fork:
|
||||
state, _ = _do_altair_fork(state, spec, post_spec, fork_epoch, with_block=False)
|
||||
state, _ = do_altair_fork(state, spec, post_spec, fork_epoch, with_block=False)
|
||||
|
||||
# continue regular state transition with new spec into next epoch
|
||||
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
|
||||
blocks.extend([
|
||||
post_tag(block) for block in
|
||||
_state_transition_across_slots(post_spec, state, to_slot)
|
||||
])
|
||||
transition_to_next_epoch_and_append_blocks(post_spec, state, post_tag, blocks)
|
||||
|
||||
assert state.slot % post_spec.SLOTS_PER_EPOCH == 0
|
||||
assert post_spec.get_current_epoch(state) == fork_epoch + 1
|
||||
|
@ -178,19 +105,15 @@ def test_transition_missing_last_pre_fork_block(state, fork_epoch, spec, post_sp
|
|||
blocks = []
|
||||
blocks.extend([
|
||||
pre_tag(block) for block in
|
||||
_state_transition_across_slots(spec, state, to_slot, block_filter=_skip_slots(last_slot_of_pre_fork))
|
||||
state_transition_across_slots(spec, state, to_slot, block_filter=skip_slots(last_slot_of_pre_fork))
|
||||
])
|
||||
|
||||
# irregular state transition to handle fork:
|
||||
state, block = _do_altair_fork(state, spec, post_spec, fork_epoch)
|
||||
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
|
||||
blocks.append(post_tag(block))
|
||||
|
||||
# continue regular state transition with new spec into next epoch
|
||||
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
|
||||
blocks.extend([
|
||||
post_tag(block) for block in
|
||||
_state_transition_across_slots(post_spec, state, to_slot)
|
||||
])
|
||||
transition_to_next_epoch_and_append_blocks(post_spec, state, post_tag, blocks)
|
||||
|
||||
assert state.slot % post_spec.SLOTS_PER_EPOCH == 0
|
||||
assert post_spec.get_current_epoch(state) == fork_epoch + 1
|
||||
|
@ -221,18 +144,18 @@ def test_transition_only_blocks_post_fork(state, fork_epoch, spec, post_spec, pr
|
|||
blocks = []
|
||||
blocks.extend([
|
||||
pre_tag(block) for block in
|
||||
_state_transition_across_slots(spec, state, to_slot, block_filter=_no_blocks)
|
||||
state_transition_across_slots(spec, state, to_slot, block_filter=no_blocks)
|
||||
])
|
||||
|
||||
# irregular state transition to handle fork:
|
||||
state, _ = _do_altair_fork(state, spec, post_spec, fork_epoch, with_block=False)
|
||||
state, _ = do_altair_fork(state, spec, post_spec, fork_epoch, with_block=False)
|
||||
|
||||
# continue regular state transition with new spec into next epoch
|
||||
to_slot = post_spec.SLOTS_PER_EPOCH + state.slot
|
||||
last_slot = (fork_epoch + 1) * post_spec.SLOTS_PER_EPOCH
|
||||
blocks.extend([
|
||||
post_tag(block) for block in
|
||||
_state_transition_across_slots(post_spec, state, to_slot, block_filter=_only_at(last_slot))
|
||||
state_transition_across_slots(post_spec, state, to_slot, block_filter=only_at(last_slot))
|
||||
])
|
||||
|
||||
assert state.slot % post_spec.SLOTS_PER_EPOCH == 0
|
||||
|
@ -292,7 +215,7 @@ def _run_transition_test_with_attestations(state,
|
|||
assert (state.slot + 1) % spec.SLOTS_PER_EPOCH == 0
|
||||
|
||||
# irregular state transition to handle fork:
|
||||
state, block = _do_altair_fork(state, spec, post_spec, fork_epoch)
|
||||
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
|
||||
blocks.append(post_tag(block))
|
||||
|
||||
# continue regular state transition with new spec into next epoch
|
||||
|
@ -405,11 +328,11 @@ def test_transition_with_no_attestations_until_after_fork(state, fork_epoch, spe
|
|||
blocks = []
|
||||
blocks.extend([
|
||||
pre_tag(block) for block in
|
||||
_state_transition_across_slots(spec, state, to_slot)
|
||||
state_transition_across_slots(spec, state, to_slot)
|
||||
])
|
||||
|
||||
# irregular state transition to handle fork:
|
||||
state, block = _do_altair_fork(state, spec, post_spec, fork_epoch)
|
||||
state, block = do_altair_fork(state, spec, post_spec, fork_epoch)
|
||||
blocks.append(post_tag(block))
|
||||
|
||||
# continue regular state transition but add attestations
|
||||
|
|
|
@ -0,0 +1,335 @@
|
|||
from enum import Enum, auto
|
||||
|
||||
from eth2spec.test.helpers.attester_slashings import (
|
||||
get_valid_attester_slashing_by_indices,
|
||||
)
|
||||
from eth2spec.test.helpers.attestations import next_slots_with_attestations
|
||||
from eth2spec.test.helpers.block import (
|
||||
build_empty_block_for_next_slot,
|
||||
build_empty_block,
|
||||
sign_block,
|
||||
)
|
||||
from eth2spec.test.helpers.deposits import (
|
||||
prepare_state_and_deposit,
|
||||
)
|
||||
from eth2spec.test.helpers.proposer_slashings import (
|
||||
get_valid_proposer_slashing,
|
||||
)
|
||||
from eth2spec.test.helpers.state import (
|
||||
next_slot,
|
||||
state_transition_and_sign_block,
|
||||
transition_to,
|
||||
)
|
||||
from eth2spec.test.helpers.voluntary_exits import (
|
||||
prepare_signed_exits,
|
||||
)
|
||||
|
||||
|
||||
class OperationType(Enum):
|
||||
PROPOSER_SLASHING = auto()
|
||||
ATTESTER_SLASHING = auto()
|
||||
DEPOSIT = auto()
|
||||
VOLUNTARY_EXIT = auto()
|
||||
|
||||
|
||||
def _set_operations_by_dict(block, operation_dict):
|
||||
for key, value in operation_dict.items():
|
||||
setattr(block.body, key, value)
|
||||
|
||||
|
||||
def _state_transition_and_sign_block_at_slot(spec,
|
||||
state,
|
||||
operation_dict=None):
|
||||
"""
|
||||
Cribbed from ``transition_unsigned_block`` helper
|
||||
where the early parts of the state transition have already
|
||||
been applied to ``state``.
|
||||
|
||||
Used to produce a block during an irregular state transition.
|
||||
|
||||
The optional `operation_dict` is a dict of {'<BeaconBlockBody field>': <value>}.
|
||||
This is used for assigning the block operations.
|
||||
p.s. we can't just pass `body` and assign it because randao_reveal and eth1_data was set in `build_empty_block`
|
||||
Thus use dict to pass operations.
|
||||
"""
|
||||
block = build_empty_block(spec, state)
|
||||
|
||||
if operation_dict:
|
||||
_set_operations_by_dict(block, operation_dict)
|
||||
|
||||
assert state.latest_block_header.slot < block.slot
|
||||
assert state.slot == block.slot
|
||||
spec.process_block(state, block)
|
||||
block.state_root = state.hash_tree_root()
|
||||
return sign_block(spec, state, block)
|
||||
|
||||
|
||||
def _all_blocks(_):
|
||||
return True
|
||||
|
||||
|
||||
def skip_slots(*slots):
|
||||
"""
|
||||
Skip making a block if its slot is
|
||||
passed as an argument to this filter
|
||||
"""
|
||||
def f(state_at_prior_slot):
|
||||
return state_at_prior_slot.slot + 1 not in slots
|
||||
return f
|
||||
|
||||
|
||||
def no_blocks(_):
|
||||
return False
|
||||
|
||||
|
||||
def only_at(slot):
|
||||
"""
|
||||
Only produce a block if its slot is ``slot``.
|
||||
"""
|
||||
def f(state_at_prior_slot):
|
||||
return state_at_prior_slot.slot + 1 == slot
|
||||
return f
|
||||
|
||||
|
||||
def state_transition_across_slots(spec, state, to_slot, block_filter=_all_blocks):
|
||||
assert state.slot < to_slot
|
||||
while state.slot < to_slot:
|
||||
should_make_block = block_filter(state)
|
||||
if should_make_block:
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
yield signed_block
|
||||
else:
|
||||
next_slot(spec, state)
|
||||
|
||||
|
||||
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.
|
||||
"""
|
||||
assert state.slot < 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)
|
||||
if proposer_index not in ignoring_proposers:
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
yield signed_block
|
||||
if state.slot >= to_slot:
|
||||
found_valid = True
|
||||
else:
|
||||
next_slot(spec, state)
|
||||
|
||||
|
||||
def do_altair_fork(state, spec, post_spec, fork_epoch, with_block=True, operation_dict=None):
|
||||
spec.process_slots(state, state.slot + 1)
|
||||
|
||||
assert state.slot % spec.SLOTS_PER_EPOCH == 0
|
||||
assert spec.get_current_epoch(state) == fork_epoch
|
||||
|
||||
state = post_spec.upgrade_to_altair(state)
|
||||
|
||||
assert state.fork.epoch == fork_epoch
|
||||
assert state.fork.previous_version == post_spec.config.GENESIS_FORK_VERSION
|
||||
assert state.fork.current_version == post_spec.config.ALTAIR_FORK_VERSION
|
||||
|
||||
if with_block:
|
||||
return state, _state_transition_and_sign_block_at_slot(post_spec, state, operation_dict=operation_dict)
|
||||
else:
|
||||
return state, None
|
||||
|
||||
|
||||
def transition_until_fork(spec, state, fork_epoch):
|
||||
to_slot = fork_epoch * spec.SLOTS_PER_EPOCH - 1
|
||||
transition_to(spec, state, to_slot)
|
||||
|
||||
|
||||
def _transition_until_fork_minus_one(spec, state, fork_epoch):
|
||||
to_slot = fork_epoch * spec.SLOTS_PER_EPOCH - 2
|
||||
transition_to(spec, state, to_slot)
|
||||
|
||||
|
||||
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:
|
||||
block_filter = only_at(to_slot)
|
||||
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
|
||||
result_blocks
|
||||
])
|
||||
|
||||
|
||||
def run_transition_with_operation(state,
|
||||
fork_epoch,
|
||||
spec,
|
||||
post_spec,
|
||||
pre_tag,
|
||||
post_tag,
|
||||
operation_type,
|
||||
operation_at_slot):
|
||||
"""
|
||||
Generate `operation_type` operation with the spec before fork.
|
||||
The operation would be included into the block at `operation_at_slot`.
|
||||
"""
|
||||
is_at_fork = operation_at_slot == fork_epoch * spec.SLOTS_PER_EPOCH
|
||||
is_right_before_fork = operation_at_slot == fork_epoch * spec.SLOTS_PER_EPOCH - 1
|
||||
assert is_at_fork or is_right_before_fork
|
||||
|
||||
if is_at_fork:
|
||||
transition_until_fork(spec, state, fork_epoch)
|
||||
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 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)
|
||||
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 == OperationType.VOLUNTARY_EXIT:
|
||||
selected_validator_index = 0
|
||||
signed_exits = prepare_signed_exits(spec, state, [selected_validator_index])
|
||||
operation_dict = {'voluntary_exits': signed_exits}
|
||||
|
||||
blocks = []
|
||||
|
||||
if is_right_before_fork:
|
||||
# add a block with operation.
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
_set_operations_by_dict(block, operation_dict)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
blocks.append(pre_tag(signed_block))
|
||||
|
||||
def _check_state():
|
||||
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 == 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 == OperationType.DEPOSIT:
|
||||
assert not post_spec.is_active_validator(
|
||||
state.validators[selected_validator_index],
|
||||
post_spec.get_current_epoch(state)
|
||||
)
|
||||
elif operation_type == OperationType.VOLUNTARY_EXIT:
|
||||
validator = state.validators[selected_validator_index]
|
||||
assert validator.exit_epoch < post_spec.FAR_FUTURE_EPOCH
|
||||
|
||||
if is_right_before_fork:
|
||||
_check_state()
|
||||
|
||||
yield "pre", state
|
||||
|
||||
# irregular state transition to handle fork:
|
||||
_operation_at_slot = operation_dict if is_at_fork else None
|
||||
state, block = do_altair_fork(state, spec, post_spec, fork_epoch, operation_dict=_operation_at_slot)
|
||||
blocks.append(post_tag(block))
|
||||
|
||||
if is_at_fork:
|
||||
_check_state()
|
||||
|
||||
# after the fork
|
||||
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,
|
||||
ignoring_proposers=ignoring_proposers,
|
||||
)
|
||||
|
||||
yield "blocks", blocks
|
||||
yield "post", state
|
||||
|
||||
|
||||
def _transition_until_active(post_spec, state, post_tag, blocks, validator_index):
|
||||
# continue regular state transition with new spec into next epoch
|
||||
transition_to_next_epoch_and_append_blocks(post_spec, state, post_tag, blocks)
|
||||
# finalize activation_eligibility_epoch
|
||||
_, blocks_in_epoch, state = next_slots_with_attestations(
|
||||
post_spec,
|
||||
state,
|
||||
post_spec.SLOTS_PER_EPOCH * 2,
|
||||
fill_cur_epoch=True,
|
||||
fill_prev_epoch=True,
|
||||
)
|
||||
blocks.extend([post_tag(block) for block in blocks_in_epoch])
|
||||
assert state.finalized_checkpoint.epoch >= state.validators[validator_index].activation_eligibility_epoch
|
||||
|
||||
# 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)
|
||||
|
||||
assert state.validators[validator_index].activation_epoch < post_spec.FAR_FUTURE_EPOCH
|
||||
|
||||
to_slot = state.validators[validator_index].activation_epoch * post_spec.SLOTS_PER_EPOCH
|
||||
blocks.extend([
|
||||
post_tag(block) for block in
|
||||
state_transition_across_slots(post_spec, state, to_slot, block_filter=only_at(to_slot))
|
||||
])
|
||||
assert post_spec.is_active_validator(state.validators[validator_index], post_spec.get_current_epoch(state))
|
|
@ -6,7 +6,28 @@ from eth2spec.test.helpers.deposits import mock_deposit
|
|||
from eth2spec.test.helpers.state import next_epoch
|
||||
|
||||
|
||||
def set_some_activations(spec, state, rng, activation_epoch=None):
|
||||
if activation_epoch is None:
|
||||
activation_epoch = spec.get_current_epoch(state)
|
||||
num_validators = len(state.validators)
|
||||
selected_indices = []
|
||||
for index in range(num_validators):
|
||||
# If is slashed or exiting, skip
|
||||
if state.validators[index].slashed or state.validators[index].exit_epoch != spec.FAR_FUTURE_EPOCH:
|
||||
continue
|
||||
# Set ~1/10 validators' activation_eligibility_epoch and activation_epoch
|
||||
if rng.randrange(num_validators) < num_validators // 10:
|
||||
state.validators[index].activation_eligibility_epoch = max(
|
||||
int(activation_epoch) - int(spec.MAX_SEED_LOOKAHEAD) - 1,
|
||||
spec.GENESIS_EPOCH,
|
||||
)
|
||||
state.validators[index].activation_epoch = activation_epoch
|
||||
selected_indices.append(index)
|
||||
return selected_indices
|
||||
|
||||
|
||||
def set_some_new_deposits(spec, state, rng):
|
||||
deposited_indices = []
|
||||
num_validators = len(state.validators)
|
||||
# Set ~1/10 to just recently deposited
|
||||
for index in range(num_validators):
|
||||
|
@ -15,46 +36,64 @@ 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:
|
||||
# The validators that just made a deposit
|
||||
deposited_indices.append(index)
|
||||
return deposited_indices
|
||||
|
||||
|
||||
def exit_random_validators(spec, state, rng, fraction=None):
|
||||
if fraction is None:
|
||||
# Exit ~1/2
|
||||
fraction = 0.5
|
||||
def exit_random_validators(spec, state, rng, fraction=0.5, exit_epoch=None, withdrawable_epoch=None, from_epoch=None):
|
||||
"""
|
||||
Set some validators' exit_epoch and withdrawable_epoch.
|
||||
|
||||
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)
|
||||
If exit_epoch is configured, use the given exit_epoch. Otherwise, randomly set exit_epoch and withdrawable_epoch.
|
||||
"""
|
||||
if from_epoch is None:
|
||||
from_epoch = spec.MAX_SEED_LOOKAHEAD + 1
|
||||
epoch_diff = int(from_epoch) - int(spec.get_current_epoch(state))
|
||||
for _ in range(epoch_diff):
|
||||
# NOTE: if `epoch_diff` is negative, then this loop body does not execute.
|
||||
next_epoch(spec, state)
|
||||
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
exited_indices = []
|
||||
for index in spec.get_active_validator_indices(state, current_epoch):
|
||||
sampled = rng.random() < fraction
|
||||
if not sampled:
|
||||
continue
|
||||
|
||||
exited_indices.append(index)
|
||||
validator = state.validators[index]
|
||||
validator.exit_epoch = rng.choice([current_epoch, current_epoch - 1, current_epoch - 2, current_epoch - 3])
|
||||
# ~1/2 are withdrawable (note, unnatural span between exit epoch and withdrawable epoch)
|
||||
if rng.choice([True, False]):
|
||||
validator.withdrawable_epoch = current_epoch
|
||||
if exit_epoch is None:
|
||||
assert withdrawable_epoch is None
|
||||
validator.exit_epoch = rng.choice([current_epoch, current_epoch - 1, current_epoch - 2, current_epoch - 3])
|
||||
# ~1/2 are withdrawable (note, unnatural span between exit epoch and withdrawable epoch)
|
||||
if rng.choice([True, False]):
|
||||
validator.withdrawable_epoch = current_epoch
|
||||
else:
|
||||
validator.withdrawable_epoch = current_epoch + 1
|
||||
else:
|
||||
validator.withdrawable_epoch = current_epoch + 1
|
||||
validator.exit_epoch = exit_epoch
|
||||
if withdrawable_epoch is None:
|
||||
validator.withdrawable_epoch = validator.exit_epoch + spec.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY
|
||||
else:
|
||||
validator.withdrawable_epoch = withdrawable_epoch
|
||||
|
||||
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
|
||||
sampled = rng.random() < fraction
|
||||
if index == 0 or sampled:
|
||||
spec.slash_validator(state, index)
|
||||
slashed_indices.append(index)
|
||||
return slashed_indices
|
||||
|
||||
|
||||
def randomize_epoch_participation(spec, state, epoch, rng):
|
||||
|
@ -123,7 +162,7 @@ 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), exit_fraction=None, slash_fraction=None):
|
||||
def randomize_state(spec, state, rng=Random(8020), exit_fraction=0.5, slash_fraction=0.5):
|
||||
set_some_new_deposits(spec, state, rng)
|
||||
exit_random_validators(spec, state, rng, fraction=exit_fraction)
|
||||
slash_random_validators(spec, state, rng, fraction=slash_fraction)
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
from typing import Iterable
|
||||
|
||||
from eth2spec.test.helpers.constants import ALTAIR, MINIMAL, MAINNET, PHASE0
|
||||
from eth2spec.test.altair.transition import test_transition as test_altair_transition
|
||||
from eth2spec.test.altair.transition import (
|
||||
test_transition as test_altair_transition,
|
||||
test_activations_and_exits as test_altair_activations_and_exits,
|
||||
test_leaking as test_altair_leaking,
|
||||
test_slashing as test_altair_slashing,
|
||||
test_operations as test_altair_operations,
|
||||
)
|
||||
|
||||
from eth2spec.gen_helpers.gen_base import gen_runner, gen_typing
|
||||
from eth2spec.gen_helpers.gen_from_tests.gen import generate_from_tests
|
||||
|
@ -25,7 +31,13 @@ def create_provider(tests_src, preset_name: str, pre_fork_name: str, post_fork_n
|
|||
return gen_typing.TestProvider(prepare=prepare_fn, make_cases=cases_fn)
|
||||
|
||||
|
||||
TRANSITION_TESTS = ((PHASE0, ALTAIR, test_altair_transition),)
|
||||
TRANSITION_TESTS = (
|
||||
(PHASE0, ALTAIR, test_altair_transition),
|
||||
(PHASE0, ALTAIR, test_altair_activations_and_exits),
|
||||
(PHASE0, ALTAIR, test_altair_leaking),
|
||||
(PHASE0, ALTAIR, test_altair_slashing),
|
||||
(PHASE0, ALTAIR, test_altair_operations),
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
Loading…
Reference in New Issue