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_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
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_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
def test_normal_transition(state, fork_epoch, spec, post_spec, pre_tag, post_tag): """ Transition from the initial ``state`` to the epoch after the ``fork_epoch``, producing blocks for every slot along the way. """ yield "pre", state assert spec.get_current_epoch(state) < fork_epoch # regular state transition until fork: to_slot = fork_epoch * spec.SLOTS_PER_EPOCH - 1 blocks = [] blocks.extend([ pre_tag(block) for block in state_transition_across_slots(spec, state, to_slot) ]) # irregular state transition to handle fork: state, block = do_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) assert state.slot % post_spec.SLOTS_PER_EPOCH == 0 assert post_spec.get_current_epoch(state) == fork_epoch + 1 slots_with_blocks = [block.message.slot for block in blocks] assert len(set(slots_with_blocks)) == len(slots_with_blocks) assert set(range(1, state.slot + 1)) == set(slots_with_blocks) yield "blocks", blocks yield "post", state
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_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
def test_sample_transition(state, fork_epoch, spec, post_spec, pre_tag, post_tag): transition_until_fork(spec, state, fork_epoch) # check pre state assert spec.get_current_epoch(state) < fork_epoch yield "pre", state # irregular state transition to handle fork: blocks = [] state, block = do_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
def test_transition_with_no_attestations_until_after_fork( state, fork_epoch, spec, post_spec, pre_tag, post_tag): """ Transition from the initial ``state`` to the ``fork_epoch`` with no attestations, then transition forward with enough attestations to finalize the fork epoch. """ yield "pre", state assert spec.get_current_epoch(state) < fork_epoch # regular state transition until fork: to_slot = fork_epoch * spec.SLOTS_PER_EPOCH - 1 blocks = [] blocks.extend([ pre_tag(block) for block in state_transition_across_slots(spec, state, to_slot) ]) # irregular state transition to handle fork: state, block = do_fork(state, spec, post_spec, fork_epoch) blocks.append(post_tag(block)) # continue regular state transition but add attestations # for enough epochs to finalize the ``fork_epoch`` block = next_epoch_via_signed_block(post_spec, state) blocks.append(post_tag(block)) for _ in range(4): _, blocks_in_epoch, state = next_slots_with_attestations( post_spec, state, post_spec.SLOTS_PER_EPOCH, False, True, ) blocks.extend([post_tag(block) for block in blocks_in_epoch]) assert state.slot % post_spec.SLOTS_PER_EPOCH == 0 assert post_spec.get_current_epoch(state) == fork_epoch + 5 assert state.current_justified_checkpoint.epoch == fork_epoch + 3 assert state.finalized_checkpoint.epoch == fork_epoch + 1 yield "blocks", blocks yield "post", state
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_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
def test_transition_missing_last_pre_fork_block(state, fork_epoch, spec, post_spec, pre_tag, post_tag): """ Transition from the initial ``state`` to the epoch after the ``fork_epoch``, producing blocks for every slot along the way except for the last block of the old fork. """ yield "pre", state assert spec.get_current_epoch(state) < fork_epoch # regular state transition until fork: last_slot_of_pre_fork = fork_epoch * spec.SLOTS_PER_EPOCH - 1 to_slot = last_slot_of_pre_fork 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)) ]) # irregular state transition to handle fork: state, block = do_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) assert state.slot % post_spec.SLOTS_PER_EPOCH == 0 assert post_spec.get_current_epoch(state) == fork_epoch + 1 slots_with_blocks = [block.message.slot for block in blocks] assert len(set(slots_with_blocks)) == len(slots_with_blocks) expected_slots = set(range(1, state.slot + 1)).difference( set([last_slot_of_pre_fork])) assert expected_slots == set(slots_with_blocks) yield "blocks", blocks yield "post", state
def test_transition_only_blocks_post_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag): """ Transition from the initial ``state`` to the epoch after the ``fork_epoch``, skipping blocks for every slot along the way except for the first block in the ending epoch. """ yield "pre", state assert spec.get_current_epoch(state) < fork_epoch # regular state transition until fork: last_slot_of_pre_fork = fork_epoch * spec.SLOTS_PER_EPOCH - 1 to_slot = last_slot_of_pre_fork blocks = [] blocks.extend([ pre_tag(block) for block in state_transition_across_slots( spec, state, to_slot, block_filter=no_blocks) ]) # irregular state transition to handle fork: state, _ = do_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)) ]) assert state.slot % post_spec.SLOTS_PER_EPOCH == 0 assert post_spec.get_current_epoch(state) == fork_epoch + 1 slots_with_blocks = [block.message.slot for block in blocks] assert len(slots_with_blocks) == 1 assert slots_with_blocks[0] == last_slot yield "blocks", blocks yield "post", state
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_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
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_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) exited_pubkeys = [ state.validators[index].pubkey for index in exited_indices ] some_sync_committee_exited = any( set(exited_pubkeys).intersection( list(state.current_sync_committee.pubkeys))) if post_spec.fork == ALTAIR: # in Altair fork, the sync committee members would be set with only active validators assert not some_sync_committee_exited else: assert some_sync_committee_exited # 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
def _run_transition_test_with_attestations(state, fork_epoch, spec, post_spec, pre_tag, post_tag, participation_fn=None, expect_finality=True): yield "pre", state current_epoch = spec.get_current_epoch(state) assert current_epoch < fork_epoch assert current_epoch == spec.GENESIS_EPOCH # skip genesis epoch to avoid dealing with some edge cases... block = next_epoch_via_signed_block(spec, state) # regular state transition until fork: fill_cur_epoch = False fill_prev_epoch = True blocks = [pre_tag(block)] current_epoch = spec.get_current_epoch(state) for _ in range(current_epoch, fork_epoch - 1): _, blocks_in_epoch, state = next_slots_with_attestations( spec, state, spec.SLOTS_PER_EPOCH, fill_cur_epoch, fill_prev_epoch, participation_fn=participation_fn, ) blocks.extend([pre_tag(block) for block in blocks_in_epoch]) _, blocks_in_epoch, state = next_slots_with_attestations( spec, state, spec.SLOTS_PER_EPOCH - 1, fill_cur_epoch, fill_prev_epoch, participation_fn=participation_fn, ) 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_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=participation_fn, ) blocks.extend([post_tag(block) for block in blocks_in_epoch]) assert state.slot % post_spec.SLOTS_PER_EPOCH == 0 assert post_spec.get_current_epoch(state) == fork_epoch + 4 if expect_finality: assert state.current_justified_checkpoint.epoch == fork_epoch + 2 assert state.finalized_checkpoint.epoch == fork_epoch else: assert state.current_justified_checkpoint.epoch == spec.GENESIS_EPOCH assert state.finalized_checkpoint.epoch == spec.GENESIS_EPOCH assert len(blocks) == (fork_epoch + 3) * post_spec.SLOTS_PER_EPOCH + 1 assert len(blocks) == len(set(blocks)) blocks_without_attestations = [ block for block in blocks if len(block.message.body.attestations) == 0 ] assert len(blocks_without_attestations) == 2 slots_without_attestations = [ b.message.slot for b in blocks_without_attestations ] assert set(slots_without_attestations) == set( [spec.SLOTS_PER_EPOCH, fork_epoch * spec.SLOTS_PER_EPOCH]) yield "blocks", blocks yield "post", state