prysm-pulse/network/forks/fork.go
kasey 2e49fdb3d2
Start chain from bellatrix state (#11746)
* WIP trying to start from bellatrix state

* env var to control log path with unique paths

due to flaky test re-run behavior, logs from a failed test run are
overwritten by subsequent retries. This makes it difficult to retrieve
logs after the first failed run. It also takes some squinting through
output to find the location of the log file in the first place. This
flag enables logs to be placed in an arbitrary path. Note that bazel
sandboxing generally will force this path to be in the /tmp tree.

* WIP - grabbing changes from rm-pre-genesis branch

* combine bellatrix state w/ rm-pre-genesis branch

* WIP

* use encoding/detect for genesis state bytes

* WIP more fixes towards start from bellatrix

* remove debug wrapping

* WIP

* multiple bugfixes

* fix fork ordering bug and bellatrix genesis blocks

* send deposits, spam tx to advance, fix miner alloc

* WIP

* WIP mess

* WIP

* Print process ID information for purposes of attaching a debugger

* bugs: genesis body_root and deposit index mismatch

* fix voting period start, skip altair check

* add changes

* make it better

* rm startup FCU, rm logs

* cleanup import grouping&ordering

* restore FCU log, get rid of tmp var

* rm newline

* restore newline

* restore wrapped error

* rm newline

* removing boot node version override

this doesn't seem to matter?

* add issue number to todo comment

* rm commented code

* rm vmdebug geth flag

* unexport values only used with genesis test pkg

and add comments where missing from exported values.

* adding comments to special cases for testnets

* migrate comments from PR to actual code :)

* rm unused test param

* mark e2e spawns exempt from gosec warning

* Fix DeepSource errors in `proposer_bellatrix.go` (#11739)

* Fix DeepSource errors in

* Omit receiver name

* Address PR comments

* Remove unused variable

* Fix more DeepSource errors

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Remove `Test_IsExecutionEnabledCapella` (#11752)

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Add REST implementation for Validator's `ProposeBeaconBlock` (#11731)

* WIP

* WIP

* WIP

* Add tests

* WIP

* Add more tests

* Address DeepSource errors

* Remove unused param

* Add more tests

* Address PR comments

* Address PR comments

* Fix formatting

* Remove unused parameter

* Fix TestLittleEndianBytesToBigInt

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* fix validator client (#11755)

* fix validator client

(cherry picked from commit deb138959a2ffcb89cd2e3eb8304477526f4a168)

* Use signed changes in middleware block

Co-authored-by: Potuz <potuz@prysmaticlabs.com>

* API `finalized` metadata field - update protos (#11749)

* API `finalized` metadata field - update protos

* change nums

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>

* log breaks unit tests that don't do full arg setup

easiest to just remove it for now

* restore prior behavior of phase0 block for altair

* update unit tests to account for special case

* loosen condition for fork version to match config

we don't know which fork version genesis will start from, so we
shouldn't force it to be a phase0 genesis.

* skip until we can mod configs at runtime

* NewGenesisBlockForState computes state root itself

* rm noisy log

* this log would be noisy in mainnet

* fix format specifier, []byte -> string

* core.Genesis UnmarshalJson has a value receiver :)

* no longer needs to be exported

Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
Co-authored-by: prestonvanloon <preston@prysmaticlabs.com>
Co-authored-by: nisdas <nishdas93@gmail.com>
Co-authored-by: Patrice Vignola <vignola.patrice@gmail.com>
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
Co-authored-by: terencechain <terence@prysmaticlabs.com>
Co-authored-by: Potuz <potuz@prysmaticlabs.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-12-14 07:13:49 +08:00

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 &ethpb.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)
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
}