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))
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_altair_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_block(post_spec, state) blocks.append(post_tag(sign_block(post_spec, state, 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 apply_next_slots_with_attestations(spec, state, store, slots, fill_cur_epoch, fill_prev_epoch, test_steps, participation_fn=None): _, new_signed_blocks, post_state = next_slots_with_attestations( spec, state, slots, fill_cur_epoch, fill_prev_epoch, participation_fn=participation_fn) for signed_block in new_signed_blocks: block = signed_block.message yield from tick_and_add_block(spec, store, signed_block, test_steps) block_root = block.hash_tree_root() assert store.blocks[block_root] == block last_signed_block = signed_block assert store.block_states[block_root].hash_tree_root() == post_state.hash_tree_root() return post_state, store, last_signed_block
def test_new_justified_is_later_than_store_justified(spec, state): """ J: Justified F: Finalized fork_1_state (forked from genesis): epoch [0] <- [1] <- [2] <- [3] <- [4] F J fork_2_state (forked from fork_1_state's epoch 2): epoch └──── [3] <- [4] <- [5] <- [6] F J fork_3_state (forked from genesis): [0] <- [1] <- [2] <- [3] <- [4] <- [5] F J """ # The 1st fork, from genesis fork_1_state = state.copy() # The 3rd fork, from genesis fork_3_state = state.copy() test_steps = [] # Initialization store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) yield 'anchor_state', state yield 'anchor_block', anchor_block current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time on_tick_and_append_step(spec, store, current_time, test_steps) assert store.time == current_time # ----- Process fork_1_state # Skip epoch 0 next_epoch(spec, fork_1_state) # Fill epoch 1 with previous epoch attestations fork_1_state, store, _ = yield from apply_next_epoch_with_attestations( spec, fork_1_state, store, False, True, test_steps=test_steps) # Fork `fork_2_state` at the start of epoch 2 fork_2_state = fork_1_state.copy() assert spec.get_current_epoch(fork_2_state) == 2 # Skip epoch 2 next_epoch(spec, fork_1_state) # # Fill epoch 3 & 4 with previous epoch attestations for _ in range(2): fork_1_state, store, _ = yield from apply_next_epoch_with_attestations( spec, fork_1_state, store, False, True, test_steps=test_steps) assert fork_1_state.finalized_checkpoint.epoch == store.finalized_checkpoint.epoch == 0 assert fork_1_state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3 assert store.justified_checkpoint == fork_1_state.current_justified_checkpoint # ------ fork_2_state: Create a chain to set store.best_justified_checkpoint # NOTE: The goal is to make `store.best_justified_checkpoint.epoch > store.justified_checkpoint.epoch` all_blocks = [] # Proposed an empty block at epoch 2, 1st slot block = build_empty_block_for_next_slot(spec, fork_2_state) signed_block = state_transition_and_sign_block(spec, fork_2_state, block) yield from tick_and_add_block(spec, store, signed_block, test_steps) assert fork_2_state.current_justified_checkpoint.epoch == 0 # Skip to epoch 4 for _ in range(2): next_epoch(spec, fork_2_state) assert fork_2_state.current_justified_checkpoint.epoch == 0 # Propose a block at epoch 4, 5th slot # Propose a block at epoch 5, 5th slot for _ in range(2): next_epoch(spec, fork_2_state) next_slots(spec, fork_2_state, 4) signed_block = state_transition_with_full_attestations_block( spec, fork_2_state, True, True) yield from tick_and_add_block(spec, store, signed_block, test_steps) assert fork_2_state.current_justified_checkpoint.epoch == 0 # Propose a block at epoch 6, SAFE_SLOTS_TO_UPDATE_JUSTIFIED + 2 slot next_epoch(spec, fork_2_state) next_slots(spec, fork_2_state, spec.SAFE_SLOTS_TO_UPDATE_JUSTIFIED + 2) signed_block = state_transition_with_full_attestations_block( spec, fork_2_state, True, True) assert fork_2_state.finalized_checkpoint.epoch == 0 assert fork_2_state.current_justified_checkpoint.epoch == 5 # Check SAFE_SLOTS_TO_UPDATE_JUSTIFIED time = store.genesis_time + fork_2_state.slot * spec.config.SECONDS_PER_SLOT on_tick_and_append_step(spec, store, time, test_steps) assert spec.compute_slots_since_epoch_start( spec.get_current_slot(store)) >= spec.SAFE_SLOTS_TO_UPDATE_JUSTIFIED # Run on_block yield from add_block(spec, store, signed_block, test_steps) assert store.finalized_checkpoint.epoch == 0 assert store.justified_checkpoint.epoch == 3 assert store.best_justified_checkpoint.epoch == 5 # ------ fork_3_state: Create another chain to test the # "Update justified if new justified is later than store justified" case all_blocks = [] for _ in range(3): next_epoch(spec, fork_3_state) # epoch 3 _, signed_blocks, fork_3_state = next_epoch_with_attestations( spec, fork_3_state, True, True) all_blocks += signed_blocks assert fork_3_state.finalized_checkpoint.epoch == 0 # epoch 4, attest the first 5 blocks _, blocks, fork_3_state = next_slots_with_attestations( spec, fork_3_state, 5, True, True) all_blocks += blocks.copy() assert fork_3_state.finalized_checkpoint.epoch == 0 # Propose a block at epoch 5, 5th slot next_epoch(spec, fork_3_state) next_slots(spec, fork_3_state, 4) signed_block = state_transition_with_full_block(spec, fork_3_state, True, True) all_blocks.append(signed_block.copy()) assert fork_3_state.finalized_checkpoint.epoch == 0 # Propose a block at epoch 6, 5th slot next_epoch(spec, fork_3_state) next_slots(spec, fork_3_state, 4) signed_block = state_transition_with_full_block(spec, fork_3_state, True, True) all_blocks.append(signed_block.copy()) assert fork_3_state.finalized_checkpoint.epoch == 3 assert fork_3_state.current_justified_checkpoint.epoch == 4 # Apply blocks of `fork_3_state` to `store` for block in all_blocks: if store.time < spec.compute_time_at_slot(fork_2_state, block.message.slot): time = store.genesis_time + block.message.slot * spec.config.SECONDS_PER_SLOT on_tick_and_append_step(spec, store, time, test_steps) yield from add_block(spec, store, block, test_steps) assert store.finalized_checkpoint == fork_3_state.finalized_checkpoint assert store.justified_checkpoint == fork_3_state.current_justified_checkpoint assert store.justified_checkpoint != store.best_justified_checkpoint assert store.best_justified_checkpoint == fork_2_state.current_justified_checkpoint yield 'steps', test_steps
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_block(spec, state) # regular state transition until fork: fill_cur_epoch = False fill_prev_epoch = True blocks = [pre_tag(sign_block(spec, state, 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_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=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