def test_balance_driven_status_transitions(spec, state): current_epoch = spec.get_current_epoch(state) validator_index = spec.get_active_validator_indices(state, current_epoch)[-1] assert state.validator_registry[ validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH # set validator balance to below ejection threshold state.validator_registry[ validator_index].effective_balance = spec.EJECTION_BALANCE yield 'pre', state # trigger epoch transition block = build_empty_block_for_next_slot(spec, state) block.slot += spec.SLOTS_PER_EPOCH sign_block(spec, state, block) spec.state_transition(state, block) yield 'blocks', [block], List[spec.BeaconBlock] yield 'post', state assert state.validator_registry[ validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
def test_attestation(state): state.slot = spec.SLOTS_PER_EPOCH yield 'pre', state attestation = get_valid_attestation(state, signed=True) # Add to state via block transition pre_current_attestations_len = len(state.current_epoch_attestations) attestation_block = build_empty_block_for_next_slot(state) attestation_block.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY attestation_block.body.attestations.append(attestation) sign_block(state, attestation_block) state_transition(state, attestation_block) assert len(state.current_epoch_attestations) == pre_current_attestations_len + 1 # Epoch transition should move to previous_epoch_attestations pre_current_attestations_root = spec.hash_tree_root(state.current_epoch_attestations) epoch_block = build_empty_block_for_next_slot(state) epoch_block.slot += spec.SLOTS_PER_EPOCH sign_block(state, epoch_block) state_transition(state, epoch_block) yield 'blocks', [attestation_block, epoch_block], [spec.BeaconBlock] yield 'post', state assert len(state.current_epoch_attestations) == 0 assert spec.hash_tree_root(state.previous_epoch_attestations) == pre_current_attestations_root
def test_proposer_slashing(state): # copy for later balance lookups. pre_state = deepcopy(state) proposer_slashing = get_valid_proposer_slashing(state, signed_1=True, signed_2=True) validator_index = proposer_slashing.proposer_index assert not state.validator_registry[validator_index].slashed yield 'pre', state # # Add to state via block transition # block = build_empty_block_for_next_slot(state) block.body.proposer_slashings.append(proposer_slashing) sign_block(state, block) yield 'blocks', [block], [spec.BeaconBlock] state_transition(state, block) yield 'post', state # check if slashed slashed_validator = state.validator_registry[validator_index] assert slashed_validator.slashed assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH # lost whistleblower reward assert get_balance(state, validator_index) < get_balance(pre_state, validator_index)
def test_transfer(state): # overwrite default 0 to test spec.MAX_TRANSFERS = 1 sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1] amount = get_balance(state, sender_index) transfer = get_valid_transfer(state, state.slot + 1, sender_index, amount, signed=True) recipient_index = transfer.recipient pre_transfer_recipient_balance = get_balance(state, recipient_index) # un-activate so validator can transfer state.validator_registry[sender_index].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH yield 'pre', state # Add to state via block transition block = build_empty_block_for_next_slot(state) block.body.transfers.append(transfer) sign_block(state, block) yield 'blocks', [block], [spec.BeaconBlock] state_transition(state, block) yield 'post', state sender_balance = get_balance(state, sender_index) recipient_balance = get_balance(state, recipient_index) assert sender_balance == 0 assert recipient_balance == pre_transfer_recipient_balance + amount
def add_attestation_to_state(spec, state, attestation, slot): block = build_empty_block_for_next_slot(spec, state) block.slot = slot block.body.attestations.append(attestation) spec.process_slots(state, block.slot) sign_block(spec, state, block) spec.state_transition(state, block)
def run_process_registry_updates(spec, state, valid=True): """ Run ``process_crosslinks``, yielding: - pre-state ('pre') - post-state ('post'). If ``valid == False``, run expecting ``AssertionError`` """ # transition state to slot before state transition slot = state.slot + (spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH) - 1 block = build_empty_block_for_next_slot(spec, state) block.slot = slot sign_block(spec, state, block) state_transition(state, block) # cache state before epoch transition spec.process_slot(state) # process components of epoch transition before registry update spec.process_justification_and_finalization(state) spec.process_crosslinks(state) spec.process_rewards_and_penalties(state) yield 'pre', state spec.process_registry_updates(state) yield 'post', state
def test_deposit_in_block(spec, state): initial_registry_len = len(state.validators) initial_balances_len = len(state.balances) validator_index = len(state.validators) amount = spec.MAX_EFFECTIVE_BALANCE deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True) yield 'pre', state block = build_empty_block_for_next_slot(spec, state) block.body.deposits.append(deposit) sign_block(spec, state, block) state_transition_and_sign_block(spec, state, block) yield 'blocks', [block] yield 'post', state assert len(state.validators) == initial_registry_len + 1 assert len(state.balances) == initial_balances_len + 1 assert get_balance(state, validator_index) == spec.MAX_EFFECTIVE_BALANCE assert state.validators[validator_index].pubkey == pubkeys[validator_index]
def test_eth1_data_votes_no_consensus(spec, state): # Don't run when it will take very, very long to simulate. Minimal configuration suffices. if spec.SLOTS_PER_ETH1_VOTING_PERIOD > 16: return pre_eth1_hash = state.eth1_data.block_hash offset_block = build_empty_block(spec, state, slot=spec.SLOTS_PER_ETH1_VOTING_PERIOD - 1) sign_block(spec, state, offset_block) state_transition_and_sign_block(spec, state, offset_block) yield 'pre', state a = b'\xaa' * 32 b = b'\xbb' * 32 blocks = [] for i in range(0, spec.SLOTS_PER_ETH1_VOTING_PERIOD): block = build_empty_block_for_next_slot(spec, state) # wait for precisely 50% for A, then start voting B for other 50% block.body.eth1_data.block_hash = b if i * 2 >= spec.SLOTS_PER_ETH1_VOTING_PERIOD else a sign_block(spec, state, block) state_transition_and_sign_block(spec, state, block) blocks.append(block) assert len(state.eth1_data_votes) == spec.SLOTS_PER_ETH1_VOTING_PERIOD assert state.eth1_data.block_hash == pre_eth1_hash yield 'blocks', blocks yield 'post', state
def state_transition_and_sign_block(spec, state, block): """ State transition via the provided ``block`` then package the block with the state root and signature. """ spec.state_transition(state, block) block.state_root = state.hash_tree_root() sign_block(spec, state, block)
def test_invalid_state_root(spec, state): yield 'pre', state block = build_empty_block_for_next_slot(spec, state) block.state_root = b"\xaa" * 32 sign_block(spec, state, block) expect_assertion_error( lambda: spec.state_transition(state, block, validate_state_root=True)) yield 'blocks', [block] yield 'post', None
def test_skipped_slots(spec, state): pre_slot = state.slot yield 'pre', state block = build_empty_block_for_next_slot(spec, state) block.slot += 3 sign_block(spec, state, block) yield 'blocks', [block], List[spec.BeaconBlock] spec.state_transition(state, block) yield 'post', state assert state.slot == block.slot for slot in range(pre_slot, state.slot): assert spec.get_block_root_at_slot(state, slot) == block.parent_root
def test_empty_epoch_transition(state): pre_slot = state.slot yield 'pre', state block = build_empty_block_for_next_slot(state) block.slot += spec.SLOTS_PER_EPOCH sign_block(state, block) yield 'blocks', [block], [spec.BeaconBlock] state_transition(state, block) yield 'post', state assert state.slot == block.slot for slot in range(pre_slot, state.slot): assert get_block_root_at_slot(state, slot) == block.previous_block_root
def test_empty_epoch_transition(spec, state): pre_slot = state.slot yield 'pre', state block = build_empty_block_for_next_slot(spec, state) block.slot += spec.SLOTS_PER_EPOCH sign_block(spec, state, block) state_transition_and_sign_block(spec, state, block) yield 'blocks', [block] yield 'post', state assert state.slot == block.slot for slot in range(pre_slot, state.slot): assert spec.get_block_root_at_slot(state, slot) == block.parent_root
def test_on_block_bad_parent_root(spec, state): # Initialization store = spec.get_genesis_store(state) time = 100 spec.on_tick(store, time) # Fail receiving block of `GENESIS_SLOT + 1` slot block = build_empty_block_for_next_slot(spec, state) spec.state_transition(state, block) block.state_root = state.hash_tree_root() block.parent_root = b'\x45' * 32 sign_block(spec, state, block) run_on_block(spec, store, block, False)
def process_and_sign_block_without_header_validations(spec, state, block): """ Artificially bypass the restrictions in the state transition to transition and sign block WARNING UNSAFE: Only use when generating valid-looking invalid blocks for test vectors """ # Perform single mutation in `process_block_header` state.latest_block_header = spec.BeaconBlockHeader( slot=block.slot, proposer_index=block.proposer_index, parent_root=block.parent_root, state_root=spec.Bytes32(), body_root=block.body.hash_tree_root(), ) # Perform rest of process_block transitions spec.process_randao(state, block.body) spec.process_eth1_data(state, block.body) spec.process_operations(state, block.body) # Insert post-state rot block.state_root = state.hash_tree_root() # Sign block return sign_block(spec, state, block)
def process_and_sign_block_without_header_validations(spec, state, block): """ Artificially bypass the restrictions in the state transition to transition and sign block WARNING UNSAFE: Only use when generating valid-looking invalid blocks for test vectors """ # Perform single mutation in `process_block_header` state.latest_block_header = spec.BeaconBlockHeader( slot=block.slot, proposer_index=block.proposer_index, parent_root=block.parent_root, state_root=spec.Bytes32(), body_root=block.body.hash_tree_root(), ) if is_post_merge(spec): if spec.is_execution_enabled(state, block.body): spec.process_execution_payload(state, block.body.execution_payload, spec.EXECUTION_ENGINE) # Perform rest of process_block transitions spec.process_randao(state, block.body) spec.process_eth1_data(state, block.body) spec.process_operations(state, block.body) if is_post_altair(spec): spec.process_sync_aggregate(state, block.body.sync_aggregate) # Insert post-state rot block.state_root = state.hash_tree_root() # Sign block return sign_block(spec, state, block)
def test_skipped_slots(spec, state): pre_slot = state.slot yield 'pre', state block = build_empty_block_for_next_slot(spec, state) block.slot += 3 sign_block(spec, state, block) state_transition_and_sign_block(spec, state, block) yield 'blocks', [block], List[spec.BeaconBlock] yield 'post', state assert state.slot == block.slot assert spec.get_randao_mix(state, spec.get_current_epoch(state)) != spec.ZERO_HASH for slot in range(pre_slot, state.slot): assert spec.get_block_root_at_slot(state, slot) == block.parent_root
def test_expected_deposit_in_block(spec, state): # Make the state expect a deposit, then don't provide it. state.eth1_data.deposit_count += 1 yield 'pre', state block = build_empty_block_for_next_slot(spec, state) sign_block(spec, state, block) bad = False try: state_transition_and_sign_block(spec, state, block) bad = True except AssertionError: pass if bad: raise AssertionError("expected deposit was not enforced") yield 'blocks', [block] yield 'post', None
def test_historical_batch(spec, state): state.slot += spec.SLOTS_PER_HISTORICAL_ROOT - ( state.slot % spec.SLOTS_PER_HISTORICAL_ROOT) - 1 pre_historical_roots_len = len(state.historical_roots) yield 'pre', state block = build_empty_block_for_next_slot(spec, state, signed=True) sign_block(spec, state, block) state_transition_and_sign_block(spec, state, block) yield 'blocks', [block] yield 'post', state assert state.slot == block.slot assert spec.get_current_epoch(state) % (spec.SLOTS_PER_HISTORICAL_ROOT // spec.SLOTS_PER_EPOCH) == 0 assert len(state.historical_roots) == pre_historical_roots_len + 1
def test_voluntary_exit(state): validator_index = get_active_validator_indices( state, get_current_epoch(state) )[-1] # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH yield 'pre', state voluntary_exit = VoluntaryExit( epoch=get_current_epoch(state), validator_index=validator_index, ) voluntary_exit.signature = bls_sign( message_hash=signing_root(voluntary_exit), privkey=privkeys[validator_index], domain=get_domain( state=state, domain_type=spec.DOMAIN_VOLUNTARY_EXIT, ) ) # Add to state via block transition initiate_exit_block = build_empty_block_for_next_slot(state) initiate_exit_block.body.voluntary_exits.append(voluntary_exit) sign_block(state, initiate_exit_block) state_transition(state, initiate_exit_block) assert state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH # Process within epoch transition exit_block = build_empty_block_for_next_slot(state) exit_block.slot += spec.SLOTS_PER_EPOCH sign_block(state, exit_block) state_transition(state, exit_block) yield 'blocks', [initiate_exit_block, exit_block], [spec.BeaconBlock] yield 'post', state assert state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
def state_transition_and_sign_block(spec, state, block, expect_fail=False): """ State transition via the provided ``block`` then package the block with the correct state root and signature. """ if expect_fail: expect_assertion_error(lambda: transition_unsigned_block(spec, state, block)) else: transition_unsigned_block(spec, state, block) block.state_root = state.hash_tree_root() return sign_block(spec, state, block)
def run_process_crosslinks(state, valid=True): """ Run ``process_crosslinks``, yielding: - pre-state ('pre') - post-state ('post'). If ``valid == False``, run expecting ``AssertionError`` """ # transition state to slot before state transition slot = state.slot + (spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH) - 1 block = build_empty_block_for_next_slot(state) block.slot = slot sign_block(state, block) state_transition(state, block) # cache state before epoch transition cache_state(state) yield 'pre', state process_crosslinks(state) yield 'post', state
def test_attester_slashing(spec, state): # copy for later balance lookups. pre_state = deepcopy(state) attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True) validator_index = ( attester_slashing.attestation_1.custody_bit_0_indices + attester_slashing.attestation_1.custody_bit_1_indices)[0] assert not state.validators[validator_index].slashed yield 'pre', state # # Add to state via block transition # block = build_empty_block_for_next_slot(spec, state) block.body.attester_slashings.append(attester_slashing) sign_block(spec, state, block) state_transition_and_sign_block(spec, state, block) yield 'blocks', [block] yield 'post', state slashed_validator = state.validators[validator_index] assert slashed_validator.slashed assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH # lost whistleblower reward assert get_balance(state, validator_index) < get_balance( pre_state, validator_index) proposer_index = spec.get_beacon_proposer_index(state) # gained whistleblower reward assert (get_balance(state, proposer_index) > get_balance( pre_state, proposer_index))
def test_deposit_top_up(state): validator_index = 0 amount = spec.MAX_EFFECTIVE_BALANCE // 4 deposit = prepare_state_and_deposit(state, validator_index, amount) initial_registry_len = len(state.validator_registry) initial_balances_len = len(state.balances) validator_pre_balance = get_balance(state, validator_index) yield 'pre', state block = build_empty_block_for_next_slot(state) block.body.deposits.append(deposit) sign_block(state, block) yield 'blocks', [block], [spec.BeaconBlock] state_transition(state, block) yield 'post', state assert len(state.validator_registry) == initial_registry_len assert len(state.balances) == initial_balances_len assert get_balance(state, validator_index) == validator_pre_balance + amount
def _state_transition_and_sign_block_at_slot(spec, state): """ Cribbed from ``transition_unsigned_block`` helper where the early parts of the state transition have already been applied to ``state``. Used to produce a block during an irregular state transition. """ block = build_empty_block(spec, state) assert state.latest_block_header.slot < block.slot assert state.slot == block.slot spec.process_block(state, block) block.state_root = state.hash_tree_root() return sign_block(spec, state, block)
def test_invalid_proposer_index_sig_from_proposer_index(spec, state): yield 'pre', state block = build_empty_block_for_next_slot(spec, state) # Set invalid proposer index but correct signature wrt proposer_index active_indices = spec.get_active_validator_indices(state, spec.get_current_epoch(state)) active_indices = [i for i in active_indices if i != block.proposer_index] block.proposer_index = active_indices[0] # invalid proposer index invalid_signed_block = sign_block(spec, state, block, block.proposer_index) expect_assertion_error(lambda: spec.state_transition(state, invalid_signed_block)) yield 'blocks', [invalid_signed_block] yield 'post', None
def test_prev_slot_block_transition(spec, state): # Go to clean slot spec.process_slots(state, state.slot + 1) # Make a block for it block = build_empty_block(spec, state, slot=state.slot) proposer_index = spec.get_beacon_proposer_index(state) # Transition to next slot, above block will not be invalid on top of new state. spec.process_slots(state, state.slot + 1) yield 'pre', state # State is beyond block slot, but the block can still be realistic when invalid. # Try the transition, and update the state root to where it is halted. Then sign with the supposed proposer. expect_assertion_error(lambda: transition_unsigned_block(spec, state, block)) block.state_root = state.hash_tree_root() signed_block = sign_block(spec, state, block, proposer_index=proposer_index) yield 'blocks', [signed_block] yield 'post', None
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 test_on_block_bad_parent_root(spec, state): 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 # Fail receiving block of `GENESIS_SLOT + 1` slot block = build_empty_block_for_next_slot(spec, state) transition_unsigned_block(spec, state, block) block.state_root = state.hash_tree_root() block.parent_root = b'\x45' * 32 signed_block = sign_block(spec, state, block) yield from add_block(spec, store, signed_block, test_steps, valid=False) yield 'steps', test_steps
def _state_transition_and_sign_block_at_slot(spec, state, operation_dict=None): """ Cribbed from ``transition_unsigned_block`` helper where the early parts of the state transition have already been applied to ``state``. Used to produce a block during an irregular state transition. The optional `operation_dict` is a dict of {'<BeaconBlockBody field>': <value>}. This is used for assigning the block operations. p.s. we can't just pass `body` and assign it because randao_reveal and eth1_data was set in `build_empty_block` Thus use dict to pass operations. """ block = build_empty_block(spec, state) if operation_dict: _set_operations_by_dict(block, operation_dict) assert state.latest_block_header.slot < block.slot assert state.slot == block.slot spec.process_block(state, block) block.state_root = state.hash_tree_root() return sign_block(spec, state, block)