mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2025-01-04 00:44:27 +00:00
410 lines
14 KiB
Go
410 lines
14 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 (
|
|
"bytes"
|
|
"fmt"
|
|
|
|
block "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/validators"
|
|
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
|
b "github.com/prysmaticlabs/prysm/shared/bytes"
|
|
"github.com/prysmaticlabs/prysm/shared/params"
|
|
)
|
|
|
|
// Attestations returns the pending attestations of slots in the epoch
|
|
// (state.slot-EPOCH_LENGTH...state.slot-1), not attestations that got
|
|
// included in the chain during the epoch.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// return [a for a in state.latest_attestations
|
|
// if state.slot - EPOCH_LENGTH <= a.data.slot < state.slot]
|
|
func Attestations(state *pb.BeaconState) []*pb.PendingAttestationRecord {
|
|
epochLength := params.BeaconConfig().EpochLength
|
|
var thisEpochAttestations []*pb.PendingAttestationRecord
|
|
var earliestSlot uint64
|
|
|
|
for _, attestation := range state.LatestAttestations {
|
|
|
|
// If the state slot is less than epochLength, then the earliestSlot would
|
|
// result in a negative number. Therefore we should default to
|
|
// earliestSlot = 0 in this case.
|
|
if state.Slot > epochLength {
|
|
earliestSlot = state.Slot - epochLength
|
|
}
|
|
|
|
if earliestSlot <= attestation.Data.Slot && attestation.Data.Slot < state.Slot {
|
|
thisEpochAttestations = append(thisEpochAttestations, attestation)
|
|
}
|
|
}
|
|
return thisEpochAttestations
|
|
}
|
|
|
|
// BoundaryAttestations returns the pending attestations from
|
|
// the epoch's boundary block.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// return [a for a in this_epoch_attestations if a.data.epoch_boundary_root ==
|
|
// get_block_root(state, state.slot-EPOCH_LENGTH) and a.justified_slot ==
|
|
// state.justified_slot]
|
|
func BoundaryAttestations(
|
|
state *pb.BeaconState,
|
|
thisEpochAttestations []*pb.PendingAttestationRecord,
|
|
) ([]*pb.PendingAttestationRecord, error) {
|
|
epochLength := params.BeaconConfig().EpochLength
|
|
var boundarySlot uint64
|
|
var boundaryAttestations []*pb.PendingAttestationRecord
|
|
|
|
for _, attestation := range thisEpochAttestations {
|
|
|
|
// If boundary slot is less than epoch length, then it would
|
|
// result in a negative number. Therefore we should
|
|
// default boundarySlot = 0 in this case.
|
|
if state.Slot > epochLength {
|
|
boundarySlot = state.Slot - epochLength
|
|
}
|
|
|
|
boundaryBlockRoot, err := block.BlockRoot(state, boundarySlot)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
attestationData := attestation.Data
|
|
sameRoot := bytes.Equal(attestationData.JustifiedBlockRootHash32, boundaryBlockRoot)
|
|
sameSlotNum := attestationData.JustifiedSlot == state.JustifiedSlot
|
|
if sameRoot && sameSlotNum {
|
|
boundaryAttestations = append(boundaryAttestations, attestation)
|
|
}
|
|
}
|
|
return boundaryAttestations, nil
|
|
}
|
|
|
|
// PrevAttestations returns the attestations of the previous epoch
|
|
// (state.slot - 2 * EPOCH_LENGTH...state.slot - EPOCH_LENGTH).
|
|
//
|
|
// Spec pseudocode definition:
|
|
// return [a for a in state.latest_attestations
|
|
// if state.slot - 2 * EPOCH_LENGTH <= a.slot < state.slot - EPOCH_LENGTH]
|
|
func PrevAttestations(state *pb.BeaconState) []*pb.PendingAttestationRecord {
|
|
epochLength := params.BeaconConfig().EpochLength
|
|
var prevEpochAttestations []*pb.PendingAttestationRecord
|
|
var earliestSlot uint64
|
|
var lastSlot uint64
|
|
|
|
for _, attestation := range state.LatestAttestations {
|
|
|
|
// If the state slot is less than 2 * epochLength, then the earliestSlot would
|
|
// result in a negative number. Therefore we should default to
|
|
// earliestSlot = 0 in this case.
|
|
if state.Slot > 2*epochLength {
|
|
earliestSlot = state.Slot - 2*epochLength
|
|
}
|
|
// If the state slot is less than epochLength, then the lastSlot would
|
|
// result in a negative number. Therefore we should default to
|
|
// lastSlot = 0 in this case.
|
|
if state.Slot > epochLength {
|
|
lastSlot = state.Slot - epochLength
|
|
}
|
|
|
|
if earliestSlot <= attestation.Data.Slot &&
|
|
attestation.Data.Slot < lastSlot {
|
|
prevEpochAttestations = append(prevEpochAttestations, attestation)
|
|
}
|
|
}
|
|
|
|
return prevEpochAttestations
|
|
}
|
|
|
|
// PrevJustifiedAttestations returns the justified attestations
|
|
// of the previous 2 epochs.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// return [a for a in this_epoch_attestations + previous_epoch_attestations
|
|
// if a.justified_slot == state.previous_justified_slot]
|
|
func PrevJustifiedAttestations(
|
|
state *pb.BeaconState,
|
|
thisEpochAttestations []*pb.PendingAttestationRecord,
|
|
prevEpochAttestations []*pb.PendingAttestationRecord,
|
|
) []*pb.PendingAttestationRecord {
|
|
|
|
var prevJustifiedAttestations []*pb.PendingAttestationRecord
|
|
epochAttestations := append(thisEpochAttestations, prevEpochAttestations...)
|
|
|
|
for _, attestation := range epochAttestations {
|
|
if attestation.Data.JustifiedSlot == state.PreviousJustifiedSlot {
|
|
prevJustifiedAttestations = append(prevJustifiedAttestations, attestation)
|
|
}
|
|
}
|
|
return prevJustifiedAttestations
|
|
}
|
|
|
|
// PrevBoundaryAttestations returns the boundary attestations
|
|
// at the start of the previous epoch.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// return [a for a in previous_epoch_justified_attestations
|
|
// if a.epoch_boundary_root == get_block_root(state, state.slot - 2 * EPOCH_LENGTH)]
|
|
func PrevBoundaryAttestations(
|
|
state *pb.BeaconState,
|
|
prevEpochJustifiedAttestations []*pb.PendingAttestationRecord,
|
|
) ([]*pb.PendingAttestationRecord, error) {
|
|
var earliestSlot uint64
|
|
|
|
// If the state slot is less than 2 * epochLength, then the earliestSlot would
|
|
// result in a negative number. Therefore we should default to
|
|
// earliestSlot = 0 in this case.
|
|
if state.Slot > 2*params.BeaconConfig().EpochLength {
|
|
earliestSlot = state.Slot - 2*params.BeaconConfig().EpochLength
|
|
}
|
|
|
|
var prevBoundaryAttestations []*pb.PendingAttestationRecord
|
|
prevBoundaryBlockRoot, err := block.BlockRoot(state,
|
|
earliestSlot)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, attestation := range prevEpochJustifiedAttestations {
|
|
if bytes.Equal(attestation.Data.EpochBoundaryRootHash32, prevBoundaryBlockRoot) {
|
|
prevBoundaryAttestations = append(prevBoundaryAttestations, attestation)
|
|
}
|
|
}
|
|
return prevBoundaryAttestations, nil
|
|
}
|
|
|
|
// PrevHeadAttestations returns the pending attestations from
|
|
// the canonical beacon chain.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// return [a for a in previous_epoch_attestations
|
|
// if a.beacon_block_root == get_block_root(state, a.slot)]
|
|
func PrevHeadAttestations(
|
|
state *pb.BeaconState,
|
|
prevEpochAttestations []*pb.PendingAttestationRecord,
|
|
) ([]*pb.PendingAttestationRecord, error) {
|
|
|
|
var headAttestations []*pb.PendingAttestationRecord
|
|
for _, attestation := range prevEpochAttestations {
|
|
canonicalBlockRoot, err := block.BlockRoot(state, attestation.Data.Slot)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
attestationData := attestation.Data
|
|
if bytes.Equal(attestationData.BeaconBlockRootHash32, canonicalBlockRoot) {
|
|
headAttestations = append(headAttestations, attestation)
|
|
}
|
|
}
|
|
return headAttestations, nil
|
|
}
|
|
|
|
// TotalBalance returns the total balance at stake of the validators
|
|
// from the shard committee regardless of validators attested or not.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// Let total_balance =
|
|
// sum([get_effective_balance(state, i) for i in active_validator_indices])
|
|
func TotalBalance(
|
|
state *pb.BeaconState,
|
|
activeValidatorIndices []uint32) uint64 {
|
|
|
|
var totalBalance uint64
|
|
for _, index := range activeValidatorIndices {
|
|
totalBalance += validators.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)
|
|
func InclusionSlot(state *pb.BeaconState, validatorIndex uint32) (uint64, error) {
|
|
|
|
for _, attestation := range state.LatestAttestations {
|
|
participatedValidators, err := validators.AttestationParticipants(state, attestation.Data, attestation.ParticipationBitfield)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("could not get attestation participants: %v", err)
|
|
}
|
|
|
|
for _, index := range participatedValidators {
|
|
if index == validatorIndex {
|
|
return attestation.SlotIncluded, nil
|
|
}
|
|
}
|
|
}
|
|
return 0, fmt.Errorf("could not find inclusion slot for validator index %d", validatorIndex)
|
|
}
|
|
|
|
// InclusionDistance returns the difference in slot number of when attestation
|
|
// gets submitted and when it gets included.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// Let inclusion_distance(state, index) =
|
|
// a.slot_included - a.data.slot where a is the above attestation same as
|
|
// inclusion_slot
|
|
func InclusionDistance(state *pb.BeaconState, validatorIndex uint32) (uint64, error) {
|
|
|
|
for _, attestation := range state.LatestAttestations {
|
|
participatedValidators, err := validators.AttestationParticipants(state, attestation.Data, attestation.ParticipationBitfield)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("could not get attestation participants: %v", err)
|
|
}
|
|
|
|
for _, index := range participatedValidators {
|
|
if index == validatorIndex {
|
|
return attestation.SlotIncluded - attestation.Data.Slot, nil
|
|
}
|
|
}
|
|
}
|
|
return 0, fmt.Errorf("could not find inclusion distance for validator index %d", validatorIndex)
|
|
}
|
|
|
|
// AttestingValidators returns the validators of the winning root.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// Let `attesting_validators(shard_committee)` be equal to
|
|
// `attesting_validator_indices(shard_committee, winning_root(shard_committee))` for convenience
|
|
func AttestingValidators(
|
|
state *pb.BeaconState,
|
|
shardCommittee *pb.ShardCommittee,
|
|
thisEpochAttestations []*pb.PendingAttestationRecord,
|
|
prevEpochAttestations []*pb.PendingAttestationRecord) ([]uint32, error) {
|
|
|
|
root, err := winningRoot(
|
|
state,
|
|
shardCommittee,
|
|
thisEpochAttestations,
|
|
prevEpochAttestations)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not get winning root: %v", err)
|
|
}
|
|
|
|
indices, err := validators.AttestingValidatorIndices(
|
|
state,
|
|
shardCommittee,
|
|
root,
|
|
thisEpochAttestations,
|
|
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(shard_committee) =
|
|
// sum([get_effective_balance(state, i) for i in shard_committee.committee])
|
|
func TotalAttestingBalance(
|
|
state *pb.BeaconState,
|
|
shardCommittee *pb.ShardCommittee,
|
|
thisEpochAttestations []*pb.PendingAttestationRecord,
|
|
prevEpochAttestations []*pb.PendingAttestationRecord) (uint64, error) {
|
|
|
|
var totalBalance uint64
|
|
attestedValidatorIndices, err := AttestingValidators(state, shardCommittee, thisEpochAttestations, prevEpochAttestations)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("could not get attesting validator indices: %v", err)
|
|
}
|
|
|
|
for _, index := range attestedValidatorIndices {
|
|
totalBalance += validators.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 = (state.slot - state.finalized_slot) // EPOCH_LENGTH
|
|
func SinceFinality(state *pb.BeaconState) uint64 {
|
|
return (state.Slot - state.FinalizedSlot) / params.BeaconConfig().EpochLength
|
|
}
|
|
|
|
// 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(shard_committee) be equal to the value of shard_block_root
|
|
// such that sum([get_effective_balance(state, i)
|
|
// for i in attesting_validator_indices(shard_committee, shard_block_root)])
|
|
// is maximized (ties broken by favoring lower shard_block_root values)
|
|
func winningRoot(
|
|
state *pb.BeaconState,
|
|
shardCommittee *pb.ShardCommittee,
|
|
thisEpochAttestations []*pb.PendingAttestationRecord,
|
|
prevEpochAttestations []*pb.PendingAttestationRecord) ([]byte, error) {
|
|
|
|
var winnerBalance uint64
|
|
var winnerRoot []byte
|
|
var candidateRoots [][]byte
|
|
attestations := append(thisEpochAttestations, prevEpochAttestations...)
|
|
|
|
for _, attestation := range attestations {
|
|
if attestation.Data.Shard == shardCommittee.Shard {
|
|
candidateRoots = append(candidateRoots, attestation.Data.ShardBlockRootHash32)
|
|
}
|
|
}
|
|
|
|
for _, candidateRoot := range candidateRoots {
|
|
indices, err := validators.AttestingValidatorIndices(
|
|
state,
|
|
shardCommittee,
|
|
candidateRoot,
|
|
thisEpochAttestations,
|
|
prevEpochAttestations)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not get attesting validator indices: %v", err)
|
|
}
|
|
|
|
var rootBalance uint64
|
|
for _, index := range indices {
|
|
rootBalance += validators.EffectiveBalance(state, index)
|
|
}
|
|
|
|
if rootBalance > winnerBalance ||
|
|
(rootBalance == winnerBalance && b.LowerThan(candidateRoot, winnerRoot)) {
|
|
winnerBalance = rootBalance
|
|
winnerRoot = candidateRoot
|
|
}
|
|
}
|
|
return winnerRoot, nil
|
|
}
|
|
|
|
// randaoMix returns the randao mix (xor'ed seed)
|
|
// of a given slot. It is used to shuffle validators.
|
|
//
|
|
// Spec pseudocode definition:
|
|
// def get_randao_mix(state: BeaconState,
|
|
// slot: int) -> Hash32:
|
|
// """
|
|
// Returns the randao mix at a recent ``slot``.
|
|
// """
|
|
// assert state.slot <= slot + LATEST_RANDAO_MIXES_LENGTH
|
|
// assert slot < state.slot
|
|
// return state.latest_block_roots[slot % LATEST_RANDAO_MIXES_LENGTH]
|
|
func randaoMix(state *pb.BeaconState, slot uint64) ([]byte, error) {
|
|
var lowerBound uint64
|
|
if state.Slot > config.LatestRandaoMixesLength {
|
|
lowerBound = state.Slot - config.LatestRandaoMixesLength
|
|
}
|
|
upperBound := state.Slot
|
|
if lowerBound > slot || slot >= upperBound {
|
|
return nil, fmt.Errorf("input randaoMix slot %d out of bounds: %d <= slot < %d",
|
|
slot, lowerBound, upperBound)
|
|
}
|
|
return state.LatestRandaoMixesHash32S[slot%config.LatestRandaoMixesLength], nil
|
|
}
|