2018-08-24 04:09:59 +00:00
|
|
|
package casper
|
|
|
|
|
|
|
|
import (
|
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/params"
|
2018-08-25 18:59:46 +00:00
|
|
|
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
2018-10-09 05:58:54 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/mathutil"
|
2018-08-24 04:09:59 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
|
|
|
var log = logrus.WithField("prefix", "casper")
|
|
|
|
|
|
|
|
// CalculateRewards adjusts validators balances by applying rewards or penalties
|
|
|
|
// based on FFG incentive structure.
|
2018-09-21 19:56:39 +00:00
|
|
|
// FFG Rewards scheme rewards validator who have voted on blocks, and penalises those validators
|
|
|
|
// who are offline. The penalties are more severe the longer they are offline.
|
2018-09-19 14:23:26 +00:00
|
|
|
func CalculateRewards(
|
2018-09-21 19:56:39 +00:00
|
|
|
slot uint64,
|
|
|
|
voterIndices []uint32,
|
2018-09-19 14:23:26 +00:00
|
|
|
validators []*pb.ValidatorRecord,
|
2018-09-21 19:56:39 +00:00
|
|
|
totalParticipatedDeposit uint64,
|
|
|
|
timeSinceFinality uint64) []*pb.ValidatorRecord {
|
2018-10-10 02:34:50 +00:00
|
|
|
totalDeposit := TotalActiveValidatorDeposit(validators)
|
|
|
|
activeValidators := ActiveValidatorIndices(validators)
|
2018-10-23 16:07:43 +00:00
|
|
|
rewardQuotient := RewardQuotient(validators)
|
|
|
|
penaltyQuotient := quadraticPenaltyQuotient()
|
2018-09-21 19:56:39 +00:00
|
|
|
|
|
|
|
log.Debugf("Applying rewards and penalties for the validators for slot %d", slot)
|
2018-09-28 06:48:39 +00:00
|
|
|
if timeSinceFinality <= 3*params.GetConfig().CycleLength {
|
2018-09-21 19:56:39 +00:00
|
|
|
for _, validatorIndex := range activeValidators {
|
|
|
|
var voted bool
|
|
|
|
|
|
|
|
for _, voterIndex := range voterIndices {
|
|
|
|
if voterIndex == validatorIndex {
|
|
|
|
voted = true
|
|
|
|
balance := validators[validatorIndex].GetBalance()
|
2018-10-04 04:49:18 +00:00
|
|
|
newbalance := int64(balance) + int64(balance/rewardQuotient)*(2*int64(totalParticipatedDeposit)-int64(totalDeposit))/int64(totalDeposit)
|
|
|
|
validators[validatorIndex].Balance = uint64(newbalance)
|
2018-09-21 19:56:39 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !voted {
|
|
|
|
newBalance := validators[validatorIndex].GetBalance()
|
|
|
|
newBalance -= newBalance / rewardQuotient
|
|
|
|
validators[validatorIndex].Balance = newBalance
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
for _, validatorIndex := range activeValidators {
|
|
|
|
var voted bool
|
|
|
|
|
|
|
|
for _, voterIndex := range voterIndices {
|
|
|
|
if voterIndex == validatorIndex {
|
|
|
|
voted = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !voted {
|
|
|
|
newBalance := validators[validatorIndex].GetBalance()
|
|
|
|
newBalance -= newBalance/rewardQuotient + newBalance*timeSinceFinality/penaltyQuotient
|
|
|
|
validators[validatorIndex].Balance = newBalance
|
2018-08-24 04:09:59 +00:00
|
|
|
}
|
|
|
|
}
|
2018-09-21 19:56:39 +00:00
|
|
|
|
2018-08-24 04:09:59 +00:00
|
|
|
}
|
2018-09-21 19:56:39 +00:00
|
|
|
|
|
|
|
return validators
|
|
|
|
}
|
|
|
|
|
2018-09-27 03:20:59 +00:00
|
|
|
// RewardQuotient returns the reward quotient for validators which will be used to
|
2018-09-21 19:56:39 +00:00
|
|
|
// reward validators for voting on blocks, or penalise them for being offline.
|
2018-10-10 02:34:50 +00:00
|
|
|
func RewardQuotient(validators []*pb.ValidatorRecord) uint64 {
|
|
|
|
totalDepositETH := TotalActiveValidatorDepositInEth(validators)
|
2018-10-09 05:58:54 +00:00
|
|
|
return params.GetConfig().BaseRewardQuotient * mathutil.IntegerSquareRoot(totalDepositETH)
|
2018-09-21 19:56:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// quadraticPenaltyQuotient is the quotient that will be used to apply penalties to offline
|
|
|
|
// validators.
|
|
|
|
func quadraticPenaltyQuotient() uint64 {
|
2018-10-23 16:07:43 +00:00
|
|
|
dropTimeFactor := params.GetConfig().SqrtExpDropTime
|
2018-10-04 04:49:18 +00:00
|
|
|
return dropTimeFactor * dropTimeFactor
|
2018-09-21 19:56:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// QuadraticPenalty returns the penalty that will be applied to an offline validator
|
|
|
|
// based on the number of slots that they are offline.
|
|
|
|
func QuadraticPenalty(numberOfSlots uint64) uint64 {
|
|
|
|
slotFactor := (numberOfSlots * numberOfSlots) / 2
|
|
|
|
penaltyQuotient := quadraticPenaltyQuotient()
|
2018-10-23 16:07:43 +00:00
|
|
|
return slotFactor / penaltyQuotient
|
2018-08-24 04:09:59 +00:00
|
|
|
}
|
2018-09-27 03:20:59 +00:00
|
|
|
|
|
|
|
// RewardValidatorCrosslink applies rewards to validators part of a shard committee for voting on a shard.
|
2018-10-04 04:49:18 +00:00
|
|
|
// TODO(#538): Change this to big.Int as tests using 64 bit integers fail due to integer overflow.
|
2018-09-27 03:20:59 +00:00
|
|
|
func RewardValidatorCrosslink(totalDeposit uint64, participatedDeposits uint64, rewardQuotient uint64, validator *pb.ValidatorRecord) {
|
2018-10-04 04:49:18 +00:00
|
|
|
currentBalance := int64(validator.Balance)
|
|
|
|
currentBalance += int64(currentBalance) / int64(rewardQuotient) * (2*int64(participatedDeposits) - int64(totalDeposit)) / int64(totalDeposit)
|
|
|
|
validator.Balance = uint64(currentBalance)
|
2018-09-27 03:20:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// PenaliseValidatorCrosslink applies penalties to validators part of a shard committee for not voting on a shard.
|
|
|
|
func PenaliseValidatorCrosslink(timeSinceLastConfirmation uint64, rewardQuotient uint64, validator *pb.ValidatorRecord) {
|
2018-10-04 04:49:18 +00:00
|
|
|
newBalance := validator.Balance
|
2018-09-27 03:20:59 +00:00
|
|
|
quadraticQuotient := quadraticPenaltyQuotient()
|
2018-10-04 04:49:18 +00:00
|
|
|
newBalance -= newBalance/rewardQuotient + newBalance*timeSinceLastConfirmation/quadraticQuotient
|
2018-09-27 03:20:59 +00:00
|
|
|
validator.Balance = newBalance
|
|
|
|
}
|