def process_final_updates(state: BeaconState, config: Eth2Config) -> BeaconState: new_eth1_data_votes = _determine_next_eth1_votes(state, config) new_validators = _update_effective_balances(state, config) new_start_shard = _compute_next_start_shard(state, config) new_active_index_roots = _compute_next_active_index_roots(state, config) new_compact_committees_roots = _compute_next_compact_committees_roots( state.copy(validators=new_validators, start_shard=new_start_shard), config) new_slashings = _compute_next_slashings(state, config) new_randao_mixes = _compute_next_randao_mixes(state, config) new_historical_roots = _compute_next_historical_roots(state, config) return state.copy( eth1_data_votes=new_eth1_data_votes, validators=new_validators, start_shard=new_start_shard, active_index_roots=new_active_index_roots, compact_committees_roots=new_compact_committees_roots, slashings=new_slashings, randao_mixes=new_randao_mixes, historical_roots=new_historical_roots, previous_epoch_attestations=state.current_epoch_attestations, current_epoch_attestations=tuple(), )
def exit_validator(state: BeaconState, index: ValidatorIndex, epoch_length: int, entry_exit_delay: int) -> BeaconState: """ Exit the validator with the given ``index``. Return the updated state (immutable). """ validator = state.validator_registry[index] entry_exit_effect_epoch = get_entry_exit_effect_epoch( state.current_epoch(epoch_length), entry_exit_delay, ) # The following updates only occur if not previous exited if validator.exit_epoch <= entry_exit_effect_epoch: return state # Update state.validator_registry_exit_count state = state.copy( validator_registry_exit_count=state.validator_registry_exit_count + 1, ) # Update validator.exit_epoch and exit_epoch.exit_count validator = validator.copy( exit_epoch=state.current_epoch(epoch_length) + entry_exit_delay, exit_count=state.validator_registry_exit_count, ) state = state.update_validator_registry(index, validator) return state
def _determine_new_justified_epoch_and_bitfield( state: BeaconState, config: Eth2Config) -> Tuple[Epoch, Bitfield]: genesis_epoch = config.GENESIS_EPOCH previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH, genesis_epoch) current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) previous_epoch_justifiable = _is_epoch_justifiable(state, previous_epoch, config) current_epoch_justifiable = _is_epoch_justifiable(state, current_epoch, config) ( new_current_justified_epoch, justification_bits, ) = _determine_updated_justifications( previous_epoch_justifiable, previous_epoch, current_epoch_justifiable, current_epoch, state.current_justified_checkpoint.epoch, (False, ) + state.justification_bits[:-1], ) return (new_current_justified_epoch, justification_bits)
def process_randao(state: BeaconState, block: BaseBeaconBlock, config: Eth2Config) -> BeaconState: proposer_index = get_beacon_proposer_index( state=state, committee_config=CommitteeConfig(config)) epoch = state.current_epoch(config.SLOTS_PER_EPOCH) validate_randao_reveal( state=state, proposer_index=proposer_index, epoch=epoch, randao_reveal=block.body.randao_reveal, slots_per_epoch=config.SLOTS_PER_EPOCH, ) randao_mix_index = epoch % config.EPOCHS_PER_HISTORICAL_VECTOR new_randao_mix = bitwise_xor( get_randao_mix( state=state, epoch=epoch, epochs_per_historical_vector=config.EPOCHS_PER_HISTORICAL_VECTOR, ), hash_eth2(block.body.randao_reveal), ) return state.copy(randao_mixes=update_tuple_item( state.randao_mixes, randao_mix_index, new_randao_mix))
def _validate_attestation_data(state: BeaconState, data: AttestationData, config: Eth2Config) -> None: slots_per_epoch = config.SLOTS_PER_EPOCH current_epoch = state.current_epoch(slots_per_epoch) previous_epoch = state.previous_epoch(slots_per_epoch, config.GENESIS_EPOCH) attestation_slot = data.slot if data.target.epoch == current_epoch: expected_checkpoint = state.current_justified_checkpoint else: expected_checkpoint = state.previous_justified_checkpoint _validate_eligible_committee_index( state, attestation_slot, data.index, config.MAX_COMMITTEES_PER_SLOT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) _validate_eligible_target_epoch(data.target.epoch, current_epoch, previous_epoch) _validate_slot_matches_target_epoch(data.target.epoch, attestation_slot, slots_per_epoch) validate_attestation_slot( attestation_slot, state.slot, slots_per_epoch, config.MIN_ATTESTATION_INCLUSION_DELAY, ) _validate_checkpoint(data.source, expected_checkpoint)
def process_cache_state(state: BeaconState, config: Eth2Config) -> BeaconState: slots_per_historical_root = config.SLOTS_PER_HISTORICAL_ROOT # Update state.latest_state_roots latest_state_root = state.root updated_latest_state_roots = _update_historical_root( state.latest_state_roots, state.slot, slots_per_historical_root, latest_state_root, ) if state.latest_block_header.state_root == ZERO_HASH32: latest_block_header = state.latest_block_header state = state.copy( latest_block_header=latest_block_header.copy( state_root=latest_state_root, ), ) # Update state.latest_block_roots updated_latest_block_roots = _update_historical_root( state.latest_block_roots, state.slot, slots_per_historical_root, state.latest_block_header.signing_root, ) state = state.copy( latest_block_roots=updated_latest_block_roots, latest_state_roots=updated_latest_state_roots, ) return state
def create_mock_attester_slashing_is_surround_vote( state: BeaconState, config: Eth2Config, keymap: Dict[BLSPubkey, int], attestation_epoch: Epoch, ) -> AttesterSlashing: # target_epoch_2 < target_epoch_1 attestation_slot_2 = compute_start_slot_at_epoch(attestation_epoch, config.SLOTS_PER_EPOCH) attestation_slot_1 = Slot(attestation_slot_2 + config.SLOTS_PER_EPOCH) slashable_attestation_1 = create_mock_slashable_attestation( state.copy(slot=attestation_slot_1, current_justified_epoch=config.GENESIS_EPOCH), config, keymap, attestation_slot_1, ) slashable_attestation_2 = create_mock_slashable_attestation( state.copy( slot=attestation_slot_1, current_justified_epoch=config.GENESIS_EPOCH + 1, # source_epoch_1 < source_epoch_2 ), config, keymap, attestation_slot_2, ) return AttesterSlashing(attestation_1=slashable_attestation_1, attestation_2=slashable_attestation_2)
def process_final_updates(state: BeaconState, config: Eth2Config) -> BeaconState: current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) next_epoch = state.next_epoch(config.SLOTS_PER_EPOCH) state = _update_latest_active_index_roots(state, CommitteeConfig(config)) state = state.copy( latest_slashed_balances=update_tuple_item( state.latest_slashed_balances, next_epoch % config.LATEST_SLASHED_EXIT_LENGTH, state.latest_slashed_balances[current_epoch % config.LATEST_SLASHED_EXIT_LENGTH], ), latest_randao_mixes=update_tuple_item( state.latest_randao_mixes, next_epoch % config.LATEST_RANDAO_MIXES_LENGTH, get_randao_mix( state=state, epoch=current_epoch, slots_per_epoch=config.SLOTS_PER_EPOCH, latest_randao_mixes_length=config.LATEST_RANDAO_MIXES_LENGTH, ), ), ) state = _update_historical_roots(state, next_epoch, config) # Rotate current/previous epoch attestations state = state.copy( previous_epoch_attestations=state.current_epoch_attestations, current_epoch_attestations=(), ) return state
def process_randao(state: BeaconState, block: BaseBeaconBlock, config: Eth2Config) -> BeaconState: proposer_index = get_beacon_proposer_index( state=state, slot=state.slot, committee_config=CommitteeConfig(config), ) proposer = state.validator_registry[proposer_index] epoch = state.current_epoch(config.SLOTS_PER_EPOCH) validate_randao_reveal( randao_reveal=block.body.randao_reveal, proposer_index=proposer_index, proposer_pubkey=proposer.pubkey, epoch=epoch, fork=state.fork, ) randao_mix_index = epoch % config.LATEST_RANDAO_MIXES_LENGTH new_randao_mix = bitwise_xor( get_randao_mix( state=state, epoch=epoch, slots_per_epoch=config.SLOTS_PER_EPOCH, latest_randao_mixes_length=config.LATEST_RANDAO_MIXES_LENGTH, ), hash_eth2(block.body.randao_reveal), ) return state.copy(latest_randao_mixes=update_tuple_item( state.latest_randao_mixes, randao_mix_index, new_randao_mix, ), )
def process_slot_transition(state: BeaconState, config: Eth2Config, previous_block_root: Hash32) -> BeaconState: slots_per_historical_root = config.SLOTS_PER_HISTORICAL_ROOT # Update state.latest_state_roots # TODO ensure this becomes the `hash_tree_root` of the `state` latest_state_root = state.root updated_latest_state_roots = _update_historical_root( state.latest_state_roots, state.slot, slots_per_historical_root, latest_state_root, ) # Update state.slot state = state.copy( slot=state.slot + 1 ) # Update state.latest_block_roots updated_latest_block_roots = _update_historical_root( state.latest_block_roots, state.slot - 1, slots_per_historical_root, previous_block_root, ) state = state.copy( latest_block_roots=updated_latest_block_roots, latest_state_roots=updated_latest_state_roots, ) return state
def process_justification_and_finalization(state: BeaconState, config: Eth2Config) -> BeaconState: current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) if current_epoch <= GENESIS_EPOCH + 1: return state ( new_current_justified_checkpoint, justification_bits, ) = _determine_new_justified_checkpoint_and_bitfield(state, config) new_finalized_checkpoint = _determine_new_finalized_checkpoint( state, justification_bits, config) return state.mset( "justification_bits", justification_bits, "previous_justified_checkpoint", state.current_justified_checkpoint, "current_justified_checkpoint", new_current_justified_checkpoint, "finalized_checkpoint", new_finalized_checkpoint, )
def process_final_updates(state: BeaconState, config: BeaconConfig) -> BeaconState: current_epoch = state.current_epoch(config.EPOCH_LENGTH) next_epoch = state.next_epoch(config.EPOCH_LENGTH) state = state.copy( latest_penalized_balances=update_tuple_item( state.latest_penalized_balances, next_epoch % config.LATEST_PENALIZED_EXIT_LENGTH, state.latest_penalized_balances[ current_epoch % config.LATEST_PENALIZED_EXIT_LENGTH], ), latest_randao_mixes=update_tuple_item( state.latest_randao_mixes, next_epoch % config.LATEST_PENALIZED_EXIT_LENGTH, get_randao_mix( state=state, epoch=current_epoch, epoch_length=config.EPOCH_LENGTH, latest_randao_mixes_length=config.LATEST_RANDAO_MIXES_LENGTH, ), ), ) latest_attestations = tuple( filter( lambda attestation: (slot_to_epoch(attestation.data.slot, config. EPOCH_LENGTH) >= current_epoch), state.latest_attestations)) state = state.copy(latest_attestations=latest_attestations, ) return state
def _update_shuffling_epoch(state: BeaconState, slots_per_epoch: int) -> BeaconState: """ Updates the ``current_shuffling_epoch`` to the ``state``'s next epoch. """ return state.copy( current_shuffling_epoch=state.next_epoch(slots_per_epoch), )
def process_crosslinks(state: BeaconState, config: Eth2Config) -> BeaconState: current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH) new_current_crosslinks = state.current_crosslinks for epoch in (previous_epoch, current_epoch): active_validators_indices = get_active_validator_indices( state.validators, epoch) epoch_committee_count = get_committee_count( len(active_validators_indices), config.SHARD_COUNT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) epoch_start_shard = get_start_shard( state, epoch, CommitteeConfig(config), ) for shard_offset in range(epoch_committee_count): shard = Shard( (epoch_start_shard + shard_offset) % config.SHARD_COUNT) crosslink_committee = set( get_crosslink_committee( state, epoch, shard, CommitteeConfig(config), )) if not crosslink_committee: # empty crosslink committee this epoch continue winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices( state=state, epoch=epoch, shard=shard, config=config, ) threshold_met = _is_threshold_met_against_committee( state, attesting_indices, crosslink_committee, ) if threshold_met: new_current_crosslinks = update_tuple_item( new_current_crosslinks, shard, winning_crosslink, ) return state.copy( previous_crosslinks=state.current_crosslinks, current_crosslinks=new_current_crosslinks, )
def process_deposit(state: BeaconState, deposit: Deposit, slots_per_epoch: int, deposit_contract_tree_depth: int) -> BeaconState: """ Process a deposit from Ethereum 1.0. """ validate_deposit(state, deposit, deposit_contract_tree_depth) # Increment the next deposit index we are expecting. Note that this # needs to be done here because while the deposit contract will never # create an invalid Merkle branch, it may admit an invalid deposit # object, and we need to be able to skip over it state = state.copy(deposit_index=state.deposit_index + 1, ) validator_pubkeys = tuple(v.pubkey for v in state.validator_registry) deposit_input = deposit.deposit_data.deposit_input pubkey = deposit_input.pubkey amount = deposit.deposit_data.amount withdrawal_credentials = deposit_input.withdrawal_credentials if pubkey not in validator_pubkeys: # Verify the proof of possession proof_is_valid = bls.verify( pubkey=pubkey, message_hash=deposit_input.signing_root, signature=deposit_input.signature, domain=get_domain( state.fork, state.current_epoch(slots_per_epoch), SignatureDomain.DOMAIN_DEPOSIT, ), ) if not proof_is_valid: return state validator = Validator.create_pending_validator( pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, ) # Note: In phase 2 registry indices that has been withdrawn for a long time # will be recycled. state = add_pending_validator( state, validator, amount, ) else: # Top-up - increase balance by deposit index = ValidatorIndex(validator_pubkeys.index(pubkey)) validator = state.validator_registry[index] # Update validator's balance and state state = state.update_validator_balance( validator_index=index, balance=state.validator_balances[index] + amount, ) return state
def get_matching_source_attestations( state: BeaconState, epoch: Epoch, config: Eth2Config) -> Tuple[PendingAttestation, ...]: if epoch == state.current_epoch(config.SLOTS_PER_EPOCH): return state.current_epoch_attestations elif epoch == state.previous_epoch(config.SLOTS_PER_EPOCH): return state.previous_epoch_attestations else: raise InvalidEpochError
def _compute_next_randao_mixes(state: BeaconState, config: Eth2Config) -> Tuple[Hash32, ...]: current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) next_epoch = state.next_epoch(config.SLOTS_PER_EPOCH) return state.randao_mixes.set( next_epoch % config.EPOCHS_PER_HISTORICAL_VECTOR, get_randao_mix(state, current_epoch, config.EPOCHS_PER_HISTORICAL_VECTOR), )
def _process_rewards_and_penalties_for_crosslinks( state: BeaconState, config: Eth2Config, effective_balances: Dict[ValidatorIndex, Gwei], base_rewards: Dict[ValidatorIndex, Gwei] ) -> Tuple[Dict[ValidatorIndex, Gwei], Dict[ValidatorIndex, Gwei]]: # noqa: E501 previous_epoch_start_slot = get_epoch_start_slot( state.previous_epoch(config.SLOTS_PER_EPOCH), config.SLOTS_PER_EPOCH, ) current_epoch_start_slot = get_epoch_start_slot( state.current_epoch(config.SLOTS_PER_EPOCH), config.SLOTS_PER_EPOCH, ) rewards_received = { ValidatorIndex(index): Gwei(0) for index in range(len(state.validator_registry)) } penalties_received = rewards_received.copy() for slot in range(previous_epoch_start_slot, current_epoch_start_slot): crosslink_committees_at_slot = get_crosslink_committees_at_slot( state, slot, CommitteeConfig(config), ) for crosslink_committee, shard in crosslink_committees_at_slot: winning_root, attesting_validator_indices = get_winning_root_and_participants( state=state, shard=shard, effective_balances=effective_balances, committee_config=CommitteeConfig(config), ) total_attesting_balance = get_total_balance( state.validator_balances, attesting_validator_indices, config.MAX_DEPOSIT_AMOUNT, ) total_balance = get_total_balance_from_effective_balances( effective_balances, crosslink_committee, ) for index in attesting_validator_indices: rewards_received = _update_rewards_or_penalies( index, base_rewards[index] * total_attesting_balance // total_balance, rewards_received, ) for index in set(crosslink_committee).difference( attesting_validator_indices): penalties_received = _update_rewards_or_penalies( index, base_rewards[index], penalties_received, ) return (rewards_received, penalties_received)
def _settle_penality_to_validator_and_whistleblower( *, state: BeaconState, validator_index: ValidatorIndex, latest_penalized_exit_length: int, whistleblower_reward_quotient: int, epoch_length: int, max_deposit: Ether) -> BeaconState: """ Apply penality/reward to validator and whistleblower and update the meta data More intuitive pseudo-code: current_epoch_penalization_index = (state.slot // EPOCH_LENGTH) % LATEST_PENALIZED_EXIT_LENGTH state.latest_penalized_exit_balances[current_epoch_penalization_index] += ( get_effective_balance(state, index) ) whistleblower_index = get_beacon_proposer_index(state, state.slot) whistleblower_reward = get_effective_balance(state, index) // WHISTLEBLOWER_REWARD_QUOTIENT state.validator_balances[whistleblower_index] += whistleblower_reward state.validator_balances[index] -= whistleblower_reward validator.penalized_slot = state.slot """ # Update `state.latest_penalized_exit_balances` current_epoch_penalization_index = ( state.slot // epoch_length) % latest_penalized_exit_length effective_balance = get_effective_balance( state.validator_balances, validator_index, max_deposit, ) penalized_exit_balance = ( state.latest_penalized_exit_balances[current_epoch_penalization_index] + effective_balance) latest_penalized_exit_balances = update_tuple_item( tuple_data=state.latest_penalized_exit_balances, index=current_epoch_penalization_index, new_value=penalized_exit_balance, ) state = state.copy( latest_penalized_exit_balances=latest_penalized_exit_balances, ) # Update whistleblower's balance whistleblower_reward = (effective_balance // whistleblower_reward_quotient) whistleblower_index = get_beacon_proposer_index(state, state.slot, epoch_length) state = state.update_validator_balance( whistleblower_index, state.validator_balances[whistleblower_index] + whistleblower_reward, ) # Update validator's balance and `penalized_slot` field validator = state.validator_registry[validator_index] validator = validator.copy(penalized_slot=state.slot, ) state = state.update_validator( validator_index, validator, state.validator_balances[validator_index] - whistleblower_reward, ) return state
def _compute_next_slashed_balances(state: BeaconState, config: Eth2Config) -> Tuple[Gwei, ...]: current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) next_epoch = state.next_epoch(config.SLOTS_PER_EPOCH) return update_tuple_item( state.slashed_balances, next_epoch % config.EPOCHS_PER_SLASHED_BALANCES_VECTOR, state.slashed_balances[ current_epoch % config.EPOCHS_PER_SLASHED_BALANCES_VECTOR ], )
def process_deposit(state: BeaconState, deposit: Deposit, config: Eth2Config) -> BeaconState: """ Process a deposit from Ethereum 1.0. """ validate_deposit_proof(state, deposit, DEPOSIT_CONTRACT_TREE_DEPTH) # Increment the next deposit index we are expecting. Note that this # needs to be done here because while the deposit contract will never # create an invalid Merkle branch, it may admit an invalid deposit # object, and we need to be able to skip over it state = state.copy( eth1_deposit_index=state.eth1_deposit_index + 1, ) pubkey = deposit.data.pubkey amount = deposit.data.amount validator_pubkeys = tuple(v.pubkey for v in state.validators) if pubkey not in validator_pubkeys: # Verify the deposit signature (proof of possession) for new validators. # Note: The deposit contract does not check signatures. # Note: Deposits are valid across forks, thus the deposit domain # is retrieved directly from `compute_domain`. is_valid_proof_of_possession = bls.verify( message_hash=deposit.data.signing_root, pubkey=pubkey, signature=deposit.data.signature, domain=compute_domain( SignatureDomain.DOMAIN_DEPOSIT, ), ) if not is_valid_proof_of_possession: return state withdrawal_credentials = deposit.data.withdrawal_credentials validator = Validator.create_pending_validator( pubkey, withdrawal_credentials, amount, config, ) return state.copy( validators=state.validators + (validator,), balances=state.balances + (amount, ), ) else: index = ValidatorIndex(validator_pubkeys.index(pubkey)) return increase_balance( state, index, amount, )
def get_committee_assignment( state: BeaconState, config: BeaconConfig, epoch: Epoch, validator_index: ValidatorIndex, registry_change: bool = False) -> CommitteeAssignment: """ Return the ``CommitteeAssignment`` in the ``epoch`` for ``validator_index`` and ``registry_change``. ``CommitteeAssignment.committee`` is the tuple array of validators in the committee ``CommitteeAssignment.shard`` is the shard to which the committee is assigned ``CommitteeAssignment.slot`` is the slot at which the committee is assigned ``CommitteeAssignment.is_proposer`` is a bool signalling if the validator is expected to propose a beacon block at the assigned slot. """ current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) previous_epoch = state.previous_epoch(config.SLOTS_PER_EPOCH, config.GENESIS_EPOCH) next_epoch = Epoch(current_epoch + 1) validate_epoch_within_previous_and_next(epoch, previous_epoch, next_epoch) epoch_start_slot = get_epoch_start_slot(epoch, config.SLOTS_PER_EPOCH) committee_config = CommitteeConfig(config) for slot in range(epoch_start_slot, epoch_start_slot + config.SLOTS_PER_EPOCH): crosslink_committees = get_crosslink_committees_at_slot( state, slot, committee_config, registry_change=registry_change, ) selected_committees = [ committee for committee in crosslink_committees if validator_index in committee[0] ] if len(selected_committees) > 0: validators = selected_committees[0][0] shard = selected_committees[0][1] is_proposer = validator_index == get_beacon_proposer_index( state, Slot(slot), committee_config, registry_change=registry_change, ) return CommitteeAssignment(validators, shard, Slot(slot), is_proposer) raise NoCommitteeAssignment
def prepare_validator_for_withdrawal( state: BeaconState, index: ValidatorIndex, slots_per_epoch: int, min_validator_withdrawability_delay: int) -> BeaconState: """ Set the validator with the given ``index`` as withdrawable ``MIN_VALIDATOR_WITHDRAWABILITY_DELAY`` after the current epoch. """ validator = state.validator_registry[index].copy( withdrawable_epoch=(state.current_epoch(slots_per_epoch) + min_validator_withdrawability_delay)) state = state.update_validator_registry(index, validator) return state
def slash_validator(state: BeaconState, index: ValidatorIndex, config: Eth2Config, whistleblower_index: ValidatorIndex = None) -> BeaconState: """ Slash the validator with index ``index``. Exit the validator, penalize the validator, and reward the whistleblower. """ # NOTE: remove in phase 1 assert whistleblower_index is None slots_per_epoch = config.SLOTS_PER_EPOCH current_epoch = state.current_epoch(slots_per_epoch) state = initiate_validator_exit(state, index, config) state = state.update_validator_with_fn( index, _set_validator_slashed, current_epoch, config.EPOCHS_PER_SLASHINGS_VECTOR, ) slashed_balance = state.validators[index].effective_balance slashed_epoch = current_epoch % config.EPOCHS_PER_SLASHINGS_VECTOR state = state.copy(slashings=update_tuple_item_with_fn( state.slashings, slashed_epoch, lambda balance, slashed_balance: Gwei(balance + slashed_balance), slashed_balance, )) state = decrease_balance( state, index, slashed_balance // config.MIN_SLASHING_PENALTY_QUOTIENT) proposer_index = get_beacon_proposer_index(state, CommitteeConfig(config)) if whistleblower_index is None: whistleblower_index = proposer_index whistleblower_reward = Gwei(slashed_balance // config.WHISTLEBLOWER_REWARD_QUOTIENT) proposer_reward = Gwei(whistleblower_reward // config.PROPOSER_REWARD_QUOTIENT) state = increase_balance(state, proposer_index, proposer_reward) state = increase_balance( state, whistleblower_index, Gwei(whistleblower_reward - proposer_reward), ) return state
def get_committee_assignment( state: BeaconState, config: Eth2Config, epoch: Epoch, validator_index: ValidatorIndex, ) -> CommitteeAssignment: """ Return the ``CommitteeAssignment`` in the ``epoch`` for ``validator_index``. ``CommitteeAssignment.committee`` is the tuple array of validators in the committee ``CommitteeAssignment.shard`` is the shard to which the committee is assigned ``CommitteeAssignment.slot`` is the slot at which the committee is assigned ``CommitteeAssignment.is_proposer`` is a bool signalling if the validator is expected to propose a beacon block at the assigned slot. """ next_epoch = state.next_epoch(config.SLOTS_PER_EPOCH) if epoch > next_epoch: raise ValidationError( f"Epoch for committee assignment ({epoch}) must not be after next epoch {next_epoch}." ) active_validators = get_active_validator_indices(state.validators, epoch) committees_per_slot = ( get_committee_count( len(active_validators), config.SHARD_COUNT, config.SLOTS_PER_EPOCH, config.TARGET_COMMITTEE_SIZE, ) // config.SLOTS_PER_EPOCH ) epoch_start_slot = compute_start_slot_of_epoch(epoch, config.SLOTS_PER_EPOCH) epoch_start_shard = get_start_shard(state, epoch, CommitteeConfig(config)) for slot in range(epoch_start_slot, epoch_start_slot + config.SLOTS_PER_EPOCH): offset = committees_per_slot * (slot % config.SLOTS_PER_EPOCH) slot_start_shard = (epoch_start_shard + offset) % config.SHARD_COUNT for i in range(committees_per_slot): shard = Shard((slot_start_shard + i) % config.SHARD_COUNT) committee = get_crosslink_committee( state, epoch, shard, CommitteeConfig(config) ) if validator_index in committee: is_proposer = validator_index == get_beacon_proposer_index( state.copy(slot=slot), CommitteeConfig(config) ) return CommitteeAssignment( committee, Shard(shard), Slot(slot), is_proposer ) raise NoCommitteeAssignment
def _process_activation_eligibility_or_ejections( state: BeaconState, index: ValidatorIndex, config: Eth2Config) -> BeaconState: current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) validator = state.validators[index] if validator.is_eligible_for_activation_queue(config): validator = validator.set("activation_eligibility_epoch", current_epoch + 1) if (validator.is_active(current_epoch) and validator.effective_balance <= config.EJECTION_BALANCE): validator = initiate_exit_for_validator(validator, state, config) return state.transform(("validators", index), validator)
def get_start_shard(state: BeaconState, epoch: Epoch, config: CommitteeConfig) -> Shard: current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) next_epoch = state.next_epoch(config.SLOTS_PER_EPOCH) if epoch > next_epoch: raise ValidationError("Asking for start shard for an epoch after next") check_epoch = int(next_epoch) shard = (state.start_shard + get_shard_delta(state, current_epoch, config)) % config.SHARD_COUNT while check_epoch > epoch: check_epoch -= 1 shard = (shard + config.SHARD_COUNT - get_shard_delta( state, Epoch(check_epoch), config)) % config.SHARD_COUNT return shard
def slash_validator( state: BeaconState, index: ValidatorIndex, config: Eth2Config, whistleblower_index: ValidatorIndex = None, ) -> BeaconState: """ Slash the validator with index ``index``. Exit the validator, penalize the validator, and reward the whistleblower. """ # NOTE: remove in phase 1 assert whistleblower_index is None slots_per_epoch = config.SLOTS_PER_EPOCH current_epoch = state.current_epoch(slots_per_epoch) state = initiate_validator_exit(state, index, config) state = state.transform( ("validators", index), partial( _set_validator_slashed, current_epoch=current_epoch, epochs_per_slashings_vector=config.EPOCHS_PER_SLASHINGS_VECTOR, ), ) slashed_balance = state.validators[index].effective_balance slashed_epoch = current_epoch % config.EPOCHS_PER_SLASHINGS_VECTOR state = state.transform(("slashings", slashed_epoch), lambda balance: Gwei(balance + slashed_balance)) state = decrease_balance( state, index, slashed_balance // config.MIN_SLASHING_PENALTY_QUOTIENT) proposer_index = get_beacon_proposer_index(state, config) if whistleblower_index is None: whistleblower_index = proposer_index whistleblower_reward = Gwei(slashed_balance // config.WHISTLEBLOWER_REWARD_QUOTIENT) proposer_reward = Gwei(whistleblower_reward // config.PROPOSER_REWARD_QUOTIENT) state = increase_balance(state, proposer_index, proposer_reward) state = increase_balance(state, whistleblower_index, Gwei(whistleblower_reward - proposer_reward)) return state
def create_mock_deposit( state: BeaconState, pubkey: BLSPubkey, keymap: Dict[BLSPubkey, int], withdrawal_credentials: Hash32, config: Eth2Config, leaves: Sequence[Hash32] = None, ) -> Tuple[BeaconState, Deposit]: deposits, root = create_mock_deposits_and_root( (pubkey, ), keymap, config, withdrawal_credentials=(withdrawal_credentials, ), leaves=leaves, ) # sanity check assert len(deposits) == 1 deposit = deposits[0] state = state.copy( eth1_data=state.eth1_data.copy( deposit_root=root, deposit_count=state.eth1_data.deposit_count + len(deposits), ), eth1_deposit_index=0 if not leaves else len(leaves), ) return state, deposit
def _validate_sender_eligibility(state: BeaconState, transfer: Transfer, config: Eth2Config) -> None: current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH) sender = state.validators[transfer.sender] sender_balance = state.balances[transfer.sender] eligible_for_activation = sender.activation_eligibility_epoch != FAR_FUTURE_EPOCH is_withdrawable = current_epoch >= sender.withdrawable_epoch is_transfer_total_allowed = ( transfer.amount + transfer.fee + config.MAX_EFFECTIVE_BALANCE <= sender_balance) if (not eligible_for_activation ) or is_withdrawable or is_transfer_total_allowed: return if eligible_for_activation: raise ValidationError( f"Sender in transfer {transfer} is eligible for activation.") if not is_withdrawable: raise ValidationError( f"Sender in transfer {transfer} is not withdrawable.") if not is_transfer_total_allowed: raise ValidationError( f"Sender does not have sufficient funds in transfer {transfer}.")