Files
prysm/genesis/storage.go
Bastin 92bd211e4d upgrade v6 to v7 (#15989)
* upgrade v6 to v7

* changelog

* update-go-ssz
2025-11-06 16:16:23 +00:00

180 lines
4.5 KiB
Go

package genesis
import (
"fmt"
"os"
"path"
"strconv"
"strings"
"sync"
"time"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/encoding/ssz/detect"
"github.com/OffchainLabs/prysm/v7/io/file"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
)
// ValidatorsRoot returns the genesis validators root.
func ValidatorsRoot() [32]byte {
return data.ValidatorsRoot
}
// Time returns the genesis time.
func Time() time.Time {
return data.Time
}
// State returns the full genesis BeaconState. It can return an error because this value is lazy loaded.
// The returned value will always be a copy of the underlying value.
func State() (state.BeaconState, error) {
st, err := stateInternal()
if state.IsNil(st) || err != nil {
return nil, err
}
return st.Copy(), nil
}
func stateInternal() (state.BeaconState, error) {
gd := getPkgVar()
if !gd.initialized {
// If the state is not explicitly initialized, try to load embedded states if available.
name := params.BeaconConfig().ConfigName
if ed, ok := embeddedGenesisData[name]; ok {
return ed.embeddedState()
}
return nil, ErrGenesisStateNotInitialized
}
if !state.IsNil(gd.State) {
return gd.State, nil
}
if gd.embeddedState != nil {
st, err := gd.embeddedState()
if err != nil {
return nil, errors.Wrap(err, "load embedded genesis state")
}
if !state.IsNil(st) {
gd.State = st
return gd.State, nil
}
}
return loadState()
}
// Store is an exported method that allows another package to set the genesis data value and persist it to disk.
// It is exported to be used by implementations of the Provider interface.
func Store(d GenesisData) error {
if err := ensureWritable(d.FileDir); err != nil {
return err
}
if err := persist(d); err != nil {
return errors.Wrap(err, "persist genesis data")
}
setPkgVar(d, true)
return nil
}
type fnamePart int
const (
genesisPart fnamePart = 0
timePart fnamePart = 1
gvrPart fnamePart = 2
)
// data is a private package level variable that holds the genesis data.
// Other packages interact with it via wrapper functions like Set() and State().
var data GenesisData
var stateMu sync.Mutex
// GenesisData bundles all the package level data. It is exported to allow implementations of the Provider interface to set genesis data.
type GenesisData struct {
ValidatorsRoot [32]byte
Time time.Time
FileDir string
State state.BeaconState
embeddedBytes func() ([]byte, error)
embeddedState func() (state.BeaconState, error)
initialized bool
}
// FilePath returns the full path to the genesis state file.
func (d GenesisData) FilePath() string {
parts := [3]string{}
parts[genesisPart] = "genesis"
parts[timePart] = strconv.FormatInt(d.Time.Unix(), 10)
parts[gvrPart] = hexutil.Encode(d.ValidatorsRoot[:])
return path.Join(d.FileDir, strings.Join(parts[:], "-")+".ssz")
}
func persist(d GenesisData) error {
if state.IsNil(d.State) {
return ErrGenesisStateNotInitialized
}
if d.FileDir == "" {
return ErrFilePathUnset
}
fpath := d.FilePath()
sb, err := d.State.MarshalSSZ()
if err != nil {
return errors.Wrap(err, "marshal ssz")
}
if err := file.WriteFile(fpath, sb); err != nil {
return fmt.Errorf("error writing genesis state to %s: %w", fpath, err)
}
log.WithField("filePath", fpath).Info("Genesis state written to disk.")
return nil
}
func getPkgVar() GenesisData {
stateMu.Lock()
defer stateMu.Unlock()
return data
}
func setPkgVar(d GenesisData, initialized bool) {
stateMu.Lock()
defer stateMu.Unlock()
d.initialized = initialized
data = d
}
func loadState() (state.BeaconState, error) {
stateMu.Lock()
defer stateMu.Unlock()
s, err := stateFromFile(data.FilePath())
if err != nil {
return nil, errors.Wrapf(err, "InitializeFromProtoUnsafePhase0")
}
data.State = s
return data.State, nil
}
func stateFromFile(fpath string) (state.BeaconState, error) {
sb, err := file.ReadFileAsBytes(fpath)
if err != nil {
return nil, errors.Wrapf(err, "error reading genesis state from %s", fpath)
}
return detect.UnmarshalState(sb)
}
func ensureWritable(dir string) (err error) {
if dir == "" {
return ErrFilePathUnset
}
if err := file.MkdirAll(dir); err != nil {
return errors.Wrapf(err, "error creating genesis data directory %s", dir)
}
lockPath := path.Join(dir, "genesis.lock")
defer func() {
if err == nil {
err = os.Remove(lockPath)
}
}()
return os.WriteFile(lockPath, []byte{1}, 0600)
}