hdiff restart-support: persist and validate exponents metadata

This commit is contained in:
Preston Van Loon
2026-02-04 15:15:06 -06:00
parent 7df09b12a5
commit 25b3ee468f
3 changed files with 83 additions and 3 deletions

View File

@@ -7,9 +7,11 @@ import (
"fmt"
"os"
"path"
"slices"
"time"
"github.com/OffchainLabs/prysm/v7/beacon-chain/db/iface"
"github.com/OffchainLabs/prysm/v7/cmd/beacon-chain/flags"
"github.com/OffchainLabs/prysm/v7/config/features"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
@@ -223,6 +225,22 @@ func (kv *Store) startStateDiff(ctx context.Context) error {
}
if hasOffset {
storedExponents, err := kv.loadStateDiffExponents()
if err != nil {
return errors.Wrap(err, "state-diff metadata missing or invalid; re-sync required")
}
currentExponents := flags.Get().StateDiffExponents
if !slices.Equal(storedExponents, currentExponents) {
return fmt.Errorf(
"state-diff exponents changed; database incompatible. "+
"Database was initialized with: %v. "+
"Current configuration: %v. "+
"Options: use original exponents (--state-diff-exponents=%s) or delete database and re-sync from genesis/checkpoint.",
storedExponents,
currentExponents,
formatStateDiffExponents(storedExponents),
)
}
// Existing state-diff database - restarts not yet supported.
return errors.New("restarting with existing state-diff database not yet supported")
}

View File

@@ -5,6 +5,7 @@ import (
"encoding/binary"
"errors"
"fmt"
"strings"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
statenative "github.com/OffchainLabs/prysm/v7/beacon-chain/state/state-native"
@@ -61,6 +62,38 @@ func decodeStateDiffExponents(encoded []byte) ([]int, error) {
return exponents, nil
}
func formatStateDiffExponents(exponents []int) string {
if len(exponents) == 0 {
return ""
}
parts := make([]string, len(exponents))
for i, exp := range exponents {
parts[i] = fmt.Sprintf("%d", exp)
}
return strings.Join(parts, ",")
}
func (s *Store) loadStateDiffExponents() ([]int, error) {
var encoded []byte
err := s.db.View(func(tx *bbolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bbolt.ErrBucketNotFound
}
value := bucket.Get(exponentsKey)
if value == nil {
return errors.New("state diff exponents not found")
}
encoded = make([]byte, len(value))
copy(encoded, value)
return nil
})
if err != nil {
return nil, err
}
return decodeStateDiffExponents(encoded)
}
func makeKeyForStateDiffTree(level int, slot uint64) []byte {
buf := make([]byte, 16)
buf[0] = byte(level)
@@ -190,8 +223,13 @@ func (s *Store) initializeStateDiff(slot primitives.Slot, initialState state.Rea
return nil
}
}
// Write offset directly to the database (without using cache which doesn't exist yet).
err := s.db.Update(func(tx *bbolt.Tx) error {
exponentsBytes, err := encodeStateDiffExponents(flags.Get().StateDiffExponents)
if err != nil {
return pkgerrors.Wrap(err, "failed to encode state diff exponents")
}
// Write metadata directly to the database (without using cache which doesn't exist yet).
err = s.db.Update(func(tx *bbolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bbolt.ErrBucketNotFound
@@ -199,7 +237,10 @@ func (s *Store) initializeStateDiff(slot primitives.Slot, initialState state.Rea
offsetBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(offsetBytes, uint64(slot))
return bucket.Put(offsetKey, offsetBytes)
if err := bucket.Put(offsetKey, offsetBytes); err != nil {
return err
}
return bucket.Put(exponentsKey, exponentsBytes)
})
if err != nil {
return pkgerrors.Wrap(err, "failed to set offset")

View File

@@ -9,6 +9,7 @@ import (
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
"github.com/OffchainLabs/prysm/v7/cmd/beacon-chain/flags"
"github.com/OffchainLabs/prysm/v7/config/features"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v7/math"
@@ -75,6 +76,26 @@ func TestStateDiff_EncodeDecodeExponents(t *testing.T) {
})
}
func TestStateDiff_InitializeStoresExponents(t *testing.T) {
setDefaultStateDiffExponents()
resetCfg := features.InitWithReset(&features.Flags{EnableStateDiff: true})
defer resetCfg()
db := setupDB(t)
st, _ := createState(t, 0, version.Phase0)
require.NoError(t, db.initializeStateDiff(0, st))
stored, err := db.loadStateDiffExponents()
require.NoError(t, err)
require.DeepEqual(t, flags.Get().StateDiffExponents, stored)
}
func TestStateDiff_LoadExponentsMissing(t *testing.T) {
db := setupDB(t)
_, err := db.loadStateDiffExponents()
require.ErrorContains(t, "exponents not found", err)
}
func TestStateDiff_ComputeLevel(t *testing.T) {
db := setupDB(t)
setDefaultStateDiffExponents()