Files
prysm/beacon-chain/core/validators/validator.go
2019-06-18 17:22:39 -07:00

280 lines
10 KiB
Go

// Package validators contains libraries to shuffle validators
// and retrieve active validator indices from a given slot
// or an attestation. It also provides helper functions to locate
// validator based on pubic key.
package validators
import (
"fmt"
"sync"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/sirupsen/logrus"
)
var log = logrus.WithField("prefix", "validator")
type validatorStore struct {
sync.RWMutex
// activatedValidators is a mapping that tracks validator activation epoch to validators index.
activatedValidators map[uint64][]uint64
// exitedValidators is a mapping that tracks validator exit epoch to validators index.
exitedValidators map[uint64][]uint64
}
//VStore validator map for quick
var VStore = validatorStore{
activatedValidators: make(map[uint64][]uint64),
exitedValidators: make(map[uint64][]uint64),
}
// ActivateValidator takes in validator index and updates
// validator's activation slot.
//
// Spec pseudocode definition:
// def activate_validator(state: BeaconState, index: ValidatorIndex, is_genesis: bool) -> None:
// """
// Activate the validator of the given ``index``.
// Note that this function mutates ``state``.
// """
// validator = state.validator_registry[index]
//
// validator.activation_epoch = GENESIS_EPOCH if is_genesis else get_entry_exit_effect_epoch(get_current_epoch(state))
func ActivateValidator(state *pb.BeaconState, idx uint64, genesis bool) (*pb.BeaconState, error) {
validator := state.Validators[idx]
if genesis {
validator.ActivationEligibilityEpoch = 0
validator.ActivationEpoch = 0
} else {
validator.ActivationEpoch = helpers.DelayedActivationExitEpoch(helpers.CurrentEpoch(state))
}
state.Validators[idx] = validator
log.WithFields(logrus.Fields{
"index": idx,
"activationEpoch": validator.ActivationEpoch,
}).Info("Validator activated")
return state, nil
}
// InitiateValidatorExit takes in validator index and updates
// validator with correct voluntary exit parameters.
//
// Spec pseudocode definition:
// def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None:
// """
// Initiate the exit of the validator of the given ``index``.
// """
// # Return if validator already initiated exit
// validator = state.validator_registry[index]
// if validator.exit_epoch != FAR_FUTURE_EPOCH:
// return
//
// # Compute exit queue epoch
// exit_epochs = [v.exit_epoch for v in state.validator_registry if v.exit_epoch != FAR_FUTURE_EPOCH]
// exit_queue_epoch = max(exit_epochs + [get_delayed_activation_exit_epoch(get_current_epoch(state))])
// exit_queue_churn = len([v for v in state.validator_registry if v.exit_epoch == exit_queue_epoch])
// if exit_queue_churn >= get_churn_limit(state):
// exit_queue_epoch += 1
//
// # Set validator exit epoch and withdrawable epoch
// validator.exit_epoch = exit_queue_epoch
// validator.withdrawable_epoch = validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY
func InitiateValidatorExit(state *pb.BeaconState, idx uint64) (*pb.BeaconState, error) {
validator := state.Validators[idx]
if validator.ExitEpoch != params.BeaconConfig().FarFutureEpoch {
return state, nil
}
exitEpochs := []uint64{}
for _, val := range state.Validators {
if val.ExitEpoch != params.BeaconConfig().FarFutureEpoch {
exitEpochs = append(exitEpochs, val.ExitEpoch)
}
}
exitEpochs = append(exitEpochs, helpers.DelayedActivationExitEpoch(helpers.CurrentEpoch(state)))
// Obtain the exit queue epoch as the maximum number in the exit epochs array.
exitQueueEpoch := exitEpochs[0]
for _, i := range exitEpochs {
if exitQueueEpoch < i {
exitQueueEpoch = i
}
}
// We use the exit queue churn to determine if we have passed a churn limit.
exitQueueChurn := 0
for _, val := range state.Validators {
if val.ExitEpoch == exitQueueEpoch {
exitQueueChurn++
}
}
churn, err := helpers.ChurnLimit(state)
if err != nil {
return nil, fmt.Errorf("could not get churn limit: %v", err)
}
if uint64(exitQueueChurn) >= churn {
exitQueueEpoch++
}
state.Validators[idx].ExitEpoch = exitQueueEpoch
state.Validators[idx].WithdrawableEpoch = exitQueueEpoch + params.BeaconConfig().MinValidatorWithdrawalDelay
return state, nil
}
// ExitValidator takes in validator index and does house
// keeping work to exit validator with entry exit delay.
//
// Spec pseudocode definition:
// def exit_validator(state: BeaconState, index: ValidatorIndex) -> None:
// """
// Exit the validator of the given ``index``.
// Note that this function mutates ``state``.
// """
// validator = state.validator_registry[index]
//
// # The following updates only occur if not previous exited
// if validator.exit_epoch <= get_entry_exit_effect_epoch(get_current_epoch(state)):
// return
//
// validator.exit_epoch = get_entry_exit_effect_epoch(get_current_epoch(state))
func ExitValidator(state *pb.BeaconState, idx uint64) *pb.BeaconState {
validator := state.Validators[idx]
if validator.ExitEpoch != params.BeaconConfig().FarFutureEpoch {
return state
}
validator.ExitEpoch = helpers.DelayedActivationExitEpoch(helpers.CurrentEpoch(state))
return state
}
// SlashValidator slashes the malicious validator's balance and awards
// the whistleblower's balance.
//
// Spec pseudocode definition:
// def slash_validator(state: BeaconState, index: ValidatorIndex) -> None:
// """
// Slash the validator of the given ``index``.
// Note that this function mutates ``state``.
// """
// current_epoch = get_current_epoch(state)
// initiate_validator_exit(state, slashed_index)
// state.validator_registry[slashed_index].slashed = True
// state.validator_registry[slashed_index].withdrawable_epoch = current_epoch + LATEST_SLASHED_EXIT_LENGTH
// slashed_balance = state.validator_registry[slashed_index].effective_balance
// state.latest_slashed_balances[current_epoch % LATEST_SLASHED_EXIT_LENGTH] += slashed_balance
//
// proposer_index = get_beacon_proposer_index(state)
// if whistleblower_index is None:
// whistleblower_index = proposer_index
// whistleblowing_reward = slashed_balance // WHISTLEBLOWING_REWARD_QUOTIENT
// proposer_reward = whistleblowing_reward // PROPOSER_REWARD_QUOTIENT
// increase_balance(state, proposer_index, proposer_reward)
// increase_balance(state, whistleblower_index, whistleblowing_reward - proposer_reward)
// decrease_balance(state, slashed_index, whistleblowing_reward)
func SlashValidator(state *pb.BeaconState, slashedIdx uint64, whistleBlowerIdx uint64) (*pb.BeaconState, error) {
state = ExitValidator(state, slashedIdx)
currentEpoch := helpers.CurrentEpoch(state)
state.Validators[slashedIdx].Slashed = true
state.Validators[slashedIdx].WithdrawableEpoch = currentEpoch + params.BeaconConfig().SlashedExitLength
slashedBalance := state.Validators[slashedIdx].EffectiveBalance
state.SlashedBalances[currentEpoch%params.BeaconConfig().SlashedExitLength] += slashedBalance
proposerIdx, err := helpers.BeaconProposerIndex(state)
if err != nil {
return nil, fmt.Errorf("could not get proposer idx: %v", err)
}
var whistleBlower uint64
if whistleBlowerIdx == 0 {
whistleBlower = proposerIdx
}
whistleblowerReward := slashedBalance / params.BeaconConfig().WhistleBlowingRewardQuotient
proposerReward := whistleblowerReward / params.BeaconConfig().ProposerRewardQuotient
state = helpers.IncreaseBalance(state, proposerIdx, proposerReward)
state = helpers.IncreaseBalance(state, whistleBlower, whistleblowerReward-proposerReward)
state = helpers.DecreaseBalance(state, slashedIdx, whistleblowerReward)
return state, nil
}
// InitializeValidatorStore sets the current active validators from the current
// state.
func InitializeValidatorStore(bState *pb.BeaconState) error {
VStore.Lock()
defer VStore.Unlock()
currentEpoch := helpers.CurrentEpoch(bState)
activeValidatorIndices, err := helpers.ActiveValidatorIndices(bState, currentEpoch)
if err != nil {
return err
}
VStore.activatedValidators[currentEpoch] = activeValidatorIndices
return nil
}
// InsertActivatedVal locks the validator store, inserts the activated validator
// indices, then unlocks the store again. This method may be used by
// external services in testing to populate the validator store.
func InsertActivatedVal(epoch uint64, validators []uint64) {
VStore.Lock()
defer VStore.Unlock()
VStore.activatedValidators[epoch] = validators
}
// InsertActivatedIndices locks the validator store, inserts the activated validator
// indices corresponding to their activation epochs.
func InsertActivatedIndices(epoch uint64, indices []uint64) {
VStore.Lock()
defer VStore.Unlock()
VStore.activatedValidators[epoch] = append(VStore.activatedValidators[epoch], indices...)
}
// InsertExitedVal locks the validator store, inserts the exited validator
// indices, then unlocks the store again. This method may be used by
// external services in testing to remove the validator store.
func InsertExitedVal(epoch uint64, validators []uint64) {
VStore.Lock()
defer VStore.Unlock()
VStore.exitedValidators[epoch] = validators
}
// ActivatedValFromEpoch locks the validator store, retrieves the activated validator
// indices of a given epoch, then unlocks the store again.
func ActivatedValFromEpoch(epoch uint64) []uint64 {
VStore.RLock()
defer VStore.RUnlock()
if _, exists := VStore.activatedValidators[epoch]; !exists {
return nil
}
return VStore.activatedValidators[epoch]
}
// ExitedValFromEpoch locks the validator store, retrieves the exited validator
// indices of a given epoch, then unlocks the store again.
func ExitedValFromEpoch(epoch uint64) []uint64 {
VStore.RLock()
defer VStore.RUnlock()
if _, exists := VStore.exitedValidators[epoch]; !exists {
return nil
}
return VStore.exitedValidators[epoch]
}
// DeleteActivatedVal locks the validator store, delete the activated validator
// indices of a given epoch, then unlocks the store again.
func DeleteActivatedVal(epoch uint64) {
VStore.Lock()
defer VStore.Unlock()
delete(VStore.activatedValidators, epoch)
}
// DeleteExitedVal locks the validator store, delete the exited validator
// indices of a given epoch, then unlocks the store again.
func DeleteExitedVal(epoch uint64) {
VStore.Lock()
defer VStore.Unlock()
delete(VStore.exitedValidators, epoch)
}