diff --git a/changelog/terence_guard-unreleased-forks.md b/changelog/terence_guard-unreleased-forks.md new file mode 100644 index 0000000000..e7460cafb6 --- /dev/null +++ b/changelog/terence_guard-unreleased-forks.md @@ -0,0 +1,3 @@ +### Added + +- Add supported version for fork versions diff --git a/runtime/version/BUILD.bazel b/runtime/version/BUILD.bazel index 4c8bfaa546..74ff582e76 100644 --- a/runtime/version/BUILD.bazel +++ b/runtime/version/BUILD.bazel @@ -25,6 +25,11 @@ go_library( go_test( name = "go_default_test", srcs = ["fork_test.go"], - embed = [":go_default_library"], - deps = ["@com_github_stretchr_testify//assert:go_default_library"], + deps = [ + ":go_default_library", + "//config/params:go_default_library", + "//consensus-types/primitives:go_default_library", + "@com_github_stretchr_testify//assert:go_default_library", + "@com_github_stretchr_testify//require:go_default_library", + ], ) diff --git a/runtime/version/fork.go b/runtime/version/fork.go index 385cc58046..708196d605 100644 --- a/runtime/version/fork.go +++ b/runtime/version/fork.go @@ -25,11 +25,19 @@ var versionToString = map[int]string{ Deneb: "deneb", Electra: "electra", Fulu: "fulu", + Gloas: "gloas", } // stringToVersion and allVersions are populated in init() var stringToVersion = map[string]int{} var allVersions []int +var supportedVersions []int + +// unsupportedVersions contains fork versions that exist in the enums but are not yet +// enabled on any supported network. These versions are removed from All(). +var unsupportedVersions = map[int]struct{}{ + Gloas: {}, +} // ErrUnrecognizedVersionName means a string does not match the list of canonical version names. var ErrUnrecognizedVersionName = errors.New("version name doesn't map to a known value in the enum") @@ -53,9 +61,15 @@ func String(version int) string { return name } -// All returns a list of all known fork versions. +// All returns a list of all supported fork versions. func All() []int { - return allVersions + return supportedVersions +} + +// IsUnsupported reports whether the provided version is currently gate-kept. +func IsUnsupported(version int) bool { + _, ok := unsupportedVersions[version] + return ok } func init() { @@ -67,4 +81,12 @@ func init() { i++ } sort.Ints(allVersions) + + supportedVersions = make([]int, 0, len(allVersions)) + for _, v := range allVersions { + if _, skip := unsupportedVersions[v]; skip { + continue + } + supportedVersions = append(supportedVersions, v) + } } diff --git a/runtime/version/fork_test.go b/runtime/version/fork_test.go index 21068847e3..8f1716e906 100644 --- a/runtime/version/fork_test.go +++ b/runtime/version/fork_test.go @@ -1,11 +1,15 @@ -package version +package version_test import ( "slices" "sort" "testing" + "github.com/OffchainLabs/prysm/v7/config/params" + "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" + "github.com/OffchainLabs/prysm/v7/runtime/version" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestVersionString(t *testing.T) { @@ -16,18 +20,18 @@ func TestVersionString(t *testing.T) { }{ { name: "phase0", - version: Phase0, + version: version.Phase0, want: "phase0", }, { name: "altair", - version: Altair, + version: version.Altair, want: "altair", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := String(tt.version); got != tt.want { + if got := version.String(tt.version); got != tt.want { t.Errorf("String() = %v, want %v", got, tt.want) } }) @@ -35,7 +39,7 @@ func TestVersionString(t *testing.T) { } func TestVersionSorting(t *testing.T) { - versions := All() + versions := version.All() expected := slices.Clone(versions) sort.Ints(expected) tests := []struct { @@ -54,3 +58,74 @@ func TestVersionSorting(t *testing.T) { }) } } + +func TestUnsupportedVersionsExcludedFromAll(t *testing.T) { + for _, v := range unsupportedVersions() { + assert.NotContains(t, version.All(), v, "unsupported version %s should not be returned by version.All()", version.String(v)) + } +} + +func TestUnsupportedVersionsAreNotScheduledOnTestnets(t *testing.T) { + unsupported := unsupportedVersions() + if len(unsupported) == 0 { + t.Skip("no unsupported versions defined") + } + + testnetConfigs := []*params.BeaconChainConfig{ + params.HoleskyConfig(), + params.SepoliaConfig(), + params.HoodiConfig(), + } + + for _, v := range unsupported { + for _, cfg := range testnetConfigs { + epoch := forkEpochForVersion(cfg, v) + require.Equalf( + t, + cfg.FarFutureEpoch, + epoch, + "unsupported version %s should not be scheduled on %s (epoch=%d)", + version.String(v), + cfg.ConfigName, + epoch, + ) + } + } +} + +func forkEpochForVersion(cfg *params.BeaconChainConfig, v int) primitives.Epoch { + switch v { + case version.Phase0: + return cfg.GenesisEpoch + case version.Altair: + return cfg.AltairForkEpoch + case version.Bellatrix: + return cfg.BellatrixForkEpoch + case version.Capella: + return cfg.CapellaForkEpoch + case version.Deneb: + return cfg.DenebForkEpoch + case version.Electra: + return cfg.ElectraForkEpoch + case version.Fulu: + return cfg.FuluForkEpoch + default: + if version.IsUnsupported(v) { + return cfg.FarFutureEpoch + } + panic("forkEpochForVersion missing version " + version.String(v)) + } +} + +func unsupportedVersions() []int { + var unsupportedVersions []int + for v := 0; ; v++ { + if version.String(v) == "unknown version" { + break + } + if version.IsUnsupported(v) { + unsupportedVersions = append(unsupportedVersions, v) + } + } + return unsupportedVersions +}