Skip to content

Commit

Permalink
PR feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
hwwhww committed Oct 14, 2021
1 parent cbba542 commit b861207
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from eth2spec.test.helpers.inactivity_scores import (
randomize_inactivity_scores,
zero_inactivity_scores,
slash_some_validators_for_inactivity_scores_test,
)
from eth2spec.test.helpers.state import (
next_epoch,
Expand Down Expand Up @@ -205,6 +204,20 @@ def test_random_inactivity_scores_full_participation_leaking(spec, state):
assert spec.is_in_inactivity_leak(state)


def slash_some_validators_for_inactivity_scores_test(spec, state, rng=Random(40404040)):
# ``run_inactivity_scores_test`` runs at the next epoch from `state`.
# We retrieve the proposer of this future state to avoid
# accidentally slashing that validator
future_state = state.copy()
next_epoch_via_block(spec, future_state)

proposer_index = spec.get_beacon_proposer_index(future_state)
# Slash ~1/4 of validaors
for validator_index in range(len(state.validators)):
if rng.choice(range(4)) == 0 and validator_index != proposer_index:
spec.slash_validator(state, validator_index)


@with_altair_and_later
@spec_state_test
def test_some_slashed_zero_scores_full_participation(spec, state):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
from eth2spec.test.helpers.deposits import prepare_state_and_deposit
from eth2spec.test.helpers.fork_transition import (
do_altair_fork,
set_validators_exit_epoch,
state_transition_across_slots,
transition_until_fork,
transition_to_next_epoch_and_append_blocks,
)
from eth2spec.test.helpers.random import set_some_new_deposits
from eth2spec.test.helpers.random import (
exit_random_validators,
set_some_new_deposits,
)
from eth2spec.test.helpers.voluntary_exits import prepare_signed_exits


Expand All @@ -26,9 +28,11 @@ def test_transition_with_one_fourth_exiting_validators_exit_post_fork(state,
pre_tag,
post_tag):
"""
1/4 exiting but still active validators at the fork transition.
1/4 validators initiated voluntary exit before the fork,
and are exiting but still active *after* the fork transition.
"""
exited_indices = set_validators_exit_epoch(spec, state, exit_epoch=10, rng=random.Random(5566), fraction=0.25)
exited_indices = exit_random_validators(
spec, state, rng=random.Random(5566), fraction=0.25, exit_epoch=10, forward=False)

transition_until_fork(spec, state, fork_epoch)

Expand All @@ -55,7 +59,7 @@ def test_transition_with_one_fourth_exiting_validators_exit_post_fork(state,
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)
transition_to_next_epoch_and_append_blocks(post_spec, state, post_tag, blocks, only_last_block=True)

# check state
for index in exited_indices:
Expand All @@ -76,9 +80,11 @@ def test_transition_with_one_fourth_exiting_validators_exit_at_fork(state,
pre_tag,
post_tag):
"""
1/4 exiting but still active validators at the fork transition.
1/4 validators initiated voluntary exit before the fork,
and being exited and inactive *right after* the fork transition.
"""
exited_indices = set_validators_exit_epoch(spec, state, exit_epoch=2, rng=random.Random(5566), fraction=0.25)
exited_indices = exit_random_validators(
spec, state, rng=random.Random(5566), fraction=0.25, exit_epoch=fork_epoch, forward=False)

transition_until_fork(spec, state, fork_epoch)

Expand Down Expand Up @@ -111,7 +117,7 @@ def test_transition_with_one_fourth_exiting_validators_exit_at_fork(state,
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)
transition_to_next_epoch_and_append_blocks(post_spec, state, post_tag, blocks, only_last_block=True)

yield "blocks", blocks
yield "post", state
Expand All @@ -120,7 +126,7 @@ def test_transition_with_one_fourth_exiting_validators_exit_at_fork(state,
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=260)
def test_transition_with_voluntary_exit_at_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Create an attester slashing at the transition.
Create a voluntary exit at 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
Expand All @@ -142,7 +148,7 @@ def test_transition_with_voluntary_exit_at_fork(state, fork_epoch, spec, post_sp
assert validator.exit_epoch < post_spec.FAR_FUTURE_EPOCH

# continue regular state transition with new spec into next epoch
transition_to_next_epoch_and_append_blocks(post_spec, state, post_tag, blocks)
transition_to_next_epoch_and_append_blocks(post_spec, state, post_tag, blocks, only_last_block=True)

yield "blocks", blocks
yield "post", state
Expand Down Expand Up @@ -175,7 +181,7 @@ def test_transition_with_non_empty_activation_queue(state, fork_epoch, spec, pos
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)
transition_to_next_epoch_and_append_blocks(post_spec, state, post_tag, blocks, only_last_block=True)

yield "blocks", blocks
yield "post", state
Expand Down Expand Up @@ -205,9 +211,6 @@ def test_transition_with_deposit_at_fork(state, fork_epoch, spec, post_spec, pre

assert not post_spec.is_active_validator(state.validators[validator_index], post_spec.get_current_epoch(state))

# 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,
Expand All @@ -217,10 +220,10 @@ def test_transition_with_deposit_at_fork(state, fork_epoch, spec, post_spec, pre
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
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)
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

Expand Down
35 changes: 2 additions & 33 deletions tests/core/pyspec/eth2spec/test/altair/transition/test_leaking.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def test_transition_with_leaking_pre_fork(state, fork_epoch, spec, post_spec, pr
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)
transition_to_next_epoch_and_append_blocks(post_spec, state, post_tag, blocks, only_last_block=True)

yield "blocks", blocks
yield "post", state
Expand Down Expand Up @@ -57,38 +57,7 @@ def test_transition_with_leaking_at_fork(state, fork_epoch, spec, post_spec, pre
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)

yield "blocks", blocks
yield "post", state


@fork_transition_test(PHASE0, ALTAIR, fork_epoch=5)
def test_transition_with_leaking_post_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 after 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 not 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)

# check state again
assert spec.is_in_inactivity_leak(state)
transition_to_next_epoch_and_append_blocks(post_spec, state, post_tag, blocks, only_last_block=True)

yield "blocks", blocks
yield "post", state
21 changes: 11 additions & 10 deletions tests/core/pyspec/eth2spec/test/altair/transition/test_slashing.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
transition_until_fork,
transition_to_next_epoch_and_append_blocks,
)
from eth2spec.test.helpers.inactivity_scores import (
slash_some_validators_for_inactivity_scores_test,
from eth2spec.test.helpers.random import (
slash_random_validators,
)


Expand All @@ -32,8 +32,7 @@ def test_transition_with_one_fourth_slashed_active_validators_pre_fork(state,
1/4 validators are slashed but still active at the fork transition.
"""
# slash 1/4 validators
slashed_indices = slash_some_validators_for_inactivity_scores_test(
spec, state, rng=random.Random(5566), fraction=0.25)
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
Expand All @@ -50,18 +49,17 @@ def test_transition_with_one_fourth_slashed_active_validators_pre_fork(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))
state, _ = do_altair_fork(state, spec, post_spec, fork_epoch, with_block=False)

# ensure that some of the current sync committee members are the slashed
# 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
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)
Expand Down Expand Up @@ -103,7 +101,10 @@ def test_transition_with_attester_slashing_at_fork(state, fork_epoch, spec, post
assert state.validators[validator_index].slashed

# continue regular state transition with new spec into next epoch
transition_to_next_epoch_and_append_blocks(post_spec, state, post_tag, blocks)
transition_to_next_epoch_and_append_blocks(post_spec, state, post_tag, blocks, only_last_block=True)
for block in blocks:
print('block.slot', block.message.slot)
print('len(blocks)', len(blocks))

yield "blocks", blocks
yield "post", state
Expand Down Expand Up @@ -132,7 +133,7 @@ def test_transition_with_proposer_slashing_at_fork(state, fork_epoch, spec, post
assert slashed_proposer.slashed

# continue regular state transition with new spec into next epoch
transition_to_next_epoch_and_append_blocks(post_spec, state, post_tag, blocks)
transition_to_next_epoch_and_append_blocks(post_spec, state, post_tag, blocks, only_last_block=True)

yield "blocks", blocks
yield "post", state
20 changes: 15 additions & 5 deletions tests/core/pyspec/eth2spec/test/helpers/fork_transition.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,15 @@ def _state_transition_and_sign_block_at_slot(spec,
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)
# 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.
if operation_dict is not None:

if operation_dict:
for key, value in operation_dict.items():
setattr(block.body, key, value)

Expand Down Expand Up @@ -134,9 +138,15 @@ def transition_until_fork(spec, state, fork_epoch):
transition_to(spec, state, to_slot)


def transition_to_next_epoch_and_append_blocks(spec, state, post_tag, blocks):
def transition_to_next_epoch_and_append_blocks(spec, state, post_tag, blocks, only_last_block=False):
to_slot = spec.SLOTS_PER_EPOCH + state.slot

if only_last_block:
block_filter = only_at(to_slot)
else:
block_filter = _all_blocks

blocks.extend([
post_tag(block) for block in
state_transition_across_slots(spec, state, to_slot)
state_transition_across_slots(spec, state, to_slot, block_filter=block_filter)
])
23 changes: 0 additions & 23 deletions tests/core/pyspec/eth2spec/test/helpers/inactivity_scores.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,9 @@
from random import Random

from eth2spec.test.helpers.state import (
next_epoch_via_block,
)


def randomize_inactivity_scores(spec, state, minimum=0, maximum=50000, rng=Random(4242)):
state.inactivity_scores = [rng.randint(minimum, maximum) for _ in range(len(state.validators))]


def zero_inactivity_scores(spec, state, rng=None):
state.inactivity_scores = [0] * len(state.validators)


def slash_some_validators_for_inactivity_scores_test(spec, state, rng=Random(40404040), fraction=0.25):
"""
``run_inactivity_scores_test`` runs at the next epoch from `state`.
We retrieve the proposer of this future state to avoid
accidentally slashing that validator
"""
future_state = state.copy()
next_epoch_via_block(spec, future_state)
proposer_index = spec.get_beacon_proposer_index(future_state)
selected_count = int(len(state.validators) * fraction)
selected_indices = rng.sample(range(len(state.validators)), selected_count)
if proposer_index in selected_indices:
selected_indices.remove(proposer_index)
for validator_index in selected_indices:
spec.slash_validator(state, validator_index)

return selected_indices
41 changes: 31 additions & 10 deletions tests/core/pyspec/eth2spec/test/helpers/random.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,41 +26,62 @@ def set_some_new_deposits(spec, state, rng):
return eligible_indices, queuing_indices


def exit_random_validators(spec, state, rng, fraction=None):
def exit_random_validators(spec, state, rng, fraction=None, 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 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 forward:
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)
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

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):
Expand Down
Loading

0 comments on commit b861207

Please sign in to comment.