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 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.transform(("randao_mixes", randao_mix_index), new_randao_mix)
def initiate_validator_exit(state: BeaconState, index: ValidatorIndex, config: Eth2Config) -> BeaconState: """ Initiate exit for the validator with the given ``index``. Return the updated state (immutable). """ return state.transform( ("validators", index), partial(initiate_exit_for_validator, state=state, config=config), )
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 _process_slot(state: BeaconState, config: Eth2Config) -> BeaconState: slots_per_historical_root = config.SLOTS_PER_HISTORICAL_ROOT previous_state_root = state.hash_tree_root updated_state_roots = _update_historical_root(state.state_roots, state.slot, slots_per_historical_root, previous_state_root) if state.latest_block_header.state_root == ZERO_HASH32: state = state.transform(("latest_block_header", "state_root"), previous_state_root) updated_block_roots = _update_historical_root( state.block_roots, state.slot, slots_per_historical_root, state.latest_block_header.signing_root, ) return state.mset("block_roots", updated_block_roots, "state_roots", updated_state_roots)
def decrease_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> BeaconState: return state.transform( ("balances", index), lambda balance: Gwei(0) if delta > balance else Gwei(balance - delta), )
def increase_balance(state: BeaconState, index: ValidatorIndex, delta: Gwei) -> BeaconState: return state.transform(("balances", index), lambda balance: Gwei(balance + delta))
def _mini_stf(state: BeaconState, block: Optional[BeaconBlock], config: Eth2Config) -> BeaconState: """ A simplified state transition for testing state storage. - updates ``state_roots`` with the previous slot's state root - updates ``block_roots`` with the previous slot's block root - updates ``randao_mixes`` with an arbitrary mix at the current epoch - creates a new ``latest_block_header`` and adds it to the state - fills in the rest of the attributes with arbitrary values """ current_slot = state.slot + 1 current_epoch = current_slot // config.SLOTS_PER_EPOCH if block: latest_block_header = block.header else: latest_block_header = state.latest_block_header # state changes that depend on the previous state for retrieval randao_mix = Root(Hash32(current_slot.to_bytes(32, byteorder="little"))) state = (state.transform( ("state_roots", state.slot % config.SLOTS_PER_HISTORICAL_ROOT), state.hash_tree_root, ).transform( ("block_roots", state.slot % config.SLOTS_PER_HISTORICAL_ROOT), state.latest_block_header.hash_tree_root, ).transform( ("randao_mixes", current_epoch % config.EPOCHS_PER_HISTORICAL_VECTOR), randao_mix, ).mset("slot", current_slot, "latest_block_header", latest_block_header)) # state changes that do not depend on the previous state for retrieval new_validators = [ Validator.create(pubkey=BLSPubkey(n.to_bytes(48, byteorder="little"))) for n in range(current_slot, current_slot + 20) ] new_eth1_data_votes = [ Eth1Data.create( deposit_root=Root(Hash32(n.to_bytes(32, byteorder="little")))) for n in range(current_slot, current_slot + 7) ] new_previous_epoch_attestations = [ PendingAttestation.create(proposer_index=ValidatorIndex(n)) for n in range(current_slot, current_slot + 5) ] new_current_epoch_attestations = [ PendingAttestation.create(proposer_index=ValidatorIndex(n)) for n in range(current_slot + 5, current_slot + 10) ] state = state.mset( "validators", new_validators, "balances", (32, ) * len(new_validators), "eth1_data_votes", new_eth1_data_votes, "eth1_data", new_eth1_data_votes[0], "previous_epoch_attestations", new_previous_epoch_attestations, "current_epoch_attestations", new_current_epoch_attestations, "previous_justified_checkpoint", Checkpoint.create(epoch=Epoch(current_slot + 42)), "current_justified_checkpoint", Checkpoint.create(epoch=Epoch(current_slot + 43)), "finalized_checkpoint", Checkpoint.create(epoch=Epoch(current_slot + 44)), ) return state