Skip to content

Commit

Permalink
Refactor More Casper Functions Into Beacon-Chain/Core (#890)
Browse files Browse the repository at this point in the history
  • Loading branch information
rauljordan authored Nov 22, 2018
1 parent 3bcbda0 commit 30e3474
Show file tree
Hide file tree
Showing 13 changed files with 161 additions and 297 deletions.
40 changes: 0 additions & 40 deletions beacon-chain/casper/BUILD.bazel

This file was deleted.

2 changes: 2 additions & 0 deletions beacon-chain/core/incentives/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/incentives",
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//beacon-chain/utils:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/bitutil:go_default_library",
"//shared/mathutil:go_default_library",
"//shared/params:go_default_library",
],
Expand Down
112 changes: 72 additions & 40 deletions beacon-chain/core/incentives/incentives.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,42 @@
package incentives

import (
"github.com/prysmaticlabs/prysm/beacon-chain/utils"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/bitutil"
"github.com/prysmaticlabs/prysm/shared/mathutil"
"github.com/prysmaticlabs/prysm/shared/params"
)

// TallyVoteBalances calculates all the votes behind a block and
// then rewards validators for their participation in voting for that block.
func TallyVoteBalances(
blockHash [32]byte,
blockVoteCache utils.BlockVoteCache,
validators []*pb.ValidatorRecord,
activeValidatorIndices []uint32,
totalActiveValidatorDeposit uint64,
timeSinceFinality uint64,
) (uint64, []*pb.ValidatorRecord) {
blockVote, ok := blockVoteCache[blockHash]
if !ok {
return 0, validators
}

blockVoteBalance := blockVote.VoteTotalDeposit
voterIndices := blockVote.VoterIndices
validators = CalculateRewards(
voterIndices,
activeValidatorIndices,
validators,
totalActiveValidatorDeposit,
blockVoteBalance,
timeSinceFinality,
)

return blockVoteBalance, validators
}

// CalculateRewards adjusts validators balances by applying rewards or penalties
// based on FFG incentive structure.
// FFG Rewards scheme rewards validator who have voted on blocks, and penalises those validators
Expand All @@ -35,7 +66,7 @@ func CalculateRewards(
if voterIndex == validatorIndex {
voted = true
balance := validators[validatorIndex].GetBalance()
newBalance := calculateBalance(balance, rewardQuotient, totalParticipatedDeposit, totalActiveValidatorDeposit)
newBalance := int64(balance) + int64(balance/rewardQuotient)*(2*int64(totalParticipatedDeposit)-int64(totalActiveValidatorDeposit))/int64(totalActiveValidatorDeposit)
validators[validatorIndex].Balance = uint64(newBalance)
break
}
Expand Down Expand Up @@ -71,6 +102,35 @@ func CalculateRewards(
return validators
}

// ApplyCrosslinkRewardsAndPenalties applies the appropriate rewards and
// penalties according to the attestation for a shard.
func ApplyCrosslinkRewardsAndPenalties(
crosslinkRecords []*pb.CrosslinkRecord,
slot uint64,
attesterIndices []uint32,
attestation *pb.AggregatedAttestation,
validators []*pb.ValidatorRecord,
totalActiveValidatorDeposit uint64,
totalBalance uint64,
voteBalance uint64,
) error {
rewardQuotient := RewardQuotient(totalActiveValidatorDeposit)
for _, attesterIndex := range attesterIndices {
timeSinceLastConfirmation := slot - crosslinkRecords[attestation.Shard].GetSlot()

checkBit, err := bitutil.CheckBit(attestation.AttesterBitfield, int(attesterIndex))
if err != nil {
return err
}
if checkBit {
RewardValidatorCrosslink(totalBalance, voteBalance, rewardQuotient, validators[attesterIndex])
} else {
PenaliseValidatorCrosslink(timeSinceLastConfirmation, rewardQuotient, validators[attesterIndex])
}
}
return nil
}

// RewardQuotient returns the reward quotient for validators which will be used to
// reward validators for voting on blocks, or penalise them for being offline.
func RewardQuotient(totalActiveValidatorDeposit uint64) uint64 {
Expand All @@ -93,55 +153,27 @@ func QuadraticPenalty(numberOfSlots uint64) uint64 {
return slotFactor / penaltyQuotient
}

// RewardValidatorCrosslink applies rewards to validators part of a shard
// committee for voting on a shard.
// 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,
) *pb.ValidatorRecord {
balance := calculateBalance(validator.Balance, rewardQuotient, participatedDeposits, totalDeposit)
return &pb.ValidatorRecord{
Pubkey: validator.Pubkey,
WithdrawalShard: validator.WithdrawalShard,
WithdrawalAddress: validator.WithdrawalAddress,
RandaoCommitment: validator.RandaoCommitment,
Balance: balance,
Status: validator.Status,
ExitSlot: validator.ExitSlot,
}
) {
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.
// 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,
) *pb.ValidatorRecord {
) {
newBalance := validator.Balance
quadraticQuotient := QuadraticPenaltyQuotient()
balance := validator.Balance
balance -= balance/rewardQuotient + balance*timeSinceLastConfirmation/quadraticQuotient
return &pb.ValidatorRecord{
Pubkey: validator.Pubkey,
WithdrawalShard: validator.WithdrawalShard,
WithdrawalAddress: validator.WithdrawalAddress,
RandaoCommitment: validator.RandaoCommitment,
Balance: balance,
Status: validator.Status,
ExitSlot: validator.ExitSlot,
}
}

// calculateBalance applies the Casper FFG reward calculation based on reward quotients
// and total deposits from validators.
func calculateBalance(
balance uint64,
rewardQuotient uint64,
totalParticipatedDeposit uint64,
totalActiveValidatorDeposit uint64,
) uint64 {
participationNumerator := 2*int64(totalParticipatedDeposit) - int64(totalActiveValidatorDeposit)
return uint64(int64(balance) + int64(balance/rewardQuotient)*participationNumerator/int64(totalActiveValidatorDeposit))
newBalance -= newBalance/rewardQuotient + newBalance*timeSinceLastConfirmation/quadraticQuotient
validator.Balance = newBalance
}
6 changes: 3 additions & 3 deletions beacon-chain/core/incentives/incentives_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func TestComputeValidatorRewardsAndPenalties(t *testing.T) {
t.Fatalf("validator balance not updated correctly: %d, %d", rewardedValidators[0].Balance, expectedBalance)
}

expectedBalance = calculateBalance(defaultBalance, rewQuotient, participatedDeposit, totalDeposit)
expectedBalance = uint64(int64(defaultBalance) + int64(defaultBalance/rewQuotient)*(2*int64(participatedDeposit)-int64(totalDeposit))/int64(totalDeposit))

if rewardedValidators[6].Balance != expectedBalance {
t.Fatalf("validator balance not updated correctly: %d, %d", rewardedValidators[6].Balance, expectedBalance)
Expand Down Expand Up @@ -142,7 +142,7 @@ func TestRewardCrosslink(t *testing.T) {
Balance: 1e18,
}

validator = RewardValidatorCrosslink(totalDeposit, participatedDeposit, rewardQuotient, validator)
RewardValidatorCrosslink(totalDeposit, participatedDeposit, rewardQuotient, validator)

if validator.Balance != 1e18 {
t.Errorf("validator balances have changed when they were not supposed to %d", validator.Balance)
Expand All @@ -158,7 +158,7 @@ func TestPenaltyCrosslink(t *testing.T) {
timeSinceConfirmation := uint64(10)
quadraticQuotient := QuadraticPenaltyQuotient()

validator = PenaliseValidatorCrosslink(timeSinceConfirmation, rewardQuotient, validator)
PenaliseValidatorCrosslink(timeSinceConfirmation, rewardQuotient, validator)
expectedBalance := 1e18 - (1e18/rewardQuotient + 1e18*timeSinceConfirmation/quadraticQuotient)

if validator.Balance != expectedBalance {
Expand Down
24 changes: 24 additions & 0 deletions beacon-chain/core/state/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "go_default_library",
srcs = ["processing.go"],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/state",
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//beacon-chain/core/validators:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/params:go_default_library",
],
)

go_test(
name = "go_default_test",
srcs = ["processing_test.go"],
embed = [":go_default_library"],
deps = [
"//proto/beacon/p2p/v1:go_default_library",
"//shared/bytes:go_default_library",
"//shared/params:go_default_library",
],
)
Original file line number Diff line number Diff line change
@@ -1,51 +1,18 @@
package casper
package state

import (
"encoding/binary"

"github.com/prysmaticlabs/prysm/beacon-chain/core/incentives"
v "github.com/prysmaticlabs/prysm/beacon-chain/core/validators"
"github.com/prysmaticlabs/prysm/beacon-chain/utils"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/bitutil"
"github.com/prysmaticlabs/prysm/shared/params"
)

// TallyVoteBalances calculates all the votes behind a block and then rewards validators for their
// participation in voting for that block.
func TallyVoteBalances(
blockHash [32]byte,
blockVoteCache utils.BlockVoteCache,
validators []*pb.ValidatorRecord,
timeSinceFinality uint64) (uint64, []*pb.ValidatorRecord) {

blockVote, ok := blockVoteCache[blockHash]
if !ok {
return 0, validators
}

blockVoteBalance := blockVote.VoteTotalDeposit
voterIndices := blockVote.VoterIndices
activeValidatorIndices := v.ActiveValidatorIndices(validators)
totalDeposit := v.TotalActiveValidatorDeposit(validators)
validators = incentives.CalculateRewards(
voterIndices,
activeValidatorIndices,
validators,
totalDeposit,
blockVoteBalance,
timeSinceFinality,
)

return blockVoteBalance, validators
}

// FinalizeAndJustifySlots justifies slots and sets the justified streak according to Casper FFG
// conditions. It also finalizes slots when the conditions are fulfilled.
func FinalizeAndJustifySlots(
slot uint64, justifiedSlot uint64, finalizedSlot uint64,
justifiedStreak uint64, blockVoteBalance uint64, totalDeposits uint64) (uint64, uint64, uint64) {

cycleLength := params.BeaconConfig().CycleLength

if 3*blockVoteBalance >= 2*totalDeposits {
Expand All @@ -66,41 +33,10 @@ func FinalizeAndJustifySlots(
return justifiedSlot, finalizedSlot, justifiedStreak
}

// ApplyCrosslinkRewardsAndPenalties applies the appropriate rewards and penalties according to the attestation
// for a shard.
func ApplyCrosslinkRewardsAndPenalties(
crosslinkRecords []*pb.CrosslinkRecord,
slot uint64,
attesterIndices []uint32,
attestation *pb.AggregatedAttestation,
validators []*pb.ValidatorRecord,
totalBalance uint64,
voteBalance uint64) error {

totalDeposit := v.TotalActiveValidatorDeposit(validators)
rewardQuotient := incentives.RewardQuotient(totalDeposit)

for _, attesterIndex := range attesterIndices {
timeSinceLastConfirmation := slot - crosslinkRecords[attestation.Shard].GetSlot()

checkBit, err := bitutil.CheckBit(attestation.AttesterBitfield, int(attesterIndex))
if err != nil {
return err
}
if checkBit {
validators[attesterIndex] = incentives.RewardValidatorCrosslink(totalBalance, voteBalance, rewardQuotient, validators[attesterIndex])
} else {
validators[attesterIndex] = incentives.PenaliseValidatorCrosslink(timeSinceLastConfirmation, rewardQuotient, validators[attesterIndex])
}
}
return nil
}

// ProcessCrosslink checks the vote balances and if there is a supermajority it sets the crosslink
// for that shard.
func ProcessCrosslink(slot uint64, voteBalance uint64, totalBalance uint64,
attestation *pb.AggregatedAttestation, crosslinkRecords []*pb.CrosslinkRecord) []*pb.CrosslinkRecord {

// if 2/3 of committee voted on this crosslink, update the crosslink
// with latest dynasty number, shard block hash, and slot number.
voteMajority := 3*voteBalance >= 2*totalBalance
Expand All @@ -115,18 +51,18 @@ func ProcessCrosslink(slot uint64, voteBalance uint64, totalBalance uint64,

// ProcessSpecialRecords processes the pending special record objects,
// this is called during crystallized state transition.
func ProcessSpecialRecords(slotNumber uint64, validators []*pb.ValidatorRecord, pendingSpecials []*pb.SpecialRecord) ([]*pb.ValidatorRecord, error) {
func ProcessSpecialRecords(slotNumber uint64, validators []*pb.ValidatorRecord,
pendingSpecials []*pb.SpecialRecord) ([]*pb.ValidatorRecord, error) {
// For each special record object in active state.
for _, specialRecord := range pendingSpecials {

// Covers validators submitted logouts from last cycle.
if specialRecord.Kind == uint32(params.Logout) {
validatorIndex := binary.BigEndian.Uint64(specialRecord.Data[0])
exitedValidator := v.ExitValidator(validators[validatorIndex], slotNumber, false)
validators[validatorIndex] = exitedValidator
// TODO(#633): Verify specialRecord.Data[1] as signature. BLSVerify(pubkey=validator.pubkey, msg=hash(LOGOUT_MESSAGE + bytes8(version))
// TODO(#633): Verify specialRecord.Data[1] as signature.
// BLSVerify(pubkey=validator.pubkey, msg=hash(LOGOUT_MESSAGE + bytes8(version))
}

}
return validators, nil
}
Loading

0 comments on commit 30e3474

Please sign in to comment.