def test_voluntary_exit(spec, state): validator_index = spec.get_active_validator_indices( state, spec.get_current_epoch(state))[-1] # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH signed_exits = prepare_signed_exits(spec, state, [validator_index]) yield 'pre', state # Add to state via block transition initiate_exit_block = build_empty_block_for_next_slot(spec, state) initiate_exit_block.body.voluntary_exits = signed_exits signed_initiate_exit_block = state_transition_and_sign_block( spec, state, initiate_exit_block) assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH # Process within epoch transition exit_block = build_empty_block(spec, state, state.slot + spec.SLOTS_PER_EPOCH) signed_exit_block = state_transition_and_sign_block( spec, state, exit_block) yield 'blocks', [signed_initiate_exit_block, signed_exit_block] yield 'post', state assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
def run_slash_and_exit(spec, state, slash_index, exit_index, valid=True): """ Helper function to run a test that slashes and exits two validators """ # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit state.slot += spec.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH yield 'pre', state block = build_empty_block_for_next_slot(spec, state) proposer_slashing = get_valid_proposer_slashing(spec, state, slashed_index=slash_index, signed_1=True, signed_2=True) signed_exit = prepare_signed_exits(spec, state, [exit_index])[0] block.body.proposer_slashings.append(proposer_slashing) block.body.voluntary_exits.append(signed_exit) signed_block = state_transition_and_sign_block(spec, state, block, expect_fail=(not valid)) yield 'blocks', [signed_block] if not valid: yield 'post', None return yield 'post', state
def get_random_voluntary_exits(spec, state, to_be_slashed_indices, rng): num_exits = rng.randrange(spec.MAX_VOLUNTARY_EXITS) indices = set( spec.get_active_validator_indices( state, spec.get_current_epoch(state)).copy()) eligible_indices = indices - to_be_slashed_indices exit_indices = [eligible_indices.pop() for _ in range(num_exits)] return prepare_signed_exits(spec, state, exit_indices)
def get_random_voluntary_exits(spec, state, to_be_slashed_indices, rng): num_exits = rng.randrange(1, spec.MAX_VOLUNTARY_EXITS) active_indices = set( spec.get_active_validator_indices( state, spec.get_current_epoch(state)).copy()) indices = set(index for index in active_indices if _eligible_for_exit(spec, state, index)) eligible_indices = indices - to_be_slashed_indices indices_count = min(num_exits, len(eligible_indices)) exit_indices = [eligible_indices.pop() for _ in range(indices_count)] return prepare_signed_exits(spec, state, exit_indices)
def test_double_validator_exit_same_block(spec, state): validator_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1] # move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit state.slot += spec.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH # Same index tries to exit twice, but should only be able to do so once. signed_exits = prepare_signed_exits(spec, state, [validator_index, validator_index]) yield 'pre', state # Add to state via block transition initiate_exit_block = build_empty_block_for_next_slot(spec, state) initiate_exit_block.body.voluntary_exits = signed_exits signed_initiate_exit_block = state_transition_and_sign_block(spec, state, initiate_exit_block, expect_fail=True) yield 'blocks', [signed_initiate_exit_block] yield 'post', None
def run_transition_with_operation(state, fork_epoch, spec, post_spec, pre_tag, post_tag, operation_type, operation_at_slot): """ Generate `operation_type` operation with the spec before fork. The operation would be included into the block at `operation_at_slot`. """ is_at_fork = operation_at_slot == fork_epoch * spec.SLOTS_PER_EPOCH is_right_before_fork = operation_at_slot == fork_epoch * spec.SLOTS_PER_EPOCH - 1 assert is_at_fork or is_right_before_fork if is_at_fork: transition_until_fork(spec, state, fork_epoch) elif is_right_before_fork: _transition_until_fork_minus_one(spec, state, fork_epoch) is_slashing_operation = operation_type in (OperationType.PROPOSER_SLASHING, OperationType.ATTESTER_SLASHING) # prepare operation selected_validator_index = None if is_slashing_operation: # avoid slashing the next proposer future_state = state.copy() next_slot(spec, future_state) proposer_index = spec.get_beacon_proposer_index(future_state) selected_validator_index = (proposer_index + 1) % len(state.validators) if operation_type == OperationType.PROPOSER_SLASHING: proposer_slashing = get_valid_proposer_slashing( spec, state, slashed_index=selected_validator_index, signed_1=True, signed_2=True) operation_dict = {'proposer_slashings': [proposer_slashing]} else: # operation_type == OperationType.ATTESTER_SLASHING: attester_slashing = get_valid_attester_slashing_by_indices( spec, state, [selected_validator_index], signed_1=True, signed_2=True, ) operation_dict = {'attester_slashings': [attester_slashing]} elif operation_type == OperationType.DEPOSIT: # create a new deposit selected_validator_index = len(state.validators) amount = spec.MAX_EFFECTIVE_BALANCE deposit = prepare_state_and_deposit(spec, state, selected_validator_index, amount, signed=True) operation_dict = {'deposits': [deposit]} elif operation_type == OperationType.VOLUNTARY_EXIT: selected_validator_index = 0 signed_exits = prepare_signed_exits(spec, state, [selected_validator_index]) operation_dict = {'voluntary_exits': signed_exits} def _check_state(): if operation_type == OperationType.PROPOSER_SLASHING: slashed_proposer = state.validators[ proposer_slashing.signed_header_1.message.proposer_index] assert slashed_proposer.slashed elif operation_type == OperationType.ATTESTER_SLASHING: indices = set( attester_slashing.attestation_1.attesting_indices ).intersection(attester_slashing.attestation_2.attesting_indices) assert selected_validator_index in indices assert len(indices) > 0 for validator_index in indices: assert state.validators[validator_index].slashed elif operation_type == OperationType.DEPOSIT: assert not post_spec.is_active_validator( state.validators[selected_validator_index], post_spec.get_current_epoch(state)) elif operation_type == OperationType.VOLUNTARY_EXIT: validator = state.validators[selected_validator_index] assert validator.exit_epoch < post_spec.FAR_FUTURE_EPOCH yield "pre", state blocks = [] if is_right_before_fork: # add a block with operation. block = build_empty_block_for_next_slot(spec, state) _set_operations_by_dict(block, operation_dict) signed_block = state_transition_and_sign_block(spec, state, block) blocks.append(pre_tag(signed_block)) _check_state() # irregular state transition to handle fork: _operation_at_slot = operation_dict if is_at_fork else None state, block = do_fork(state, spec, post_spec, fork_epoch, operation_dict=_operation_at_slot) blocks.append(post_tag(block)) if is_at_fork: _check_state() # after the fork if operation_type == OperationType.DEPOSIT: state = _transition_until_active(post_spec, state, post_tag, blocks, selected_validator_index) else: # avoid using the slashed validators as block proposers ignoring_proposers = [selected_validator_index ] if is_slashing_operation else None # 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, ignoring_proposers=ignoring_proposers, ) yield "blocks", blocks yield "post", state