Beispiel #1
0
def test_invalid_signature_past_block(spec, state):
    committee_indices = compute_committee_indices(spec, state, state.current_sync_committee)

    blocks = []
    for _ in range(2):
        # NOTE: need to transition twice to move beyond the degenerate case at genesis
        block = build_empty_block_for_next_slot(spec, state)
        # Valid sync committee signature here...
        block.body.sync_aggregate = spec.SyncAggregate(
            sync_committee_bits=[True] * len(committee_indices),
            sync_committee_signature=compute_aggregate_sync_committee_signature(
                spec,
                state,
                block.slot - 1,
                committee_indices,
            )
        )

        signed_block = state_transition_and_sign_block(spec, state, block)
        blocks.append(signed_block)

    invalid_block = build_empty_block_for_next_slot(spec, state)
    # Invalid signature from a slot other than the previous
    invalid_block.body.sync_aggregate = spec.SyncAggregate(
        sync_committee_bits=[True] * len(committee_indices),
        sync_committee_signature=compute_aggregate_sync_committee_signature(
            spec,
            state,
            invalid_block.slot - 2,
            committee_indices,
        )
    )

    yield from run_sync_committee_processing(spec, state, invalid_block, expect_exception=True)
def test_sync_committee_with_participating_withdrawable_member(spec, state):
    # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
    state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH

    # move forward via some blocks
    for _ in range(3):
        next_epoch_via_block(spec, state)

    committee_indices = compute_committee_indices(spec, state)
    rng = random.Random(1010)

    exited_index = _exit_validator_from_committee_and_transition_state(
        spec,
        state,
        committee_indices,
        rng,
        lambda v: v.withdrawable_epoch + 1,
    )

    current_epoch = spec.get_current_epoch(state)
    assert current_epoch > state.validators[exited_index].withdrawable_epoch

    block = build_empty_block_for_next_slot(spec, state)
    block.body.sync_aggregate = spec.SyncAggregate(
        sync_committee_bits=[True] * len(committee_indices),
        sync_committee_signature=compute_aggregate_sync_committee_signature(
            spec,
            state,
            block.slot - 1,
            committee_indices,  # full committee signs
            block_root=block.parent_root,
        )
    )
    yield from run_sync_committee_processing(spec, state, block)
Beispiel #3
0
def run_successful_sync_committee_test(spec, state, committee_indices,
                                       committee_bits):
    pre_state = state.copy()

    block = build_empty_block_for_next_slot(spec, state)
    block.body.sync_aggregate = spec.SyncAggregate(
        sync_committee_bits=committee_bits,
        sync_committee_signature=compute_aggregate_sync_committee_signature(
            spec,
            state,
            block.slot - 1,
            [
                index
                for index, bit in zip(committee_indices, committee_bits) if bit
            ],
        ))

    yield from run_sync_committee_processing(spec, state, block)

    validate_sync_committee_rewards(
        spec,
        pre_state,
        state,
        committee_indices,
        committee_bits,
        block.proposer_index,
    )
def test_proposer_in_committee_with_participation(spec, state):
    committee_indices = compute_committee_indices(spec, state, state.current_sync_committee)
    participation = [True for _ in committee_indices]

    # NOTE: seem to reliably be getting a matching proposer in the first epoch w/ ``MINIMAL`` preset.
    for _ in range(spec.SLOTS_PER_EPOCH):
        block = build_empty_block_for_next_slot(spec, state)
        proposer_index = block.proposer_index
        proposer_pubkey = state.validators[proposer_index].pubkey
        proposer_is_in_sync_committee = proposer_pubkey in state.current_sync_committee.pubkeys

        # Valid sync committee signature here...
        block.body.sync_aggregate = spec.SyncAggregate(
            sync_committee_bits=participation,
            sync_committee_signature=compute_aggregate_sync_committee_signature(
                spec,
                state,
                block.slot - 1,
                committee_indices,
                block_root=block.parent_root,
            )
        )

        if proposer_is_in_sync_committee:
            assert state.validators[block.proposer_index].pubkey in state.current_sync_committee.pubkeys
            yield from run_sync_committee_processing(spec, state, block)
            return
        else:
            state_transition_and_sign_block(spec, state, block)
    raise AssertionError("failed to find a proposer in the sync committee set; check test setup")
def test_invalid_signature_previous_committee(spec, state):
    # NOTE: the `state` provided is at genesis and the process to select
    # sync committees currently returns the same committee for the first and second
    # periods at genesis.
    # To get a distinct committee so we can generate an "old" signature, we need to advance
    # 2 EPOCHS_PER_SYNC_COMMITTEE_PERIOD periods.
    current_epoch = spec.get_current_epoch(state)
    old_sync_committee = state.next_sync_committee

    epoch_in_future_sync_commitee_period = current_epoch + 2 * spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD
    slot_in_future_sync_committee_period = epoch_in_future_sync_commitee_period * spec.SLOTS_PER_EPOCH
    transition_to(spec, state, slot_in_future_sync_committee_period)

    # Use the previous sync committee to produce the signature.
    # Ensure that the pubkey sets are different.
    assert set(old_sync_committee.pubkeys) != set(state.current_sync_committee.pubkeys)
    committee_indices = compute_committee_indices(spec, state, old_sync_committee)

    block = build_empty_block_for_next_slot(spec, state)
    block.body.sync_aggregate = spec.SyncAggregate(
        sync_committee_bits=[True] * len(committee_indices),
        sync_committee_signature=compute_aggregate_sync_committee_signature(
            spec,
            state,
            block.slot - 1,
            committee_indices,
            block_root=block.parent_root,
        )
    )

    yield from run_sync_committee_processing(spec, state, block, expect_exception=True)
Beispiel #6
0
def get_random_sync_aggregate(spec,
                              state,
                              slot,
                              block_root=None,
                              fraction_participated=1.0,
                              rng=Random(2099)):
    committee_indices = compute_committee_indices(spec, state,
                                                  state.current_sync_committee)
    participant_count = int(len(committee_indices) * fraction_participated)
    participant_indices = rng.sample(range(len(committee_indices)),
                                     participant_count)
    participants = [committee_indices[index] for index in participant_indices]
    signature = compute_aggregate_sync_committee_signature(
        spec,
        state,
        slot,
        participants,
        block_root=block_root,
    )
    return spec.SyncAggregate(
        sync_committee_bits=[
            index in participant_indices
            for index in range(len(committee_indices))
        ],
        sync_committee_signature=signature,
    )
Beispiel #7
0
def get_sync_aggregate(spec,
                       state,
                       block_header,
                       block_root=None,
                       signature_slot=None):
    if signature_slot is None:
        signature_slot = block_header.slot

    all_pubkeys = [v.pubkey for v in state.validators]
    committee = [
        all_pubkeys.index(pubkey)
        for pubkey in state.current_sync_committee.pubkeys
    ]
    sync_committee_bits = [True] * len(committee)
    sync_committee_signature = compute_aggregate_sync_committee_signature(
        spec,
        state,
        block_header.slot,
        committee,
        block_root=block_root,
    )
    return spec.SyncAggregate(
        sync_committee_bits=sync_committee_bits,
        sync_committee_signature=sync_committee_signature,
    )
Beispiel #8
0
def test_valid_signature_future_committee(spec, state):
    # NOTE: the `state` provided is at genesis and the process to select
    # sync committees currently returns the same committee for the first and second
    # periods at genesis.
    # To get a distinct committee so we can generate an "old" signature, we need to advance
    # 2 EPOCHS_PER_SYNC_COMMITTEE_PERIOD periods.
    current_epoch = spec.get_current_epoch(state)
    old_current_sync_committee = state.current_sync_committee
    old_next_sync_committee = state.next_sync_committee

    epoch_in_future_sync_committee_period = current_epoch + 2 * spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD
    slot_in_future_sync_committee_period = epoch_in_future_sync_committee_period * spec.SLOTS_PER_EPOCH
    transition_to(spec, state, slot_in_future_sync_committee_period)

    sync_committee = state.current_sync_committee
    next_sync_committee = state.next_sync_committee

    assert next_sync_committee != sync_committee
    assert sync_committee != old_current_sync_committee
    assert sync_committee != old_next_sync_committee

    committee_indices = compute_committee_indices(spec, state, sync_committee)

    block = build_empty_block_for_next_slot(spec, state)
    block.body.sync_aggregate = spec.SyncAggregate(
        sync_committee_bits=[True] * len(committee_indices),
        sync_committee_signature=compute_aggregate_sync_committee_signature(
            spec,
            state,
            block.slot - 1,
            committee_indices,
        )
    )

    yield from run_sync_committee_processing(spec, state, block)
def test_invalid_signature_previous_committee(spec, state):
    # NOTE: the `state` provided is at genesis and the process to select
    # sync committees currently returns the same committee for the first and second
    # periods at genesis.
    # To get a distinct committee so we can generate an "old" signature, we need to advance
    # 2 EPOCHS_PER_SYNC_COMMITTEE_PERIOD periods.
    current_epoch = spec.get_current_epoch(state)
    previous_committee = state.next_sync_committee

    epoch_in_future_sync_commitee_period = current_epoch + 2 * spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD
    slot_in_future_sync_committee_period = epoch_in_future_sync_commitee_period * spec.SLOTS_PER_EPOCH
    transition_to(spec, state, slot_in_future_sync_committee_period)

    pubkeys = [validator.pubkey for validator in state.validators]
    committee = [pubkeys.index(pubkey) for pubkey in previous_committee.pubkeys]

    yield 'pre', state

    block = build_empty_block_for_next_slot(spec, state)
    block.body.sync_committee_bits = [True] * len(committee)
    block.body.sync_committee_signature = compute_aggregate_sync_committee_signature(
        spec,
        state,
        block.slot - 1,
        committee,
    )

    yield 'blocks', [block]
    expect_assertion_error(lambda: spec.process_sync_committee(state, block.body))
    yield 'post', None
Beispiel #10
0
def run_sync_committee_sanity_test(spec,
                                   state,
                                   fraction_full=1.0,
                                   rng=Random(454545)):
    all_pubkeys = [v.pubkey for v in state.validators]
    committee = [
        all_pubkeys.index(pubkey)
        for pubkey in state.current_sync_committee.pubkeys
    ]
    selected_indices = rng.sample(range(len(committee)),
                                  int(len(committee) * fraction_full))
    sync_committee_bits = [
        i in selected_indices for i in range(len(committee))
    ]
    participants = [
        validator_index for i, validator_index in enumerate(committee)
        if sync_committee_bits[i]
    ]

    yield 'pre', state

    block = build_empty_block_for_next_slot(spec, state)
    block.body.sync_aggregate = spec.SyncAggregate(
        sync_committee_bits=sync_committee_bits,
        sync_committee_signature=compute_aggregate_sync_committee_signature(
            spec,
            state,
            block.slot - 1,
            participants,
        ))
    signed_block = state_transition_and_sign_block(spec, state, block)

    yield 'blocks', [signed_block]
    yield 'post', state
def test_process_light_client_update_not_updated(spec, state):
    pre_snapshot = spec.LightClientSnapshot(
        header=spec.BeaconBlockHeader(),
        current_sync_committee=state.current_sync_committee,
        next_sync_committee=state.next_sync_committee,
    )
    store = spec.LightClientStore(snapshot=pre_snapshot, valid_updates=[])

    # Block at slot 1 doesn't increase sync committee period, so it won't update snapshot
    block = build_empty_block_for_next_slot(spec, state)
    signed_block = state_transition_and_sign_block(spec, state, block)
    block_header = spec.BeaconBlockHeader(
        slot=signed_block.message.slot,
        proposer_index=signed_block.message.proposer_index,
        parent_root=signed_block.message.parent_root,
        state_root=signed_block.message.state_root,
        body_root=signed_block.message.body.hash_tree_root(),
    )
    # Sync committee signing the header
    committee = spec.get_sync_committee_indices(state,
                                                spec.get_current_epoch(state))
    sync_committee_bits = [True] * len(committee)
    sync_committee_signature = compute_aggregate_sync_committee_signature(
        spec,
        state,
        block.slot,
        committee,
    )
    next_sync_committee_branch = [
        spec.Bytes32()
        for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX))
    ]

    # Ensure that finality checkpoint is genesis
    assert state.finalized_checkpoint.epoch == 0
    # Finality is unchanged
    finality_header = spec.BeaconBlockHeader()
    finality_branch = [
        spec.Bytes32()
        for _ in range(spec.floorlog2(spec.FINALIZED_ROOT_INDEX))
    ]

    update = spec.LightClientUpdate(
        header=block_header,
        next_sync_committee=state.next_sync_committee,
        next_sync_committee_branch=next_sync_committee_branch,
        finality_header=finality_header,
        finality_branch=finality_branch,
        sync_committee_bits=sync_committee_bits,
        sync_committee_signature=sync_committee_signature,
        fork_version=state.fork.current_version,
    )

    spec.process_light_client_update(store, update, state.slot,
                                     state.genesis_validators_root)

    assert len(store.valid_updates) == 1
    assert store.valid_updates[0] == update
    assert store.snapshot == pre_snapshot
def test_invalid_signature_past_block(spec, state):
    committee = spec.get_sync_committee_indices(state, spec.get_current_epoch(state))

    yield 'pre', state

    blocks = []
    for _ in range(2):
        # NOTE: need to transition twice to move beyond the degenerate case at genesis
        block = build_empty_block_for_next_slot(spec, state)
        # Valid sync committee signature here...
        block.body.sync_committee_bits = [True] * len(committee)
        block.body.sync_committee_signature = compute_aggregate_sync_committee_signature(
            spec,
            state,
            block.slot - 1,
            committee,
        )

        signed_block = state_transition_and_sign_block(spec, state, block)
        blocks.append(signed_block)

    invalid_block = build_empty_block_for_next_slot(spec, state)
    # Invalid signature from a slot other than the previous
    invalid_block.body.sync_committee_bits = [True] * len(committee)
    invalid_block.body.sync_committee_signature = compute_aggregate_sync_committee_signature(
        spec,
        state,
        invalid_block.slot - 2,
        committee,
    )
    blocks.append(invalid_block)

    expect_assertion_error(lambda: transition_unsigned_block(spec, state, invalid_block))

    yield 'blocks', blocks
    yield 'post', None
def test_invalid_signature_bad_domain(spec, state):
    committee_indices = compute_committee_indices(spec, state)

    block = build_empty_block_for_next_slot(spec, state)
    block.body.sync_aggregate = spec.SyncAggregate(
        sync_committee_bits=[True] * len(committee_indices),
        sync_committee_signature=compute_aggregate_sync_committee_signature(
            spec,
            state,
            block.slot - 1,
            committee_indices,  # full committee signs
            block_root=block.parent_root,
            domain_type=spec.DOMAIN_BEACON_ATTESTER,  # Incorrect domain
        )
    )
    yield from run_sync_committee_processing(spec, state, block, expect_exception=True)
Beispiel #14
0
def test_invalid_signature_missing_participant(spec, state):
    committee_indices = compute_committee_indices(spec, state, state.current_sync_committee)
    rng = random.Random(2020)
    random_participant = rng.choice(committee_indices)

    block = build_empty_block_for_next_slot(spec, state)
    # Exclude one participant whose signature was included.
    block.body.sync_aggregate = spec.SyncAggregate(
        sync_committee_bits=[index != random_participant for index in committee_indices],
        sync_committee_signature=compute_aggregate_sync_committee_signature(
            spec,
            state,
            block.slot - 1,
            committee_indices,  # full committee signs
        )
    )
    yield from run_sync_committee_processing(spec, state, block, expect_exception=True)
Beispiel #15
0
def test_invalid_signature_extra_participant(spec, state):
    committee_indices = compute_committee_indices(spec, state, state.current_sync_committee)
    rng = random.Random(3030)
    random_participant = rng.choice(committee_indices)

    block = build_empty_block_for_next_slot(spec, state)
    # Exclude one signature even though the block claims the entire committee participated.
    block.body.sync_aggregate = spec.SyncAggregate(
        sync_committee_bits=[True] * len(committee_indices),
        sync_committee_signature=compute_aggregate_sync_committee_signature(
            spec,
            state,
            block.slot - 1,
            [index for index in committee_indices if index != random_participant],
        )
    )

    yield from run_sync_committee_processing(spec, state, block, expect_exception=True)
def test_invalid_signature_missing_participant(spec, state):
    committee = spec.get_sync_committee_indices(state, spec.get_current_epoch(state))
    random_participant = random.choice(committee)

    yield 'pre', state

    block = build_empty_block_for_next_slot(spec, state)
    # Exclude one participant whose signature was included.
    block.body.sync_committee_bits = [index != random_participant for index in committee]
    block.body.sync_committee_signature = compute_aggregate_sync_committee_signature(
        spec,
        state,
        block.slot - 1,
        committee,  # full committee signs
    )

    yield 'blocks', [block]
    expect_assertion_error(lambda: spec.process_sync_committee(state, block.body))
    yield 'post', None
def test_invalid_signature_extra_participant(spec, state):
    committee = spec.get_sync_committee_indices(state, spec.get_current_epoch(state))
    random_participant = random.choice(committee)

    yield 'pre', state

    block = build_empty_block_for_next_slot(spec, state)
    # Exclude one signature even though the block claims the entire committee participated.
    block.body.sync_committee_bits = [True] * len(committee)
    block.body.sync_committee_signature = compute_aggregate_sync_committee_signature(
        spec,
        state,
        block.slot - 1,
        [index for index in committee if index != random_participant],
    )

    yield 'blocks', [block]
    expect_assertion_error(lambda: spec.process_sync_committee(state, block.body))
    yield 'post', None
Beispiel #18
0
def run_sync_committee_sanity_test(spec, state, fraction_full=1.0):
    committee = spec.get_sync_committee_indices(state, spec.get_current_epoch(state))
    participants = random.sample(committee, int(len(committee) * fraction_full))

    yield 'pre', state

    block = build_empty_block_for_next_slot(spec, state)
    block.body.sync_aggregate = spec.SyncAggregate(
        sync_committee_bits=[index in participants for index in committee],
        sync_committee_signature=compute_aggregate_sync_committee_signature(
            spec,
            state,
            block.slot - 1,
            participants,
        )
    )
    signed_block = state_transition_and_sign_block(spec, state, block)

    yield 'blocks', [signed_block]
    yield 'post', state
def test_sync_committee_rewards(spec, state):
    committee = spec.get_sync_committee_indices(state, spec.get_current_epoch(state))
    committee_size = len(committee)
    active_validator_count = len(spec.get_active_validator_indices(state, spec.get_current_epoch(state)))

    yield 'pre', state

    pre_balances = state.balances.copy()

    block = build_empty_block_for_next_slot(spec, state)
    block.body.sync_committee_bits = [True] * committee_size
    block.body.sync_committee_signature = compute_aggregate_sync_committee_signature(
        spec,
        state,
        block.slot - 1,
        committee,
    )

    signed_block = state_transition_and_sign_block(spec, state, block)

    yield 'blocks', [signed_block]
    yield 'post', state

    for index in range(len(state.validators)):
        expected_reward = 0

        if index == block.proposer_index:
            expected_reward += sum([spec.get_proposer_reward(state, index) for index in committee])

        if index in committee:
            expected_reward += compute_sync_committee_participant_reward(
                spec,
                state,
                index,
                active_validator_count,
                committee_size
            )

        assert state.balances[index] == pre_balances[index] + expected_reward
def test_sync_committee_with_nonparticipating_exited_member(spec, state):
    # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
    state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH

    # move forward via some blocks
    for _ in range(3):
        next_epoch_via_block(spec, state)

    committee_indices = compute_committee_indices(spec, state)
    rng = random.Random(1010)

    exited_index = _exit_validator_from_committee_and_transition_state(
        spec,
        state,
        committee_indices,
        rng,
        lambda v: v.exit_epoch,
    )
    exited_pubkey = state.validators[exited_index].pubkey

    current_epoch = spec.get_current_epoch(state)
    assert current_epoch < state.validators[exited_index].withdrawable_epoch

    exited_committee_index = state.current_sync_committee.pubkeys.index(exited_pubkey)
    block = build_empty_block_for_next_slot(spec, state)
    committee_bits = [i != exited_committee_index for i in committee_indices]
    committee_indices = [index for index in committee_indices if index != exited_committee_index]
    block.body.sync_aggregate = spec.SyncAggregate(
        sync_committee_bits=committee_bits,
        sync_committee_signature=compute_aggregate_sync_committee_signature(
            spec,
            state,
            block.slot - 1,
            committee_indices,  # with exited validator removed
            block_root=block.parent_root,
        )
    )
    yield from run_sync_committee_processing(spec, state, block)
Beispiel #21
0
def run_sync_committee_sanity_test(spec, state, fraction_full=1.0):
    all_pubkeys = [v.pubkey for v in state.validators]
    committee = [
        all_pubkeys.index(pubkey)
        for pubkey in state.current_sync_committee.pubkeys
    ]
    participants = random.sample(committee,
                                 int(len(committee) * fraction_full))

    yield 'pre', state

    block = build_empty_block_for_next_slot(spec, state)
    block.body.sync_aggregate = spec.SyncAggregate(
        sync_committee_bits=[index in participants for index in committee],
        sync_committee_signature=compute_aggregate_sync_committee_signature(
            spec,
            state,
            block.slot - 1,
            participants,
        ))
    signed_block = state_transition_and_sign_block(spec, state, block)

    yield 'blocks', [signed_block]
    yield 'post', state
def test_process_light_client_update_timeout(spec, state):
    pre_snapshot = spec.LightClientSnapshot(
        header=spec.BeaconBlockHeader(),
        current_sync_committee=state.current_sync_committee,
        next_sync_committee=state.next_sync_committee,
    )
    store = spec.LightClientStore(snapshot=pre_snapshot, valid_updates=[])

    # Forward to next sync committee period
    next_slots(spec, state,
               spec.SLOTS_PER_EPOCH * (spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD))
    snapshot_period = spec.compute_epoch_at_slot(
        pre_snapshot.header.slot) // spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD
    update_period = spec.compute_epoch_at_slot(
        state.slot) // spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD
    assert snapshot_period + 1 == update_period

    block = build_empty_block_for_next_slot(spec, state)
    signed_block = state_transition_and_sign_block(spec, state, block)
    block_header = spec.BeaconBlockHeader(
        slot=signed_block.message.slot,
        proposer_index=signed_block.message.proposer_index,
        parent_root=signed_block.message.parent_root,
        state_root=signed_block.message.state_root,
        body_root=signed_block.message.body.hash_tree_root(),
    )

    # Sync committee signing the finalized_block_header
    committee = spec.get_sync_committee_indices(state,
                                                spec.get_current_epoch(state))
    sync_committee_bits = [True] * len(committee)
    sync_committee_signature = compute_aggregate_sync_committee_signature(
        spec,
        state,
        block_header.slot,
        committee,
        block_root=spec.Root(block_header.hash_tree_root()),
    )

    # Sync committee is updated
    next_sync_committee_branch = build_proof(state.get_backing(),
                                             spec.NEXT_SYNC_COMMITTEE_INDEX)
    # Finality is unchanged
    finality_header = spec.BeaconBlockHeader()
    finality_branch = [
        spec.Bytes32()
        for _ in range(spec.floorlog2(spec.FINALIZED_ROOT_INDEX))
    ]

    update = spec.LightClientUpdate(
        header=block_header,
        next_sync_committee=state.next_sync_committee,
        next_sync_committee_branch=next_sync_committee_branch,
        finality_header=finality_header,
        finality_branch=finality_branch,
        sync_committee_bits=sync_committee_bits,
        sync_committee_signature=sync_committee_signature,
        fork_version=state.fork.current_version,
    )

    spec.process_light_client_update(store, update, state.slot,
                                     state.genesis_validators_root)

    # snapshot has been updated
    assert len(store.valid_updates) == 0
    assert store.snapshot.header == update.header
def test_process_light_client_update_finality_updated(spec, state):
    pre_snapshot = spec.LightClientSnapshot(
        header=spec.BeaconBlockHeader(),
        current_sync_committee=state.current_sync_committee,
        next_sync_committee=state.next_sync_committee,
    )
    store = spec.LightClientStore(snapshot=pre_snapshot, valid_updates=[])

    # Change finality
    blocks = []
    next_slots(spec, state, spec.SLOTS_PER_EPOCH * 2)
    for epoch in range(3):
        prev_state, new_blocks, state = next_epoch_with_attestations(
            spec, state, True, True)
        blocks += new_blocks
    # Ensure that finality checkpoint has changed
    assert state.finalized_checkpoint.epoch == 3
    # Ensure that it's same period
    snapshot_period = spec.compute_epoch_at_slot(
        pre_snapshot.header.slot) // spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD
    update_period = spec.compute_epoch_at_slot(
        state.slot) // spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD
    assert snapshot_period == update_period

    # Updated sync_committee and finality
    next_sync_committee_branch = [
        spec.Bytes32()
        for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX))
    ]
    finalized_block_header = blocks[spec.SLOTS_PER_EPOCH - 1].message
    assert finalized_block_header.slot == spec.compute_start_slot_at_epoch(
        state.finalized_checkpoint.epoch)
    assert finalized_block_header.hash_tree_root(
    ) == state.finalized_checkpoint.root
    finality_branch = build_proof(state.get_backing(),
                                  spec.FINALIZED_ROOT_INDEX)

    # Build block header
    block = build_empty_block(spec, state)
    block_header = spec.BeaconBlockHeader(
        slot=block.slot,
        proposer_index=block.proposer_index,
        parent_root=block.parent_root,
        state_root=state.hash_tree_root(),
        body_root=block.body.hash_tree_root(),
    )

    # Sync committee signing the finalized_block_header
    committee = spec.get_sync_committee_indices(state,
                                                spec.get_current_epoch(state))
    sync_committee_bits = [True] * len(committee)
    sync_committee_signature = compute_aggregate_sync_committee_signature(
        spec,
        state,
        block_header.slot,
        committee,
        block_root=spec.Root(block_header.hash_tree_root()),
    )

    update = spec.LightClientUpdate(
        header=finalized_block_header,
        next_sync_committee=state.next_sync_committee,
        next_sync_committee_branch=next_sync_committee_branch,
        finality_header=block_header,  # block_header is the signed header
        finality_branch=finality_branch,
        sync_committee_bits=sync_committee_bits,
        sync_committee_signature=sync_committee_signature,
        fork_version=state.fork.current_version,
    )

    spec.process_light_client_update(store, update, state.slot,
                                     state.genesis_validators_root)

    # snapshot has been updated
    assert len(store.valid_updates) == 0
    assert store.snapshot.header == update.header