Files
prysm/genesis/initialize_test.go
Preston Van Loon 2fd6bd8150 Add golang.org/x/tools modernize static analyzer and fix violations (#15946)
* Ran gopls modernize to fix everything

go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest -fix -test ./...

* Override rules_go provided dependency for golang.org/x/tools to v0.38.0.

To update this, checked out rules_go, then ran `bazel run //go/tools/releaser -- upgrade-dep -mirror=false org_golang_x_tools` and copied the patches.

* Fix buildtag violations and ignore buildtag violations in external

* Introduce modernize analyzer package.

* Add modernize "any" analyzer.

* Fix violations of any analyzer

* Add modernize "appendclipped" analyzer.

* Fix violations of appendclipped

* Add modernize "bloop" analyzer.

* Add modernize "fmtappendf" analyzer.

* Add modernize "forvar" analyzer.

* Add modernize "mapsloop" analyzer.

* Add modernize "minmax" analyzer.

* Fix violations of minmax analyzer

* Add modernize "omitzero" analyzer.

* Add modernize "rangeint" analyzer.

* Fix violations of rangeint.

* Add modernize "reflecttypefor" analyzer.

* Fix violations of reflecttypefor analyzer.

* Add modernize "slicescontains" analyzer.

* Add modernize "slicessort" analyzer.

* Add modernize "slicesdelete" analyzer. This is disabled by default for now. See https://go.dev/issue/73686.

* Add modernize "stringscutprefix" analyzer.

* Add modernize "stringsbuilder" analyzer.

* Fix violations of stringsbuilder analyzer.

* Add modernize "stringsseq" analyzer.

* Add modernize "testingcontext" analyzer.

* Add modernize "waitgroup" analyzer.

* Changelog fragment

* gofmt

* gazelle

* Add modernize "newexpr" analyzer.

* Disable newexpr until go1.26

* Add more details in WORKSPACE on how to update the override

* @nalepae feedback on min()

* gofmt

* Fix violations of forvar
2025-11-14 01:27:22 +00:00

313 lines
11 KiB
Go

package genesis_test
import (
"context"
"errors"
"os"
"path/filepath"
"testing"
"time"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
state_native "github.com/OffchainLabs/prysm/v7/beacon-chain/state/state-native"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v7/genesis"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v7/testing/require"
"github.com/OffchainLabs/prysm/v7/testing/util"
"github.com/ethereum/go-ethereum/common/hexutil"
)
func TestInitialize(t *testing.T) {
require.NoError(t, genesis.Initialize(t.Context(), "testdata"))
require.Equal(t, params.MainnetName, params.BeaconConfig().ConfigName)
}
func TestEmbeddedMainnetHardcodedValues(t *testing.T) {
// Initialize genesis with mainnet config to load embedded state
require.NoError(t, genesis.Initialize(t.Context(), t.TempDir()))
// Get the initialized genesis state
state, err := genesis.State()
require.NoError(t, err)
require.NotNil(t, state)
// Verify hardcoded validators root matches the computed value from the state
expectedValidatorsRoot := [32]byte{75, 54, 61, 185, 78, 40, 97, 32, 215, 110, 185, 5, 52, 15, 221, 78, 84, 191, 233, 240, 107, 243, 63, 246, 207, 90, 210, 127, 81, 27, 254, 149}
actualValidatorsRoot := state.GenesisValidatorsRoot()
require.Equal(t, expectedValidatorsRoot, [32]byte(actualValidatorsRoot), "hardcoded validators root does not match embedded state")
// Verify hardcoded genesis time matches the computed value from the state
expectedTime := time.Unix(1606824023, 0)
actualTime := state.GenesisTime()
require.Equal(t, expectedTime, actualTime, "hardcoded genesis time does not match embedded state")
}
// mockProvider is a test provider for genesis state
type mockProvider struct {
name string
err error
state state.BeaconState
}
func (m *mockProvider) Genesis(context.Context) (state.BeaconState, error) {
if m.err != nil {
return nil, m.err
}
return m.state, nil
}
// createTestGenesisState creates a deterministic genesis state for testing.
// This avoids using the embedded mainnet state which could cause false positives.
func createTestGenesisState(t *testing.T, numValidators uint64, slot primitives.Slot) state.BeaconState {
// Create a deterministic genesis state using test utilities
deposits, _, err := util.DeterministicDepositsAndKeys(numValidators)
require.NoError(t, err)
eth1Data, err := util.DeterministicEth1Data(len(deposits))
require.NoError(t, err)
// Create a minimal beacon state directly
pb := &ethpb.BeaconState{
Slot: slot,
GenesisTime: uint64(time.Unix(2000000000, 0).Unix()), // Use a different time than mainnet
GenesisValidatorsRoot: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32},
Eth1Data: eth1Data,
Validators: make([]*ethpb.Validator, numValidators),
Balances: make([]uint64, numValidators),
Fork: &ethpb.Fork{
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
Epoch: 0,
},
LatestBlockHeader: &ethpb.BeaconBlockHeader{
Slot: 0,
ParentRoot: make([]byte, 32),
StateRoot: make([]byte, 32),
BodyRoot: make([]byte, 32),
},
BlockRoots: make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot),
StateRoots: make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot),
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
JustificationBits: []byte{0},
PreviousJustifiedCheckpoint: &ethpb.Checkpoint{Root: make([]byte, 32)},
CurrentJustifiedCheckpoint: &ethpb.Checkpoint{Root: make([]byte, 32)},
FinalizedCheckpoint: &ethpb.Checkpoint{Root: make([]byte, 32)},
}
// Initialize validators and balances
for i := range numValidators {
pb.Validators[i] = &ethpb.Validator{
PublicKey: deposits[i].Data.PublicKey,
WithdrawalCredentials: deposits[i].Data.WithdrawalCredentials,
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
Slashed: false,
ActivationEligibilityEpoch: 0,
ActivationEpoch: 0,
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
}
pb.Balances[i] = params.BeaconConfig().MaxEffectiveBalance
}
// Initialize arrays with proper sizes
for i := 0; i < len(pb.BlockRoots); i++ {
pb.BlockRoots[i] = make([]byte, 32)
}
for i := 0; i < len(pb.StateRoots); i++ {
pb.StateRoots[i] = make([]byte, 32)
}
for i := 0; i < len(pb.RandaoMixes); i++ {
pb.RandaoMixes[i] = make([]byte, 32)
}
st, err := state_native.InitializeFromProtoUnsafePhase0(pb)
require.NoError(t, err)
return st
}
func TestInitializeWithProviders(t *testing.T) {
originalConfig := params.BeaconConfig().Copy()
defer params.OverrideBeaconConfig(originalConfig)
t.Run("providers_used_when_no_embedded_or_file", func(t *testing.T) {
// Use a custom config that won't have embedded data
customConfig := params.MainnetConfig().Copy()
customConfig.ConfigName = "test-config-no-embedded"
params.OverrideBeaconConfig(customConfig)
// Use a deterministic test state instead of mainnet to avoid false positives
testState := createTestGenesisState(t, 64, 0)
provider := &mockProvider{
state: testState,
name: "test-provider",
}
err := genesis.Initialize(t.Context(), t.TempDir(), provider)
require.NoError(t, err)
// Verify the state was stored
storedState, err := genesis.State()
require.NoError(t, err)
require.NotNil(t, storedState)
require.DeepEqual(t, testState.GenesisValidatorsRoot(), storedState.GenesisValidatorsRoot())
// Verify it's not the mainnet state
require.NotEqual(t, uint64(1606824023), storedState.GenesisTime().Unix())
})
t.Run("multiple_providers_first_success_wins", func(t *testing.T) {
// Use a custom config that won't have embedded data
customConfig := params.MainnetConfig().Copy()
customConfig.ConfigName = "test-config-multiple-providers"
params.OverrideBeaconConfig(customConfig)
// Use deterministic test states with different slots
state1 := createTestGenesisState(t, 64, 50)
state2 := createTestGenesisState(t, 32, 100)
// Create providers - first fails, second succeeds
failingProvider := &mockProvider{
err: errors.New("provider failed"),
name: "failing-provider",
}
successProvider1 := &mockProvider{
state: state1,
name: "success-provider-1",
}
successProvider2 := &mockProvider{
state: state2,
name: "success-provider-2",
}
// Initialize with multiple providers
err := genesis.Initialize(t.Context(), t.TempDir(), failingProvider, successProvider1, successProvider2)
require.NoError(t, err)
// Verify first successful provider's state was used
storedState, err := genesis.State()
require.NoError(t, err)
require.NotNil(t, storedState)
// state1 has slot 50, state2 has slot 100
require.Equal(t, primitives.Slot(50), storedState.Slot())
// Verify it's state1 by checking validator count
require.Equal(t, 64, storedState.NumValidators())
})
t.Run("all_providers_fail_returns_error", func(t *testing.T) {
// Use a custom config that won't have embedded data
customConfig := params.MinimalSpecConfig().Copy()
customConfig.ConfigName = "test-config-all-fail"
params.OverrideBeaconConfig(customConfig)
// Create failing providers
provider1 := &mockProvider{
err: errors.New("provider 1 failed"),
name: "failing-provider-1",
}
provider2 := &mockProvider{
err: errors.New("provider 2 failed"),
name: "failing-provider-2",
}
// Initialize should fail when all providers fail
err := genesis.Initialize(t.Context(), t.TempDir(), provider1, provider2)
require.ErrorIs(t, err, genesis.ErrGenesisStateNotInitialized)
})
t.Run("no_providers_and_no_data_returns_error", func(t *testing.T) {
// Use a custom config that won't have embedded data
customConfig := params.MinimalSpecConfig().Copy()
customConfig.ConfigName = "test-config-no-providers"
params.OverrideBeaconConfig(customConfig)
// Initialize with no providers should fail
err := genesis.Initialize(t.Context(), t.TempDir())
require.ErrorIs(t, err, genesis.ErrGenesisStateNotInitialized)
})
t.Run("provider_returns_nil_state", func(t *testing.T) {
// Use a custom config that won't have embedded data
customConfig := params.MinimalSpecConfig().Copy()
customConfig.ConfigName = "test-config-nil-state"
params.OverrideBeaconConfig(customConfig)
// Create provider that returns nil state
provider := &mockProvider{
state: nil,
name: "nil-state-provider",
}
// Initialize should fail
err := genesis.Initialize(t.Context(), t.TempDir(), provider)
require.ErrorIs(t, err, genesis.ErrGenesisStateNotInitialized)
})
t.Run("empty_dir_path_with_providers", func(t *testing.T) {
// Use a custom config that won't have embedded data
customConfig := params.MainnetConfig().Copy()
customConfig.ConfigName = "test-config-empty-dir"
params.OverrideBeaconConfig(customConfig)
// Use a deterministic test state
testState := createTestGenesisState(t, 16, 0)
// Create successful provider
provider := &mockProvider{
state: testState,
name: "test-provider",
}
// Initialize with empty dir should fail
err := genesis.Initialize(t.Context(), "", provider)
require.ErrorIs(t, err, genesis.ErrFilePathUnset)
})
t.Run("genesis_file_takes_precedence_over_providers", func(t *testing.T) {
// Create temp directory with genesis file
tmpDir := t.TempDir()
// Use a custom config that won't have embedded data
customConfig := params.MainnetConfig().Copy()
customConfig.ConfigName = "test-config-file-precedence"
params.OverrideBeaconConfig(customConfig)
// Create deterministic test states for file and provider
fileState := createTestGenesisState(t, 128, 75)
fileTime := time.Unix(1234567890, 0)
require.NoError(t, fileState.SetGenesisTime(fileTime))
// Save state to file with proper naming convention
marshaled, err := fileState.MarshalSSZ()
require.NoError(t, err)
gvr := fileState.GenesisValidatorsRoot()
gvrHex := hexutil.Encode(gvr)
filename := filepath.Join(tmpDir, "genesis-1234567890-"+gvrHex+".ssz")
err = os.WriteFile(filename, marshaled, 0644)
require.NoError(t, err)
// Create a provider with different state
providerState := createTestGenesisState(t, 256, 200)
provider := &mockProvider{
state: providerState,
name: "test-provider",
}
// Initialize should use file, not provider
err = genesis.Initialize(t.Context(), tmpDir, provider)
require.NoError(t, err)
// Verify file state was used, not provider state
storedState, err := genesis.State()
require.NoError(t, err)
require.NotNil(t, storedState)
// fileState has slot 75 and 128 validators, providerState has slot 200 and 256 validators
require.Equal(t, primitives.Slot(75), storedState.Slot())
require.Equal(t, 128, storedState.NumValidators())
require.Equal(t, fileTime, storedState.GenesisTime())
})
}