mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-08 07:03:58 -05:00
Checkpoint Sync 1/5 - fork/version detection and unmarshaling support (#10380)
* fork/version detection and unmarshaling support * Update config/params/config.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update proto/detect/configfork.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * PR feedback * move ssz initialization into the detect package * clarify comment * VersionForEpoch is much simpler/clearer in reverse * simpler VersionForEpoch; build AllConfigs in init * use fieldparams for Version * Update proto/detect/configfork_test.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * remove custom ForkName type, use runtime/version * pr cleanup * random fix from bad gh ui suggestion; privatize * privatize fieldSpec methods; + unit tests * Update proto/detect/configfork.go Co-authored-by: Potuz <potuz@prysmaticlabs.com> * fix bad github ui suggestion * ensure unique versions for simpler config match * fmt & adding unit test for ByState() * table-driven unit test for ByState * TestUnmarshalState * OrderedSchedule -> network/forks per PR feedback * goimports * lint fixes * move proto/detect -> ssz/encoding/detect * use typeUndefined in String * backport config tests from e2e PR * fix config parity test; make debugging it easier * lint * fix fork schedule initialization * cleanup * fix build * fix big ole derp * anything for you, deep source * goimportsss * InitializeForkSchedule in LoadChainConfigFile * PR feedback Co-authored-by: kasey <kasey@users.noreply.github.com> Co-authored-by: Radosław Kapka <rkapka@wp.pl> Co-authored-by: Potuz <potuz@prysmaticlabs.com>
This commit is contained in:
@@ -47,6 +47,7 @@ go_library(
|
||||
"//config/params:go_default_library",
|
||||
"//container/slice:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//encoding/ssz/detect:go_default_library",
|
||||
"//io/file:go_default_library",
|
||||
"//monitoring/progress:go_default_library",
|
||||
"//monitoring/tracing:go_default_library",
|
||||
|
||||
@@ -7,10 +7,9 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
statev2 "github.com/prysmaticlabs/prysm/beacon-chain/state/v2"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/ssz/detect"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
)
|
||||
|
||||
// SaveOrigin loads an ssz serialized Block & BeaconState from an io.Reader
|
||||
@@ -18,33 +17,36 @@ import (
|
||||
// syncing, using the provided values as their point of origin. This is an alternative
|
||||
// to syncing from genesis, and should only be run on an empty database.
|
||||
func (s *Store) SaveOrigin(ctx context.Context, stateReader, blockReader io.Reader) error {
|
||||
// unmarshal both block and state before trying to save anything
|
||||
// so that we fail early if there is any issue with the ssz data
|
||||
blk := ðpb.SignedBeaconBlockAltair{}
|
||||
sb, err := ioutil.ReadAll(stateReader)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to read origin state bytes")
|
||||
}
|
||||
bb, err := ioutil.ReadAll(blockReader)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error reading block given to SaveOrigin")
|
||||
}
|
||||
if err := blk.UnmarshalSSZ(bb); err != nil {
|
||||
return errors.Wrap(err, "could not unmarshal checkpoint block")
|
||||
}
|
||||
wblk, err := wrapper.WrappedAltairSignedBeaconBlock(blk)
|
||||
|
||||
cf, err := detect.FromState(sb)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not wrap checkpoint block")
|
||||
return errors.Wrap(err, "failed to detect config and fork for origin state")
|
||||
}
|
||||
bs, err := statev2.InitializeFromSSZReader(stateReader)
|
||||
bs, err := cf.UnmarshalBeaconState(sb)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not initialize checkpoint state from reader")
|
||||
return errors.Wrap(err, "could not unmarshal origin state")
|
||||
}
|
||||
wblk, err := cf.UnmarshalBeaconBlock(bb)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to unmarshal origin SignedBeaconBlock")
|
||||
}
|
||||
|
||||
blockRoot, err := wblk.Block().HashTreeRoot()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not compute HashTreeRoot of checkpoint block")
|
||||
}
|
||||
// save block
|
||||
if err := s.SaveBlock(ctx, wblk); err != nil {
|
||||
return errors.Wrap(err, "could not save checkpoint block")
|
||||
}
|
||||
blockRoot, err := blk.Block.HashTreeRoot()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not compute HashTreeRoot of checkpoint block")
|
||||
}
|
||||
|
||||
// save state
|
||||
if err = s.SaveState(ctx, bs, blockRoot); err != nil {
|
||||
@@ -70,7 +72,7 @@ func (s *Store) SaveOrigin(ctx context.Context, stateReader, blockReader io.Read
|
||||
|
||||
// rebuild the checkpoint from the block
|
||||
// use it to mark the block as justified and finalized
|
||||
slotEpoch, err := blk.Block.Slot.SafeDivSlot(params.BeaconConfig().SlotsPerEpoch)
|
||||
slotEpoch, err := wblk.Block().Slot().SafeDivSlot(params.BeaconConfig().SlotsPerEpoch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ func configureTracing(cliCtx *cli.Context) error {
|
||||
func configureChainConfig(cliCtx *cli.Context) {
|
||||
if cliCtx.IsSet(cmd.ChainConfigFileFlag.Name) {
|
||||
chainConfigFileName := cliCtx.String(cmd.ChainConfigFileFlag.Name)
|
||||
params.LoadChainConfigFile(chainConfigFileName)
|
||||
params.LoadChainConfigFile(chainConfigFileName, nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,21 +10,7 @@ go_library(
|
||||
"prometheus.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/state",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//contracts/deposit:__subpackages__",
|
||||
"//proto/migration:__pkg__",
|
||||
"//proto/prysm/v1alpha1:__subpackages__",
|
||||
"//proto/testing:__subpackages__",
|
||||
"//slasher/rpc:__subpackages__",
|
||||
"//testing/benchmark:__pkg__",
|
||||
"//testing/slasher/simulator:__pkg__",
|
||||
"//testing/spectest:__subpackages__",
|
||||
"//testing/util:__pkg__",
|
||||
"//tools/benchmark-files-gen:__pkg__",
|
||||
"//tools/exploredb:__pkg__",
|
||||
"//tools/pcli:__pkg__",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
|
||||
@@ -2,8 +2,6 @@ package v2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
"sort"
|
||||
|
||||
@@ -30,27 +28,6 @@ func InitializeFromProto(st *ethpb.BeaconStateAltair) (state.BeaconStateAltair,
|
||||
return InitializeFromProtoUnsafe(proto.Clone(st).(*ethpb.BeaconStateAltair))
|
||||
}
|
||||
|
||||
// InitializeFromSSZReader can be used when the source for a serialized BeaconState object
|
||||
// is an io.Reader. This allows client code to remain agnostic about whether the data comes
|
||||
// from the network or a file without needing to read the entire state into mem as a large byte slice.
|
||||
func InitializeFromSSZReader(r io.Reader) (state.BeaconStateAltair, error) {
|
||||
b, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return InitializeFromSSZBytes(b)
|
||||
}
|
||||
|
||||
// InitializeFromSSZBytes is a convenience method to obtain a BeaconState by unmarshaling
|
||||
// a slice of bytes containing the ssz-serialized representation of the state.
|
||||
func InitializeFromSSZBytes(marshaled []byte) (*BeaconState, error) {
|
||||
st := ðpb.BeaconStateAltair{}
|
||||
if err := st.UnmarshalSSZ(marshaled); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return InitializeFromProtoUnsafe(st)
|
||||
}
|
||||
|
||||
// InitializeFromProtoUnsafe directly uses the beacon state protobuf fields
|
||||
// and sets them as fields of the BeaconState type.
|
||||
func InitializeFromProtoUnsafe(st *ethpb.BeaconStateAltair) (*BeaconState, error) {
|
||||
|
||||
@@ -29,20 +29,7 @@ go_library(
|
||||
"unsupported_setters.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/state/v1",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//contracts/deposit:__subpackages__",
|
||||
"//proto/migration:__subpackages__",
|
||||
"//proto/prysm/v1alpha1:__subpackages__",
|
||||
"//proto/testing:__subpackages__",
|
||||
"//runtime/interop:__subpackages__",
|
||||
"//slasher/rpc:__subpackages__",
|
||||
"//testing/benchmark:__pkg__",
|
||||
"//testing/spectest:__subpackages__",
|
||||
"//testing/util:__pkg__",
|
||||
"//tools/benchmark-files-gen:__pkg__",
|
||||
"//tools/pcli:__pkg__",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/fieldtrie:go_default_library",
|
||||
|
||||
@@ -29,12 +29,7 @@ go_library(
|
||||
"types.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/state/v2",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//proto/migration:__subpackages__",
|
||||
"//testing/spectest:__subpackages__",
|
||||
"//testing/util:__pkg__",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/fieldtrie:go_default_library",
|
||||
|
||||
@@ -2,8 +2,6 @@ package v2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
"sort"
|
||||
|
||||
@@ -33,33 +31,6 @@ func InitializeFromProto(st *ethpb.BeaconStateAltair) (state.BeaconStateAltair,
|
||||
return InitializeFromProtoUnsafe(proto.Clone(st).(*ethpb.BeaconStateAltair))
|
||||
}
|
||||
|
||||
// InitializeFromSSZReader can be used when the source for a serialized BeaconState object
|
||||
// is an io.Reader. This allows client code to remain agnostic about whether the data comes
|
||||
// from the network or a file without needing to read the entire state into mem as a large byte slice.
|
||||
func InitializeFromSSZReader(r io.Reader) (state.BeaconStateAltair, error) {
|
||||
if features.Get().EnableNativeState {
|
||||
return statenative.InitializeFromSSZReader(r)
|
||||
}
|
||||
b, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return InitializeFromSSZBytes(b)
|
||||
}
|
||||
|
||||
// InitializeFromSSZBytes is a convenience method to obtain a BeaconState by unmarshaling
|
||||
// a slice of bytes containing the ssz-serialized representation of the state.
|
||||
func InitializeFromSSZBytes(marshaled []byte) (state.BeaconStateAltair, error) {
|
||||
if features.Get().EnableNativeState {
|
||||
return statenative.InitializeFromSSZBytes(marshaled)
|
||||
}
|
||||
st := ðpb.BeaconStateAltair{}
|
||||
if err := st.UnmarshalSSZ(marshaled); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return InitializeFromProtoUnsafe(st)
|
||||
}
|
||||
|
||||
// InitializeFromProtoUnsafe directly uses the beacon state protobuf pointer
|
||||
// and sets it as the inner state of the BeaconState type.
|
||||
func InitializeFromProtoUnsafe(st *ethpb.BeaconStateAltair) (state.BeaconStateAltair, error) {
|
||||
|
||||
@@ -31,11 +31,7 @@ go_library(
|
||||
"types.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/state/v3",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//testing/spectest:__subpackages__",
|
||||
"//testing/util:__pkg__",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/fieldtrie:go_default_library",
|
||||
|
||||
@@ -22,5 +22,6 @@ const (
|
||||
MaxBytesPerTxLength = 1073741824 // MaxBytesPerTxLength defines the maximum number of bytes that can be included in a transaction.
|
||||
FeeRecipientLength = 20 // FeeRecipientLength defines the byte length of a fee recipient.
|
||||
LogsBloomLength = 256 // LogsBloomLength defines the byte length of a logs bloom.
|
||||
VersionLength = 4 // VersionLength defines the byte length of a fork version number.
|
||||
EthBurnAddressHex = "0x0000000000000000000000000000000000000000" // EthBurnAddressHex defines the hex encoded address of the eth1.0 burn contract.
|
||||
)
|
||||
|
||||
@@ -22,5 +22,6 @@ const (
|
||||
MaxBytesPerTxLength = 1073741824 // MaxBytesPerTxLength defines the maximum number of bytes that can be included in a transaction.
|
||||
FeeRecipientLength = 20 // FeeRecipientLength defines the byte length of a fee recipient.
|
||||
LogsBloomLength = 256 // LogsBloomLength defines the byte length of a logs bloom.
|
||||
VersionLength = 4 // VersionLength defines the byte length of a fork version number.
|
||||
EthBurnAddressHex = "0x0000000000000000000000000000000000000000" // EthBurnAddressHex defines the hex encoded address of the eth1.0 burn contract.
|
||||
)
|
||||
|
||||
@@ -20,11 +20,13 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/config/params",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//math:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//params:go_default_library",
|
||||
"@com_github_mohae_deepcopy//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@in_gopkg_yaml_v2//:go_default_library",
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
)
|
||||
|
||||
@@ -135,14 +136,14 @@ type BeaconChainConfig struct {
|
||||
SlashingProtectionPruningEpochs types.Epoch // SlashingProtectionPruningEpochs defines a period after which all prior epochs are pruned in the validator database.
|
||||
|
||||
// Fork-related values.
|
||||
GenesisForkVersion []byte `yaml:"GENESIS_FORK_VERSION" spec:"true"` // GenesisForkVersion is used to track fork version between state transitions.
|
||||
AltairForkVersion []byte `yaml:"ALTAIR_FORK_VERSION" spec:"true"` // AltairForkVersion is used to represent the fork version for altair.
|
||||
AltairForkEpoch types.Epoch `yaml:"ALTAIR_FORK_EPOCH" spec:"true"` // AltairForkEpoch is used to represent the assigned fork epoch for altair.
|
||||
BellatrixForkVersion []byte `yaml:"BELLATRIX_FORK_VERSION" spec:"true"` // BellatrixForkVersion is used to represent the fork version for bellatrix.
|
||||
BellatrixForkEpoch types.Epoch `yaml:"BELLATRIX_FORK_EPOCH" spec:"true"` // BellatrixForkEpoch is used to represent the assigned fork epoch for bellatrix.
|
||||
ShardingForkVersion []byte `yaml:"SHARDING_FORK_VERSION" spec:"true"` // ShardingForkVersion is used to represent the fork version for sharding.
|
||||
ShardingForkEpoch types.Epoch `yaml:"SHARDING_FORK_EPOCH" spec:"true"` // ShardingForkEpoch is used to represent the assigned fork epoch for sharding.
|
||||
ForkVersionSchedule map[[4]byte]types.Epoch // Schedule of fork epochs by version.
|
||||
GenesisForkVersion []byte `yaml:"GENESIS_FORK_VERSION" spec:"true"` // GenesisForkVersion is used to track fork version between state transitions.
|
||||
AltairForkVersion []byte `yaml:"ALTAIR_FORK_VERSION" spec:"true"` // AltairForkVersion is used to represent the fork version for altair.
|
||||
AltairForkEpoch types.Epoch `yaml:"ALTAIR_FORK_EPOCH" spec:"true"` // AltairForkEpoch is used to represent the assigned fork epoch for altair.
|
||||
BellatrixForkVersion []byte `yaml:"BELLATRIX_FORK_VERSION" spec:"true"` // BellatrixForkVersion is used to represent the fork version for bellatrix.
|
||||
BellatrixForkEpoch types.Epoch `yaml:"BELLATRIX_FORK_EPOCH" spec:"true"` // BellatrixForkEpoch is used to represent the assigned fork epoch for bellatrix.
|
||||
ShardingForkVersion []byte `yaml:"SHARDING_FORK_VERSION" spec:"true"` // ShardingForkVersion is used to represent the fork version for sharding.
|
||||
ShardingForkEpoch types.Epoch `yaml:"SHARDING_FORK_EPOCH" spec:"true"` // ShardingForkEpoch is used to represent the assigned fork epoch for sharding.
|
||||
ForkVersionSchedule map[[fieldparams.VersionLength]byte]types.Epoch // Schedule of fork epochs by version.
|
||||
|
||||
// Weak subjectivity values.
|
||||
SafetyDecay uint64 // SafetyDecay is defined as the loss in the 1/3 consensus safety margin of the casper FFG mechanism.
|
||||
@@ -193,11 +194,16 @@ type BeaconChainConfig struct {
|
||||
// InitializeForkSchedule initializes the schedules forks baked into the config.
|
||||
func (b *BeaconChainConfig) InitializeForkSchedule() {
|
||||
// Reset Fork Version Schedule.
|
||||
b.ForkVersionSchedule = map[[4]byte]types.Epoch{}
|
||||
// Set Genesis fork data.
|
||||
b.ForkVersionSchedule[bytesutil.ToBytes4(b.GenesisForkVersion)] = b.GenesisEpoch
|
||||
// Set Altair fork data.
|
||||
b.ForkVersionSchedule[bytesutil.ToBytes4(b.AltairForkVersion)] = b.AltairForkEpoch
|
||||
// Set Bellatrix fork data.
|
||||
b.ForkVersionSchedule[bytesutil.ToBytes4(b.BellatrixForkVersion)] = b.BellatrixForkEpoch
|
||||
b.ForkVersionSchedule = configForkSchedule(b)
|
||||
}
|
||||
|
||||
func configForkSchedule(b *BeaconChainConfig) map[[fieldparams.VersionLength]byte]types.Epoch {
|
||||
fvs := map[[fieldparams.VersionLength]byte]types.Epoch{}
|
||||
// Set Genesis fork data.
|
||||
fvs[bytesutil.ToBytes4(b.GenesisForkVersion)] = b.GenesisEpoch
|
||||
// Set Altair fork data.
|
||||
fvs[bytesutil.ToBytes4(b.AltairForkVersion)] = b.AltairForkEpoch
|
||||
// Set Bellatrix fork data.
|
||||
fvs[bytesutil.ToBytes4(b.BellatrixForkVersion)] = b.BellatrixForkEpoch
|
||||
return fvs
|
||||
}
|
||||
|
||||
@@ -12,19 +12,37 @@ import (
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func isMinimal(lines []string) bool {
|
||||
for _, l := range lines {
|
||||
if strings.HasPrefix(l, "PRESET_BASE: 'minimal'") ||
|
||||
strings.HasPrefix(l, `PRESET_BASE: "minimal"`) ||
|
||||
strings.HasPrefix(l, "PRESET_BASE: minimal") ||
|
||||
strings.HasPrefix(l, "# Minimal preset") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// LoadChainConfigFile load, convert hex values into valid param yaml format,
|
||||
// unmarshal , and apply beacon chain config file.
|
||||
func LoadChainConfigFile(chainConfigFileName string) {
|
||||
func LoadChainConfigFile(chainConfigFileName string, conf *BeaconChainConfig) {
|
||||
yamlFile, err := ioutil.ReadFile(chainConfigFileName) // #nosec G304
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("Failed to read chain config file.")
|
||||
}
|
||||
// Default to using mainnet.
|
||||
conf := MainnetConfig().Copy()
|
||||
// To track if config name is defined inside config file.
|
||||
hasConfigName := false
|
||||
// Convert 0x hex inputs to fixed bytes arrays
|
||||
lines := strings.Split(string(yamlFile), "\n")
|
||||
if conf == nil {
|
||||
if isMinimal(lines) {
|
||||
conf = MinimalSpecConfig().Copy()
|
||||
} else {
|
||||
// Default to using mainnet.
|
||||
conf = MainnetConfig().Copy()
|
||||
}
|
||||
}
|
||||
for i, line := range lines {
|
||||
// No need to convert the deposit contract address to byte array (as config expects a string).
|
||||
if strings.HasPrefix(line, "DEPOSIT_CONTRACT_ADDRESS") {
|
||||
@@ -33,12 +51,6 @@ func LoadChainConfigFile(chainConfigFileName string) {
|
||||
if strings.HasPrefix(line, "CONFIG_NAME") {
|
||||
hasConfigName = true
|
||||
}
|
||||
if strings.HasPrefix(line, "PRESET_BASE: 'minimal'") ||
|
||||
strings.HasPrefix(line, `PRESET_BASE: "minimal"`) ||
|
||||
strings.HasPrefix(line, "PRESET_BASE: minimal") ||
|
||||
strings.HasPrefix(line, "# Minimal preset") {
|
||||
conf = MinimalSpecConfig().Copy()
|
||||
}
|
||||
if !strings.HasPrefix(line, "#") && strings.Contains(line, "0x") {
|
||||
parts := ReplaceHexStringWithYAMLFormat(line)
|
||||
lines[i] = strings.Join(parts, "\n")
|
||||
@@ -58,6 +70,7 @@ func LoadChainConfigFile(chainConfigFileName string) {
|
||||
// recompute SqrRootSlotsPerEpoch constant to handle non-standard values of SlotsPerEpoch
|
||||
conf.SqrRootSlotsPerEpoch = types.Slot(math.IntegerSquareRoot(uint64(conf.SlotsPerEpoch)))
|
||||
log.Debugf("Config file values: %+v", conf)
|
||||
conf.InitializeForkSchedule()
|
||||
OverrideBeaconConfig(conf)
|
||||
}
|
||||
|
||||
@@ -149,33 +162,38 @@ func ReplaceHexStringWithYAMLFormat(line string) []string {
|
||||
// ConfigToYaml takes a provided config and outputs its contents
|
||||
// in yaml. This allows prysm's custom configs to be read by other clients.
|
||||
func ConfigToYaml(cfg *BeaconChainConfig) []byte {
|
||||
lines := []string{}
|
||||
lines = append(lines, fmt.Sprintf("PRESET_BASE: '%s'", cfg.PresetBase))
|
||||
lines = append(lines, fmt.Sprintf("CONFIG_NAME: '%s'", cfg.ConfigName))
|
||||
lines = append(lines, fmt.Sprintf("MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: %d", cfg.MinGenesisActiveValidatorCount))
|
||||
lines = append(lines, fmt.Sprintf("GENESIS_DELAY: %d", cfg.GenesisDelay))
|
||||
lines = append(lines, fmt.Sprintf("MIN_GENESIS_TIME: %d", cfg.MinGenesisTime))
|
||||
lines = append(lines, fmt.Sprintf("GENESIS_FORK_VERSION: %#x", cfg.GenesisForkVersion))
|
||||
lines = append(lines, fmt.Sprintf("CHURN_LIMIT_QUOTIENT: %d", cfg.ChurnLimitQuotient))
|
||||
lines = append(lines, fmt.Sprintf("SECONDS_PER_SLOT: %d", cfg.SecondsPerSlot))
|
||||
lines = append(lines, fmt.Sprintf("SLOTS_PER_EPOCH: %d", cfg.SlotsPerEpoch))
|
||||
lines = append(lines, fmt.Sprintf("SECONDS_PER_ETH1_BLOCK: %d", cfg.SecondsPerETH1Block))
|
||||
lines = append(lines, fmt.Sprintf("ETH1_FOLLOW_DISTANCE: %d", cfg.Eth1FollowDistance))
|
||||
lines = append(lines, fmt.Sprintf("EPOCHS_PER_ETH1_VOTING_PERIOD: %d", cfg.EpochsPerEth1VotingPeriod))
|
||||
lines = append(lines, fmt.Sprintf("SHARD_COMMITTEE_PERIOD: %d", cfg.ShardCommitteePeriod))
|
||||
lines = append(lines, fmt.Sprintf("MIN_VALIDATOR_WITHDRAWABILITY_DELAY: %d", cfg.MinValidatorWithdrawabilityDelay))
|
||||
lines = append(lines, fmt.Sprintf("MAX_SEED_LOOKAHEAD: %d", cfg.MaxSeedLookahead))
|
||||
lines = append(lines, fmt.Sprintf("EJECTION_BALANCE: %d", cfg.EjectionBalance))
|
||||
lines = append(lines, fmt.Sprintf("MIN_PER_EPOCH_CHURN_LIMIT: %d", cfg.MinPerEpochChurnLimit))
|
||||
lines = append(lines, fmt.Sprintf("DEPOSIT_CHAIN_ID: %d", cfg.DepositChainID))
|
||||
lines = append(lines, fmt.Sprintf("DEPOSIT_NETWORK_ID: %d", cfg.DepositNetworkID))
|
||||
lines = append(lines, fmt.Sprintf("ALTAIR_FORK_EPOCH: %d", cfg.AltairForkEpoch))
|
||||
lines = append(lines, fmt.Sprintf("ALTAIR_FORK_VERSION: %#x", cfg.AltairForkVersion))
|
||||
lines = append(lines, fmt.Sprintf("INACTIVITY_SCORE_BIAS: %d", cfg.InactivityScoreBias))
|
||||
lines = append(lines, fmt.Sprintf("INACTIVITY_SCORE_RECOVERY_RATE: %d", cfg.InactivityScoreRecoveryRate))
|
||||
lines = append(lines, fmt.Sprintf("TERMINAL_TOTAL_DIFFICULTY: %s", cfg.TerminalTotalDifficulty))
|
||||
lines = append(lines, fmt.Sprintf("TERMINAL_BLOCK_HASH: %#x", cfg.TerminalBlockHash))
|
||||
lines = append(lines, fmt.Sprintf("TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: %d", cfg.TerminalBlockHashActivationEpoch))
|
||||
lines := []string{
|
||||
fmt.Sprintf("PRESET_BASE: '%s'", cfg.PresetBase),
|
||||
fmt.Sprintf("CONFIG_NAME: '%s'", cfg.ConfigName),
|
||||
fmt.Sprintf("MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: %d", cfg.MinGenesisActiveValidatorCount),
|
||||
fmt.Sprintf("GENESIS_DELAY: %d", cfg.GenesisDelay),
|
||||
fmt.Sprintf("MIN_GENESIS_TIME: %d", cfg.MinGenesisTime),
|
||||
fmt.Sprintf("GENESIS_FORK_VERSION: %#x", cfg.GenesisForkVersion),
|
||||
fmt.Sprintf("CHURN_LIMIT_QUOTIENT: %d", cfg.ChurnLimitQuotient),
|
||||
fmt.Sprintf("SECONDS_PER_SLOT: %d", cfg.SecondsPerSlot),
|
||||
fmt.Sprintf("SLOTS_PER_EPOCH: %d", cfg.SlotsPerEpoch),
|
||||
fmt.Sprintf("SECONDS_PER_ETH1_BLOCK: %d", cfg.SecondsPerETH1Block),
|
||||
fmt.Sprintf("ETH1_FOLLOW_DISTANCE: %d", cfg.Eth1FollowDistance),
|
||||
fmt.Sprintf("EPOCHS_PER_ETH1_VOTING_PERIOD: %d", cfg.EpochsPerEth1VotingPeriod),
|
||||
fmt.Sprintf("SHARD_COMMITTEE_PERIOD: %d", cfg.ShardCommitteePeriod),
|
||||
fmt.Sprintf("MIN_VALIDATOR_WITHDRAWABILITY_DELAY: %d", cfg.MinValidatorWithdrawabilityDelay),
|
||||
fmt.Sprintf("MAX_SEED_LOOKAHEAD: %d", cfg.MaxSeedLookahead),
|
||||
fmt.Sprintf("EJECTION_BALANCE: %d", cfg.EjectionBalance),
|
||||
fmt.Sprintf("MIN_PER_EPOCH_CHURN_LIMIT: %d", cfg.MinPerEpochChurnLimit),
|
||||
fmt.Sprintf("DEPOSIT_CHAIN_ID: %d", cfg.DepositChainID),
|
||||
fmt.Sprintf("DEPOSIT_NETWORK_ID: %d", cfg.DepositNetworkID),
|
||||
fmt.Sprintf("ALTAIR_FORK_EPOCH: %d", cfg.AltairForkEpoch),
|
||||
fmt.Sprintf("ALTAIR_FORK_VERSION: %#x", cfg.AltairForkVersion),
|
||||
fmt.Sprintf("BELLATRIX_FORK_EPOCH: %d", cfg.BellatrixForkEpoch),
|
||||
fmt.Sprintf("BELLATRIX_FORK_VERSION: %#x", cfg.BellatrixForkVersion),
|
||||
fmt.Sprintf("SHARDING_FORK_EPOCH: %d", cfg.ShardingForkEpoch),
|
||||
fmt.Sprintf("SHARDING_FORK_VERSION: %#x", cfg.ShardingForkVersion),
|
||||
fmt.Sprintf("INACTIVITY_SCORE_BIAS: %d", cfg.InactivityScoreBias),
|
||||
fmt.Sprintf("INACTIVITY_SCORE_RECOVERY_RATE: %d", cfg.InactivityScoreRecoveryRate),
|
||||
fmt.Sprintf("TERMINAL_TOTAL_DIFFICULTY: %s", cfg.TerminalTotalDifficulty),
|
||||
fmt.Sprintf("TERMINAL_BLOCK_HASH: %#x", cfg.TerminalBlockHash),
|
||||
fmt.Sprintf("TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: %d", cfg.TerminalBlockHashActivationEpoch),
|
||||
}
|
||||
|
||||
yamlFile := []byte(strings.Join(lines, "\n"))
|
||||
return yamlFile
|
||||
|
||||
@@ -111,10 +111,10 @@ func TestLoadConfigFileMainnet(t *testing.T) {
|
||||
t.Run("mainnet", func(t *testing.T) {
|
||||
mainnetPresetsFiles := presetsFilePath(t, "mainnet")
|
||||
for _, fp := range mainnetPresetsFiles {
|
||||
params.LoadChainConfigFile(fp)
|
||||
params.LoadChainConfigFile(fp, nil)
|
||||
}
|
||||
mainnetConfigFile := configFilePath(t, "mainnet")
|
||||
params.LoadChainConfigFile(mainnetConfigFile)
|
||||
params.LoadChainConfigFile(mainnetConfigFile, nil)
|
||||
fields := fieldsFromYamls(t, append(mainnetPresetsFiles, mainnetConfigFile))
|
||||
assertVals("mainnet", fields, params.MainnetConfig(), params.BeaconConfig())
|
||||
})
|
||||
@@ -122,10 +122,10 @@ func TestLoadConfigFileMainnet(t *testing.T) {
|
||||
t.Run("minimal", func(t *testing.T) {
|
||||
minimalPresetsFiles := presetsFilePath(t, "minimal")
|
||||
for _, fp := range minimalPresetsFiles {
|
||||
params.LoadChainConfigFile(fp)
|
||||
params.LoadChainConfigFile(fp, nil)
|
||||
}
|
||||
minimalConfigFile := configFilePath(t, "minimal")
|
||||
params.LoadChainConfigFile(minimalConfigFile)
|
||||
params.LoadChainConfigFile(minimalConfigFile, nil)
|
||||
fields := fieldsFromYamls(t, append(minimalPresetsFiles, minimalConfigFile))
|
||||
assertVals("minimal", fields, params.MinimalSpecConfig(), params.BeaconConfig())
|
||||
})
|
||||
@@ -138,7 +138,7 @@ func TestLoadConfigFile_OverwriteCorrectly(t *testing.T) {
|
||||
params.OverrideBeaconConfig(params.MinimalSpecConfig())
|
||||
|
||||
// load empty config file, so that it defaults to mainnet values
|
||||
params.LoadChainConfigFile(file.Name())
|
||||
params.LoadChainConfigFile(file.Name(), nil)
|
||||
if params.BeaconConfig().MinGenesisTime != params.MainnetConfig().MinGenesisTime {
|
||||
t.Errorf("Expected MinGenesisTime to be set to mainnet value: %d found: %d",
|
||||
params.MainnetConfig().MinGenesisTime,
|
||||
@@ -230,7 +230,7 @@ func TestConfigParityYaml(t *testing.T) {
|
||||
yamlObj := params.ConfigToYaml(testCfg)
|
||||
assert.NoError(t, file.WriteFile(yamlDir, yamlObj))
|
||||
|
||||
params.LoadChainConfigFile(yamlDir)
|
||||
params.LoadChainConfigFile(yamlDir, params.E2ETestConfig().Copy())
|
||||
assert.DeepEqual(t, params.BeaconConfig(), testCfg)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,14 @@ import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
)
|
||||
|
||||
// MainnetConfig returns the configuration to be used in the main network.
|
||||
func MainnetConfig() *BeaconChainConfig {
|
||||
if mainnetBeaconConfig.ForkVersionSchedule == nil {
|
||||
mainnetBeaconConfig.InitializeForkSchedule()
|
||||
}
|
||||
return mainnetBeaconConfig
|
||||
}
|
||||
|
||||
@@ -196,19 +198,14 @@ var mainnetBeaconConfig = &BeaconChainConfig{
|
||||
SafetyDecay: 10,
|
||||
|
||||
// Fork related values.
|
||||
GenesisEpoch: genesisForkEpoch,
|
||||
GenesisForkVersion: []byte{0, 0, 0, 0},
|
||||
AltairForkVersion: []byte{1, 0, 0, 0},
|
||||
AltairForkEpoch: mainnetAltairForkEpoch,
|
||||
BellatrixForkVersion: []byte{2, 0, 0, 0},
|
||||
BellatrixForkEpoch: math.MaxUint64,
|
||||
BellatrixForkEpoch: mainnetBellatrixForkEpoch,
|
||||
ShardingForkVersion: []byte{3, 0, 0, 0},
|
||||
ShardingForkEpoch: math.MaxUint64,
|
||||
ForkVersionSchedule: map[[4]byte]types.Epoch{
|
||||
{0, 0, 0, 0}: genesisForkEpoch,
|
||||
{1, 0, 0, 0}: mainnetAltairForkEpoch,
|
||||
{2, 0, 0, 0}: mainnetBellatrixForkEpoch,
|
||||
// Any further forks must be specified here by their epoch number.
|
||||
},
|
||||
|
||||
// New values introduced in Altair hard fork 1.
|
||||
// Participation flag indices.
|
||||
|
||||
@@ -3,7 +3,6 @@ package params
|
||||
import (
|
||||
"math"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
)
|
||||
|
||||
@@ -92,12 +91,7 @@ func MinimalSpecConfig() *BeaconChainConfig {
|
||||
minimalConfig.BellatrixForkEpoch = math.MaxUint64
|
||||
minimalConfig.ShardingForkVersion = []byte{3, 0, 0, 1}
|
||||
minimalConfig.ShardingForkEpoch = math.MaxUint64
|
||||
// Manually set fork version schedule here.
|
||||
minimalConfig.ForkVersionSchedule = map[[4]byte]types.Epoch{
|
||||
{0, 0, 0, 1}: 0,
|
||||
{1, 0, 0, 1}: math.MaxUint64,
|
||||
{2, 0, 0, 1}: math.MaxUint64,
|
||||
}
|
||||
|
||||
minimalConfig.SyncCommitteeSize = 32
|
||||
minimalConfig.InactivityScoreBias = 4
|
||||
minimalConfig.EpochsPerSyncCommitteePeriod = 8
|
||||
@@ -110,5 +104,6 @@ func MinimalSpecConfig() *BeaconChainConfig {
|
||||
minimalConfig.ConfigName = ConfigNames[Minimal]
|
||||
minimalConfig.PresetBase = "minimal"
|
||||
|
||||
minimalConfig.InitializeForkSchedule()
|
||||
return minimalConfig
|
||||
}
|
||||
|
||||
@@ -28,6 +28,140 @@ func TestE2EConfigParity(t *testing.T) {
|
||||
yamlObj := params.E2EMainnetConfigYaml()
|
||||
assert.NoError(t, file.WriteFile(yamlDir, yamlObj))
|
||||
|
||||
params.LoadChainConfigFile(yamlDir)
|
||||
assert.DeepEqual(t, params.BeaconConfig(), testCfg)
|
||||
params.LoadChainConfigFile(yamlDir, params.MainnetConfig().Copy())
|
||||
|
||||
// compareConfigs makes it easier to figure out exactly what changed
|
||||
compareConfigs(t, params.BeaconConfig(), testCfg)
|
||||
// failsafe in case compareConfigs is not updated when new fields are added
|
||||
require.DeepEqual(t, params.BeaconConfig(), testCfg)
|
||||
}
|
||||
|
||||
func compareConfigs(t *testing.T, expected, actual *params.BeaconChainConfig) {
|
||||
require.DeepEqual(t, expected.GenesisEpoch, actual.GenesisEpoch)
|
||||
require.DeepEqual(t, expected.FarFutureEpoch, actual.FarFutureEpoch)
|
||||
require.DeepEqual(t, expected.FarFutureSlot, actual.FarFutureSlot)
|
||||
require.DeepEqual(t, expected.BaseRewardsPerEpoch, actual.BaseRewardsPerEpoch)
|
||||
require.DeepEqual(t, expected.DepositContractTreeDepth, actual.DepositContractTreeDepth)
|
||||
require.DeepEqual(t, expected.JustificationBitsLength, actual.JustificationBitsLength)
|
||||
require.DeepEqual(t, expected.PresetBase, actual.PresetBase)
|
||||
require.DeepEqual(t, expected.ConfigName, actual.ConfigName)
|
||||
require.DeepEqual(t, expected.TargetCommitteeSize, actual.TargetCommitteeSize)
|
||||
require.DeepEqual(t, expected.MaxValidatorsPerCommittee, actual.MaxValidatorsPerCommittee)
|
||||
require.DeepEqual(t, expected.MaxCommitteesPerSlot, actual.MaxCommitteesPerSlot)
|
||||
require.DeepEqual(t, expected.MinPerEpochChurnLimit, actual.MinPerEpochChurnLimit)
|
||||
require.DeepEqual(t, expected.ChurnLimitQuotient, actual.ChurnLimitQuotient)
|
||||
require.DeepEqual(t, expected.ShuffleRoundCount, actual.ShuffleRoundCount)
|
||||
require.DeepEqual(t, expected.MinGenesisActiveValidatorCount, actual.MinGenesisActiveValidatorCount)
|
||||
require.DeepEqual(t, expected.MinGenesisTime, actual.MinGenesisTime)
|
||||
require.DeepEqual(t, expected.TargetAggregatorsPerCommittee, actual.TargetAggregatorsPerCommittee)
|
||||
require.DeepEqual(t, expected.HysteresisQuotient, actual.HysteresisQuotient)
|
||||
require.DeepEqual(t, expected.HysteresisDownwardMultiplier, actual.HysteresisDownwardMultiplier)
|
||||
require.DeepEqual(t, expected.HysteresisUpwardMultiplier, actual.HysteresisUpwardMultiplier)
|
||||
require.DeepEqual(t, expected.MinDepositAmount, actual.MinDepositAmount)
|
||||
require.DeepEqual(t, expected.MaxEffectiveBalance, actual.MaxEffectiveBalance)
|
||||
require.DeepEqual(t, expected.EjectionBalance, actual.EjectionBalance)
|
||||
require.DeepEqual(t, expected.EffectiveBalanceIncrement, actual.EffectiveBalanceIncrement)
|
||||
require.DeepEqual(t, expected.BLSWithdrawalPrefixByte, actual.BLSWithdrawalPrefixByte)
|
||||
require.DeepEqual(t, expected.ZeroHash, actual.ZeroHash)
|
||||
require.DeepEqual(t, expected.GenesisDelay, actual.GenesisDelay)
|
||||
require.DeepEqual(t, expected.MinAttestationInclusionDelay, actual.MinAttestationInclusionDelay)
|
||||
require.DeepEqual(t, expected.SecondsPerSlot, actual.SecondsPerSlot)
|
||||
require.DeepEqual(t, expected.SlotsPerEpoch, actual.SlotsPerEpoch)
|
||||
require.DeepEqual(t, expected.SqrRootSlotsPerEpoch, actual.SqrRootSlotsPerEpoch)
|
||||
require.DeepEqual(t, expected.MinSeedLookahead, actual.MinSeedLookahead)
|
||||
require.DeepEqual(t, expected.MaxSeedLookahead, actual.MaxSeedLookahead)
|
||||
require.DeepEqual(t, expected.EpochsPerEth1VotingPeriod, actual.EpochsPerEth1VotingPeriod)
|
||||
require.DeepEqual(t, expected.SlotsPerHistoricalRoot, actual.SlotsPerHistoricalRoot)
|
||||
require.DeepEqual(t, expected.MinValidatorWithdrawabilityDelay, actual.MinValidatorWithdrawabilityDelay)
|
||||
require.DeepEqual(t, expected.ShardCommitteePeriod, actual.ShardCommitteePeriod)
|
||||
require.DeepEqual(t, expected.MinEpochsToInactivityPenalty, actual.MinEpochsToInactivityPenalty)
|
||||
require.DeepEqual(t, expected.Eth1FollowDistance, actual.Eth1FollowDistance)
|
||||
require.DeepEqual(t, expected.SafeSlotsToUpdateJustified, actual.SafeSlotsToUpdateJustified)
|
||||
require.DeepEqual(t, expected.SafeSlotsToImportOptimistically, actual.SafeSlotsToImportOptimistically)
|
||||
require.DeepEqual(t, expected.SecondsPerETH1Block, actual.SecondsPerETH1Block)
|
||||
require.DeepEqual(t, expected.ProposerScoreBoost, actual.ProposerScoreBoost)
|
||||
require.DeepEqual(t, expected.IntervalsPerSlot, actual.IntervalsPerSlot)
|
||||
require.DeepEqual(t, expected.DepositChainID, actual.DepositChainID)
|
||||
require.DeepEqual(t, expected.DepositNetworkID, actual.DepositNetworkID)
|
||||
require.DeepEqual(t, expected.DepositContractAddress, actual.DepositContractAddress)
|
||||
require.DeepEqual(t, expected.RandomSubnetsPerValidator, actual.RandomSubnetsPerValidator)
|
||||
require.DeepEqual(t, expected.EpochsPerRandomSubnetSubscription, actual.EpochsPerRandomSubnetSubscription)
|
||||
require.DeepEqual(t, expected.EpochsPerHistoricalVector, actual.EpochsPerHistoricalVector)
|
||||
require.DeepEqual(t, expected.EpochsPerSlashingsVector, actual.EpochsPerSlashingsVector)
|
||||
require.DeepEqual(t, expected.HistoricalRootsLimit, actual.HistoricalRootsLimit)
|
||||
require.DeepEqual(t, expected.ValidatorRegistryLimit, actual.ValidatorRegistryLimit)
|
||||
require.DeepEqual(t, expected.BaseRewardFactor, actual.BaseRewardFactor)
|
||||
require.DeepEqual(t, expected.WhistleBlowerRewardQuotient, actual.WhistleBlowerRewardQuotient)
|
||||
require.DeepEqual(t, expected.ProposerRewardQuotient, actual.ProposerRewardQuotient)
|
||||
require.DeepEqual(t, expected.InactivityPenaltyQuotient, actual.InactivityPenaltyQuotient)
|
||||
require.DeepEqual(t, expected.MinSlashingPenaltyQuotient, actual.MinSlashingPenaltyQuotient)
|
||||
require.DeepEqual(t, expected.ProportionalSlashingMultiplier, actual.ProportionalSlashingMultiplier)
|
||||
require.DeepEqual(t, expected.MaxProposerSlashings, actual.MaxProposerSlashings)
|
||||
require.DeepEqual(t, expected.MaxAttesterSlashings, actual.MaxAttesterSlashings)
|
||||
require.DeepEqual(t, expected.MaxAttestations, actual.MaxAttestations)
|
||||
require.DeepEqual(t, expected.MaxDeposits, actual.MaxDeposits)
|
||||
require.DeepEqual(t, expected.MaxVoluntaryExits, actual.MaxVoluntaryExits)
|
||||
require.DeepEqual(t, expected.DomainBeaconProposer, actual.DomainBeaconProposer)
|
||||
require.DeepEqual(t, expected.DomainRandao, actual.DomainRandao)
|
||||
require.DeepEqual(t, expected.DomainBeaconAttester, actual.DomainBeaconAttester)
|
||||
require.DeepEqual(t, expected.DomainDeposit, actual.DomainDeposit)
|
||||
require.DeepEqual(t, expected.DomainVoluntaryExit, actual.DomainVoluntaryExit)
|
||||
require.DeepEqual(t, expected.DomainSelectionProof, actual.DomainSelectionProof)
|
||||
require.DeepEqual(t, expected.DomainAggregateAndProof, actual.DomainAggregateAndProof)
|
||||
require.DeepEqual(t, expected.DomainSyncCommittee, actual.DomainSyncCommittee)
|
||||
require.DeepEqual(t, expected.DomainSyncCommitteeSelectionProof, actual.DomainSyncCommitteeSelectionProof)
|
||||
require.DeepEqual(t, expected.DomainContributionAndProof, actual.DomainContributionAndProof)
|
||||
require.DeepEqual(t, expected.GweiPerEth, actual.GweiPerEth)
|
||||
require.DeepEqual(t, expected.BLSSecretKeyLength, actual.BLSSecretKeyLength)
|
||||
require.DeepEqual(t, expected.BLSPubkeyLength, actual.BLSPubkeyLength)
|
||||
require.DeepEqual(t, expected.DefaultBufferSize, actual.DefaultBufferSize)
|
||||
require.DeepEqual(t, expected.ValidatorPrivkeyFileName, actual.ValidatorPrivkeyFileName)
|
||||
require.DeepEqual(t, expected.WithdrawalPrivkeyFileName, actual.WithdrawalPrivkeyFileName)
|
||||
require.DeepEqual(t, expected.RPCSyncCheck, actual.RPCSyncCheck)
|
||||
require.DeepEqual(t, expected.EmptySignature, actual.EmptySignature)
|
||||
require.DeepEqual(t, expected.DefaultPageSize, actual.DefaultPageSize)
|
||||
require.DeepEqual(t, expected.MaxPeersToSync, actual.MaxPeersToSync)
|
||||
require.DeepEqual(t, expected.SlotsPerArchivedPoint, actual.SlotsPerArchivedPoint)
|
||||
require.DeepEqual(t, expected.GenesisCountdownInterval, actual.GenesisCountdownInterval)
|
||||
require.DeepEqual(t, expected.BeaconStateFieldCount, actual.BeaconStateFieldCount)
|
||||
require.DeepEqual(t, expected.BeaconStateAltairFieldCount, actual.BeaconStateAltairFieldCount)
|
||||
require.DeepEqual(t, expected.BeaconStateBellatrixFieldCount, actual.BeaconStateBellatrixFieldCount)
|
||||
require.DeepEqual(t, expected.WeakSubjectivityPeriod, actual.WeakSubjectivityPeriod)
|
||||
require.DeepEqual(t, expected.PruneSlasherStoragePeriod, actual.PruneSlasherStoragePeriod)
|
||||
require.DeepEqual(t, expected.SlashingProtectionPruningEpochs, actual.SlashingProtectionPruningEpochs)
|
||||
require.DeepEqual(t, expected.GenesisForkVersion, actual.GenesisForkVersion)
|
||||
require.DeepEqual(t, expected.AltairForkVersion, actual.AltairForkVersion)
|
||||
require.DeepEqual(t, expected.AltairForkEpoch, actual.AltairForkEpoch)
|
||||
require.DeepEqual(t, expected.BellatrixForkVersion, actual.BellatrixForkVersion)
|
||||
require.DeepEqual(t, expected.BellatrixForkEpoch, actual.BellatrixForkEpoch)
|
||||
require.DeepEqual(t, expected.ShardingForkVersion, actual.ShardingForkVersion)
|
||||
require.DeepEqual(t, expected.ShardingForkEpoch, actual.ShardingForkEpoch)
|
||||
require.DeepEqual(t, expected.ForkVersionSchedule, actual.ForkVersionSchedule)
|
||||
require.DeepEqual(t, expected.SafetyDecay, actual.SafetyDecay)
|
||||
require.DeepEqual(t, expected.TimelySourceFlagIndex, actual.TimelySourceFlagIndex)
|
||||
require.DeepEqual(t, expected.TimelyTargetFlagIndex, actual.TimelyTargetFlagIndex)
|
||||
require.DeepEqual(t, expected.TimelyHeadFlagIndex, actual.TimelyHeadFlagIndex)
|
||||
require.DeepEqual(t, expected.TimelySourceWeight, actual.TimelySourceWeight)
|
||||
require.DeepEqual(t, expected.TimelyTargetWeight, actual.TimelyTargetWeight)
|
||||
require.DeepEqual(t, expected.TimelyHeadWeight, actual.TimelyHeadWeight)
|
||||
require.DeepEqual(t, expected.SyncRewardWeight, actual.SyncRewardWeight)
|
||||
require.DeepEqual(t, expected.WeightDenominator, actual.WeightDenominator)
|
||||
require.DeepEqual(t, expected.ProposerWeight, actual.ProposerWeight)
|
||||
require.DeepEqual(t, expected.TargetAggregatorsPerSyncSubcommittee, actual.TargetAggregatorsPerSyncSubcommittee)
|
||||
require.DeepEqual(t, expected.SyncCommitteeSubnetCount, actual.SyncCommitteeSubnetCount)
|
||||
require.DeepEqual(t, expected.SyncCommitteeSize, actual.SyncCommitteeSize)
|
||||
require.DeepEqual(t, expected.InactivityScoreBias, actual.InactivityScoreBias)
|
||||
require.DeepEqual(t, expected.InactivityScoreRecoveryRate, actual.InactivityScoreRecoveryRate)
|
||||
require.DeepEqual(t, expected.EpochsPerSyncCommitteePeriod, actual.EpochsPerSyncCommitteePeriod)
|
||||
require.DeepEqual(t, expected.InactivityPenaltyQuotientAltair, actual.InactivityPenaltyQuotientAltair)
|
||||
require.DeepEqual(t, expected.MinSlashingPenaltyQuotientAltair, actual.MinSlashingPenaltyQuotientAltair)
|
||||
require.DeepEqual(t, expected.ProportionalSlashingMultiplierAltair, actual.ProportionalSlashingMultiplierAltair)
|
||||
require.DeepEqual(t, expected.MinSlashingPenaltyQuotientBellatrix, actual.MinSlashingPenaltyQuotientBellatrix)
|
||||
require.DeepEqual(t, expected.ProportionalSlashingMultiplierBellatrix, actual.ProportionalSlashingMultiplierBellatrix)
|
||||
require.DeepEqual(t, expected.InactivityPenaltyQuotientBellatrix, actual.InactivityPenaltyQuotientBellatrix)
|
||||
require.DeepEqual(t, expected.MinSyncCommitteeParticipants, actual.MinSyncCommitteeParticipants)
|
||||
require.DeepEqual(t, expected.TerminalBlockHash, actual.TerminalBlockHash)
|
||||
require.DeepEqual(t, expected.TerminalBlockHashActivationEpoch, actual.TerminalBlockHashActivationEpoch)
|
||||
require.DeepEqual(t, expected.TerminalTotalDifficulty, actual.TerminalTotalDifficulty)
|
||||
require.DeepEqual(t, expected.DefaultFeeRecipient, actual.DefaultFeeRecipient)
|
||||
}
|
||||
|
||||
@@ -50,7 +50,12 @@ func E2ETestConfig() *BeaconChainConfig {
|
||||
|
||||
// Prysm constants.
|
||||
e2eConfig.ConfigName = ConfigNames[EndToEnd]
|
||||
e2eConfig.GenesisForkVersion = []byte{0, 0, 0, 253}
|
||||
e2eConfig.AltairForkVersion = []byte{1, 0, 0, 253}
|
||||
e2eConfig.BellatrixForkVersion = []byte{2, 0, 0, 253}
|
||||
e2eConfig.ShardingForkVersion = []byte{3, 0, 0, 253}
|
||||
|
||||
e2eConfig.InitializeForkSchedule()
|
||||
return e2eConfig
|
||||
}
|
||||
|
||||
@@ -78,7 +83,12 @@ func E2EMainnetTestConfig() *BeaconChainConfig {
|
||||
|
||||
// Prysm constants.
|
||||
e2eConfig.ConfigName = ConfigNames[EndToEnd]
|
||||
e2eConfig.GenesisForkVersion = []byte{0, 0, 0, 254}
|
||||
e2eConfig.AltairForkVersion = []byte{1, 0, 0, 254}
|
||||
e2eConfig.BellatrixForkVersion = []byte{2, 0, 0, 254}
|
||||
e2eConfig.ShardingForkVersion = []byte{3, 0, 0, 254}
|
||||
|
||||
e2eConfig.InitializeForkSchedule()
|
||||
return e2eConfig
|
||||
}
|
||||
|
||||
|
||||
@@ -45,5 +45,6 @@ func PraterConfig() *BeaconChainConfig {
|
||||
cfg.BellatrixForkVersion = []byte{0x2, 0x0, 0x10, 0x20}
|
||||
cfg.TerminalTotalDifficulty = "4294967296"
|
||||
cfg.DepositContractAddress = "0xff50ed3d0ec03aC01D4C79aAd74928BFF48a7b2b"
|
||||
cfg.InitializeForkSchedule()
|
||||
return cfg
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ import (
|
||||
func TestPraterConfigMatchesUpstreamYaml(t *testing.T) {
|
||||
presetFPs := presetsFilePath(t, "mainnet")
|
||||
for _, fp := range presetFPs {
|
||||
params.LoadChainConfigFile(fp)
|
||||
params.LoadChainConfigFile(fp, nil)
|
||||
}
|
||||
configFP := testnetConfigFilePath(t, "prater")
|
||||
params.LoadChainConfigFile(configFP)
|
||||
params.LoadChainConfigFile(configFP, nil)
|
||||
fields := fieldsFromYamls(t, append(presetFPs, configFP))
|
||||
assertYamlFieldsMatch(t, "prater", fields, params.BeaconConfig(), params.PraterConfig())
|
||||
}
|
||||
|
||||
@@ -38,5 +38,6 @@ func PyrmontConfig() *BeaconChainConfig {
|
||||
cfg.DepositChainID = 5
|
||||
cfg.DepositNetworkID = 5
|
||||
cfg.DepositContractAddress = "0x8c5fecdC472E27Bc447696F431E425D02dd46a8c"
|
||||
cfg.InitializeForkSchedule()
|
||||
return cfg
|
||||
}
|
||||
|
||||
@@ -1,21 +1,101 @@
|
||||
package params
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
|
||||
)
|
||||
|
||||
const (
|
||||
Mainnet ConfigName = iota
|
||||
Minimal
|
||||
EndToEnd
|
||||
Pyrmont
|
||||
Prater
|
||||
EndToEndMainnet
|
||||
)
|
||||
|
||||
// ConfigName enum describes the type of known network in use.
|
||||
type ConfigName int
|
||||
|
||||
func (n ConfigName) String() string {
|
||||
s, ok := ConfigNames[n]
|
||||
if !ok {
|
||||
return "undefined"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// ConfigNames provides network configuration names.
|
||||
var ConfigNames = map[ConfigName]string{
|
||||
Mainnet: "mainnet",
|
||||
Minimal: "minimal",
|
||||
EndToEnd: "end-to-end",
|
||||
Pyrmont: "pyrmont",
|
||||
Prater: "prater",
|
||||
Mainnet: "mainnet",
|
||||
Minimal: "minimal",
|
||||
EndToEnd: "end-to-end",
|
||||
Pyrmont: "pyrmont",
|
||||
Prater: "prater",
|
||||
EndToEndMainnet: "end-to-end-mainnet",
|
||||
}
|
||||
|
||||
// ConfigName enum describes the type of known network in use.
|
||||
type ConfigName = int
|
||||
// KnownConfigs provides an index of all known BeaconChainConfig values.
|
||||
var KnownConfigs = map[ConfigName]func() *BeaconChainConfig{
|
||||
Mainnet: MainnetConfig,
|
||||
Prater: PraterConfig,
|
||||
Pyrmont: PyrmontConfig,
|
||||
Minimal: MinimalSpecConfig,
|
||||
EndToEnd: E2ETestConfig,
|
||||
EndToEndMainnet: E2EMainnetTestConfig,
|
||||
}
|
||||
|
||||
var knownForkVersions map[[fieldparams.VersionLength]byte]ConfigName
|
||||
|
||||
var errUnknownForkVersion = errors.New("version not found in fork version schedule for any known config")
|
||||
|
||||
// ConfigForVersion find the BeaconChainConfig corresponding to the version bytes.
|
||||
// Version bytes for BeaconChainConfig values in KnownConfigs are proven to be unique during package initialization.
|
||||
func ConfigForVersion(version [fieldparams.VersionLength]byte) (*BeaconChainConfig, error) {
|
||||
cfg, ok := knownForkVersions[version]
|
||||
if !ok {
|
||||
return nil, errors.Wrapf(errUnknownForkVersion, "version=%#x", version)
|
||||
}
|
||||
return KnownConfigs[cfg](), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
knownForkVersions = make(map[[fieldparams.VersionLength]byte]ConfigName)
|
||||
for n, cfunc := range KnownConfigs {
|
||||
cfg := cfunc()
|
||||
// ensure that fork schedule is consistent w/ struct fields for all known configurations
|
||||
if err := equalForkSchedules(configForkSchedule(cfg), cfg.ForkVersionSchedule); err != nil {
|
||||
panic(errors.Wrapf(err, "improperly initialized for schedule for config %s", n.String()))
|
||||
}
|
||||
// ensure that all fork versions are unique
|
||||
for v := range cfg.ForkVersionSchedule {
|
||||
pn, exists := knownForkVersions[v]
|
||||
if exists {
|
||||
previous := KnownConfigs[pn]()
|
||||
msg := fmt.Sprintf("version %#x is duplicated in 2 configs, %s at epoch %d, %s at epoch %d",
|
||||
v, pn, previous.ForkVersionSchedule[v], n, cfg.ForkVersionSchedule[v])
|
||||
panic(msg)
|
||||
}
|
||||
knownForkVersions[v] = n
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func equalForkSchedules(a, b map[[fieldparams.VersionLength]byte]types.Epoch) error {
|
||||
if len(a) != len(b) {
|
||||
return fmt.Errorf("different lengths, a=%d, b=%d", len(a), len(b))
|
||||
}
|
||||
for k, v := range a {
|
||||
bv, ok := b[k]
|
||||
if !ok {
|
||||
return fmt.Errorf("fork version %#x from 'a', not present in 'b'", k)
|
||||
}
|
||||
if v != bv {
|
||||
return fmt.Errorf("fork version mismatch, epoch in a=%d, b=%d", v, bv)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
52
encoding/ssz/detect/BUILD.bazel
Normal file
52
encoding/ssz/detect/BUILD.bazel
Normal file
@@ -0,0 +1,52 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"configfork.go",
|
||||
"fieldspec.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/encoding/ssz/detect",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/v1:go_default_library",
|
||||
"//beacon-chain/state/v2:go_default_library",
|
||||
"//beacon-chain/state/v3:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//network/forks:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//proto/prysm/v1alpha1/block:go_default_library",
|
||||
"//proto/prysm/v1alpha1/wrapper:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
"//time/slots:go_default_library",
|
||||
"@com_github_ferranbt_fastssz//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"configfork_test.go",
|
||||
"fieldspec_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//proto/engine/v1:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//proto/prysm/v1alpha1/block:go_default_library",
|
||||
"//proto/prysm/v1alpha1/wrapper:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
"//testing/util:go_default_library",
|
||||
"//time/slots:go_default_library",
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
],
|
||||
)
|
||||
179
encoding/ssz/detect/configfork.go
Normal file
179
encoding/ssz/detect/configfork.go
Normal file
@@ -0,0 +1,179 @@
|
||||
package detect
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/network/forks"
|
||||
|
||||
ssz "github.com/ferranbt/fastssz"
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
|
||||
v2 "github.com/prysmaticlabs/prysm/beacon-chain/state/v2"
|
||||
v3 "github.com/prysmaticlabs/prysm/beacon-chain/state/v3"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
"github.com/prysmaticlabs/prysm/runtime/version"
|
||||
"github.com/prysmaticlabs/prysm/time/slots"
|
||||
)
|
||||
|
||||
// VersionedUnmarshaler represents the intersection of Configuration (eg mainnet, testnet) and Fork (eg phase0, altair).
|
||||
// Using a detected VersionedUnmarshaler, a BeaconState or SignedBeaconBlock can be correctly unmarshaled without the need to
|
||||
// hard code a concrete type in paths where only the marshaled bytes, or marshaled bytes and a version, are available.
|
||||
type VersionedUnmarshaler struct {
|
||||
Config *params.BeaconChainConfig
|
||||
// Fork aligns with the fork names in config/params/values.go
|
||||
Fork int
|
||||
// Version corresponds to the Version type defined in the beacon-chain spec, aka a "fork version number":
|
||||
// https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#custom-types
|
||||
Version [fieldparams.VersionLength]byte
|
||||
}
|
||||
|
||||
var beaconStateCurrentVersion = fieldSpec{
|
||||
// 52 = 8 (genesis_time) + 32 (genesis_validators_root) + 8 (slot) + 4 (previous_version)
|
||||
offset: 52,
|
||||
t: typeBytes4,
|
||||
}
|
||||
|
||||
// FromState exploits the fixed-size lower-order bytes in a BeaconState as a heuristic to obtain the value of the
|
||||
// state.version field without first unmarshaling the BeaconState. The Version is then internally used to lookup
|
||||
// the correct ConfigVersion.
|
||||
func FromState(marshaled []byte) (*VersionedUnmarshaler, error) {
|
||||
cv, err := beaconStateCurrentVersion.bytes4(marshaled)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return FromForkVersion(cv)
|
||||
}
|
||||
|
||||
var ErrForkNotFound = errors.New("version found in fork schedule but can't be matched to a named fork")
|
||||
|
||||
// FromForkVersion uses a lookup table to resolve a Version (from a beacon node api for instance, or obtained by peeking at
|
||||
// the bytes of a marshaled BeaconState) to a VersionedUnmarshaler.
|
||||
func FromForkVersion(cv [fieldparams.VersionLength]byte) (*VersionedUnmarshaler, error) {
|
||||
cfg, err := params.ConfigForVersion(cv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var fork int
|
||||
switch cv {
|
||||
case bytesutil.ToBytes4(cfg.GenesisForkVersion):
|
||||
fork = version.Phase0
|
||||
case bytesutil.ToBytes4(cfg.AltairForkVersion):
|
||||
fork = version.Altair
|
||||
case bytesutil.ToBytes4(cfg.BellatrixForkVersion):
|
||||
fork = version.Bellatrix
|
||||
default:
|
||||
return nil, errors.Wrapf(ErrForkNotFound, "version=%#x", cv)
|
||||
}
|
||||
return &VersionedUnmarshaler{
|
||||
Config: cfg,
|
||||
Fork: fork,
|
||||
Version: cv,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UnmarshalBeaconState uses internal knowledge in the VersionedUnmarshaler to pick the right concrete BeaconState type,
|
||||
// then Unmarshal()s the type and returns an instance of state.BeaconState if successful.
|
||||
func (cf *VersionedUnmarshaler) UnmarshalBeaconState(marshaled []byte) (s state.BeaconState, err error) {
|
||||
forkName := version.String(cf.Fork)
|
||||
switch fork := cf.Fork; fork {
|
||||
case version.Phase0:
|
||||
st := ðpb.BeaconState{}
|
||||
err = st.UnmarshalSSZ(marshaled)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to unmarshal state, detected fork=%s", forkName)
|
||||
}
|
||||
s, err = v1.InitializeFromProtoUnsafe(st)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to init state trie from state, detected fork=%s", forkName)
|
||||
}
|
||||
case version.Altair:
|
||||
st := ðpb.BeaconStateAltair{}
|
||||
err = st.UnmarshalSSZ(marshaled)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to unmarshal state, detected fork=%s", forkName)
|
||||
}
|
||||
s, err = v2.InitializeFromProtoUnsafe(st)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to init state trie from state, detected fork=%s", forkName)
|
||||
}
|
||||
case version.Bellatrix:
|
||||
st := ðpb.BeaconStateBellatrix{}
|
||||
err = st.UnmarshalSSZ(marshaled)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to unmarshal state, detected fork=%s", forkName)
|
||||
}
|
||||
s, err = v3.InitializeFromProtoUnsafe(st)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to init state trie from state, detected fork=%s", forkName)
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to initialize BeaconState for fork version=%s", forkName)
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
var beaconBlockSlot = fieldSpec{
|
||||
// ssz variable length offset (not to be confused with the fieldSpec offest) is a uint32
|
||||
// variable length. Offsets come before fixed length data, so that's 4 bytes at the beginning
|
||||
// then signature is 96 bytes, 4+96 = 100
|
||||
offset: 100,
|
||||
t: typeUint64,
|
||||
}
|
||||
|
||||
func slotFromBlock(marshaled []byte) (types.Slot, error) {
|
||||
slot, err := beaconBlockSlot.uint64(marshaled)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return types.Slot(slot), nil
|
||||
}
|
||||
|
||||
var errBlockForkMismatch = errors.New("fork or config detected from state is different than block")
|
||||
|
||||
// UnmarshalBeaconBlock uses internal knowledge in the VersionedUnmarshaler to pick the right concrete SignedBeaconBlock type,
|
||||
// then Unmarshal()s the type and returns an instance of block.SignedBeaconBlock if successful.
|
||||
func (cf *VersionedUnmarshaler) UnmarshalBeaconBlock(marshaled []byte) (block.SignedBeaconBlock, error) {
|
||||
slot, err := slotFromBlock(marshaled)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// heuristic to make sure block is from the same version as the VersionedUnmarshaler.
|
||||
// Look up the version for the epoch that the block is from, then ensure that it matches the Version in the
|
||||
// VersionedUnmarshaler.
|
||||
epoch := slots.ToEpoch(slot)
|
||||
fs := forks.NewOrderedSchedule(cf.Config)
|
||||
ver, err := fs.VersionForEpoch(epoch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ver != cf.Version {
|
||||
return nil, errors.Wrapf(errBlockForkMismatch, "slot=%d, epoch=%d, version=%#x", slot, epoch, ver)
|
||||
}
|
||||
|
||||
var blk ssz.Unmarshaler
|
||||
switch cf.Fork {
|
||||
case version.Phase0:
|
||||
blk = ðpb.SignedBeaconBlock{}
|
||||
case version.Altair:
|
||||
blk = ðpb.SignedBeaconBlockAltair{}
|
||||
case version.Bellatrix:
|
||||
blk = ðpb.SignedBeaconBlockBellatrix{}
|
||||
default:
|
||||
forkName := version.String(cf.Fork)
|
||||
return nil, fmt.Errorf("unable to initialize BeaconBlock for fork version=%s at slot=%d", forkName, slot)
|
||||
}
|
||||
err = blk.UnmarshalSSZ(marshaled)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal SignedBeaconBlock in UnmarshalSSZ")
|
||||
}
|
||||
return wrapper.WrappedSignedBeaconBlock(blk)
|
||||
}
|
||||
401
encoding/ssz/detect/configfork_test.go
Normal file
401
encoding/ssz/detect/configfork_test.go
Normal file
@@ -0,0 +1,401 @@
|
||||
package detect
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/block"
|
||||
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
||||
"github.com/prysmaticlabs/prysm/runtime/version"
|
||||
"github.com/prysmaticlabs/prysm/testing/util"
|
||||
"github.com/prysmaticlabs/prysm/time/slots"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
v1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
)
|
||||
|
||||
func TestSlotFromBlock(t *testing.T) {
|
||||
b := testBlockGenesis()
|
||||
var slot types.Slot = 3
|
||||
b.Block.Slot = slot
|
||||
bb, err := b.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
sfb, err := slotFromBlock(bb)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, slot, sfb)
|
||||
|
||||
ba := testBlockAltair()
|
||||
ba.Block.Slot = slot
|
||||
bab, err := ba.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
sfba, err := slotFromBlock(bab)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, slot, sfba)
|
||||
|
||||
bm := testBlockBellatrix()
|
||||
bm.Block.Slot = slot
|
||||
bmb, err := ba.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
sfbm, err := slotFromBlock(bmb)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, slot, sfbm)
|
||||
}
|
||||
|
||||
func TestByState(t *testing.T) {
|
||||
bc, cleanup := hackBellatrixMaxuint()
|
||||
defer cleanup()
|
||||
altairSlot, err := slots.EpochStart(bc.AltairForkEpoch)
|
||||
bellaSlot, err := slots.EpochStart(bc.BellatrixForkEpoch)
|
||||
require.NoError(t, err)
|
||||
cases := []struct {
|
||||
name string
|
||||
version int
|
||||
slot types.Slot
|
||||
forkversion [4]byte
|
||||
}{
|
||||
{
|
||||
name: "genesis",
|
||||
version: version.Phase0,
|
||||
slot: 0,
|
||||
forkversion: bytesutil.ToBytes4(bc.GenesisForkVersion),
|
||||
},
|
||||
{
|
||||
name: "altair",
|
||||
version: version.Altair,
|
||||
slot: altairSlot,
|
||||
forkversion: bytesutil.ToBytes4(bc.AltairForkVersion),
|
||||
},
|
||||
{
|
||||
name: "bellatrix",
|
||||
version: version.Bellatrix,
|
||||
slot: bellaSlot,
|
||||
forkversion: bytesutil.ToBytes4(bc.BellatrixForkVersion),
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
st, err := stateForVersion(c.version)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, st.SetFork(ðpb.Fork{
|
||||
PreviousVersion: make([]byte, 4),
|
||||
CurrentVersion: c.forkversion[:],
|
||||
Epoch: 0,
|
||||
}))
|
||||
require.NoError(t, st.SetSlot(c.slot))
|
||||
m, err := st.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
cf, err := FromState(m)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, c.version, cf.Fork)
|
||||
require.Equal(t, c.forkversion, cf.Version)
|
||||
require.Equal(t, bc.ConfigName, cf.Config.ConfigName)
|
||||
}
|
||||
}
|
||||
|
||||
func stateForVersion(v int) (state.BeaconState, error) {
|
||||
switch v {
|
||||
case version.Phase0:
|
||||
return util.NewBeaconState()
|
||||
case version.Altair:
|
||||
return util.NewBeaconStateAltair()
|
||||
case version.Bellatrix:
|
||||
return util.NewBeaconStateBellatrix()
|
||||
default:
|
||||
return nil, fmt.Errorf("unrecognoized version %d", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalState(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
bc, cleanup := hackBellatrixMaxuint()
|
||||
defer cleanup()
|
||||
altairSlot, err := slots.EpochStart(bc.AltairForkEpoch)
|
||||
bellaSlot, err := slots.EpochStart(bc.BellatrixForkEpoch)
|
||||
require.NoError(t, err)
|
||||
cases := []struct {
|
||||
name string
|
||||
version int
|
||||
slot types.Slot
|
||||
forkversion [4]byte
|
||||
}{
|
||||
{
|
||||
name: "genesis",
|
||||
version: version.Phase0,
|
||||
slot: 0,
|
||||
forkversion: bytesutil.ToBytes4(bc.GenesisForkVersion),
|
||||
},
|
||||
{
|
||||
name: "altair",
|
||||
version: version.Altair,
|
||||
slot: altairSlot,
|
||||
forkversion: bytesutil.ToBytes4(bc.AltairForkVersion),
|
||||
},
|
||||
{
|
||||
name: "bellatrix",
|
||||
version: version.Bellatrix,
|
||||
slot: bellaSlot,
|
||||
forkversion: bytesutil.ToBytes4(bc.BellatrixForkVersion),
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
st, err := stateForVersion(c.version)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, st.SetFork(ðpb.Fork{
|
||||
PreviousVersion: make([]byte, 4),
|
||||
CurrentVersion: c.forkversion[:],
|
||||
Epoch: 0,
|
||||
}))
|
||||
require.NoError(t, st.SetSlot(c.slot))
|
||||
m, err := st.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
cf, err := FromState(m)
|
||||
require.NoError(t, err)
|
||||
s, err := cf.UnmarshalBeaconState(m)
|
||||
require.NoError(t, err)
|
||||
expected, err := st.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
actual, err := s.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func hackBellatrixMaxuint() (*params.BeaconChainConfig, func()) {
|
||||
// We monkey patch the config to use a smaller value for the bellatrix fork epoch.
|
||||
// Upstream configs use MaxUint64, which leads to a multiplication overflow when converting epoch->slot.
|
||||
// Unfortunately we have unit tests that assert our config matches the upstream config, so we have to choose between
|
||||
// breaking conformance, adding a special case to the conformance unit test, or patch it here.
|
||||
previous := params.BeaconConfig()
|
||||
bc := params.MainnetConfig().Copy()
|
||||
bc.BellatrixForkEpoch = math.MaxUint32
|
||||
bc.InitializeForkSchedule()
|
||||
params.OverrideBeaconConfig(bc)
|
||||
// override the param used for mainnet with the patched version
|
||||
params.KnownConfigs[params.Mainnet] = func() *params.BeaconChainConfig {
|
||||
return bc
|
||||
}
|
||||
return bc, func() {
|
||||
// put the previous BeaconChainConfig back in place at the end of the test
|
||||
params.OverrideBeaconConfig(previous)
|
||||
// restore the normal MainnetConfig func in the KnownConfigs mapping
|
||||
params.KnownConfigs[params.Mainnet] = params.MainnetConfig
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalBlock(t *testing.T) {
|
||||
bc, cleanup := hackBellatrixMaxuint()
|
||||
defer cleanup()
|
||||
require.Equal(t, types.Epoch(math.MaxUint32), params.KnownConfigs[params.Mainnet]().BellatrixForkEpoch)
|
||||
genv := bytesutil.ToBytes4(bc.GenesisForkVersion)
|
||||
altairv := bytesutil.ToBytes4(bc.AltairForkVersion)
|
||||
bellav := bytesutil.ToBytes4(bc.BellatrixForkVersion)
|
||||
altairS, err := slots.EpochStart(bc.AltairForkEpoch)
|
||||
bellaS, err := slots.EpochStart(bc.BellatrixForkEpoch)
|
||||
require.NoError(t, err)
|
||||
cases := []struct {
|
||||
b func(*testing.T, types.Slot) block.SignedBeaconBlock
|
||||
name string
|
||||
version [4]byte
|
||||
slot types.Slot
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "genesis - slot 0",
|
||||
b: signedTestBlockGenesis,
|
||||
version: genv,
|
||||
},
|
||||
{
|
||||
name: "last slot of phase 0",
|
||||
b: signedTestBlockGenesis,
|
||||
version: genv,
|
||||
slot: altairS - 1,
|
||||
},
|
||||
{
|
||||
name: "first slot of altair",
|
||||
b: signedTestBlockAltair,
|
||||
version: altairv,
|
||||
slot: altairS,
|
||||
},
|
||||
{
|
||||
name: "last slot of altair",
|
||||
b: signedTestBlockAltair,
|
||||
version: altairv,
|
||||
slot: bellaS - 1,
|
||||
},
|
||||
{
|
||||
name: "first slot of bellatrix",
|
||||
b: signedTestBlockBellatrix,
|
||||
version: bellav,
|
||||
slot: bellaS,
|
||||
},
|
||||
{
|
||||
name: "bellatrix block in altair slot",
|
||||
b: signedTestBlockBellatrix,
|
||||
version: bellav,
|
||||
slot: bellaS - 1,
|
||||
err: errBlockForkMismatch,
|
||||
},
|
||||
{
|
||||
name: "genesis block in altair slot",
|
||||
b: signedTestBlockGenesis,
|
||||
version: genv,
|
||||
slot: bellaS - 1,
|
||||
err: errBlockForkMismatch,
|
||||
},
|
||||
{
|
||||
name: "altair block in genesis slot",
|
||||
b: signedTestBlockAltair,
|
||||
version: altairv,
|
||||
err: errBlockForkMismatch,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
b := c.b(t, c.slot)
|
||||
marshaled, err := b.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
cf, err := FromForkVersion(c.version)
|
||||
require.NoError(t, err)
|
||||
bcf, err := cf.UnmarshalBeaconBlock(marshaled)
|
||||
if c.err != nil {
|
||||
require.ErrorIs(t, err, c.err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
expected, err := b.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
actual, err := bcf.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func signedTestBlockGenesis(t *testing.T, slot types.Slot) block.SignedBeaconBlock {
|
||||
b := testBlockGenesis()
|
||||
b.Block.Slot = slot
|
||||
s, err := wrapper.WrappedSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
return s
|
||||
}
|
||||
|
||||
func testBlockGenesis() *ethpb.SignedBeaconBlock {
|
||||
return ðpb.SignedBeaconBlock{
|
||||
Block: ðpb.BeaconBlock{
|
||||
ProposerIndex: types.ValidatorIndex(0),
|
||||
ParentRoot: make([]byte, 32),
|
||||
StateRoot: make([]byte, 32),
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
RandaoReveal: make([]byte, 96),
|
||||
Graffiti: make([]byte, 32),
|
||||
ProposerSlashings: []*ethpb.ProposerSlashing{},
|
||||
AttesterSlashings: []*ethpb.AttesterSlashing{},
|
||||
Attestations: []*ethpb.Attestation{},
|
||||
Deposits: []*ethpb.Deposit{},
|
||||
VoluntaryExits: []*ethpb.SignedVoluntaryExit{},
|
||||
Eth1Data: ðpb.Eth1Data{
|
||||
DepositRoot: make([]byte, 32),
|
||||
DepositCount: 0,
|
||||
BlockHash: make([]byte, 32),
|
||||
},
|
||||
},
|
||||
},
|
||||
Signature: make([]byte, 96),
|
||||
}
|
||||
}
|
||||
|
||||
func signedTestBlockAltair(t *testing.T, slot types.Slot) block.SignedBeaconBlock {
|
||||
b := testBlockAltair()
|
||||
b.Block.Slot = slot
|
||||
s, err := wrapper.WrappedSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
return s
|
||||
}
|
||||
|
||||
func testBlockAltair() *ethpb.SignedBeaconBlockAltair {
|
||||
return ðpb.SignedBeaconBlockAltair{
|
||||
Block: ðpb.BeaconBlockAltair{
|
||||
ProposerIndex: types.ValidatorIndex(0),
|
||||
ParentRoot: make([]byte, 32),
|
||||
StateRoot: make([]byte, 32),
|
||||
Body: ðpb.BeaconBlockBodyAltair{
|
||||
RandaoReveal: make([]byte, 96),
|
||||
Eth1Data: ðpb.Eth1Data{
|
||||
DepositRoot: make([]byte, 32),
|
||||
DepositCount: 0,
|
||||
BlockHash: make([]byte, 32),
|
||||
},
|
||||
Graffiti: make([]byte, 32),
|
||||
ProposerSlashings: []*ethpb.ProposerSlashing{},
|
||||
AttesterSlashings: []*ethpb.AttesterSlashing{},
|
||||
Attestations: []*ethpb.Attestation{},
|
||||
Deposits: []*ethpb.Deposit{},
|
||||
VoluntaryExits: []*ethpb.SignedVoluntaryExit{},
|
||||
SyncAggregate: ðpb.SyncAggregate{
|
||||
SyncCommitteeBits: make([]byte, 64),
|
||||
SyncCommitteeSignature: make([]byte, 96),
|
||||
},
|
||||
},
|
||||
},
|
||||
Signature: make([]byte, 96),
|
||||
}
|
||||
}
|
||||
|
||||
func signedTestBlockBellatrix(t *testing.T, slot types.Slot) block.SignedBeaconBlock {
|
||||
b := testBlockBellatrix()
|
||||
b.Block.Slot = slot
|
||||
s, err := wrapper.WrappedSignedBeaconBlock(b)
|
||||
require.NoError(t, err)
|
||||
return s
|
||||
}
|
||||
|
||||
func testBlockBellatrix() *ethpb.SignedBeaconBlockBellatrix {
|
||||
return ðpb.SignedBeaconBlockBellatrix{
|
||||
Block: ðpb.BeaconBlockBellatrix{
|
||||
ProposerIndex: types.ValidatorIndex(0),
|
||||
ParentRoot: make([]byte, 32),
|
||||
StateRoot: make([]byte, 32),
|
||||
Body: ðpb.BeaconBlockBodyBellatrix{
|
||||
RandaoReveal: make([]byte, 96),
|
||||
Eth1Data: ðpb.Eth1Data{
|
||||
DepositRoot: make([]byte, 32),
|
||||
DepositCount: 0,
|
||||
BlockHash: make([]byte, 32),
|
||||
},
|
||||
Graffiti: make([]byte, 32),
|
||||
ProposerSlashings: []*ethpb.ProposerSlashing{},
|
||||
AttesterSlashings: []*ethpb.AttesterSlashing{},
|
||||
Attestations: []*ethpb.Attestation{},
|
||||
Deposits: []*ethpb.Deposit{},
|
||||
VoluntaryExits: []*ethpb.SignedVoluntaryExit{},
|
||||
SyncAggregate: ðpb.SyncAggregate{
|
||||
SyncCommitteeBits: make([]byte, 64),
|
||||
SyncCommitteeSignature: make([]byte, 96),
|
||||
},
|
||||
ExecutionPayload: &v1.ExecutionPayload{
|
||||
ParentHash: make([]byte, 32),
|
||||
FeeRecipient: make([]byte, 20),
|
||||
StateRoot: make([]byte, 32),
|
||||
ReceiptsRoot: make([]byte, 32),
|
||||
LogsBloom: make([]byte, 256),
|
||||
BlockNumber: 0,
|
||||
GasLimit: 0,
|
||||
GasUsed: 0,
|
||||
Timestamp: 0,
|
||||
ExtraData: make([]byte, 32),
|
||||
BaseFeePerGas: make([]byte, 32),
|
||||
BlockHash: make([]byte, 32),
|
||||
Transactions: make([][]byte, 0),
|
||||
PrevRandao: make([]byte, 32),
|
||||
},
|
||||
},
|
||||
},
|
||||
Signature: make([]byte, 96),
|
||||
}
|
||||
}
|
||||
79
encoding/ssz/detect/fieldspec.go
Normal file
79
encoding/ssz/detect/fieldspec.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package detect
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
|
||||
)
|
||||
|
||||
type fieldType int
|
||||
|
||||
const (
|
||||
typeUndefined fieldType = iota
|
||||
typeUint64
|
||||
typeBytes4
|
||||
)
|
||||
|
||||
func (f fieldType) String() string {
|
||||
switch f {
|
||||
case typeUint64:
|
||||
return "uint64"
|
||||
case typeBytes4:
|
||||
return "bytes4"
|
||||
case typeUndefined:
|
||||
return "undefined"
|
||||
default:
|
||||
return "invalid"
|
||||
}
|
||||
}
|
||||
|
||||
func (f fieldType) Size() int {
|
||||
switch f {
|
||||
case typeUint64:
|
||||
return 8
|
||||
case typeBytes4:
|
||||
return 4
|
||||
default:
|
||||
panic("can't determine size for unrecognizedtype ")
|
||||
}
|
||||
}
|
||||
|
||||
var errWrongMethodForType = errors.New("wrong fieldSpec method for type")
|
||||
var errIndexOutOfRange = errors.New("value index would exceed byte length")
|
||||
|
||||
type fieldSpec struct {
|
||||
offset int
|
||||
t fieldType
|
||||
}
|
||||
|
||||
func (f *fieldSpec) uint64(state []byte) (uint64, error) {
|
||||
if f.t != typeUint64 {
|
||||
return 0, errors.Wrapf(errWrongMethodForType, "called uint64() for type=%s", f.t)
|
||||
}
|
||||
s, err := f.slice(state)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return binary.LittleEndian.Uint64(s), nil
|
||||
}
|
||||
|
||||
func (f *fieldSpec) bytes4(state []byte) ([4]byte, error) {
|
||||
var b4 [4]byte
|
||||
if f.t != typeBytes4 {
|
||||
return b4, errors.Wrapf(errWrongMethodForType, "called bytes4() with fieldType=%s", f.t)
|
||||
}
|
||||
val, err := f.slice(state)
|
||||
if err != nil {
|
||||
return b4, err
|
||||
}
|
||||
return bytesutil.ToBytes4(val), nil
|
||||
}
|
||||
|
||||
func (f *fieldSpec) slice(value []byte) ([]byte, error) {
|
||||
size := f.t.Size()
|
||||
if len(value) < f.offset+size {
|
||||
return nil, errors.Wrapf(errIndexOutOfRange, "offset=%d, size=%d, byte len=%d", f.offset, size, len(value))
|
||||
}
|
||||
return value[f.offset : f.offset+size], nil
|
||||
}
|
||||
104
encoding/ssz/detect/fieldspec_test.go
Normal file
104
encoding/ssz/detect/fieldspec_test.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package detect
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
)
|
||||
|
||||
func TestTypeMismatch(t *testing.T) {
|
||||
wrong := fieldSpec{
|
||||
offset: 52,
|
||||
t: typeBytes4,
|
||||
}
|
||||
_, err := wrong.uint64([]byte{})
|
||||
require.ErrorIs(t, err, errWrongMethodForType)
|
||||
|
||||
wrong = fieldSpec{
|
||||
offset: 100,
|
||||
t: typeUint64,
|
||||
}
|
||||
_, err = wrong.bytes4([]byte{})
|
||||
require.ErrorIs(t, err, errWrongMethodForType)
|
||||
}
|
||||
|
||||
func TestFieldSpecUint(t *testing.T) {
|
||||
var expectedUint uint64 = 23
|
||||
buf := make([]byte, binary.MaxVarintLen64)
|
||||
uv := binary.PutUvarint(buf, expectedUint)
|
||||
require.Equal(t, 1, uv)
|
||||
padded := make([]byte, 100)
|
||||
uintOffset := 10
|
||||
copy(padded[uintOffset:], buf)
|
||||
fs := fieldSpec{offset: uintOffset, t: typeUint64}
|
||||
u, err := fs.uint64(padded)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedUint, u)
|
||||
}
|
||||
|
||||
func TestFieldSpecBytes4(t *testing.T) {
|
||||
expectedBytes := []byte("cafe")
|
||||
padded := make([]byte, 100)
|
||||
byteOffset := 42
|
||||
copy(padded[byteOffset:], expectedBytes)
|
||||
fs := fieldSpec{offset: byteOffset, t: typeBytes4}
|
||||
b, err := fs.bytes4(padded)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, expectedBytes, b[:])
|
||||
}
|
||||
|
||||
func TestFieldSpecSlice(t *testing.T) {
|
||||
cases := []struct {
|
||||
offset int
|
||||
fieldType fieldType
|
||||
slice []byte
|
||||
err error
|
||||
name string
|
||||
expected []byte
|
||||
}{
|
||||
{
|
||||
offset: 0,
|
||||
fieldType: typeBytes4,
|
||||
slice: []byte{},
|
||||
err: errIndexOutOfRange,
|
||||
name: "zero length, out of range",
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
fieldType: typeBytes4,
|
||||
slice: []byte("1234"),
|
||||
err: errIndexOutOfRange,
|
||||
name: "non-zero length, out of range",
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
fieldType: typeBytes4,
|
||||
slice: []byte("12345"),
|
||||
expected: []byte("2345"),
|
||||
name: "success",
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
fieldType: typeUint64,
|
||||
slice: []byte("123456789"),
|
||||
expected: []byte("23456789"),
|
||||
name: "uint success",
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
s := fieldSpec{
|
||||
offset: c.offset,
|
||||
t: c.fieldType,
|
||||
}
|
||||
b, err := s.slice(c.slice)
|
||||
if c.err == nil {
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, c.expected, b)
|
||||
} else {
|
||||
require.ErrorIs(t, err, c.err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,16 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["fork.go"],
|
||||
srcs = [
|
||||
"errors.go",
|
||||
"fork.go",
|
||||
"ordered.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/network/forks",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//beacon-chain/core/signing:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
@@ -18,13 +23,17 @@ go_library(
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["fork_test.go"],
|
||||
srcs = [
|
||||
"fork_test.go",
|
||||
"ordered_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/core/signing:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//testing/assert:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
6
network/forks/errors.go
Normal file
6
network/forks/errors.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package forks
|
||||
|
||||
import "github.com/pkg/errors"
|
||||
|
||||
// ErrVersionNotFound indicates the config package couldn't determine the version for an epoch using the fork schedule.
|
||||
var ErrVersionNotFound = errors.New("could not find an entry in the fork schedule")
|
||||
54
network/forks/ordered.go
Normal file
54
network/forks/ordered.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package forks
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
)
|
||||
|
||||
// ForkScheduleEntry is a Version+Epoch tuple for sorted storage in an OrderedSchedule
|
||||
type ForkScheduleEntry struct {
|
||||
Version [fieldparams.VersionLength]byte
|
||||
Epoch types.Epoch
|
||||
}
|
||||
|
||||
// OrderedSchedule provides a type that can be used to sort the fork schedule and find the Version
|
||||
// the chain should be at for a given epoch (via VersionForEpoch).
|
||||
type OrderedSchedule []ForkScheduleEntry
|
||||
|
||||
// Len implements the Len method of sort.Interface
|
||||
func (o OrderedSchedule) Len() int { return len(o) }
|
||||
|
||||
// Swap implements the Swap method of sort.Interface
|
||||
func (o OrderedSchedule) Swap(i, j int) { o[i], o[j] = o[j], o[i] }
|
||||
|
||||
// Less implements the Less method of sort.Interface
|
||||
func (o OrderedSchedule) Less(i, j int) bool { return o[i].Epoch < o[j].Epoch }
|
||||
|
||||
// VersionForEpoch finds the Version with the highest epoch <= the given epoch
|
||||
func (o OrderedSchedule) VersionForEpoch(epoch types.Epoch) ([fieldparams.VersionLength]byte, error) {
|
||||
for i := len(o) - 1; i >= 0; i-- {
|
||||
if o[i].Epoch <= epoch {
|
||||
return o[i].Version, nil
|
||||
}
|
||||
}
|
||||
return [fieldparams.VersionLength]byte{}, errors.Wrapf(ErrVersionNotFound, "no epoch in list <= %d", epoch)
|
||||
}
|
||||
|
||||
// Converts the ForkVersionSchedule map into a list of Version+Epoch values, ordered by Epoch from lowest to highest.
|
||||
// See docs for OrderedSchedule for more detail on what you can do with this type.
|
||||
func NewOrderedSchedule(b *params.BeaconChainConfig) OrderedSchedule {
|
||||
ofs := make(OrderedSchedule, 0)
|
||||
for version, epoch := range b.ForkVersionSchedule {
|
||||
fse := ForkScheduleEntry{
|
||||
Version: version,
|
||||
Epoch: epoch,
|
||||
}
|
||||
ofs = append(ofs, fse)
|
||||
}
|
||||
sort.Sort(ofs)
|
||||
return ofs
|
||||
}
|
||||
105
network/forks/ordered_test.go
Normal file
105
network/forks/ordered_test.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package forks
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/testing/require"
|
||||
)
|
||||
|
||||
func TestOrderedConfigSchedule(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
for name, getCfg := range params.KnownConfigs {
|
||||
cfg := getCfg()
|
||||
t.Run(name.String(), func(t *testing.T) {
|
||||
prevVersion := [4]byte{0, 0, 0, 0}
|
||||
// epoch 0 is genesis, and it's a uint so can't make it -1
|
||||
// so we use a pointer to detect the boundary condition and skip it
|
||||
var prevEpoch *types.Epoch
|
||||
for _, fse := range NewOrderedSchedule(cfg) {
|
||||
// copy loop variable so we can take the address of fields
|
||||
f := fse
|
||||
if prevEpoch == nil {
|
||||
prevEpoch = &f.Epoch
|
||||
prevVersion = f.Version
|
||||
continue
|
||||
}
|
||||
if *prevEpoch > f.Epoch {
|
||||
t.Errorf("Epochs out of order! %#x/%d before %#x/%d", f.Version, f.Epoch, prevVersion, prevEpoch)
|
||||
}
|
||||
prevEpoch = &f.Epoch
|
||||
prevVersion = f.Version
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
bc := testForkVersionScheduleBCC()
|
||||
ofs := NewOrderedSchedule(bc)
|
||||
for i := range ofs {
|
||||
if ofs[i].Epoch != types.Epoch(math.Pow(2, float64(i))) {
|
||||
t.Errorf("expected %dth element of list w/ epoch=%d, got=%d. list=%v", i, types.Epoch(2^i), ofs[i].Epoch, ofs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersionForEpoch(t *testing.T) {
|
||||
bc := testForkVersionScheduleBCC()
|
||||
ofs := NewOrderedSchedule(bc)
|
||||
testCases := []struct {
|
||||
name string
|
||||
version [4]byte
|
||||
epoch types.Epoch
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "found between versions",
|
||||
version: [4]byte{2, 1, 2, 3},
|
||||
epoch: types.Epoch(7),
|
||||
},
|
||||
{
|
||||
name: "found at end",
|
||||
version: [4]byte{4, 1, 2, 3},
|
||||
epoch: types.Epoch(100),
|
||||
},
|
||||
{
|
||||
name: "found at start",
|
||||
version: [4]byte{0, 1, 2, 3},
|
||||
epoch: types.Epoch(1),
|
||||
},
|
||||
{
|
||||
name: "found at boundary",
|
||||
version: [4]byte{1, 1, 2, 3},
|
||||
epoch: types.Epoch(2),
|
||||
},
|
||||
{
|
||||
name: "not found before",
|
||||
epoch: types.Epoch(0),
|
||||
err: ErrVersionNotFound,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
v, err := ofs.VersionForEpoch(tc.epoch)
|
||||
if tc.err == nil {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.ErrorIs(t, err, tc.err)
|
||||
}
|
||||
require.Equal(t, tc.version, v)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testForkVersionScheduleBCC() *params.BeaconChainConfig {
|
||||
return ¶ms.BeaconChainConfig{
|
||||
ForkVersionSchedule: map[[4]byte]types.Epoch{
|
||||
{1, 1, 2, 3}: types.Epoch(2),
|
||||
{0, 1, 2, 3}: types.Epoch(1),
|
||||
{4, 1, 2, 3}: types.Epoch(16),
|
||||
{3, 1, 2, 3}: types.Epoch(8),
|
||||
{2, 1, 2, 3}: types.Epoch(4),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
load(
|
||||
"@bazel_skylib//rules:common_settings.bzl",
|
||||
"string_flag",
|
||||
|
||||
@@ -110,7 +110,7 @@ func NewValidatorClient(cliCtx *cli.Context) (*ValidatorClient, error) {
|
||||
|
||||
if cliCtx.IsSet(cmd.ChainConfigFileFlag.Name) {
|
||||
chainConfigFileName := cliCtx.String(cmd.ChainConfigFileFlag.Name)
|
||||
params.LoadChainConfigFile(chainConfigFileName)
|
||||
params.LoadChainConfigFile(chainConfigFileName, nil)
|
||||
}
|
||||
|
||||
// If the --web flag is enabled to administer the validator
|
||||
@@ -128,7 +128,7 @@ func NewValidatorClient(cliCtx *cli.Context) (*ValidatorClient, error) {
|
||||
|
||||
if cliCtx.IsSet(cmd.ChainConfigFileFlag.Name) {
|
||||
chainConfigFileName := cliCtx.String(cmd.ChainConfigFileFlag.Name)
|
||||
params.LoadChainConfigFile(chainConfigFileName)
|
||||
params.LoadChainConfigFile(chainConfigFileName, nil)
|
||||
}
|
||||
|
||||
// Initializes any forks here.
|
||||
|
||||
Reference in New Issue
Block a user