prysm-pulse/beacon-chain/core/epoch/epoch_operations.go

183 lines
5.9 KiB
Go

// Package epoch contains epoch processing libraries. These libraries
// process new balance for the validators, justify and finalize new
// check points, shuffle and reassign validators to different slots and
// shards.
package epoch
import (
"fmt"
"math"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/core/validators"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
b "github.com/prysmaticlabs/prysm/shared/bytesutil"
)
// TotalBalance returns the total balance at stake of the validators
// from the shard committee regardless of validators attested or not.
//
// Spec pseudocode definition:
// def get_total_balance(state: BeaconState, validators: List[ValidatorIndex]) -> Gwei:
// """
// Return the combined effective balance of an array of validators.
// """
// return sum([get_effective_balance(state, i) for i in validators])
func TotalBalance(
state *pb.BeaconState,
activeValidatorIndices []uint64) uint64 {
var totalBalance uint64
for _, index := range activeValidatorIndices {
totalBalance += helpers.EffectiveBalance(state, index)
}
return totalBalance
}
// InclusionSlot returns the slot number of when the validator's
// attestation gets included in the beacon chain.
//
// Spec pseudocode definition:
// Let inclusion_slot(state, index) =
// a.slot_included for the attestation a where index is in
// get_attestation_participants(state, a.data, a.participation_bitfield)
// If multiple attestations are applicable, the attestation with
// lowest `slot_included` is considered.
func InclusionSlot(state *pb.BeaconState, validatorIndex uint64) (uint64, error) {
lowestSlotIncluded := uint64(math.MaxUint64)
for _, attestation := range state.LatestAttestations {
participatedValidators, err := helpers.AttestationParticipants(state, attestation.Data, attestation.AggregationBitfield)
if err != nil {
return 0, fmt.Errorf("could not get attestation participants: %v", err)
}
for _, index := range participatedValidators {
if index == validatorIndex {
if attestation.InclusionSlot < lowestSlotIncluded {
lowestSlotIncluded = attestation.InclusionSlot
}
}
}
}
if lowestSlotIncluded == math.MaxUint64 {
return 0, fmt.Errorf("could not find inclusion slot for validator index %d", validatorIndex)
}
return lowestSlotIncluded, nil
}
// AttestingValidators returns the validators of the winning root.
//
// Spec pseudocode definition:
// Let `attesting_validators(crosslink_committee)` be equal to
// `attesting_validator_indices(crosslink_committee, winning_root(crosslink_committee))` for convenience
func AttestingValidators(
state *pb.BeaconState,
shard uint64,
currentEpochAttestations []*pb.PendingAttestation,
prevEpochAttestations []*pb.PendingAttestation) ([]uint64, error) {
root, err := winningRoot(
state,
shard,
currentEpochAttestations,
prevEpochAttestations)
if err != nil {
return nil, fmt.Errorf("could not get winning root: %v", err)
}
indices, err := validators.AttestingValidatorIndices(
state,
shard,
root,
currentEpochAttestations,
prevEpochAttestations)
if err != nil {
return nil, fmt.Errorf("could not get attesting validator indices: %v", err)
}
return indices, nil
}
// TotalAttestingBalance returns the total balance at stake of the validators
// attested to the winning root.
//
// Spec pseudocode definition:
// Let total_balance(crosslink_committee) =
// sum([get_effective_balance(state, i) for i in crosslink_committee.committee])
func TotalAttestingBalance(
state *pb.BeaconState,
shard uint64,
currentEpochAttestations []*pb.PendingAttestation,
prevEpochAttestations []*pb.PendingAttestation) (uint64, error) {
var totalBalance uint64
attestedValidatorIndices, err := AttestingValidators(state, shard, currentEpochAttestations, prevEpochAttestations)
if err != nil {
return 0, fmt.Errorf("could not get attesting validator indices: %v", err)
}
for _, index := range attestedValidatorIndices {
totalBalance += helpers.EffectiveBalance(state, index)
}
return totalBalance, nil
}
// SinceFinality calculates and returns how many epoch has it been since
// a finalized slot.
//
// Spec pseudocode definition:
// epochs_since_finality = next_epoch - state.finalized_epoch
func SinceFinality(state *pb.BeaconState) uint64 {
return helpers.NextEpoch(state) - state.FinalizedEpoch
}
// winningRoot returns the shard block root with the most combined validator
// effective balance. The ties broken by favoring lower shard block root values.
//
// Spec pseudocode definition:
// Let winning_root(crosslink_committee) be equal to the value of crosslink_data_root
// such that get_total_balance(state, attesting_validator_indices(crosslink_committee, crosslink_data_root))
// is maximized (ties broken by favoring lexicographically smallest crosslink_data_root).
func winningRoot(
state *pb.BeaconState,
shard uint64,
currentEpochAttestations []*pb.PendingAttestation,
prevEpochAttestations []*pb.PendingAttestation) ([]byte, error) {
var winnerBalance uint64
var winnerRoot []byte
var candidateRoots [][]byte
attestations := append(currentEpochAttestations, prevEpochAttestations...)
for _, attestation := range attestations {
if attestation.Data.Shard == shard {
candidateRoots = append(candidateRoots, attestation.Data.CrosslinkDataRootHash32)
}
}
for _, candidateRoot := range candidateRoots {
indices, err := validators.AttestingValidatorIndices(
state,
shard,
candidateRoot,
currentEpochAttestations,
prevEpochAttestations)
if err != nil {
return nil, fmt.Errorf("could not get attesting validator indices: %v", err)
}
var rootBalance uint64
for _, index := range indices {
rootBalance += helpers.EffectiveBalance(state, index)
}
if rootBalance > winnerBalance ||
(rootBalance == winnerBalance && b.LowerThan(candidateRoot, winnerRoot)) {
winnerBalance = rootBalance
winnerRoot = candidateRoot
}
}
return winnerRoot, nil
}