mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-25 04:47:18 +00:00
185 lines
6.2 KiB
Go
185 lines
6.2 KiB
Go
// Package forks contains useful helpers for Ethereum consensus fork-related functionality.
|
|
package forks
|
|
|
|
import (
|
|
"bytes"
|
|
"math"
|
|
"sort"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/signing"
|
|
"github.com/prysmaticlabs/prysm/v3/config/params"
|
|
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
|
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
|
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
|
"github.com/prysmaticlabs/prysm/v3/time/slots"
|
|
)
|
|
|
|
// IsForkNextEpoch checks if an alloted fork is in the following epoch.
|
|
func IsForkNextEpoch(genesisTime time.Time, genesisValidatorsRoot []byte) (bool, error) {
|
|
if genesisTime.IsZero() {
|
|
return false, errors.New("genesis time is not set")
|
|
}
|
|
if len(genesisValidatorsRoot) == 0 {
|
|
return false, errors.New("genesis validators root is not set")
|
|
}
|
|
currentSlot := slots.Since(genesisTime)
|
|
currentEpoch := slots.ToEpoch(currentSlot)
|
|
fSchedule := params.BeaconConfig().ForkVersionSchedule
|
|
scheduledForks := SortedForkVersions(fSchedule)
|
|
isForkEpoch := false
|
|
for _, forkVersion := range scheduledForks {
|
|
epoch := fSchedule[forkVersion]
|
|
if currentEpoch+1 == epoch {
|
|
isForkEpoch = true
|
|
break
|
|
}
|
|
}
|
|
return isForkEpoch, nil
|
|
}
|
|
|
|
// ForkDigestFromEpoch retrieves the fork digest from the current schedule determined
|
|
// by the provided epoch.
|
|
func ForkDigestFromEpoch(currentEpoch types.Epoch, genesisValidatorsRoot []byte) ([4]byte, error) {
|
|
if len(genesisValidatorsRoot) == 0 {
|
|
return [4]byte{}, errors.New("genesis validators root is not set")
|
|
}
|
|
forkData, err := Fork(currentEpoch)
|
|
if err != nil {
|
|
return [4]byte{}, err
|
|
}
|
|
return signing.ComputeForkDigest(forkData.CurrentVersion, genesisValidatorsRoot)
|
|
}
|
|
|
|
// CreateForkDigest creates a fork digest from a genesis time and genesis
|
|
// validators root, utilizing the current slot to determine
|
|
// the active fork version in the node.
|
|
func CreateForkDigest(
|
|
genesisTime time.Time,
|
|
genesisValidatorsRoot []byte,
|
|
) ([4]byte, error) {
|
|
if genesisTime.IsZero() {
|
|
return [4]byte{}, errors.New("genesis time is not set")
|
|
}
|
|
if len(genesisValidatorsRoot) == 0 {
|
|
return [4]byte{}, errors.New("genesis validators root is not set")
|
|
}
|
|
currentSlot := slots.Since(genesisTime)
|
|
currentEpoch := slots.ToEpoch(currentSlot)
|
|
|
|
forkData, err := Fork(currentEpoch)
|
|
if err != nil {
|
|
return [4]byte{}, err
|
|
}
|
|
|
|
digest, err := signing.ComputeForkDigest(forkData.CurrentVersion, genesisValidatorsRoot)
|
|
if err != nil {
|
|
return [4]byte{}, err
|
|
}
|
|
return digest, nil
|
|
}
|
|
|
|
// Fork given a target epoch,
|
|
// returns the active fork version during this epoch.
|
|
func Fork(
|
|
targetEpoch types.Epoch,
|
|
) (*ethpb.Fork, error) {
|
|
currentForkVersion := bytesutil.ToBytes4(params.BeaconConfig().GenesisForkVersion)
|
|
previousForkVersion := bytesutil.ToBytes4(params.BeaconConfig().GenesisForkVersion)
|
|
fSchedule := params.BeaconConfig().ForkVersionSchedule
|
|
sortedForkVersions := SortedForkVersions(fSchedule)
|
|
forkEpoch := types.Epoch(0)
|
|
for _, forkVersion := range sortedForkVersions {
|
|
epoch, ok := fSchedule[forkVersion]
|
|
if !ok {
|
|
return nil, errors.Errorf("fork version %x doesn't exist in schedule", forkVersion)
|
|
}
|
|
if targetEpoch >= epoch {
|
|
previousForkVersion = currentForkVersion
|
|
currentForkVersion = forkVersion
|
|
forkEpoch = epoch
|
|
}
|
|
}
|
|
return ðpb.Fork{
|
|
PreviousVersion: previousForkVersion[:],
|
|
CurrentVersion: currentForkVersion[:],
|
|
Epoch: forkEpoch,
|
|
}, nil
|
|
}
|
|
|
|
// RetrieveForkDataFromDigest performs the inverse, where it tries to determine the fork version
|
|
// and epoch from a provided digest by looping through our current fork schedule.
|
|
func RetrieveForkDataFromDigest(digest [4]byte, genesisValidatorsRoot []byte) ([4]byte, types.Epoch, error) {
|
|
fSchedule := params.BeaconConfig().ForkVersionSchedule
|
|
for v, e := range fSchedule {
|
|
rDigest, err := signing.ComputeForkDigest(v[:], genesisValidatorsRoot)
|
|
if err != nil {
|
|
return [4]byte{}, 0, err
|
|
}
|
|
if rDigest == digest {
|
|
return v, e, nil
|
|
}
|
|
}
|
|
return [4]byte{}, 0, errors.Errorf("no fork exists for a digest of %#x", digest)
|
|
}
|
|
|
|
// NextForkData retrieves the next fork data according to the
|
|
// provided current epoch.
|
|
func NextForkData(currEpoch types.Epoch) ([4]byte, types.Epoch, error) {
|
|
fSchedule := params.BeaconConfig().ForkVersionSchedule
|
|
sortedForkVersions := SortedForkVersions(fSchedule)
|
|
nextForkEpoch := types.Epoch(math.MaxUint64)
|
|
var nextForkVersion [4]byte
|
|
for _, forkVersion := range sortedForkVersions {
|
|
epoch, ok := fSchedule[forkVersion]
|
|
if !ok {
|
|
return [4]byte{}, 0, errors.Errorf("fork version %x doesn't exist in schedule", forkVersion)
|
|
}
|
|
// If we get an epoch larger than out current epoch
|
|
// we set this as our next fork epoch and exit the
|
|
// loop.
|
|
if epoch > currEpoch {
|
|
nextForkEpoch = epoch
|
|
nextForkVersion = forkVersion
|
|
break
|
|
}
|
|
// In the event the retrieved epoch is less than
|
|
// our current epoch, we mark the previous
|
|
// fork's version as the next fork version.
|
|
if epoch <= currEpoch {
|
|
// The next fork version is updated to
|
|
// always include the most current fork version.
|
|
nextForkVersion = forkVersion
|
|
}
|
|
}
|
|
return nextForkVersion, nextForkEpoch, nil
|
|
}
|
|
|
|
// SortedForkVersions sorts the provided fork schedule in ascending order
|
|
// by epoch.
|
|
func SortedForkVersions(forkSchedule map[[4]byte]types.Epoch) [][4]byte {
|
|
sortedVersions := make([][4]byte, len(forkSchedule))
|
|
i := 0
|
|
for k := range forkSchedule {
|
|
sortedVersions[i] = k
|
|
i++
|
|
}
|
|
sort.Slice(sortedVersions, func(a, b int) bool {
|
|
// va == "version" a, ie the [4]byte version id
|
|
va, vb := sortedVersions[a], sortedVersions[b]
|
|
// ea == "epoch" a, ie the types.Epoch corresponding to va
|
|
ea, eb := forkSchedule[va], forkSchedule[vb]
|
|
// Try to sort by epochs first, which works fine when epochs are all distinct.
|
|
// in the case of testnets starting from a given fork, all epochs leading to the fork will be zero.
|
|
if ea != eb {
|
|
return ea < eb
|
|
}
|
|
// If the epochs are equal, break the tie with a lexicographic comparison of the fork version bytes.
|
|
// eg 2 versions both with a fork epoch of 0, 0x00000000 would come before 0x01000000.
|
|
// sort.Slice takes a 'less' func, ie `return a < b`, and when va < vb, bytes.Compare will return -1
|
|
return bytes.Compare(va[:], vb[:]) < 0
|
|
})
|
|
return sortedVersions
|
|
}
|