Compare commits

...

24 Commits

Author SHA1 Message Date
Bastin
ff6bdd2304 fix conflicts 2025-05-09 17:32:20 +02:00
Bastin
00908ce586 draft - add readstate 2025-05-09 17:29:27 +02:00
Bastin
f95cd9edb6 draft - add readstate 2025-05-09 17:17:33 +02:00
Bastin
1ae2c53329 Merge branch 'develop' into db-hdiff 2025-05-09 10:58:18 +02:00
Bastin
ebe53ed849 draft - add saveHdiff 2025-05-09 10:57:29 +02:00
Bastin
daf5139917 draft - add saveHdiff 2025-05-09 10:57:29 +02:00
Bastin
4077329804 draft - add readState 2025-05-09 10:57:29 +02:00
Bastin
0e1ddfb403 draft - add readState 2025-05-09 10:57:29 +02:00
Bastin
58c85bfab7 draft - add saveHdiff 2025-05-09 10:57:28 +02:00
Bastin
f4e6bc73c7 add saveFullSnapshot 2025-05-09 10:54:39 +02:00
Bastin
06dff01bc8 deps 2025-05-09 10:54:39 +02:00
Bastin
e94628d302 add writeState 2025-05-09 10:54:39 +02:00
Potuz
f1596b6c47 use pointers instead of structures 2025-05-08 17:27:16 -03:00
Potuz
49ff1ae771 Add function to compute diffs 2025-05-08 14:02:58 -03:00
Bastin
a13a5604ac draft - add saveHdiff 2025-05-08 19:02:57 +02:00
Bastin
7071b6998d draft - add saveHdiff 2025-05-08 17:28:18 +02:00
Bastin
4cbf791332 draft - add readState 2025-05-08 15:04:47 +02:00
Bastin
6c859c33d6 draft - add readState 2025-05-07 23:13:14 +02:00
potuz
c29a8708fb Add state diff native types
Adds a state diff native type and marshalling functions for
serialization.
2025-05-07 16:52:29 -03:00
Bastin
0f3527971a draft - add saveHdiff 2025-05-07 14:47:52 +02:00
Bastin
d404e47dff add saveFullSnapshot 2025-05-06 17:55:28 +02:00
Bastin
2b164df38d deps 2025-05-06 14:59:32 +02:00
Bastin
81020ca939 add writeState 2025-05-06 14:59:16 +02:00
potuz
95e4ff4dc2 Add state diff native types
Adds a state diff native type and marshalling functions for
serialization.
2025-05-05 20:15:18 -03:00
15 changed files with 2299 additions and 0 deletions

View File

@@ -41,6 +41,8 @@ type ReadOnlyDatabase interface {
StateSummary(ctx context.Context, blockRoot [32]byte) (*ethpb.StateSummary, error)
HasStateSummary(ctx context.Context, blockRoot [32]byte) bool
HighestSlotStatesBelow(ctx context.Context, slot primitives.Slot) ([]state.ReadOnlyBeaconState, error)
// StateDiff related methods.
StateDiff(ctx context.Context, slot primitives.Slot) (state.BeaconState, error)
// Checkpoint operations.
JustifiedCheckpoint(ctx context.Context) (*ethpb.Checkpoint, error)
FinalizedCheckpoint(ctx context.Context) (*ethpb.Checkpoint, error)
@@ -83,6 +85,8 @@ type NoHeadAccessDatabase interface {
DeleteStates(ctx context.Context, blockRoots [][32]byte) error
SaveStateSummary(ctx context.Context, summary *ethpb.StateSummary) error
SaveStateSummaries(ctx context.Context, summaries []*ethpb.StateSummary) error
// Statediff related methods.
SaveStateDiff(ctx context.Context, state state.ReadOnlyBeaconState) error
// Checkpoint operations.
SaveJustifiedCheckpoint(ctx context.Context, checkpoint *ethpb.Checkpoint) error
SaveFinalizedCheckpoint(ctx context.Context, checkpoint *ethpb.Checkpoint) error

View File

@@ -25,6 +25,7 @@ go_library(
"migration_state_validators.go",
"schema.go",
"state.go",
"state_diff.go",
"state_summary.go",
"state_summary_cache.go",
"utils.go",
@@ -44,6 +45,7 @@ go_library(
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/hdiff:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/light-client:go_default_library",
"//consensus-types/primitives:go_default_library",
@@ -94,6 +96,7 @@ go_test(
"migration_archived_index_test.go",
"migration_block_slot_index_test.go",
"migration_state_validators_test.go",
"state_diff_test.go",
"state_summary_test.go",
"state_test.go",
"utils_test.go",
@@ -116,6 +119,7 @@ go_test(
"//consensus-types/light-client:go_default_library",
"//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library",
"//math:go_default_library",
"//proto/dbval:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",

View File

@@ -112,6 +112,7 @@ var Buckets = [][]byte{
lightClientUpdatesBucket,
lightClientBootstrapBucket,
lightClientSyncCommitteeBucket,
stateDiffBucket,
// Indices buckets.
blockSlotIndicesBucket,
stateSlotIndicesBucket,

View File

@@ -16,6 +16,7 @@ var (
stateValidatorsBucket = []byte("state-validators")
feeRecipientBucket = []byte("fee-recipient")
registrationBucket = []byte("registration")
stateDiffBucket = []byte("state-diff")
// Light Client Updates Bucket
lightClientUpdatesBucket = []byte("light-client-updates")

View File

@@ -0,0 +1,272 @@
package kv
import (
"context"
"github.com/OffchainLabs/prysm/v6/beacon-chain/state"
"github.com/OffchainLabs/prysm/v6/config/params"
"github.com/OffchainLabs/prysm/v6/consensus-types/hdiff"
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v6/math"
"github.com/OffchainLabs/prysm/v6/monitoring/tracing/trace"
"github.com/pkg/errors"
bolt "go.etcd.io/bbolt"
)
/*
We use a level-based approach to save state diffs. The levels are 0-6, where each level corresponds to an exponent of 2 (exponents[lvl]).
The data at level 0 is saved every 2**exponent[0] slots and always contains a full state snapshot that is used as a base for the delta saved at other levels.
*/
var (
anchorCache = make(map[int]state.ReadOnlyBeaconState, len(params.StateHierarchyExponents())) // cache full states at the last node at each level
)
// SaveStateDiff takes a state and decides between saving a full state snapshot or a diff.
func (s *Store) SaveStateDiff(ctx context.Context, st state.ReadOnlyBeaconState) error {
ctx, span := trace.StartSpan(ctx, "BeaconDB.SaveStateDiff")
defer span.End()
slot := st.Slot()
offset, err := s.loadOrInitOffset(slot)
if err != nil {
return err
}
if uint64(slot) < offset {
return ErrSlotBeforeOffset
}
// Find the level to save the state.
lvl := computeLevel(offset, slot)
if lvl == -1 {
return nil
}
// Save full state if level is 0.
if lvl == 0 {
return s.saveFullSnapshot(lvl, st)
}
// Get anchor state to compute the diff from.
anchorState, err := s.getAnchorState(offset, lvl, slot)
if err != nil {
return err
}
err = s.saveHdiff(lvl, anchorState, st)
if err != nil {
return err
}
return nil
}
// StateDiff retrieves the full state for a given slot.
func (s *Store) StateDiff(ctx context.Context, slot primitives.Slot) (state.BeaconState, error) {
offset, err := s.getOffset()
if err != nil {
return nil, err
}
if uint64(slot) < offset {
return nil, ErrSlotBeforeOffset
}
snapshot, diffChain, err := s.getBaseAndDiffChain(offset, slot)
// TODO: apply the diff chain to the snapshot and return the final state.
return nil, nil
}
// SaveHdiff computes the diff between the anchor state and the current state and saves it to the database.
func (s *Store) saveHdiff(lvl int, anchor, st state.ReadOnlyBeaconState) error {
slot := uint64(st.Slot())
key := makeKey(lvl, slot)
// TODO: compute actual diff
diff := hdiff.HdiffSerialized{}
err := s.db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bolt.ErrBucketNotFound
}
// TODO: save the diff bytes per field with suffix
buf := make([]byte, len(key)+len("_s"))
copy(buf, key)
copy(buf[len(key):], "_s")
if err := bucket.Put(buf, diff); err != nil {
return err
}
copy(buf[len(key):], "_v")
if err := bucket.Put(buf, diff); err != nil {
return err
}
copy(buf[len(key):], "_b")
if err := bucket.Put(buf, diff); err != nil {
return err
}
return nil
})
if err != nil {
return err
}
// Save the full state to the cache (if not the last level).
if lvl != len(params.StateHierarchyExponents())-1 {
anchorCache[lvl] = st
}
return nil
}
// SaveFullSnapshot saves the full level 0 state snapshot to the database.
func (s *Store) saveFullSnapshot(lvl int, st state.ReadOnlyBeaconState) error {
slot := uint64(st.Slot())
key := makeKey(lvl, slot)
stateBytes, err := st.MarshalSSZ()
// add version key to value
enc, err := addKey(st.Version(), stateBytes)
if err != nil {
return err
}
err = s.db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bolt.ErrBucketNotFound
}
if err := bucket.Put(key, enc); err != nil {
return err
}
return nil
})
if err != nil {
return err
}
// Save the full state to the cache.
anchorCache[lvl] = st
return nil
}
func addKey(v int, bytes []byte) ([]byte, error) {
key, err := keyForSnapshot(v)
if err != nil {
return nil, err
}
enc := make([]byte, len(key)+len(bytes))
copy(enc, key)
copy(enc[len(key):], bytes)
return enc, nil
}
func (s *Store) getBaseAndDiffChain(offset uint64, slot primitives.Slot) (state.BeaconState, []*hdiff.HdiffSerialized, error) {
rel := uint64(slot) - offset
lvl := computeLevel(offset, slot)
if lvl == -1 {
return nil, nil, errors.New("slot not in tree")
}
exponents := params.StateHierarchyExponents()
baseSpan := math.PowerOf2(exponents[0])
baseAnchorSlot := (rel / baseSpan * baseSpan) + offset
var diffChainIndices []uint64
for i := 1; i < lvl; i++ {
span := math.PowerOf2(exponents[i])
diffSlot := rel / span * span
if diffSlot == baseAnchorSlot {
continue
}
diffChainIndices = appendUnique(diffChainIndices, diffSlot+offset)
}
baseSnapshot, err := s.getFullSnapshot(lvl, baseAnchorSlot)
if err != nil {
return nil, nil, err
}
diffChain := make([]*hdiff.HdiffSerialized, len(diffChainIndices))
for _, diffSlot := range diffChainIndices {
diff, err := s.getDiff(computeLevel(offset, primitives.Slot(diffSlot)), diffSlot)
if err != nil {
return nil, nil, err
}
diffChain = append(diffChain, diff)
}
return baseSnapshot, diffChain, nil
}
func (s *Store) getDiff(lvl int, slot uint64) (*hdiff.HdiffSerialized, error) {
key := makeKey(lvl, slot)
var stateDiff []byte
var validatorDiff []byte
var balancesDiff []byte
err := s.db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bolt.ErrBucketNotFound
}
buf := make([]byte, len(key)+len("_s"))
copy(buf, key)
copy(buf[len(key):], "_s")
stateDiff = bucket.Get(buf)
if stateDiff == nil {
return errors.New("state diff not found")
}
copy(buf[len(key):], "_v")
validatorDiff = bucket.Get(buf)
if validatorDiff == nil {
return errors.New("validator diff not found")
}
copy(buf[len(key):], "_b")
balancesDiff = bucket.Get(buf)
if balancesDiff == nil {
return errors.New("balances diff not found")
}
return nil
})
if err != nil {
return nil, err
}
return &hdiff.HdiffSerialized{}, nil
}
func (s *Store) getFullSnapshot(lvl int, slot uint64) (state.BeaconState, error) {
key := makeKey(lvl, slot)
var enc []byte
err := s.db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bolt.ErrBucketNotFound
}
enc = bucket.Get(key)
if enc == nil {
return errors.New("state not found")
}
return nil
})
if err != nil {
return nil, err
}
return s.decodeStateSnapshot(enc)
}
func appendUnique(s []uint64, v uint64) []uint64 {
for _, x := range s {
if x == v {
return s
}
}
return append(s, v)
}

View File

@@ -0,0 +1,167 @@
package kv
import (
"context"
"encoding/binary"
"errors"
"fmt"
"github.com/OffchainLabs/prysm/v6/beacon-chain/state"
state_native "github.com/OffchainLabs/prysm/v6/beacon-chain/state/state-native"
"github.com/OffchainLabs/prysm/v6/config/params"
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v6/math"
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v6/runtime/version"
"go.etcd.io/bbolt"
)
var (
offsetKey = []byte("offset")
ErrSlotBeforeOffset = errors.New("slot is before root offset")
)
func makeKey(level int, slot uint64) []byte {
buf := make([]byte, 1+8)
buf[0] = byte(level)
binary.BigEndian.PutUint64(buf[1:], slot)
return buf
}
func (s *Store) getAnchorState(offset uint64, lvl int, slot primitives.Slot) (anchor state.ReadOnlyBeaconState, err error) {
if lvl == 0 {
return nil, errors.New("no anchor for level 0")
}
relSlot := uint64(slot) - offset
prevExp := params.StateHierarchyExponents()[lvl-1]
span := math.PowerOf2(prevExp)
anchorSlot := primitives.Slot((relSlot / span * span) + offset)
anchorLvl := computeLevel(offset, anchorSlot)
if anchorLvl == -1 {
return nil, errors.New("could not compute anchor level")
}
// Check if we have the anchor in cache.
anchor, ok := anchorCache[anchorLvl]
if ok {
return anchor, nil
}
// If not, load it from the database.
anchor, err = s.StateDiff(context.Background(), anchorSlot)
if err != nil {
return nil, err
}
// Save it in the cache.
anchorCache[anchorLvl] = anchor
return anchor, nil
}
// ComputeLevel computes the level in the diff tree. Returns -1 in case slot should not be in tree.
func computeLevel(offset uint64, slot primitives.Slot) int {
rel := uint64(slot) - offset
for i, exp := range params.StateHierarchyExponents() {
span := math.PowerOf2(exp)
if rel%span == 0 {
return i
}
}
// If rel isnt on any of the boundaries, we should ignore saving it.
return -1
}
func (s *Store) loadOrInitOffset(slot primitives.Slot) (offset uint64, err error) {
return offset, s.db.Update(func(tx *bbolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bbolt.ErrBucketNotFound
}
offsetBytes := bucket.Get(offsetKey)
if offsetBytes != nil {
offset = binary.BigEndian.Uint64(offsetBytes)
return nil
}
offset = uint64(slot)
offsetBytes = make([]byte, 8)
binary.BigEndian.PutUint64(offsetBytes, offset)
if err := bucket.Put(offsetKey, offsetBytes); err != nil {
return err
}
return nil
})
}
func (s *Store) getOffset() (offset uint64, err error) {
return offset, s.db.View(func(tx *bbolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bbolt.ErrBucketNotFound
}
offsetBytes := bucket.Get(offsetKey)
if offsetBytes != nil {
offset = binary.BigEndian.Uint64(offsetBytes)
return nil
}
return bbolt.ErrIncompatibleValue
})
}
func keyForSnapshot(v int) ([]byte, error) {
switch v {
case version.Electra:
return ElectraKey, nil
case version.Deneb:
return denebKey, nil
case version.Capella:
return capellaKey, nil
case version.Bellatrix:
return bellatrixKey, nil
case version.Altair:
return altairKey, nil
default:
return nil, fmt.Errorf("unsupported version %s", version.String(v))
}
}
func (s *Store) decodeStateSnapshot(enc []byte) (state.BeaconState, error) {
switch {
case HasElectraKey(enc):
var electraState ethpb.BeaconStateElectra
if err := electraState.UnmarshalSSZ(enc[len(ElectraKey):]); err != nil {
return nil, err
}
return state_native.InitializeFromProtoElectra(&electraState)
case hasDenebKey(enc):
var denebState ethpb.BeaconStateDeneb
if err := denebState.UnmarshalSSZ(enc[len(denebKey):]); err != nil {
return nil, err
}
return state_native.InitializeFromProtoDeneb(&denebState)
case hasCapellaKey(enc):
var capellaState ethpb.BeaconStateCapella
if err := capellaState.UnmarshalSSZ(enc[len(capellaKey):]); err != nil {
return nil, err
}
return state_native.InitializeFromProtoCapella(&capellaState)
case hasBellatrixKey(enc):
var bellatrixState ethpb.BeaconStateBellatrix
if err := bellatrixState.UnmarshalSSZ(enc[len(bellatrixKey):]); err != nil {
return nil, err
}
return state_native.InitializeFromProtoBellatrix(&bellatrixState)
case hasAltairKey(enc):
var altairState ethpb.BeaconStateAltair
if err := altairState.UnmarshalSSZ(enc[len(altairKey):]); err != nil {
return nil, err
}
return state_native.InitializeFromProtoAltair(&altairState)
default:
return nil, fmt.Errorf("unsupported encoding %x", enc)
}
}

View File

@@ -0,0 +1,163 @@
package kv
import (
"testing"
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v6/math"
"github.com/OffchainLabs/prysm/v6/testing/require"
"github.com/OffchainLabs/prysm/v6/testing/util"
"go.etcd.io/bbolt"
)
func TestStateDiff_LoadOrInitOffset(t *testing.T) {
db := setupDB(t)
offset, err := loadOrInitOffset(db, 10)
require.NoError(t, err)
require.Equal(t, uint64(10), offset)
offset, err = loadOrInitOffset(db, 20)
require.NoError(t, err)
require.Equal(t, uint64(10), offset)
offset, err = loadOrInitOffset(db, 5)
require.NoError(t, err)
require.Equal(t, uint64(10), offset)
}
func TestStateDiff_ComputeLevel(t *testing.T) {
db := setupDB(t)
offset, err := loadOrInitOffset(db, 0)
require.NoError(t, err)
require.Equal(t, uint64(0), offset)
// 2 ** 21
lvl, shouldSave := computeLevel(math.PowerOf2(21))
require.Equal(t, 0, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 21 * 3
lvl, shouldSave = computeLevel(math.PowerOf2(21) * 3)
require.Equal(t, 0, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 18
lvl, shouldSave = computeLevel(math.PowerOf2(18))
require.Equal(t, 1, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 18 * 3
lvl, shouldSave = computeLevel(math.PowerOf2(18) * 3)
require.Equal(t, 1, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 16
lvl, shouldSave = computeLevel(math.PowerOf2(16))
require.Equal(t, 2, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 16 * 3
lvl, shouldSave = computeLevel(math.PowerOf2(16) * 3)
require.Equal(t, 2, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 13
lvl, shouldSave = computeLevel(math.PowerOf2(13))
require.Equal(t, 3, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 13 * 3
lvl, shouldSave = computeLevel(math.PowerOf2(13) * 3)
require.Equal(t, 3, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 11
lvl, shouldSave = computeLevel(math.PowerOf2(11))
require.Equal(t, 4, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 11 * 3
lvl, shouldSave = computeLevel(math.PowerOf2(11) * 3)
require.Equal(t, 4, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 9
lvl, shouldSave = computeLevel(math.PowerOf2(9))
require.Equal(t, 5, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 9 * 3
lvl, shouldSave = computeLevel(math.PowerOf2(9) * 3)
require.Equal(t, 5, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 5
lvl, shouldSave = computeLevel(math.PowerOf2(5))
require.Equal(t, 6, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 5 * 3
lvl, shouldSave = computeLevel(math.PowerOf2(5) * 3)
require.Equal(t, 6, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 7
lvl, shouldSave = computeLevel(math.PowerOf2(7))
require.Equal(t, 6, lvl)
require.Equal(t, true, shouldSave)
// 2 ** 5 + 1
lvl, shouldSave = computeLevel(math.PowerOf2(5) + 1)
require.Equal(t, false, shouldSave)
// 2 ** 5 + 16
lvl, shouldSave = computeLevel(math.PowerOf2(5) + 16)
require.Equal(t, false, shouldSave)
// 2 ** 5 + 32
lvl, shouldSave = computeLevel(math.PowerOf2(5) + 32)
require.Equal(t, true, shouldSave)
require.Equal(t, 6, lvl)
}
func TestStateDiff_SaveFullSnapshot(t *testing.T) {
db := setupDB(t)
// Set offset to zero
offset, err := loadOrInitOffset(db, 0)
require.NoError(t, err)
require.Equal(t, uint64(0), offset)
// Create state with slot 2**21 * 3
st, err := util.NewBeaconStateElectra()
require.NoError(t, err)
slot := primitives.Slot(math.PowerOf2(21))
err = st.SetSlot(slot)
require.NoError(t, err)
stssz, err := st.MarshalSSZ()
require.NoError(t, err)
err = db.SaveStateDiff(nil, st)
require.NoError(t, err)
err = db.db.View(func(tx *bbolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bbolt.ErrBucketNotFound
}
for i := 6; i >= 0; i-- {
s := bucket.Get(makeKey(i, uint64(slot)))
require.NotNil(t, s, "key not found")
if i > 0 {
require.DeepEqual(t, EmptyNodeMarker, s, "node not marked as empty")
} else {
require.DeepSSZEqual(t, stssz, s, "retrieved state does not match saved state")
}
}
return nil
})
require.NoError(t, err)
}

View File

@@ -0,0 +1,3 @@
### Added
- Add native state diff type and marshalling functions

View File

@@ -0,0 +1,9 @@
package params
var (
stateHierarchyExponents = []uint64{21, 18, 16, 13, 11, 9, 5}
)
func StateHierarchyExponents() []uint64 {
return stateHierarchyExponents
}

View File

@@ -40,6 +40,12 @@ func NewWrappedExecutionData(v proto.Message) (interfaces.ExecutionData, error)
case *enginev1.ExecutionBundleElectra:
// note: no payload changes in electra so using deneb
return WrappedExecutionPayloadDeneb(pbStruct.Payload)
case *enginev1.ExecutionPayloadHeader:
return WrappedExecutionPayloadHeader(pbStruct)
case *enginev1.ExecutionPayloadHeaderCapella:
return WrappedExecutionPayloadHeaderCapella(pbStruct)
case *enginev1.ExecutionPayloadHeaderDeneb:
return WrappedExecutionPayloadHeaderDeneb(pbStruct)
default:
return nil, ErrUnsupportedVersion
}

View File

@@ -0,0 +1,33 @@
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["state_diff.go"],
importpath = "github.com/OffchainLabs/prysm/v6/consensus-types/hdiff",
visibility = ["//visibility:public"],
deps = [
"//beacon-chain/state:go_default_library",
"//config/fieldparams:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/helpers:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//runtime/version:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_fastssz//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["state_diff_test.go"],
embed = [":go_default_library"],
deps = [
"//testing/require:go_default_library",
"//testing/util:go_default_library",
],
)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,60 @@
package hdiff
import (
"testing"
"github.com/OffchainLabs/prysm/v6/testing/require"
"github.com/OffchainLabs/prysm/v6/testing/util"
)
func Test_diffToState(t *testing.T) {
source, _ := util.DeterministicGenesisStateElectra(t, 256)
target := source.Copy()
require.NoError(t, target.SetSlot(source.Slot()+1))
hdiff, err := diffToState(source, target)
require.NoError(t, err)
require.Equal(t, hdiff.slot, target.Slot())
require.Equal(t, hdiff.targetVersion, target.Version())
}
func Test_kmpIndex(t *testing.T) {
intSlice := make([]*int, 10)
for i := 0; i < len(intSlice); i++ {
intSlice[i] = new(int)
*intSlice[i] = i
}
integerEquals := func(a, b *int) bool {
if a == nil && b == nil {
return true
}
if a == nil || b == nil {
return false
}
return *a == *b
}
t.Run("integer entries match", func(t *testing.T) {
source := []*int{intSlice[0], intSlice[1], intSlice[2], intSlice[3], intSlice[4]}
target := []*int{intSlice[2], intSlice[3], intSlice[4], intSlice[5], intSlice[6], intSlice[7], nil}
target = append(target, source...)
require.Equal(t, 2, kmpIndex(len(source), target, integerEquals))
})
t.Run("integer entries skipped", func(t *testing.T) {
source := []*int{intSlice[0], intSlice[1], intSlice[2], intSlice[3], intSlice[4]}
target := []*int{intSlice[2], intSlice[3], intSlice[4], intSlice[0], intSlice[5], nil}
target = append(target, source...)
require.Equal(t, 2, kmpIndex(len(source), target, integerEquals))
})
t.Run("integer entries repetitions", func(t *testing.T) {
source := []*int{intSlice[0], intSlice[1], intSlice[0], intSlice[0], intSlice[0]}
target := []*int{intSlice[0], intSlice[0], intSlice[1], intSlice[2], intSlice[5], nil}
target = append(target, source...)
require.Equal(t, 3, kmpIndex(len(source), target, integerEquals))
})
t.Run("integer entries no match", func(t *testing.T) {
source := []*int{intSlice[0], intSlice[1], intSlice[2], intSlice[3]}
target := []*int{intSlice[4], intSlice[5], intSlice[6], nil}
target = append(target, source...)
require.Equal(t, len(source), kmpIndex(len(source), target, integerEquals))
})
}

View File

@@ -0,0 +1,9 @@
load("@prysm//tools/go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["comparisons.go"],
importpath = "github.com/OffchainLabs/prysm/v6/consensus-types/helpers",
visibility = ["//visibility:public"],
deps = ["//proto/prysm/v1alpha1:go_default_library"],
)

View File

@@ -0,0 +1,109 @@
package helpers
import (
"bytes"
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
)
func ForksEqual(s, t *ethpb.Fork) bool {
if s == nil && t == nil {
return true
}
if s == nil || t == nil {
return false
}
if s.Epoch != t.Epoch {
return false
}
if !bytes.Equal(s.PreviousVersion, t.PreviousVersion) {
return false
}
return bytes.Equal(s.CurrentVersion, t.CurrentVersion)
}
func BlockHeadersEqual(s, t *ethpb.BeaconBlockHeader) bool {
if s == nil && t == nil {
return true
}
if s == nil || t == nil {
return false
}
if s.Slot != t.Slot {
return false
}
if s.ProposerIndex != t.ProposerIndex {
return false
}
if !bytes.Equal(s.ParentRoot, t.ParentRoot) {
return false
}
if !bytes.Equal(s.StateRoot, t.StateRoot) {
return false
}
return bytes.Equal(s.BodyRoot, t.BodyRoot)
}
func Eth1DataEqual(s, t *ethpb.Eth1Data) bool {
if s == nil && t == nil {
return true
}
if s == nil || t == nil {
return false
}
if !bytes.Equal(s.DepositRoot, t.DepositRoot) {
return false
}
if s.DepositCount != t.DepositCount {
return false
}
return bytes.Equal(s.BlockHash, t.BlockHash)
}
func PendingDepositsEqual(s, t *ethpb.PendingDeposit) bool {
if s == nil && t == nil {
return true
}
if s == nil || t == nil {
return false
}
if !bytes.Equal(s.PublicKey, t.PublicKey) {
return false
}
if !bytes.Equal(s.WithdrawalCredentials, t.WithdrawalCredentials) {
return false
}
if s.Amount != t.Amount {
return false
}
if !bytes.Equal(s.Signature, t.Signature) {
return false
}
return s.Slot == t.Slot
}
func PendingPartialWithdrawalsEqual(s, t *ethpb.PendingPartialWithdrawal) bool {
if s == nil && t == nil {
return true
}
if s == nil || t == nil {
return false
}
if s.Index != t.Index {
return false
}
if s.Amount != t.Amount {
return false
}
return s.WithdrawableEpoch == t.WithdrawableEpoch
}
func PendingConsolidationsEqual(s, t *ethpb.PendingConsolidation) bool {
if s == nil && t == nil {
return true
}
if s == nil || t == nil {
return false
}
return s.SourceIndex == t.SourceIndex && s.TargetIndex == t.TargetIndex
}