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
Example #3
0
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
Example #4
0
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