add fork finality test with random participation

This commit is contained in:
Alex Stokes 2021-05-19 12:16:24 -07:00
parent 7eba1612cf
commit df742ea8af
No known key found for this signature in database
GPG Key ID: 99B3D88FD6C55A69
2 changed files with 117 additions and 7 deletions

View File

@ -1,3 +1,4 @@
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
@ -317,3 +318,90 @@ def test_transition_with_finality(state, fork_epoch, spec, post_spec, pre_tag, p
yield "blocks", blocks
yield "post", state
@fork_transition_test(PHASE0, ALTAIR, fork_epoch=3)
def test_transition_with_random_participation(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Transition from the initial ``state`` to the epoch after the ``fork_epoch``,
including attestations so as to produce finality through the fork boundary.
"""
rng = random.Random(1337)
def _drop_random_quarter(_slot, _index, indices):
# still finalize, but drop some attestations
committee_len = len(indices)
assert committee_len >= 4
one_quarter_len = committee_len // 4
participant_count = committee_len - one_quarter_len
return rng.sample(indices, participant_count)
yield "pre", state
current_epoch = spec.get_current_epoch(state)
assert current_epoch < fork_epoch
assert current_epoch == spec.GENESIS_EPOCH
# regular state transition until fork:
fill_cur_epoch = False
fill_prev_epoch = True
blocks = []
for _ in range(current_epoch, fork_epoch - 1):
if current_epoch == spec.GENESIS_EPOCH:
fill_cur_epoch = True
fill_prev_epoch = False
_, blocks_in_epoch, state = next_slots_with_attestations(spec,
state,
spec.SLOTS_PER_EPOCH,
fill_cur_epoch,
fill_prev_epoch,
participation_fn=_drop_random_quarter)
blocks.extend([pre_tag(block) for block in blocks_in_epoch])
if current_epoch == spec.GENESIS_EPOCH:
fill_cur_epoch = False
fill_prev_epoch = True
_, blocks_in_epoch, state = next_slots_with_attestations(spec,
state,
spec.SLOTS_PER_EPOCH - 1,
fill_cur_epoch,
fill_prev_epoch,
participation_fn=_drop_random_quarter)
blocks.extend([pre_tag(block) for block in blocks_in_epoch])
assert spec.get_current_epoch(state) == fork_epoch - 1
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)
blocks.append(post_tag(block))
# continue regular state transition with new spec into next epoch
for _ in range(4):
_, blocks_in_epoch, state = next_slots_with_attestations(post_spec,
state,
post_spec.SLOTS_PER_EPOCH,
fill_cur_epoch,
fill_prev_epoch,
participation_fn=_drop_random_quarter)
blocks.extend([post_tag(block) for block in blocks_in_epoch])
assert state.slot % post_spec.SLOTS_PER_EPOCH == 0
assert post_spec.compute_epoch_at_slot(state.slot) == fork_epoch + 4
assert state.current_justified_checkpoint.epoch == fork_epoch + 2
assert state.finalized_checkpoint.epoch == fork_epoch
assert len(blocks) == (fork_epoch + 4) * post_spec.SLOTS_PER_EPOCH
assert len(blocks) == len(set(blocks))
blocks_without_attestations = [block for block in blocks if len(block.message.body.attestations) == 0]
# A boundary condition of ``next_slots_with_attestations`` skips
# over the block with ``slot == 1``.
# And the fork upgrade helper currently does not construct any attestations.
assert len(blocks_without_attestations) == 2
slots_without_attestations = [b.message.slot for b in blocks_without_attestations]
assert set(slots_without_attestations) == set([1, fork_epoch * spec.SLOTS_PER_EPOCH])
yield "blocks", blocks
yield "post", state

View File

@ -223,7 +223,11 @@ def next_slots_with_attestations(spec,
state,
slot_count,
fill_cur_epoch,
fill_prev_epoch):
fill_prev_epoch,
participation_fn=None):
"""
participation_fn: (slot, committee_index, committee_indices_set) -> participants_indices_set
"""
post_state = state.copy()
signed_blocks = []
for _ in range(slot_count):
@ -234,19 +238,37 @@ def next_slots_with_attestations(spec,
if slot_to_attest >= spec.compute_start_slot_at_epoch(spec.get_current_epoch(post_state)):
for index in range(committees_per_slot):
# if spec.fork == SHARDING: TODO: add shard data to attestation, include shard headers in block
def participants_filter(comm):
if participation_fn is None:
return comm
else:
return participation_fn(post_state.slot, index, comm)
cur_attestation = get_valid_attestation(
spec, post_state, slot_to_attest,
index=index, signed=True, on_time=True
)
cur_attestation = get_valid_attestation(spec,
post_state,
slot_to_attest,
index=index,
signed=True,
on_time=True,
filter_participant_set=participants_filter)
block.body.attestations.append(cur_attestation)
if fill_prev_epoch:
slot_to_attest = post_state.slot - spec.SLOTS_PER_EPOCH + 1
committees_per_slot = spec.get_committee_count_per_slot(state, spec.compute_epoch_at_slot(slot_to_attest))
for index in range(committees_per_slot):
prev_attestation = get_valid_attestation(
spec, post_state, slot_to_attest, index=index, signed=True, on_time=False)
def participants_filter(comm):
if participation_fn is None:
return comm
else:
return participation_fn(post_state.slot, index, comm)
prev_attestation = get_valid_attestation(spec,
post_state,
slot_to_attest,
index=index,
signed=True,
on_time=False,
filter_participant_set=participants_filter)
block.body.attestations.append(prev_attestation)
signed_block = state_transition_and_sign_block(spec, post_state, block)