prysm-pulse/beacon-chain/core/balances/rewards_penalties.go
2019-01-01 09:17:44 -08:00

390 lines
14 KiB
Go

package balances
import (
"fmt"
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch"
"github.com/prysmaticlabs/prysm/beacon-chain/core/validators"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/mathutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/slices"
)
// baseRewardQuotient takes the total balance and calculates for
// the quotient of the base reward.
//
// Spec pseudocode definition:
// base_reward_quotient =
// BASE_REWARD_QUOTIENT * integer_squareroot(total_balance // GWEI_PER_ETH)
func baseRewardQuotient(totalBalance uint64) uint64 {
baseRewardQuotient := params.BeaconConfig().BaseRewardQuotient * mathutil.IntegerSquareRoot(
totalBalance/params.BeaconConfig().Gwei)
return baseRewardQuotient
}
// baseReward takes state and validator index to calculate for
// individual validator's base reward.
//
// Spec pseudocode definition:
// base_reward(state, index) =
// get_effective_balance(state, index) // base_reward_quotient // 5
func baseReward(
state *pb.BeaconState,
validatorIndex uint32,
baseRewardQuotient uint64) uint64 {
validatorBalance := validators.EffectiveBalance(state, validatorIndex)
return validatorBalance / baseRewardQuotient / 5
}
// inactivityPenalty takes state and validator index to calculate for
// individual validator's penalty for being offline.
//
// Spec pseudocode definition:
// inactivity_penalty(state, index, epochs_since_finality) =
// base_reward(state, index) + get_effective_balance(state, index)
// * epochs_since_finality // INACTIVITY_PENALTY_QUOTIENT // 2
func inactivityPenalty(
state *pb.BeaconState,
validatorIndex uint32,
baseRewardQuotient uint64,
epochsSinceFinality uint64) uint64 {
baseReward := baseReward(state, validatorIndex, baseRewardQuotient)
validatorBalance := validators.EffectiveBalance(state, validatorIndex)
return baseReward + validatorBalance*epochsSinceFinality/params.BeaconConfig().InactivityPenaltyQuotient/2
}
// FFGSrcRewardsPenalties applies rewards or penalties
// for an expected FFG source. It uses total justified
// attesting balances, total validator balances and base
// reward quotient to calculate the reward amount.
// Validators who voted for previous justified hash
// will get a reward, everyone else will get a penalty.
//
// Spec pseudocode definition:
// Any validator index in previous_epoch_justified_attester_indices
// gains base_reward(state, index) * previous_epoch_justified_attesting_balance // total_balance.
// Any active validator v not in previous_epoch_justified_attester_indices
// loses base_reward(state, index).
func FFGSrcRewardsPenalties(
state *pb.BeaconState,
justifiedAttesterIndices []uint32,
justifiedAttestingBalance uint64,
totalBalance uint64) *pb.BeaconState {
baseRewardQuotient := baseRewardQuotient(totalBalance)
for _, index := range justifiedAttesterIndices {
state.ValidatorBalances[index] +=
baseReward(state, index, baseRewardQuotient) *
justifiedAttestingBalance /
totalBalance
}
allValidatorIndices := validators.ActiveValidatorIndices(state.ValidatorRegistry)
didNotAttestIndices := slices.Not(justifiedAttesterIndices, allValidatorIndices)
for _, index := range didNotAttestIndices {
state.ValidatorBalances[index] -=
baseReward(state, index, baseRewardQuotient)
}
return state
}
// FFGTargetRewardsPenalties applies rewards or penalties
// for an expected FFG target. It uses total boundary
// attesting balances, total validator balances and base
// reward quotient to calculate the reward amount.
// Validators who voted for epoch boundary block
// will get a reward, everyone else will get a penalty.
//
// Spec pseudocode definition:
// Any validator index in previous_epoch_boundary_attester_indices gains
// base_reward(state, index) * previous_epoch_boundary_attesting_balance // total_balance.
// Any active validator index not in previous_epoch_boundary_attester_indices loses
// base_reward(state, index).
func FFGTargetRewardsPenalties(
state *pb.BeaconState,
boundaryAttesterIndices []uint32,
boundaryAttestingBalance uint64,
totalBalance uint64) *pb.BeaconState {
baseRewardQuotient := baseRewardQuotient(totalBalance)
for _, index := range boundaryAttesterIndices {
state.ValidatorBalances[index] +=
baseReward(state, index, baseRewardQuotient) *
boundaryAttestingBalance /
totalBalance
}
allValidatorIndices := validators.ActiveValidatorIndices(state.ValidatorRegistry)
didNotAttestIndices := slices.Not(boundaryAttesterIndices, allValidatorIndices)
for _, index := range didNotAttestIndices {
state.ValidatorBalances[index] -=
baseReward(state, index, baseRewardQuotient)
}
return state
}
// ChainHeadRewardsPenalties applies rewards or penalties
// for an expected beacon chain head. It uses total head
// attesting balances, total validator balances and base
// reward quotient to calculate the reward amount.
// Validators who voted for the canonical head block
// will get a reward, everyone else will get a penalty.
//
// Spec pseudocode definition:
// Any validator index in previous_epoch_head_attester_indices gains
// base_reward(state, index) * previous_epoch_head_attesting_balance // total_balance).
// Any active validator index not in previous_epoch_head_attester_indices loses
// base_reward(state, index).
func ChainHeadRewardsPenalties(
state *pb.BeaconState,
headAttesterIndices []uint32,
headAttestingBalance uint64,
totalBalance uint64) *pb.BeaconState {
baseRewardQuotient := baseRewardQuotient(totalBalance)
for _, index := range headAttesterIndices {
state.ValidatorBalances[index] +=
baseReward(state, index, baseRewardQuotient) *
headAttestingBalance /
totalBalance
}
allValidatorIndices := validators.ActiveValidatorIndices(state.ValidatorRegistry)
didNotAttestIndices := slices.Not(headAttesterIndices, allValidatorIndices)
for _, index := range didNotAttestIndices {
state.ValidatorBalances[index] -=
baseReward(state, index, baseRewardQuotient)
}
return state
}
// InclusionDistRewards applies rewards based on
// inclusion distance. It uses calculated inclusion distance
// and base reward quotient to calculate the reward amount.
//
// Spec pseudocode definition:
// Any validator index in previous_epoch_attester_indices gains
// base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY //
// inclusion_distance(state, index)
func InclusionDistRewards(
state *pb.BeaconState,
attesterIndices []uint32,
totalBalance uint64) (*pb.BeaconState, error) {
baseRewardQuotient := baseRewardQuotient(totalBalance)
for _, index := range attesterIndices {
inclusionDistance, err := epoch.InclusionDistance(state, index)
if err != nil {
return nil, fmt.Errorf("could not get inclusion distance: %v", err)
}
state.ValidatorBalances[index] +=
baseReward(state, index, baseRewardQuotient) *
params.BeaconConfig().MinAttestationInclusionDelay /
inclusionDistance
}
return state, nil
}
// InactivityFFGSrcPenalties applies penalties to inactive
// validators that missed to vote FFG source over an
// extended of time. (epochs_since_finality > 4)
//
// Spec pseudocode definition:
// Any active validator index not in previous_epoch_justified_attester_indices,
// loses inactivity_penalty(state, index, epochs_since_finality)
func InactivityFFGSrcPenalties(
state *pb.BeaconState,
justifiedAttesterIndices []uint32,
totalBalance uint64,
epochsSinceFinality uint64) *pb.BeaconState {
baseRewardQuotient := baseRewardQuotient(totalBalance)
allValidatorIndices := validators.ActiveValidatorIndices(state.ValidatorRegistry)
didNotAttestIndices := slices.Not(justifiedAttesterIndices, allValidatorIndices)
for _, index := range didNotAttestIndices {
state.ValidatorBalances[index] -=
inactivityPenalty(state, index, baseRewardQuotient, epochsSinceFinality)
}
return state
}
// InactivityFFGTargetPenalties applies penalties to inactive
// validators that missed to vote FFG target over an
// extended of time. (epochs_since_finality > 4)
//
// Spec pseudocode definition:
// Any active validator index not in previous_epoch_boundary_attester_indices,
// loses inactivity_penalty(state, index, epochs_since_finality)
func InactivityFFGTargetPenalties(
state *pb.BeaconState,
boundaryAttesterIndices []uint32,
totalBalance uint64,
epochsSinceFinality uint64) *pb.BeaconState {
baseRewardQuotient := baseRewardQuotient(totalBalance)
allValidatorIndices := validators.ActiveValidatorIndices(state.ValidatorRegistry)
didNotAttestIndices := slices.Not(boundaryAttesterIndices, allValidatorIndices)
for _, index := range didNotAttestIndices {
state.ValidatorBalances[index] -=
inactivityPenalty(state, index, baseRewardQuotient, epochsSinceFinality)
}
return state
}
// InactivityHeadPenalties applies penalties to inactive validators
// that missed to vote on canonical head over an extended of time.
// (epochs_since_finality > 4)
//
// Spec pseudocode definition:
// Any active validator index not in previous_epoch_head_attester_indices,
// loses base_reward(state, index)
func InactivityHeadPenalties(
state *pb.BeaconState,
headAttesterIndices []uint32,
totalBalance uint64) *pb.BeaconState {
baseRewardQuotient := baseRewardQuotient(totalBalance)
allValidatorIndices := validators.ActiveValidatorIndices(state.ValidatorRegistry)
didNotAttestIndices := slices.Not(headAttesterIndices, allValidatorIndices)
for _, index := range didNotAttestIndices {
state.ValidatorBalances[index] -=
baseReward(state, index, baseRewardQuotient)
}
return state
}
// InactivityExitedPenalties applies additional (2x) penalties
// to inactive validators with status EXITED_WITH_PENALTY.
//
// Spec pseudocode definition:
// Any validator index with status == EXITED_WITH_PENALTY,
// loses 2 * inactivity_penalty(state, index, epochs_since_finality) +
// base_reward(state, index)
func InactivityExitedPenalties(
state *pb.BeaconState,
totalBalance uint64,
epochsSinceFinality uint64) *pb.BeaconState {
baseRewardQuotient := baseRewardQuotient(totalBalance)
allValidatorIndices := validators.AllValidatorsIndices(state)
for _, index := range allValidatorIndices {
if state.ValidatorRegistry[index].Status == pb.ValidatorRecord_EXITED_WITH_PENALTY {
state.ValidatorBalances[index] -=
2*inactivityPenalty(state, index, baseRewardQuotient, epochsSinceFinality) +
baseReward(state, index, baseRewardQuotient)
}
}
return state
}
// InactivityInclusionPenalties applies penalties in relation with
// inclusion delay to inactive validators.
//
// Spec pseudocode definition:
// Any validator index in previous_epoch_attester_indices loses
// base_reward(state, index) - base_reward(state, index) *
// MIN_ATTESTATION_INCLUSION_DELAY // inclusion_distance(state, index)
func InactivityInclusionPenalties(
state *pb.BeaconState,
attesterIndices []uint32,
totalBalance uint64) (*pb.BeaconState, error) {
baseRewardQuotient := baseRewardQuotient(totalBalance)
for _, index := range attesterIndices {
inclusionDistance, err := epoch.InclusionDistance(state, index)
if err != nil {
return nil, fmt.Errorf("could not get inclusion distance: %v", err)
}
baseReward := baseReward(state, index, baseRewardQuotient)
state.ValidatorBalances[index] -= baseReward -
baseReward*params.BeaconConfig().MinAttestationInclusionDelay/
inclusionDistance
}
return state, nil
}
// AttestationInclusionRewards awards the the beacon
// proposers who included previous epoch attestations.
//
// Spec pseudocode definition:
// For each index in previous_epoch_attester_indices,
// we determine the proposer proposer_index =
// get_beacon_proposer_index(state, inclusion_slot(state, index))
// and set state.validator_balances[proposer_index] +=
// base_reward(state, index) // INCLUDER_REWARD_QUOTIENT
func AttestationInclusionRewards(
state *pb.BeaconState,
totalBalance uint64,
prevEpochAttesterIndices []uint32) (*pb.BeaconState, error) {
baseRewardQuotient := baseRewardQuotient(totalBalance)
for _, index := range prevEpochAttesterIndices {
slot, err := epoch.InclusionSlot(state, index)
if err != nil {
return nil, fmt.Errorf("could not get inclusion slot: %v", err)
}
proposerIndex, err := validators.BeaconProposerIndex(state, slot)
if err != nil {
return nil, fmt.Errorf("could not get propoer index: %v", err)
}
state.ValidatorBalances[proposerIndex] +=
baseReward(state, proposerIndex, baseRewardQuotient) /
params.BeaconConfig().IncluderRewardQuotient
}
return state, nil
}
// CrosslinksRewardsPenalties awards or penalizes attesters
// for attesting shard cross links.
//
// Spec pseudocode definition:
// For every shard_committee in state.shard_committees_at_slots[:EPOCH_LENGTH]:
// For each index in shard_committee.committee, adjust balances as follows:
// If index in attesting_validators(shard_committee), state.validator_balances[index]
// += base_reward(state, index) * total_attesting_balance(shard_committee)
// total_balance(shard_committee)).
// If index not in attesting_validators(shard_committee), state.validator_balances[index]
// -= base_reward(state, index).
func CrosslinksRewardsPenalties(
state *pb.BeaconState,
totalAttestingBalance uint64,
totalBalance uint64,
attesterIndices []uint32) *pb.BeaconState {
epochLength := params.BeaconConfig().EpochLength
baseRewardQuotient := baseRewardQuotient(totalBalance)
for _, shardCommitteesAtSlot := range state.ShardAndCommitteesAtSlots[:epochLength] {
for _, shardCommitees := range shardCommitteesAtSlot.ArrayShardAndCommittee {
for _, index := range shardCommitees.Committee {
baseReward := baseReward(state, index, baseRewardQuotient)
if slices.IsIn(index, attesterIndices) {
state.ValidatorBalances[index] +=
baseReward * totalAttestingBalance / totalBalance
} else {
state.ValidatorBalances[index] -=
baseReward * totalAttestingBalance / totalBalance
}
}
}
}
return state
}