Files
prysm/genesis/initialize.go
kasey 84c8653a52 initialize genesis data asap at node start (#15470)
* initialize genesis data asap at node start

* add genesis validation tests with embedded state verification

* Add test for hardcoded mainnet genesis validator root and time from init() function

* Add test for UnmarshalState in encoding/ssz/detect/configfork.go

* Add tests for genesis.Initialize

* Move genesis/embedded to genesis/internal/embedded

* Gazelle / BUILD fix

* James feedback

* Fix lint

* Revert lock

---------

Co-authored-by: Kasey <kasey@users.noreply.github.com>
Co-authored-by: terence tsao <terence@prysmaticlabs.com>
Co-authored-by: Preston Van Loon <preston@pvl.dev>
2025-08-10 02:09:40 +00:00

112 lines
2.9 KiB
Go

package genesis
import (
"context"
"fmt"
"os"
"strconv"
"strings"
"time"
"github.com/OffchainLabs/prysm/v6/beacon-chain/state"
"github.com/OffchainLabs/prysm/v6/config/params"
"github.com/OffchainLabs/prysm/v6/encoding/bytesutil"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
)
// Initialize is mainly exported for the node initialization process to specify providers of the genesis data
// and the path to the local storage location via cli flags.
func Initialize(ctx context.Context, dir string, providers ...Provider) error {
emb, ok := embeddedGenesisData[params.BeaconConfig().ConfigName]
if ok {
setPkgVar(emb, true)
return nil
}
gd, err := findGenesisFile(dir)
if err == nil {
setPkgVar(gd, true)
return nil
}
if !errors.Is(err, ErrGenesisFileNotFound) {
return err
}
return initializeFromProviders(ctx, dir, providers...)
}
func initializeFromProviders(ctx context.Context, dir string, providers ...Provider) error {
for _, get := range providers {
gs, err := get.Genesis(ctx)
if err != nil {
log.WithField("provider", fmt.Sprintf("%T", get)).Warn("genesis provider failed")
continue
}
gd, err := newGenesisData(gs, dir)
if err != nil {
return errors.Wrapf(err, "new genesis data")
}
return Store(gd)
}
return ErrGenesisStateNotInitialized
}
func newGenesisData(st state.BeaconState, dir string) (GenesisData, error) {
if state.IsNil(st) {
return GenesisData{}, ErrGenesisStateNotInitialized
}
if dir == "" {
return GenesisData{}, ErrFilePathUnset
}
return GenesisData{
FileDir: dir,
State: st,
ValidatorsRoot: bytesutil.ToBytes32(st.GenesisValidatorsRoot()),
Time: st.GenesisTime(),
}, nil
}
func findGenesisFile(dir string) (GenesisData, error) {
if dir == "" {
return GenesisData{}, ErrFilePathUnset
}
files, err := os.ReadDir(dir)
if err != nil {
return GenesisData{}, fmt.Errorf("%w: %w", ErrGenesisFileNotFound, err)
}
for _, f := range files {
gd, err := tryParseFname(dir, f)
if err != nil {
continue
}
return gd, nil
}
return GenesisData{}, ErrGenesisFileNotFound
}
func tryParseFname(dir string, f os.DirEntry) (GenesisData, error) {
gd := GenesisData{FileDir: dir}
if f.IsDir() {
return gd, ErrNotGenesisStateFile
}
extParts := strings.Split(f.Name(), ".")
if len(extParts) != 2 || extParts[1] != "ssz" {
return gd, ErrNotGenesisStateFile
}
parts := strings.Split(extParts[0], "-")
if len(parts) != 3 || parts[genesisPart] != "genesis" {
return gd, ErrNotGenesisStateFile
}
ts, err := strconv.ParseInt(parts[timePart], 10, 64)
if err != nil {
return gd, errors.Wrap(err, "parse genesis time")
}
if ts < 0 {
return gd, errors.New("genesis time cannot be negative")
}
gd.Time = time.Unix(ts, 0)
if err := hexutil.UnmarshalFixedText("genesis_validators_root", []byte(parts[gvrPart]), gd.ValidatorsRoot[:]); err != nil {
return gd, errors.Wrap(err, "unmarshal genesis validators root")
}
return gd, nil
}