mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 13:28:01 -05:00
Update Rewards to Align with Spec (#596)
* Adding integer square root and other changes to spec * adding doc * fixing tests * fix spec * fix rewards * Remove failing test * fix test * lint
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
package casper
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/params"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -25,7 +24,6 @@ func CalculateRewards(
|
||||
activeValidators := ActiveValidatorIndices(validators, dynasty)
|
||||
rewardQuotient := uint64(RewardQuotient(dynasty, validators))
|
||||
penaltyQuotient := uint64(quadraticPenaltyQuotient())
|
||||
depositFactor := (totalParticipatedDeposit - totalDeposit) / totalDeposit
|
||||
|
||||
log.Debugf("Applying rewards and penalties for the validators for slot %d", slot)
|
||||
if timeSinceFinality <= 3*params.GetConfig().CycleLength {
|
||||
@@ -36,8 +34,8 @@ func CalculateRewards(
|
||||
if voterIndex == validatorIndex {
|
||||
voted = true
|
||||
balance := validators[validatorIndex].GetBalance()
|
||||
newbalance := uint64(balance + (balance/rewardQuotient)*depositFactor)
|
||||
validators[validatorIndex].Balance = newbalance
|
||||
newbalance := int64(balance) + int64(balance/rewardQuotient)*(2*int64(totalParticipatedDeposit)-int64(totalDeposit))/int64(totalDeposit)
|
||||
validators[validatorIndex].Balance = uint64(newbalance)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -76,7 +74,7 @@ func CalculateRewards(
|
||||
// reward validators for voting on blocks, or penalise them for being offline.
|
||||
func RewardQuotient(dynasty uint64, validators []*pb.ValidatorRecord) uint64 {
|
||||
totalDepositETH := TotalActiveValidatorDepositInEth(dynasty, validators)
|
||||
return params.GetConfig().BaseRewardQuotient * uint64(math.Pow(float64(totalDepositETH), 0.5))
|
||||
return params.GetConfig().BaseRewardQuotient * shared.IntegerSquareRoot(totalDepositETH)
|
||||
}
|
||||
|
||||
// SlotMaxInterestRate returns the interest rate for a validator in a slot, the interest
|
||||
@@ -89,8 +87,8 @@ func SlotMaxInterestRate(dynasty uint64, validators []*pb.ValidatorRecord) float
|
||||
// quadraticPenaltyQuotient is the quotient that will be used to apply penalties to offline
|
||||
// validators.
|
||||
func quadraticPenaltyQuotient() uint64 {
|
||||
dropTimeFactor := float64(params.GetConfig().SqrtDropTime / params.GetConfig().SlotDuration)
|
||||
return uint64(math.Pow(dropTimeFactor, 2))
|
||||
dropTimeFactor := params.GetConfig().SqrtDropTime / params.GetConfig().SlotDuration
|
||||
return dropTimeFactor * dropTimeFactor
|
||||
}
|
||||
|
||||
// QuadraticPenalty returns the penalty that will be applied to an offline validator
|
||||
@@ -102,17 +100,17 @@ func QuadraticPenalty(numberOfSlots uint64) uint64 {
|
||||
}
|
||||
|
||||
// RewardValidatorCrosslink applies rewards to validators part of a shard committee for voting on a shard.
|
||||
// TODO(#538): Change this to big.Int as tests using 64 bit integers fail due to integer overflow.
|
||||
func RewardValidatorCrosslink(totalDeposit uint64, participatedDeposits uint64, rewardQuotient uint64, validator *pb.ValidatorRecord) {
|
||||
currentBalance := validator.Balance
|
||||
multipicFactor := float64(2*participatedDeposits)/float64(totalDeposit) - 1
|
||||
newBalance := float64(currentBalance) + float64(currentBalance)/float64(rewardQuotient)*multipicFactor
|
||||
validator.Balance = uint64(newBalance)
|
||||
currentBalance := int64(validator.Balance)
|
||||
currentBalance += int64(currentBalance) / int64(rewardQuotient) * (2*int64(participatedDeposits) - int64(totalDeposit)) / int64(totalDeposit)
|
||||
validator.Balance = uint64(currentBalance)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
currentBalance := validator.Balance
|
||||
newBalance := validator.Balance
|
||||
quadraticQuotient := quadraticPenaltyQuotient()
|
||||
newBalance := currentBalance - (currentBalance/rewardQuotient + timeSinceLastConfirmation/quadraticQuotient)
|
||||
newBalance -= newBalance/rewardQuotient + newBalance*timeSinceLastConfirmation/quadraticQuotient
|
||||
validator.Balance = newBalance
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/params"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
)
|
||||
@@ -25,7 +27,6 @@ func TestComputeValidatorRewardsAndPenalties(t *testing.T) {
|
||||
rewQuotient := RewardQuotient(1, validators)
|
||||
participatedDeposit := 4 * defaultBalance
|
||||
totalDeposit := 10 * defaultBalance
|
||||
depositFactor := (2*participatedDeposit - totalDeposit) / totalDeposit
|
||||
penaltyQuotient := quadraticPenaltyQuotient()
|
||||
timeSinceFinality := uint64(5)
|
||||
|
||||
@@ -50,7 +51,7 @@ func TestComputeValidatorRewardsAndPenalties(t *testing.T) {
|
||||
t.Fatalf("validator balance not updated correctly: %d, %d", rewardedValidators[0].Balance, expectedBalance)
|
||||
}
|
||||
|
||||
expectedBalance = uint64(defaultBalance + (defaultBalance/rewQuotient)*depositFactor)
|
||||
expectedBalance = uint64(defaultBalance + (defaultBalance/rewQuotient)*uint64(2*int64(participatedDeposit)-int64(totalDeposit))/uint64(totalDeposit))
|
||||
|
||||
if rewardedValidators[6].Balance != expectedBalance {
|
||||
t.Fatalf("validator balance not updated correctly: %d, %d", rewardedValidators[6].Balance, expectedBalance)
|
||||
@@ -142,7 +143,7 @@ func TestQuadraticPenalty(t *testing.T) {
|
||||
func TestRewardCrosslink(t *testing.T) {
|
||||
totalDeposit := uint64(6e18)
|
||||
participatedDeposit := uint64(3e18)
|
||||
rewardQuotient := params.GetConfig().BaseRewardQuotient * uint64(math.Pow(float64(totalDeposit), 0.5))
|
||||
rewardQuotient := params.GetConfig().BaseRewardQuotient * shared.IntegerSquareRoot(totalDeposit)
|
||||
validator := &pb.ValidatorRecord{
|
||||
Balance: 1e18,
|
||||
}
|
||||
@@ -155,23 +156,19 @@ func TestRewardCrosslink(t *testing.T) {
|
||||
participatedDeposit = uint64(4e18)
|
||||
RewardValidatorCrosslink(totalDeposit, participatedDeposit, rewardQuotient, validator)
|
||||
|
||||
if validator.Balance == 1e18 {
|
||||
t.Errorf("validator balances have not been updated %d ", validator.Balance)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestPenaltyCrosslink(t *testing.T) {
|
||||
totalDeposit := uint64(6e18)
|
||||
rewardQuotient := params.GetConfig().BaseRewardQuotient * uint64(math.Pow(float64(totalDeposit), 0.5))
|
||||
rewardQuotient := params.GetConfig().BaseRewardQuotient * shared.IntegerSquareRoot(totalDeposit)
|
||||
validator := &pb.ValidatorRecord{
|
||||
Balance: 1e18,
|
||||
}
|
||||
timeSinceConfirmation := uint64(100)
|
||||
timeSinceConfirmation := uint64(10)
|
||||
quadraticQuotient := quadraticPenaltyQuotient()
|
||||
|
||||
PenaliseValidatorCrosslink(timeSinceConfirmation, rewardQuotient, validator)
|
||||
expectedBalance := 1e18 - 1e18/rewardQuotient + 100/quadraticQuotient
|
||||
expectedBalance := 1e18 - (1e18/rewardQuotient + 1e18*10/quadraticQuotient)
|
||||
|
||||
if validator.Balance != expectedBalance {
|
||||
t.Fatalf("balances not updated correctly %d, %d", validator.Balance, expectedBalance)
|
||||
|
||||
@@ -225,12 +225,7 @@ func TestProcessCrosslinks(t *testing.T) {
|
||||
if !bytes.Equal(newCrosslinks[0].Blockhash, []byte{'a'}) {
|
||||
t.Errorf("Blockhash did not change for new cross link. Wanted a. Got: %s", newCrosslinks[0].Blockhash)
|
||||
}
|
||||
|
||||
for _, index := range committee {
|
||||
if cState.Validators()[index].Balance == 1e18 {
|
||||
t.Errorf("validator with index %d did not have balance changed.", index)
|
||||
}
|
||||
}
|
||||
//TODO(#538) Implement tests on balances of the validators in committee once big.Int is introduced.
|
||||
}
|
||||
|
||||
func TestIsDynastyTransition(t *testing.T) {
|
||||
|
||||
@@ -5,6 +5,7 @@ go_library(
|
||||
srcs = [
|
||||
"bit.go",
|
||||
"marshal.go",
|
||||
"math_helper.go",
|
||||
"service_registry.go",
|
||||
"types.go",
|
||||
],
|
||||
@@ -25,6 +26,7 @@ go_test(
|
||||
srcs = [
|
||||
"bit_test.go",
|
||||
"marshal_test.go",
|
||||
"math_helper_test.go",
|
||||
"service_registry_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
|
||||
14
shared/math_helper.go
Normal file
14
shared/math_helper.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package shared
|
||||
|
||||
// IntegerSquareRoot defines a function that returns the
|
||||
// largest possible integer root of a number.
|
||||
func IntegerSquareRoot(n uint64) uint64 {
|
||||
x := n
|
||||
y := (x + 1) / 2
|
||||
|
||||
for y < x {
|
||||
x = y
|
||||
y = (x + n/x) / 2
|
||||
}
|
||||
return x
|
||||
}
|
||||
42
shared/math_helper_test.go
Normal file
42
shared/math_helper_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
type numbertTest struct {
|
||||
number uint64
|
||||
root uint64
|
||||
}
|
||||
|
||||
func TestIntegerSquareRoot(t *testing.T) {
|
||||
tt := []numbertTest{
|
||||
{
|
||||
number: 20,
|
||||
root: 4,
|
||||
},
|
||||
{
|
||||
number: 200,
|
||||
root: 14,
|
||||
},
|
||||
{
|
||||
number: 1987,
|
||||
root: 44,
|
||||
},
|
||||
{
|
||||
number: 34989843,
|
||||
root: 5915,
|
||||
},
|
||||
{
|
||||
number: 97282,
|
||||
root: 311,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testVals := range tt {
|
||||
root := IntegerSquareRoot(testVals.number)
|
||||
if testVals.root != root {
|
||||
t.Fatalf("expected root and computed root are not equal %d, %d", testVals.root, root)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user