mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 05:47:59 -05:00
Compare commits
20 Commits
payload-ev
...
remote-key
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d063d57b8 | ||
|
|
2d242a8d09 | ||
|
|
6be1541e57 | ||
|
|
b845222ce7 | ||
|
|
5bbdebee22 | ||
|
|
26100e074d | ||
|
|
768fa0e5a1 | ||
|
|
11bb8542a4 | ||
|
|
b78c2c354b | ||
|
|
55e2001a0b | ||
|
|
c093283b1b | ||
|
|
5449fd0352 | ||
|
|
3d7f7b588b | ||
|
|
2f067c4164 | ||
|
|
81266f60af | ||
|
|
207f36065a | ||
|
|
eb9feabd6f | ||
|
|
bc0868e232 | ||
|
|
35c1ab5e88 | ||
|
|
21bb6f5258 |
@@ -948,13 +948,6 @@ func (s *Service) lateBlockTasks(ctx context.Context) {
|
||||
attribute := s.getPayloadAttribute(ctx, headState, s.CurrentSlot()+1, headRoot[:])
|
||||
// return early if we are not proposing next slot
|
||||
if attribute.IsEmpty() {
|
||||
headBlock, err := s.headBlock()
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("head_root", headRoot).Error("Unable to retrieve head block to fire payload attributes event")
|
||||
}
|
||||
// notifyForkchoiceUpdate fires the payload attribute event. But in this case, we won't
|
||||
// call notifyForkchoiceUpdate, so the event is fired here.
|
||||
go s.firePayloadAttributesEvent(s.cfg.StateNotifier.StateFeed(), headBlock, headRoot, s.CurrentSlot()+1)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -2805,6 +2805,10 @@ func TestProcessLightClientUpdate(t *testing.T) {
|
||||
require.NoError(t, s.cfg.BeaconDB.SaveHeadBlockRoot(ctx, [32]byte{1, 2}))
|
||||
|
||||
for _, testVersion := range version.All()[1:] {
|
||||
if testVersion == version.Gloas {
|
||||
// TODO(16027): Unskip light client tests for Gloas
|
||||
continue
|
||||
}
|
||||
t.Run(version.String(testVersion), func(t *testing.T) {
|
||||
l := util.NewTestLightClient(t, testVersion)
|
||||
|
||||
|
||||
@@ -90,6 +90,9 @@ func IsExecutionEnabled(st state.ReadOnlyBeaconState, body interfaces.ReadOnlyBe
|
||||
if st == nil || body == nil {
|
||||
return false, errors.New("nil state or block body")
|
||||
}
|
||||
if st.Version() >= version.Capella {
|
||||
return true, nil
|
||||
}
|
||||
if IsPreBellatrixVersion(st.Version()) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -260,11 +260,12 @@ func Test_IsExecutionBlockCapella(t *testing.T) {
|
||||
|
||||
func Test_IsExecutionEnabled(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
payload *enginev1.ExecutionPayload
|
||||
header interfaces.ExecutionData
|
||||
useAltairSt bool
|
||||
want bool
|
||||
name string
|
||||
payload *enginev1.ExecutionPayload
|
||||
header interfaces.ExecutionData
|
||||
useAltairSt bool
|
||||
useCapellaSt bool
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "use older than bellatrix state",
|
||||
@@ -331,6 +332,17 @@ func Test_IsExecutionEnabled(t *testing.T) {
|
||||
}(),
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "capella state always enabled",
|
||||
payload: emptyPayload(),
|
||||
header: func() interfaces.ExecutionData {
|
||||
h, err := emptyPayloadHeader()
|
||||
require.NoError(t, err)
|
||||
return h
|
||||
}(),
|
||||
useCapellaSt: true,
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -342,6 +354,8 @@ func Test_IsExecutionEnabled(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
if tt.useAltairSt {
|
||||
st, _ = util.DeterministicGenesisStateAltair(t, 1)
|
||||
} else if tt.useCapellaSt {
|
||||
st, _ = util.DeterministicGenesisStateCapella(t, 1)
|
||||
}
|
||||
got, err := blocks.IsExecutionEnabled(st, body)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -27,6 +27,9 @@ go_library(
|
||||
"p2p.go",
|
||||
"schema.go",
|
||||
"state.go",
|
||||
"state_diff.go",
|
||||
"state_diff_cache.go",
|
||||
"state_diff_helpers.go",
|
||||
"state_summary.go",
|
||||
"state_summary_cache.go",
|
||||
"utils.go",
|
||||
@@ -41,10 +44,12 @@ go_library(
|
||||
"//beacon-chain/db/iface:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/state-native:go_default_library",
|
||||
"//cmd/beacon-chain/flags:go_default_library",
|
||||
"//config/features:go_default_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",
|
||||
@@ -53,6 +58,7 @@ go_library(
|
||||
"//encoding/ssz/detect:go_default_library",
|
||||
"//genesis:go_default_library",
|
||||
"//io/file:go_default_library",
|
||||
"//math:go_default_library",
|
||||
"//monitoring/progress:go_default_library",
|
||||
"//monitoring/tracing:go_default_library",
|
||||
"//monitoring/tracing/trace:go_default_library",
|
||||
@@ -98,6 +104,7 @@ go_test(
|
||||
"migration_block_slot_index_test.go",
|
||||
"migration_state_validators_test.go",
|
||||
"p2p_test.go",
|
||||
"state_diff_test.go",
|
||||
"state_summary_test.go",
|
||||
"state_test.go",
|
||||
"utils_test.go",
|
||||
@@ -111,6 +118,7 @@ go_test(
|
||||
"//beacon-chain/db/iface:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//beacon-chain/state/state-native:go_default_library",
|
||||
"//cmd/beacon-chain/flags:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
@@ -120,6 +128,7 @@ go_test(
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//genesis:go_default_library",
|
||||
"//math:go_default_library",
|
||||
"//proto/dbval:go_default_library",
|
||||
"//proto/engine/v1:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
@@ -133,6 +142,7 @@ go_test(
|
||||
"@com_github_golang_snappy//:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||
"@io_bazel_rules_go//go/tools/bazel:go_default_library",
|
||||
"@io_etcd_go_bbolt//:go_default_library",
|
||||
"@org_golang_google_protobuf//proto:go_default_library",
|
||||
|
||||
@@ -2,6 +2,13 @@ package kv
|
||||
|
||||
import "bytes"
|
||||
|
||||
func hasPhase0Key(enc []byte) bool {
|
||||
if len(phase0Key) >= len(enc) {
|
||||
return false
|
||||
}
|
||||
return bytes.Equal(enc[:len(phase0Key)], phase0Key)
|
||||
}
|
||||
|
||||
// In order for an encoding to be Altair compatible, it must be prefixed with altair key.
|
||||
func hasAltairKey(enc []byte) bool {
|
||||
if len(altairKey) >= len(enc) {
|
||||
|
||||
@@ -91,6 +91,7 @@ type Store struct {
|
||||
blockCache *ristretto.Cache[string, interfaces.ReadOnlySignedBeaconBlock]
|
||||
validatorEntryCache *ristretto.Cache[[]byte, *ethpb.Validator]
|
||||
stateSummaryCache *stateSummaryCache
|
||||
stateDiffCache *stateDiffCache
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
@@ -112,6 +113,7 @@ var Buckets = [][]byte{
|
||||
lightClientUpdatesBucket,
|
||||
lightClientBootstrapBucket,
|
||||
lightClientSyncCommitteeBucket,
|
||||
stateDiffBucket,
|
||||
// Indices buckets.
|
||||
blockSlotIndicesBucket,
|
||||
stateSlotIndicesBucket,
|
||||
@@ -201,6 +203,14 @@ func NewKVStore(ctx context.Context, dirPath string, opts ...KVStoreOption) (*St
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if features.Get().EnableStateDiff {
|
||||
sdCache, err := newStateDiffCache(kv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
kv.stateDiffCache = sdCache
|
||||
}
|
||||
|
||||
return kv, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -216,6 +216,10 @@ func TestStore_LightClientUpdate_CanSaveRetrieve(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
ctx := t.Context()
|
||||
for _, testVersion := range version.All()[1:] {
|
||||
if testVersion == version.Gloas {
|
||||
// TODO(16027): Unskip light client tests for Gloas
|
||||
continue
|
||||
}
|
||||
t.Run(version.String(testVersion), func(t *testing.T) {
|
||||
update, err := createUpdate(t, testVersion)
|
||||
require.NoError(t, err)
|
||||
@@ -572,6 +576,10 @@ func TestStore_LightClientBootstrap_CanSaveRetrieve(t *testing.T) {
|
||||
require.IsNil(t, retrievedBootstrap)
|
||||
})
|
||||
for _, testVersion := range version.All()[1:] {
|
||||
if testVersion == version.Gloas {
|
||||
// TODO(16027): Unskip light client tests for Gloas
|
||||
continue
|
||||
}
|
||||
t.Run(version.String(testVersion), func(t *testing.T) {
|
||||
bootstrap, err := createDefaultLightClientBootstrap(primitives.Slot(uint64(params.BeaconConfig().VersionToForkEpochMap()[testVersion]) * uint64(params.BeaconConfig().SlotsPerEpoch)))
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -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")
|
||||
@@ -46,6 +47,7 @@ var (
|
||||
|
||||
// Below keys are used to identify objects are to be fork compatible.
|
||||
// Objects that are only compatible with specific forks should be prefixed with such keys.
|
||||
phase0Key = []byte("phase0")
|
||||
altairKey = []byte("altair")
|
||||
bellatrixKey = []byte("merge")
|
||||
bellatrixBlindKey = []byte("blind-bellatrix")
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
||||
statenative "github.com/OffchainLabs/prysm/v7/beacon-chain/state/state-native"
|
||||
"github.com/OffchainLabs/prysm/v7/config/features"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
|
||||
"github.com/OffchainLabs/prysm/v7/genesis"
|
||||
@@ -28,6 +27,17 @@ func (s *Store) State(ctx context.Context, blockRoot [32]byte) (state.BeaconStat
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.State")
|
||||
defer span.End()
|
||||
startTime := time.Now()
|
||||
|
||||
// If state diff is enabled, we get the state from the state-diff db.
|
||||
if features.Get().EnableStateDiff {
|
||||
st, err := s.getStateUsingStateDiff(ctx, blockRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stateReadingTime.Observe(float64(time.Since(startTime).Milliseconds()))
|
||||
return st, nil
|
||||
}
|
||||
|
||||
enc, err := s.stateBytes(ctx, blockRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -417,6 +427,16 @@ func (s *Store) storeValidatorEntriesSeparately(ctx context.Context, tx *bolt.Tx
|
||||
func (s *Store) HasState(ctx context.Context, blockRoot [32]byte) bool {
|
||||
_, span := trace.StartSpan(ctx, "BeaconDB.HasState")
|
||||
defer span.End()
|
||||
|
||||
if features.Get().EnableStateDiff {
|
||||
hasState, err := s.hasStateUsingStateDiff(ctx, blockRoot)
|
||||
if err != nil {
|
||||
log.WithError(err).Error(fmt.Sprintf("error checking state existence using state-diff"))
|
||||
return false
|
||||
}
|
||||
return hasState
|
||||
}
|
||||
|
||||
hasState := false
|
||||
err := s.db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(stateBucket)
|
||||
@@ -470,7 +490,7 @@ func (s *Store) DeleteState(ctx context.Context, blockRoot [32]byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
slot, err := s.slotByBlockRoot(ctx, tx, blockRoot[:])
|
||||
slot, err := s.SlotByBlockRoot(ctx, blockRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -812,50 +832,45 @@ func (s *Store) stateBytes(ctx context.Context, blockRoot [32]byte) ([]byte, err
|
||||
return dst, err
|
||||
}
|
||||
|
||||
// slotByBlockRoot retrieves the corresponding slot of the input block root.
|
||||
func (s *Store) slotByBlockRoot(ctx context.Context, tx *bolt.Tx, blockRoot []byte) (primitives.Slot, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.slotByBlockRoot")
|
||||
// SlotByBlockRoot returns the slot of the input block root, based on state summary, block, or state.
|
||||
// Check for state is only done if state diff feature is not enabled.
|
||||
func (s *Store) SlotByBlockRoot(ctx context.Context, blockRoot [32]byte) (primitives.Slot, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "BeaconDB.SlotByBlockRoot")
|
||||
defer span.End()
|
||||
|
||||
bkt := tx.Bucket(stateSummaryBucket)
|
||||
enc := bkt.Get(blockRoot)
|
||||
|
||||
if enc == nil {
|
||||
// Fall back to check the block.
|
||||
bkt := tx.Bucket(blocksBucket)
|
||||
enc := bkt.Get(blockRoot)
|
||||
|
||||
if enc == nil {
|
||||
// Fallback and check the state.
|
||||
bkt = tx.Bucket(stateBucket)
|
||||
enc = bkt.Get(blockRoot)
|
||||
if enc == nil {
|
||||
return 0, errors.New("state enc can't be nil")
|
||||
}
|
||||
// no need to construct the validator entries as it is not used here.
|
||||
s, err := s.unmarshalState(ctx, enc, nil)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not unmarshal state")
|
||||
}
|
||||
if s == nil || s.IsNil() {
|
||||
return 0, errors.New("state can't be nil")
|
||||
}
|
||||
return s.Slot(), nil
|
||||
}
|
||||
b, err := unmarshalBlock(ctx, enc)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not unmarshal block")
|
||||
}
|
||||
if err := blocks.BeaconBlockIsNil(b); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return b.Block().Slot(), nil
|
||||
// check state summary first
|
||||
stateSummary, err := s.StateSummary(ctx, blockRoot)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
stateSummary := ðpb.StateSummary{}
|
||||
if err := decode(ctx, enc, stateSummary); err != nil {
|
||||
return 0, errors.Wrap(err, "could not unmarshal state summary")
|
||||
if stateSummary != nil {
|
||||
return stateSummary.Slot, nil
|
||||
}
|
||||
return stateSummary.Slot, nil
|
||||
|
||||
// fall back to block if state summary is not found
|
||||
blk, err := s.Block(ctx, blockRoot)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if blk != nil && !blk.IsNil() {
|
||||
return blk.Block().Slot(), nil
|
||||
}
|
||||
|
||||
// fall back to state, only if state diff feature is not enabled
|
||||
if features.Get().EnableStateDiff {
|
||||
return 0, errors.New("neither state summary nor block found")
|
||||
}
|
||||
|
||||
st, err := s.State(ctx, blockRoot)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if st != nil && !st.IsNil() {
|
||||
return st.Slot(), nil
|
||||
}
|
||||
|
||||
// neither state summary, block nor state found
|
||||
return 0, errors.New("neither state summary, block nor state found")
|
||||
}
|
||||
|
||||
// HighestSlotStatesBelow returns the states with the highest slot below the input slot
|
||||
@@ -1031,3 +1046,30 @@ func (s *Store) isStateValidatorMigrationOver() (bool, error) {
|
||||
}
|
||||
return returnFlag, nil
|
||||
}
|
||||
|
||||
func (s *Store) getStateUsingStateDiff(ctx context.Context, blockRoot [32]byte) (state.BeaconState, error) {
|
||||
slot, err := s.SlotByBlockRoot(ctx, blockRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
st, err := s.stateByDiff(ctx, slot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if st == nil || st.IsNil() {
|
||||
return nil, errors.New("state not found")
|
||||
}
|
||||
|
||||
return st, nil
|
||||
}
|
||||
|
||||
func (s *Store) hasStateUsingStateDiff(ctx context.Context, blockRoot [32]byte) (bool, error) {
|
||||
slot, err := s.SlotByBlockRoot(ctx, blockRoot)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
stateLvl := computeLevel(s.getOffset(), slot)
|
||||
return stateLvl != -1, nil
|
||||
}
|
||||
|
||||
232
beacon-chain/db/kv/state_diff.go
Normal file
232
beacon-chain/db/kv/state_diff.go
Normal file
@@ -0,0 +1,232 @@
|
||||
package kv
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
||||
"github.com/OffchainLabs/prysm/v7/cmd/beacon-chain/flags"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/hdiff"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
"github.com/OffchainLabs/prysm/v7/monitoring/tracing/trace"
|
||||
"github.com/pkg/errors"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
const (
|
||||
stateSuffix = "_s"
|
||||
validatorSuffix = "_v"
|
||||
balancesSuffix = "_b"
|
||||
)
|
||||
|
||||
/*
|
||||
We use a level-based approach to save state diffs. 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.
|
||||
*/
|
||||
|
||||
// saveStateByDiff takes a state and decides between saving a full state snapshot or a diff.
|
||||
func (s *Store) saveStateByDiff(ctx context.Context, st state.ReadOnlyBeaconState) error {
|
||||
_, span := trace.StartSpan(ctx, "BeaconDB.saveStateByDiff")
|
||||
defer span.End()
|
||||
|
||||
if st == nil {
|
||||
return errors.New("state is nil")
|
||||
}
|
||||
|
||||
slot := st.Slot()
|
||||
offset := s.getOffset()
|
||||
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(st)
|
||||
}
|
||||
|
||||
// Get anchor state to compute the diff from.
|
||||
anchorState, err := s.getAnchorState(offset, lvl, slot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.saveHdiff(lvl, anchorState, st)
|
||||
}
|
||||
|
||||
// stateByDiff retrieves the full state for a given slot.
|
||||
func (s *Store) stateByDiff(ctx context.Context, slot primitives.Slot) (state.BeaconState, error) {
|
||||
offset := s.getOffset()
|
||||
if uint64(slot) < offset {
|
||||
return nil, ErrSlotBeforeOffset
|
||||
}
|
||||
|
||||
snapshot, diffChain, err := s.getBaseAndDiffChain(offset, slot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, diff := range diffChain {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
snapshot, err = hdiff.ApplyDiff(ctx, snapshot, diff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return snapshot, nil
|
||||
}
|
||||
|
||||
// saveHdiff computes the diff between the anchor state and the current state and saves it to the database.
|
||||
// This function needs to be called only with the latest finalized state, and in a strictly increasing slot order.
|
||||
func (s *Store) saveHdiff(lvl int, anchor, st state.ReadOnlyBeaconState) error {
|
||||
slot := uint64(st.Slot())
|
||||
key := makeKeyForStateDiffTree(lvl, slot)
|
||||
|
||||
diff, err := hdiff.Diff(anchor, st)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.db.Update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(stateDiffBucket)
|
||||
if bucket == nil {
|
||||
return bolt.ErrBucketNotFound
|
||||
}
|
||||
buf := append(key, stateSuffix...)
|
||||
if err := bucket.Put(buf, diff.StateDiff); err != nil {
|
||||
return err
|
||||
}
|
||||
buf = append(key, validatorSuffix...)
|
||||
if err := bucket.Put(buf, diff.ValidatorDiffs); err != nil {
|
||||
return err
|
||||
}
|
||||
buf = append(key, balancesSuffix...)
|
||||
if err := bucket.Put(buf, diff.BalancesDiff); 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(flags.Get().StateDiffExponents)-1 {
|
||||
err = s.stateDiffCache.setAnchor(lvl, st)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SaveFullSnapshot saves the full level 0 state snapshot to the database.
|
||||
func (s *Store) saveFullSnapshot(st state.ReadOnlyBeaconState) error {
|
||||
slot := uint64(st.Slot())
|
||||
key := makeKeyForStateDiffTree(0, slot)
|
||||
stateBytes, err := st.MarshalSSZ()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 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, and invalidate other levels.
|
||||
s.stateDiffCache.clearAnchors()
|
||||
err = s.stateDiffCache.setAnchor(0, st)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) getDiff(lvl int, slot uint64) (hdiff.HdiffBytes, error) {
|
||||
key := makeKeyForStateDiffTree(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 := append(key, stateSuffix...)
|
||||
stateDiff = bucket.Get(buf)
|
||||
if stateDiff == nil {
|
||||
return errors.New("state diff not found")
|
||||
}
|
||||
buf = append(key, validatorSuffix...)
|
||||
validatorDiff = bucket.Get(buf)
|
||||
if validatorDiff == nil {
|
||||
return errors.New("validator diff not found")
|
||||
}
|
||||
buf = append(key, balancesSuffix...)
|
||||
balancesDiff = bucket.Get(buf)
|
||||
if balancesDiff == nil {
|
||||
return errors.New("balances diff not found")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return hdiff.HdiffBytes{}, err
|
||||
}
|
||||
|
||||
return hdiff.HdiffBytes{
|
||||
StateDiff: stateDiff,
|
||||
ValidatorDiffs: validatorDiff,
|
||||
BalancesDiff: balancesDiff,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Store) getFullSnapshot(slot uint64) (state.BeaconState, error) {
|
||||
key := makeKeyForStateDiffTree(0, 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 decodeStateSnapshot(enc)
|
||||
}
|
||||
77
beacon-chain/db/kv/state_diff_cache.go
Normal file
77
beacon-chain/db/kv/state_diff_cache.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package kv
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
||||
"github.com/OffchainLabs/prysm/v7/cmd/beacon-chain/flags"
|
||||
"go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
type stateDiffCache struct {
|
||||
sync.RWMutex
|
||||
anchors []state.ReadOnlyBeaconState
|
||||
offset uint64
|
||||
}
|
||||
|
||||
func newStateDiffCache(s *Store) (*stateDiffCache, error) {
|
||||
var offset uint64
|
||||
|
||||
err := s.db.View(func(tx *bbolt.Tx) error {
|
||||
bucket := tx.Bucket(stateDiffBucket)
|
||||
if bucket == nil {
|
||||
return bbolt.ErrBucketNotFound
|
||||
}
|
||||
|
||||
offsetBytes := bucket.Get([]byte("offset"))
|
||||
if offsetBytes == nil {
|
||||
return errors.New("state diff cache: offset not found")
|
||||
}
|
||||
offset = binary.LittleEndian.Uint64(offsetBytes)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &stateDiffCache{
|
||||
anchors: make([]state.ReadOnlyBeaconState, len(flags.Get().StateDiffExponents)-1), // -1 because last level doesn't need to be cached
|
||||
offset: offset,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *stateDiffCache) getAnchor(level int) state.ReadOnlyBeaconState {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
return c.anchors[level]
|
||||
}
|
||||
|
||||
func (c *stateDiffCache) setAnchor(level int, anchor state.ReadOnlyBeaconState) error {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if level >= len(c.anchors) || level < 0 {
|
||||
return errors.New("state diff cache: anchor level out of range")
|
||||
}
|
||||
c.anchors[level] = anchor
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *stateDiffCache) getOffset() uint64 {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
return c.offset
|
||||
}
|
||||
|
||||
func (c *stateDiffCache) setOffset(offset uint64) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
c.offset = offset
|
||||
}
|
||||
|
||||
func (c *stateDiffCache) clearAnchors() {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
c.anchors = make([]state.ReadOnlyBeaconState, len(flags.Get().StateDiffExponents)-1) // -1 because last level doesn't need to be cached
|
||||
}
|
||||
250
beacon-chain/db/kv/state_diff_helpers.go
Normal file
250
beacon-chain/db/kv/state_diff_helpers.go
Normal file
@@ -0,0 +1,250 @@
|
||||
package kv
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
||||
statenative "github.com/OffchainLabs/prysm/v7/beacon-chain/state/state-native"
|
||||
"github.com/OffchainLabs/prysm/v7/cmd/beacon-chain/flags"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/hdiff"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
"github.com/OffchainLabs/prysm/v7/math"
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
"github.com/OffchainLabs/prysm/v7/runtime/version"
|
||||
"go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
var (
|
||||
offsetKey = []byte("offset")
|
||||
ErrSlotBeforeOffset = errors.New("slot is before root offset")
|
||||
)
|
||||
|
||||
func makeKeyForStateDiffTree(level int, slot uint64) []byte {
|
||||
buf := make([]byte, 16)
|
||||
buf[0] = byte(level)
|
||||
binary.LittleEndian.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 || lvl > len(flags.Get().StateDiffExponents) {
|
||||
return nil, errors.New("invalid value for level")
|
||||
}
|
||||
|
||||
if uint64(slot) < offset {
|
||||
return nil, ErrSlotBeforeOffset
|
||||
}
|
||||
relSlot := uint64(slot) - offset
|
||||
prevExp := flags.Get().StateDiffExponents[lvl-1]
|
||||
if prevExp < 2 || prevExp >= 64 {
|
||||
return nil, fmt.Errorf("state diff exponent %d out of range for uint64", prevExp)
|
||||
}
|
||||
span := math.PowerOf2(uint64(prevExp))
|
||||
anchorSlot := primitives.Slot(uint64(slot) - relSlot%span)
|
||||
|
||||
// anchorLvl can be [0, lvl-1]
|
||||
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 = s.stateDiffCache.getAnchor(anchorLvl)
|
||||
if anchor != nil {
|
||||
return anchor, nil
|
||||
}
|
||||
|
||||
// If not, load it from the database.
|
||||
anchor, err = s.stateByDiff(context.Background(), anchorSlot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Save it in the cache.
|
||||
err = s.stateDiffCache.setAnchor(anchorLvl, anchor)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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 flags.Get().StateDiffExponents {
|
||||
if exp < 2 || exp >= 64 {
|
||||
return -1
|
||||
}
|
||||
span := math.PowerOf2(uint64(exp))
|
||||
if rel%span == 0 {
|
||||
return i
|
||||
}
|
||||
}
|
||||
// If rel isn’t on any of the boundaries, we should ignore saving it.
|
||||
return -1
|
||||
}
|
||||
|
||||
func (s *Store) setOffset(slot primitives.Slot) error {
|
||||
err := 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 {
|
||||
return fmt.Errorf("offset already set to %d", binary.LittleEndian.Uint64(offsetBytes))
|
||||
}
|
||||
|
||||
offsetBytes = make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(offsetBytes, uint64(slot))
|
||||
if err := bucket.Put(offsetKey, offsetBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Save the offset in the cache.
|
||||
s.stateDiffCache.setOffset(uint64(slot))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) getOffset() uint64 {
|
||||
return s.stateDiffCache.getOffset()
|
||||
}
|
||||
|
||||
func keyForSnapshot(v int) ([]byte, error) {
|
||||
switch v {
|
||||
case version.Fulu:
|
||||
return fuluKey, nil
|
||||
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
|
||||
case version.Phase0:
|
||||
return phase0Key, nil
|
||||
default:
|
||||
return nil, errors.New("unsupported fork")
|
||||
}
|
||||
}
|
||||
|
||||
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 decodeStateSnapshot(enc []byte) (state.BeaconState, error) {
|
||||
switch {
|
||||
case hasFuluKey(enc):
|
||||
var fuluState ethpb.BeaconStateFulu
|
||||
if err := fuluState.UnmarshalSSZ(enc[len(fuluKey):]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return statenative.InitializeFromProtoUnsafeFulu(&fuluState)
|
||||
case HasElectraKey(enc):
|
||||
var electraState ethpb.BeaconStateElectra
|
||||
if err := electraState.UnmarshalSSZ(enc[len(ElectraKey):]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return statenative.InitializeFromProtoUnsafeElectra(&electraState)
|
||||
case hasDenebKey(enc):
|
||||
var denebState ethpb.BeaconStateDeneb
|
||||
if err := denebState.UnmarshalSSZ(enc[len(denebKey):]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return statenative.InitializeFromProtoUnsafeDeneb(&denebState)
|
||||
case hasCapellaKey(enc):
|
||||
var capellaState ethpb.BeaconStateCapella
|
||||
if err := capellaState.UnmarshalSSZ(enc[len(capellaKey):]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return statenative.InitializeFromProtoUnsafeCapella(&capellaState)
|
||||
case hasBellatrixKey(enc):
|
||||
var bellatrixState ethpb.BeaconStateBellatrix
|
||||
if err := bellatrixState.UnmarshalSSZ(enc[len(bellatrixKey):]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return statenative.InitializeFromProtoUnsafeBellatrix(&bellatrixState)
|
||||
case hasAltairKey(enc):
|
||||
var altairState ethpb.BeaconStateAltair
|
||||
if err := altairState.UnmarshalSSZ(enc[len(altairKey):]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return statenative.InitializeFromProtoUnsafeAltair(&altairState)
|
||||
case hasPhase0Key(enc):
|
||||
var phase0State ethpb.BeaconState
|
||||
if err := phase0State.UnmarshalSSZ(enc[len(phase0Key):]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return statenative.InitializeFromProtoUnsafePhase0(&phase0State)
|
||||
default:
|
||||
return nil, errors.New("unsupported fork")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Store) getBaseAndDiffChain(offset uint64, slot primitives.Slot) (state.BeaconState, []hdiff.HdiffBytes, error) {
|
||||
if uint64(slot) < offset {
|
||||
return nil, nil, ErrSlotBeforeOffset
|
||||
}
|
||||
rel := uint64(slot) - offset
|
||||
lvl := computeLevel(offset, slot)
|
||||
if lvl == -1 {
|
||||
return nil, nil, errors.New("slot not in tree")
|
||||
}
|
||||
|
||||
exponents := flags.Get().StateDiffExponents
|
||||
|
||||
baseSpan := math.PowerOf2(uint64(exponents[0]))
|
||||
baseAnchorSlot := uint64(slot) - rel%baseSpan
|
||||
|
||||
type diffItem struct {
|
||||
level int
|
||||
slot uint64
|
||||
}
|
||||
|
||||
var diffChainItems []diffItem
|
||||
lastSeenAnchorSlot := baseAnchorSlot
|
||||
for i, exp := range exponents[1 : lvl+1] {
|
||||
span := math.PowerOf2(uint64(exp))
|
||||
diffSlot := rel / span * span
|
||||
if diffSlot == lastSeenAnchorSlot {
|
||||
continue
|
||||
}
|
||||
diffChainItems = append(diffChainItems, diffItem{level: i + 1, slot: diffSlot + offset})
|
||||
lastSeenAnchorSlot = diffSlot
|
||||
}
|
||||
|
||||
baseSnapshot, err := s.getFullSnapshot(baseAnchorSlot)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
diffChain := make([]hdiff.HdiffBytes, 0, len(diffChainItems))
|
||||
for _, item := range diffChainItems {
|
||||
diff, err := s.getDiff(item.level, item.slot)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
diffChain = append(diffChain, diff)
|
||||
}
|
||||
|
||||
return baseSnapshot, diffChain, nil
|
||||
}
|
||||
662
beacon-chain/db/kv/state_diff_test.go
Normal file
662
beacon-chain/db/kv/state_diff_test.go
Normal file
@@ -0,0 +1,662 @@
|
||||
package kv
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
||||
"github.com/OffchainLabs/prysm/v7/cmd/beacon-chain/flags"
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
"github.com/OffchainLabs/prysm/v7/math"
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
"github.com/OffchainLabs/prysm/v7/runtime/version"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/require"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/util"
|
||||
"go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
func TestStateDiff_LoadOrInitOffset(t *testing.T) {
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
db := setupDB(t)
|
||||
err := setOffsetInDB(db, 10)
|
||||
require.NoError(t, err)
|
||||
offset := db.getOffset()
|
||||
require.Equal(t, uint64(10), offset)
|
||||
|
||||
err = db.setOffset(10)
|
||||
require.ErrorContains(t, "offset already set", err)
|
||||
offset = db.getOffset()
|
||||
require.Equal(t, uint64(10), offset)
|
||||
}
|
||||
|
||||
func TestStateDiff_ComputeLevel(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
err := setOffsetInDB(db, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
offset := db.getOffset()
|
||||
|
||||
// 2 ** 21
|
||||
lvl := computeLevel(offset, primitives.Slot(math.PowerOf2(21)))
|
||||
require.Equal(t, 0, lvl)
|
||||
|
||||
// 2 ** 21 * 3
|
||||
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(21)*3))
|
||||
require.Equal(t, 0, lvl)
|
||||
|
||||
// 2 ** 18
|
||||
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(18)))
|
||||
require.Equal(t, 1, lvl)
|
||||
|
||||
// 2 ** 18 * 3
|
||||
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(18)*3))
|
||||
require.Equal(t, 1, lvl)
|
||||
|
||||
// 2 ** 16
|
||||
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(16)))
|
||||
require.Equal(t, 2, lvl)
|
||||
|
||||
// 2 ** 16 * 3
|
||||
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(16)*3))
|
||||
require.Equal(t, 2, lvl)
|
||||
|
||||
// 2 ** 13
|
||||
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(13)))
|
||||
require.Equal(t, 3, lvl)
|
||||
|
||||
// 2 ** 13 * 3
|
||||
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(13)*3))
|
||||
require.Equal(t, 3, lvl)
|
||||
|
||||
// 2 ** 11
|
||||
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(11)))
|
||||
require.Equal(t, 4, lvl)
|
||||
|
||||
// 2 ** 11 * 3
|
||||
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(11)*3))
|
||||
require.Equal(t, 4, lvl)
|
||||
|
||||
// 2 ** 9
|
||||
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(9)))
|
||||
require.Equal(t, 5, lvl)
|
||||
|
||||
// 2 ** 9 * 3
|
||||
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(9)*3))
|
||||
require.Equal(t, 5, lvl)
|
||||
|
||||
// 2 ** 5
|
||||
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(5)))
|
||||
require.Equal(t, 6, lvl)
|
||||
|
||||
// 2 ** 5 * 3
|
||||
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(5)*3))
|
||||
require.Equal(t, 6, lvl)
|
||||
|
||||
// 2 ** 7
|
||||
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(7)))
|
||||
require.Equal(t, 6, lvl)
|
||||
|
||||
// 2 ** 5 + 1
|
||||
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(5)+1))
|
||||
require.Equal(t, -1, lvl)
|
||||
|
||||
// 2 ** 5 + 16
|
||||
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(5)+16))
|
||||
require.Equal(t, -1, lvl)
|
||||
|
||||
// 2 ** 5 + 32
|
||||
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(5)+32))
|
||||
require.Equal(t, 6, lvl)
|
||||
|
||||
}
|
||||
|
||||
func TestStateDiff_SaveFullSnapshot(t *testing.T) {
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
for v := range version.All() {
|
||||
t.Run(version.String(v), func(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
|
||||
// Create state with slot 0
|
||||
st, enc := createState(t, 0, v)
|
||||
|
||||
err := setOffsetInDB(db, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = db.db.View(func(tx *bbolt.Tx) error {
|
||||
bucket := tx.Bucket(stateDiffBucket)
|
||||
if bucket == nil {
|
||||
return bbolt.ErrBucketNotFound
|
||||
}
|
||||
s := bucket.Get(makeKeyForStateDiffTree(0, uint64(0)))
|
||||
if s == nil {
|
||||
return bbolt.ErrIncompatibleValue
|
||||
}
|
||||
require.DeepSSZEqual(t, enc, s)
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateDiff_SaveAndReadFullSnapshot(t *testing.T) {
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
for v := range version.All() {
|
||||
t.Run(version.String(v), func(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
|
||||
st, _ := createState(t, 0, v)
|
||||
|
||||
err := setOffsetInDB(db, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
|
||||
readSt, err := db.stateByDiff(context.Background(), 0)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, readSt)
|
||||
|
||||
stSSZ, err := st.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
readStSSZ, err := readSt.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, stSSZ, readStSSZ)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateDiff_SaveDiff(t *testing.T) {
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
for v := range version.All() {
|
||||
t.Run(version.String(v), func(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
|
||||
// Create state with slot 2**21
|
||||
slot := primitives.Slot(math.PowerOf2(21))
|
||||
st, enc := createState(t, slot, v)
|
||||
|
||||
err := setOffsetInDB(db, uint64(slot))
|
||||
require.NoError(t, err)
|
||||
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = db.db.View(func(tx *bbolt.Tx) error {
|
||||
bucket := tx.Bucket(stateDiffBucket)
|
||||
if bucket == nil {
|
||||
return bbolt.ErrBucketNotFound
|
||||
}
|
||||
s := bucket.Get(makeKeyForStateDiffTree(0, uint64(slot)))
|
||||
if s == nil {
|
||||
return bbolt.ErrIncompatibleValue
|
||||
}
|
||||
require.DeepSSZEqual(t, enc, s)
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// create state with slot 2**18 (+2**21)
|
||||
slot = primitives.Slot(math.PowerOf2(18) + math.PowerOf2(21))
|
||||
st, _ = createState(t, slot, v)
|
||||
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
|
||||
key := makeKeyForStateDiffTree(1, uint64(slot))
|
||||
err = db.db.View(func(tx *bbolt.Tx) error {
|
||||
bucket := tx.Bucket(stateDiffBucket)
|
||||
if bucket == nil {
|
||||
return bbolt.ErrBucketNotFound
|
||||
}
|
||||
buf := append(key, "_s"...)
|
||||
s := bucket.Get(buf)
|
||||
if s == nil {
|
||||
return bbolt.ErrIncompatibleValue
|
||||
}
|
||||
buf = append(key, "_v"...)
|
||||
v := bucket.Get(buf)
|
||||
if v == nil {
|
||||
return bbolt.ErrIncompatibleValue
|
||||
}
|
||||
buf = append(key, "_b"...)
|
||||
b := bucket.Get(buf)
|
||||
if b == nil {
|
||||
return bbolt.ErrIncompatibleValue
|
||||
}
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateDiff_SaveAndReadDiff(t *testing.T) {
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
for v := range version.All() {
|
||||
t.Run(version.String(v), func(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
|
||||
st, _ := createState(t, 0, v)
|
||||
|
||||
err := setOffsetInDB(db, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
|
||||
slot := primitives.Slot(math.PowerOf2(5))
|
||||
st, _ = createState(t, slot, v)
|
||||
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
|
||||
readSt, err := db.stateByDiff(context.Background(), slot)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, readSt)
|
||||
|
||||
stSSZ, err := st.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
readStSSZ, err := readSt.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, stSSZ, readStSSZ)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateDiff_SaveAndReadDiff_WithRepetitiveAnchorSlots(t *testing.T) {
|
||||
globalFlags := flags.GlobalFlags{
|
||||
StateDiffExponents: []int{20, 14, 10, 7, 5},
|
||||
}
|
||||
flags.Init(&globalFlags)
|
||||
|
||||
for v := range version.All() {
|
||||
t.Run(version.String(v), func(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
|
||||
err := setOffsetInDB(db, 0)
|
||||
|
||||
st, _ := createState(t, 0, v)
|
||||
require.NoError(t, err)
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
|
||||
slot := primitives.Slot(math.PowerOf2(11))
|
||||
st, _ = createState(t, slot, v)
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
|
||||
slot = primitives.Slot(math.PowerOf2(11) + math.PowerOf2(5))
|
||||
st, _ = createState(t, slot, v)
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
|
||||
readSt, err := db.stateByDiff(context.Background(), slot)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, readSt)
|
||||
|
||||
stSSZ, err := st.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
readStSSZ, err := readSt.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, stSSZ, readStSSZ)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateDiff_SaveAndReadDiff_MultipleLevels(t *testing.T) {
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
for v := range version.All() {
|
||||
t.Run(version.String(v), func(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
|
||||
st, _ := createState(t, 0, v)
|
||||
|
||||
err := setOffsetInDB(db, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
|
||||
slot := primitives.Slot(math.PowerOf2(11))
|
||||
st, _ = createState(t, slot, v)
|
||||
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
|
||||
readSt, err := db.stateByDiff(context.Background(), slot)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, readSt)
|
||||
|
||||
stSSZ, err := st.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
readStSSZ, err := readSt.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, stSSZ, readStSSZ)
|
||||
|
||||
slot = primitives.Slot(math.PowerOf2(11) + math.PowerOf2(9))
|
||||
st, _ = createState(t, slot, v)
|
||||
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
|
||||
readSt, err = db.stateByDiff(context.Background(), slot)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, readSt)
|
||||
|
||||
stSSZ, err = st.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
readStSSZ, err = readSt.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, stSSZ, readStSSZ)
|
||||
|
||||
slot = primitives.Slot(math.PowerOf2(11) + math.PowerOf2(9) + math.PowerOf2(5))
|
||||
st, _ = createState(t, slot, v)
|
||||
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
|
||||
readSt, err = db.stateByDiff(context.Background(), slot)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, readSt)
|
||||
|
||||
stSSZ, err = st.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
readStSSZ, err = readSt.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, stSSZ, readStSSZ)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateDiff_SaveAndReadDiffForkTransition(t *testing.T) {
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
for v := range version.All()[:len(version.All())-1] {
|
||||
t.Run(version.String(v), func(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
|
||||
st, _ := createState(t, 0, v)
|
||||
|
||||
err := setOffsetInDB(db, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
|
||||
slot := primitives.Slot(math.PowerOf2(5))
|
||||
st, _ = createState(t, slot, v+1)
|
||||
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
|
||||
readSt, err := db.stateByDiff(context.Background(), slot)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, readSt)
|
||||
|
||||
stSSZ, err := st.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
readStSSZ, err := readSt.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, stSSZ, readStSSZ)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateDiff_OffsetCache(t *testing.T) {
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
// test for slot numbers 0 and 1 for every version
|
||||
for slotNum := range 2 {
|
||||
for v := range version.All() {
|
||||
t.Run(fmt.Sprintf("slotNum=%d,%s", slotNum, version.String(v)), func(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
|
||||
slot := primitives.Slot(slotNum)
|
||||
err := setOffsetInDB(db, uint64(slot))
|
||||
require.NoError(t, err)
|
||||
st, _ := createState(t, slot, v)
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
|
||||
offset := db.stateDiffCache.getOffset()
|
||||
require.Equal(t, uint64(slotNum), offset)
|
||||
|
||||
slot2 := primitives.Slot(uint64(slotNum) + math.PowerOf2(uint64(flags.Get().StateDiffExponents[0])))
|
||||
st2, _ := createState(t, slot2, v)
|
||||
err = db.saveStateByDiff(context.Background(), st2)
|
||||
require.NoError(t, err)
|
||||
|
||||
offset = db.stateDiffCache.getOffset()
|
||||
require.Equal(t, uint64(slot), offset)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateDiff_AnchorCache(t *testing.T) {
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
for v := range version.All() {
|
||||
t.Run(version.String(v), func(t *testing.T) {
|
||||
exponents := flags.Get().StateDiffExponents
|
||||
localCache := make([]state.ReadOnlyBeaconState, len(exponents)-1)
|
||||
db := setupDB(t)
|
||||
err := setOffsetInDB(db, 0) // lvl 0
|
||||
require.NoError(t, err)
|
||||
|
||||
// at first the cache should be empty
|
||||
for i := 0; i < len(flags.Get().StateDiffExponents)-1; i++ {
|
||||
anchor := db.stateDiffCache.getAnchor(i)
|
||||
require.IsNil(t, anchor)
|
||||
}
|
||||
|
||||
// add level 0
|
||||
slot := primitives.Slot(0) // offset 0 is already set
|
||||
st, _ := createState(t, slot, v)
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
localCache[0] = st
|
||||
|
||||
// level 0 should be the same
|
||||
require.DeepEqual(t, localCache[0], db.stateDiffCache.getAnchor(0))
|
||||
|
||||
// rest of the cache should be nil
|
||||
for i := 1; i < len(exponents)-1; i++ {
|
||||
require.IsNil(t, db.stateDiffCache.getAnchor(i))
|
||||
}
|
||||
|
||||
// skip last level as it does not get cached
|
||||
for i := len(exponents) - 2; i > 0; i-- {
|
||||
slot = primitives.Slot(math.PowerOf2(uint64(exponents[i])))
|
||||
st, _ := createState(t, slot, v)
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
localCache[i] = st
|
||||
|
||||
// anchor cache must match local cache
|
||||
for i := 0; i < len(exponents)-1; i++ {
|
||||
if localCache[i] == nil {
|
||||
require.IsNil(t, db.stateDiffCache.getAnchor(i))
|
||||
continue
|
||||
}
|
||||
localSSZ, err := localCache[i].MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
anchorSSZ, err := db.stateDiffCache.getAnchor(i).MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, localSSZ, anchorSSZ)
|
||||
}
|
||||
}
|
||||
|
||||
// moving to a new tree should invalidate the cache except for level 0
|
||||
twoTo21 := math.PowerOf2(21)
|
||||
slot = primitives.Slot(twoTo21)
|
||||
st, _ = createState(t, slot, v)
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
localCache = make([]state.ReadOnlyBeaconState, len(exponents)-1)
|
||||
localCache[0] = st
|
||||
|
||||
// level 0 should be the same
|
||||
require.DeepEqual(t, localCache[0], db.stateDiffCache.getAnchor(0))
|
||||
|
||||
// rest of the cache should be nil
|
||||
for i := 1; i < len(exponents)-1; i++ {
|
||||
require.IsNil(t, db.stateDiffCache.getAnchor(i))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateDiff_EncodingAndDecoding(t *testing.T) {
|
||||
for v := range version.All() {
|
||||
t.Run(version.String(v), func(t *testing.T) {
|
||||
st, enc := createState(t, 0, v) // this has addKey called inside
|
||||
stDecoded, err := decodeStateSnapshot(enc)
|
||||
require.NoError(t, err)
|
||||
st1ssz, err := st.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
st2ssz, err := stDecoded.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, st1ssz, st2ssz)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func createState(t *testing.T, slot primitives.Slot, v int) (state.ReadOnlyBeaconState, []byte) {
|
||||
p := params.BeaconConfig()
|
||||
var st state.BeaconState
|
||||
var err error
|
||||
switch v {
|
||||
case version.Phase0:
|
||||
st, err = util.NewBeaconState()
|
||||
require.NoError(t, err)
|
||||
err = st.SetFork(ðpb.Fork{
|
||||
PreviousVersion: p.GenesisForkVersion,
|
||||
CurrentVersion: p.GenesisForkVersion,
|
||||
Epoch: 0,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
case version.Altair:
|
||||
st, err = util.NewBeaconStateAltair()
|
||||
require.NoError(t, err)
|
||||
err = st.SetFork(ðpb.Fork{
|
||||
PreviousVersion: p.GenesisForkVersion,
|
||||
CurrentVersion: p.AltairForkVersion,
|
||||
Epoch: p.AltairForkEpoch,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
case version.Bellatrix:
|
||||
st, err = util.NewBeaconStateBellatrix()
|
||||
require.NoError(t, err)
|
||||
err = st.SetFork(ðpb.Fork{
|
||||
PreviousVersion: p.AltairForkVersion,
|
||||
CurrentVersion: p.BellatrixForkVersion,
|
||||
Epoch: p.BellatrixForkEpoch,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
case version.Capella:
|
||||
st, err = util.NewBeaconStateCapella()
|
||||
require.NoError(t, err)
|
||||
err = st.SetFork(ðpb.Fork{
|
||||
PreviousVersion: p.BellatrixForkVersion,
|
||||
CurrentVersion: p.CapellaForkVersion,
|
||||
Epoch: p.CapellaForkEpoch,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
case version.Deneb:
|
||||
st, err = util.NewBeaconStateDeneb()
|
||||
require.NoError(t, err)
|
||||
err = st.SetFork(ðpb.Fork{
|
||||
PreviousVersion: p.CapellaForkVersion,
|
||||
CurrentVersion: p.DenebForkVersion,
|
||||
Epoch: p.DenebForkEpoch,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
case version.Electra:
|
||||
st, err = util.NewBeaconStateElectra()
|
||||
require.NoError(t, err)
|
||||
err = st.SetFork(ðpb.Fork{
|
||||
PreviousVersion: p.DenebForkVersion,
|
||||
CurrentVersion: p.ElectraForkVersion,
|
||||
Epoch: p.ElectraForkEpoch,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
case version.Fulu:
|
||||
st, err = util.NewBeaconStateFulu()
|
||||
require.NoError(t, err)
|
||||
err = st.SetFork(ðpb.Fork{
|
||||
PreviousVersion: p.ElectraForkVersion,
|
||||
CurrentVersion: p.FuluForkVersion,
|
||||
Epoch: p.FuluForkEpoch,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
default:
|
||||
t.Fatalf("unsupported version: %d", v)
|
||||
}
|
||||
|
||||
err = st.SetSlot(slot)
|
||||
require.NoError(t, err)
|
||||
slashings := make([]uint64, 8192)
|
||||
slashings[0] = uint64(rand.Intn(10))
|
||||
err = st.SetSlashings(slashings)
|
||||
require.NoError(t, err)
|
||||
stssz, err := st.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
enc, err := addKey(v, stssz)
|
||||
require.NoError(t, err)
|
||||
return st, enc
|
||||
}
|
||||
|
||||
func setOffsetInDB(s *Store, offset uint64) error {
|
||||
err := 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 {
|
||||
return fmt.Errorf("offset already set to %d", binary.LittleEndian.Uint64(offsetBytes))
|
||||
}
|
||||
|
||||
offsetBytes = make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(offsetBytes, offset)
|
||||
if err := bucket.Put(offsetKey, offsetBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sdCache, err := newStateDiffCache(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.stateDiffCache = sdCache
|
||||
return nil
|
||||
}
|
||||
|
||||
func setDefaultStateDiffExponents() {
|
||||
globalFlags := flags.GlobalFlags{
|
||||
StateDiffExponents: []int{21, 18, 16, 13, 11, 9, 5},
|
||||
}
|
||||
flags.Init(&globalFlags)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package kv
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
mathRand "math/rand"
|
||||
@@ -9,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
||||
"github.com/OffchainLabs/prysm/v7/cmd/beacon-chain/flags"
|
||||
"github.com/OffchainLabs/prysm/v7/config/features"
|
||||
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
@@ -17,11 +19,14 @@ import (
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
|
||||
"github.com/OffchainLabs/prysm/v7/genesis"
|
||||
"github.com/OffchainLabs/prysm/v7/math"
|
||||
enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1"
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
"github.com/OffchainLabs/prysm/v7/runtime/version"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/assert"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/require"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/util"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
@@ -1329,3 +1334,297 @@ func TestStore_CleanUpDirtyStates_NoOriginRoot(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStore_CanSaveRetrieveStateUsingStateDiff(t *testing.T) {
|
||||
t.Run("No state summary or block", func(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
featCfg := &features.Flags{}
|
||||
featCfg.EnableStateDiff = true
|
||||
reset := features.InitWithReset(featCfg)
|
||||
defer reset()
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
err := setOffsetInDB(db, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
readSt, err := db.State(context.Background(), [32]byte{'A'})
|
||||
require.IsNil(t, readSt)
|
||||
require.ErrorContains(t, "neither state summary nor block found", err)
|
||||
})
|
||||
|
||||
t.Run("Slot not in tree", func(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
featCfg := &features.Flags{}
|
||||
featCfg.EnableStateDiff = true
|
||||
reset := features.InitWithReset(featCfg)
|
||||
defer reset()
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
err := setOffsetInDB(db, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
r := bytesutil.ToBytes32([]byte{'A'})
|
||||
ss := ðpb.StateSummary{Slot: 1, Root: r[:]} // slot 1 not in tree
|
||||
err = db.SaveStateSummary(context.Background(), ss)
|
||||
require.NoError(t, err)
|
||||
|
||||
readSt, err := db.State(context.Background(), r)
|
||||
require.ErrorContains(t, "slot not in tree", err)
|
||||
require.IsNil(t, readSt)
|
||||
|
||||
})
|
||||
|
||||
t.Run("State not found", func(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
featCfg := &features.Flags{}
|
||||
featCfg.EnableStateDiff = true
|
||||
reset := features.InitWithReset(featCfg)
|
||||
defer reset()
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
err := setOffsetInDB(db, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
r := bytesutil.ToBytes32([]byte{'A'})
|
||||
ss := ðpb.StateSummary{Slot: 32, Root: r[:]} // slot 32 is in tree
|
||||
err = db.SaveStateSummary(context.Background(), ss)
|
||||
require.NoError(t, err)
|
||||
|
||||
readSt, err := db.State(context.Background(), r)
|
||||
require.ErrorContains(t, "state not found", err)
|
||||
require.IsNil(t, readSt)
|
||||
})
|
||||
|
||||
t.Run("Full state snapshot", func(t *testing.T) {
|
||||
t.Run("using state summary", func(t *testing.T) {
|
||||
for v := range version.All() {
|
||||
t.Run(version.String(v), func(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
featCfg := &features.Flags{}
|
||||
featCfg.EnableStateDiff = true
|
||||
reset := features.InitWithReset(featCfg)
|
||||
defer reset()
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
err := setOffsetInDB(db, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
st, _ := createState(t, 0, v)
|
||||
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
|
||||
r := bytesutil.ToBytes32([]byte{'A'})
|
||||
ss := ðpb.StateSummary{Slot: 0, Root: r[:]}
|
||||
err = db.SaveStateSummary(context.Background(), ss)
|
||||
require.NoError(t, err)
|
||||
|
||||
readSt, err := db.State(context.Background(), r)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, readSt)
|
||||
|
||||
stSSZ, err := st.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
readStSSZ, err := readSt.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, stSSZ, readStSSZ)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("using block", func(t *testing.T) {
|
||||
for v := range version.All() {
|
||||
t.Run(version.String(v), func(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
featCfg := &features.Flags{}
|
||||
featCfg.EnableStateDiff = true
|
||||
reset := features.InitWithReset(featCfg)
|
||||
defer reset()
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
err := setOffsetInDB(db, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
st, _ := createState(t, 0, v)
|
||||
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
|
||||
blk := util.NewBeaconBlock()
|
||||
blk.Block.Slot = 0
|
||||
signedBlk, err := blocks.NewSignedBeaconBlock(blk)
|
||||
require.NoError(t, err)
|
||||
err = db.SaveBlock(context.Background(), signedBlk)
|
||||
require.NoError(t, err)
|
||||
r, err := signedBlk.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
readSt, err := db.State(context.Background(), r)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, readSt)
|
||||
|
||||
stSSZ, err := st.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
readStSSZ, err := readSt.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, stSSZ, readStSSZ)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Diffed state", func(t *testing.T) {
|
||||
t.Run("using state summary", func(t *testing.T) {
|
||||
for v := range version.All() {
|
||||
t.Run(version.String(v), func(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
featCfg := &features.Flags{}
|
||||
featCfg.EnableStateDiff = true
|
||||
reset := features.InitWithReset(featCfg)
|
||||
defer reset()
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
exponents := flags.Get().StateDiffExponents
|
||||
|
||||
err := setOffsetInDB(db, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
st, _ := createState(t, 0, v)
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
|
||||
slot := primitives.Slot(math.PowerOf2(uint64(exponents[len(exponents)-2])))
|
||||
st, _ = createState(t, slot, v)
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
|
||||
slot = primitives.Slot(math.PowerOf2(uint64(exponents[len(exponents)-2])) + math.PowerOf2(uint64(exponents[len(exponents)-1])))
|
||||
st, _ = createState(t, slot, v)
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
|
||||
r := bytesutil.ToBytes32([]byte{'A'})
|
||||
ss := ðpb.StateSummary{Slot: slot, Root: r[:]}
|
||||
err = db.SaveStateSummary(context.Background(), ss)
|
||||
require.NoError(t, err)
|
||||
|
||||
readSt, err := db.State(context.Background(), r)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, readSt)
|
||||
|
||||
stSSZ, err := st.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
readStSSZ, err := readSt.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, stSSZ, readStSSZ)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("using block", func(t *testing.T) {
|
||||
for v := range version.All() {
|
||||
t.Run(version.String(v), func(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
featCfg := &features.Flags{}
|
||||
featCfg.EnableStateDiff = true
|
||||
reset := features.InitWithReset(featCfg)
|
||||
defer reset()
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
exponents := flags.Get().StateDiffExponents
|
||||
|
||||
err := setOffsetInDB(db, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
st, _ := createState(t, 0, v)
|
||||
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
|
||||
slot := primitives.Slot(math.PowerOf2(uint64(exponents[len(exponents)-2])))
|
||||
st, _ = createState(t, slot, v)
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
|
||||
slot = primitives.Slot(math.PowerOf2(uint64(exponents[len(exponents)-2])) + math.PowerOf2(uint64(exponents[len(exponents)-1])))
|
||||
st, _ = createState(t, slot, v)
|
||||
err = db.saveStateByDiff(context.Background(), st)
|
||||
require.NoError(t, err)
|
||||
|
||||
blk := util.NewBeaconBlock()
|
||||
blk.Block.Slot = slot
|
||||
signedBlk, err := blocks.NewSignedBeaconBlock(blk)
|
||||
require.NoError(t, err)
|
||||
err = db.SaveBlock(context.Background(), signedBlk)
|
||||
require.NoError(t, err)
|
||||
r, err := signedBlk.Block().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
readSt, err := db.State(context.Background(), r)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, readSt)
|
||||
|
||||
stSSZ, err := st.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
readStSSZ, err := readSt.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, stSSZ, readStSSZ)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStore_HasStateUsingStateDiff(t *testing.T) {
|
||||
t.Run("No state summary or block", func(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
db := setupDB(t)
|
||||
featCfg := &features.Flags{}
|
||||
featCfg.EnableStateDiff = true
|
||||
reset := features.InitWithReset(featCfg)
|
||||
defer reset()
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
err := setOffsetInDB(db, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
hasSt := db.HasState(t.Context(), [32]byte{'A'})
|
||||
require.Equal(t, false, hasSt)
|
||||
require.LogsContain(t, hook, "neither state summary nor block found")
|
||||
})
|
||||
|
||||
t.Run("slot in tree or not", func(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
featCfg := &features.Flags{}
|
||||
featCfg.EnableStateDiff = true
|
||||
reset := features.InitWithReset(featCfg)
|
||||
defer reset()
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
err := setOffsetInDB(db, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
slot primitives.Slot
|
||||
expected bool
|
||||
}{
|
||||
{slot: 1, expected: false}, // slot 1 not in tree
|
||||
{slot: 32, expected: true}, // slot 32 in tree
|
||||
{slot: 0, expected: true}, // slot 0 in tree
|
||||
{slot: primitives.Slot(math.PowerOf2(21)), expected: true}, // slot in tree
|
||||
{slot: primitives.Slot(math.PowerOf2(21) - 1), expected: false}, // slot not in tree
|
||||
{slot: primitives.Slot(math.PowerOf2(22)), expected: true}, // slot in tree
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
r := bytesutil.ToBytes32([]byte{'A'})
|
||||
ss := ðpb.StateSummary{Slot: tc.slot, Root: r[:]}
|
||||
err = db.SaveStateSummary(t.Context(), ss)
|
||||
require.NoError(t, err)
|
||||
|
||||
hasSt := db.HasState(t.Context(), r)
|
||||
require.Equal(t, tc.expected, hasSt)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@@ -33,6 +33,10 @@ func TestLightClient_NewLightClientOptimisticUpdateFromBeaconState(t *testing.T)
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
for _, testVersion := range version.All()[1:] {
|
||||
if testVersion == version.Gloas {
|
||||
// TODO(16027): Unskip light client tests for Gloas
|
||||
continue
|
||||
}
|
||||
t.Run(version.String(testVersion), func(t *testing.T) {
|
||||
l := util.NewTestLightClient(t, testVersion)
|
||||
|
||||
|
||||
@@ -280,7 +280,10 @@ func configureBeacon(cliCtx *cli.Context) error {
|
||||
return errors.Wrap(err, "could not configure beacon chain")
|
||||
}
|
||||
|
||||
flags.ConfigureGlobalFlags(cliCtx)
|
||||
err := flags.ConfigureGlobalFlags(cliCtx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not configure global flags")
|
||||
}
|
||||
|
||||
if err := configureChainConfig(cliCtx); err != nil {
|
||||
return errors.Wrap(err, "could not configure chain config")
|
||||
@@ -660,6 +663,7 @@ func (b *BeaconNode) registerP2P(cliCtx *cli.Context) error {
|
||||
EnableUPnP: cliCtx.Bool(cmd.EnableUPnPFlag.Name),
|
||||
StateNotifier: b,
|
||||
DB: b.db,
|
||||
StateGen: b.stateGen,
|
||||
ClockWaiter: b.clockWaiter,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -56,6 +56,7 @@ go_library(
|
||||
"//beacon-chain/p2p/peers/scorers:go_default_library",
|
||||
"//beacon-chain/p2p/types:go_default_library",
|
||||
"//beacon-chain/startup:go_default_library",
|
||||
"//beacon-chain/state/stategen:go_default_library",
|
||||
"//cmd/beacon-chain/flags:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
@@ -153,6 +154,7 @@ go_test(
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/core/peerdas:go_default_library",
|
||||
"//beacon-chain/core/signing:go_default_library",
|
||||
"//beacon-chain/db/iface:go_default_library",
|
||||
"//beacon-chain/db/testing:go_default_library",
|
||||
"//beacon-chain/p2p/encoder:go_default_library",
|
||||
"//beacon-chain/p2p/peers:go_default_library",
|
||||
@@ -161,6 +163,7 @@ go_test(
|
||||
"//beacon-chain/p2p/testing:go_default_library",
|
||||
"//beacon-chain/p2p/types:go_default_library",
|
||||
"//beacon-chain/startup:go_default_library",
|
||||
"//beacon-chain/state/stategen/mock:go_default_library",
|
||||
"//cmd/beacon-chain/flags:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
statefeed "github.com/OffchainLabs/prysm/v7/beacon-chain/core/feed/state"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/db"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/startup"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state/stategen"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -49,6 +50,7 @@ type Config struct {
|
||||
IPColocationWhitelist []*net.IPNet
|
||||
StateNotifier statefeed.Notifier
|
||||
DB db.ReadOnlyDatabaseWithSeqNum
|
||||
StateGen stategen.StateManager
|
||||
ClockWaiter startup.ClockWaiter
|
||||
}
|
||||
|
||||
|
||||
@@ -156,29 +156,13 @@ func (s *Service) retrieveActiveValidators() (uint64, error) {
|
||||
if s.activeValidatorCount != 0 {
|
||||
return s.activeValidatorCount, nil
|
||||
}
|
||||
rt := s.cfg.DB.LastArchivedRoot(s.ctx)
|
||||
if rt == params.BeaconConfig().ZeroHash {
|
||||
genState, err := s.cfg.DB.GenesisState(s.ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if genState == nil || genState.IsNil() {
|
||||
return 0, errors.New("no genesis state exists")
|
||||
}
|
||||
activeVals, err := helpers.ActiveValidatorCount(context.Background(), genState, coreTime.CurrentEpoch(genState))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Cache active validator count
|
||||
s.activeValidatorCount = activeVals
|
||||
return activeVals, nil
|
||||
}
|
||||
bState, err := s.cfg.DB.State(s.ctx, rt)
|
||||
finalizedCheckpoint, err := s.cfg.DB.FinalizedCheckpoint(s.ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if bState == nil || bState.IsNil() {
|
||||
return 0, errors.Errorf("no state with root %#x exists", rt)
|
||||
bState, err := s.cfg.StateGen.StateByRoot(s.ctx, [32]byte(finalizedCheckpoint.Root))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
activeVals, err := helpers.ActiveValidatorCount(context.Background(), bState, coreTime.CurrentEpoch(bState))
|
||||
if err != nil {
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package p2p
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
iface "github.com/OffchainLabs/prysm/v7/beacon-chain/db/iface"
|
||||
dbutil "github.com/OffchainLabs/prysm/v7/beacon-chain/db/testing"
|
||||
mockstategen "github.com/OffchainLabs/prysm/v7/beacon-chain/state/stategen/mock"
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/assert"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/require"
|
||||
@@ -20,9 +24,11 @@ func TestCorrect_ActiveValidatorsCount(t *testing.T) {
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
db := dbutil.SetupDB(t)
|
||||
wrappedDB := &finalizedCheckpointDB{ReadOnlyDatabaseWithSeqNum: db}
|
||||
stateGen := mockstategen.NewService()
|
||||
s := &Service{
|
||||
ctx: t.Context(),
|
||||
cfg: &Config{DB: db},
|
||||
cfg: &Config{DB: wrappedDB, StateGen: stateGen},
|
||||
}
|
||||
bState, err := util.NewBeaconState(func(state *ethpb.BeaconState) error {
|
||||
validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount)
|
||||
@@ -39,6 +45,10 @@ func TestCorrect_ActiveValidatorsCount(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveGenesisData(s.ctx, bState))
|
||||
checkpoint, err := db.FinalizedCheckpoint(s.ctx)
|
||||
require.NoError(t, err)
|
||||
wrappedDB.finalized = checkpoint
|
||||
stateGen.AddStateForRoot(bState, bytesutil.ToBytes32(checkpoint.Root))
|
||||
|
||||
vals, err := s.retrieveActiveValidators()
|
||||
assert.NoError(t, err, "genesis state not retrieved")
|
||||
@@ -52,7 +62,10 @@ func TestCorrect_ActiveValidatorsCount(t *testing.T) {
|
||||
}))
|
||||
}
|
||||
require.NoError(t, bState.SetSlot(10000))
|
||||
require.NoError(t, db.SaveState(s.ctx, bState, [32]byte{'a'}))
|
||||
rootA := [32]byte{'a'}
|
||||
require.NoError(t, db.SaveState(s.ctx, bState, rootA))
|
||||
wrappedDB.finalized = ðpb.Checkpoint{Root: rootA[:]}
|
||||
stateGen.AddStateForRoot(bState, rootA)
|
||||
// Reset count
|
||||
s.activeValidatorCount = 0
|
||||
|
||||
@@ -77,3 +90,15 @@ func TestLoggingParameters(_ *testing.T) {
|
||||
logGossipParameters("testing", defaultLightClientOptimisticUpdateTopicParams())
|
||||
logGossipParameters("testing", defaultLightClientFinalityUpdateTopicParams())
|
||||
}
|
||||
|
||||
type finalizedCheckpointDB struct {
|
||||
iface.ReadOnlyDatabaseWithSeqNum
|
||||
finalized *ethpb.Checkpoint
|
||||
}
|
||||
|
||||
func (f *finalizedCheckpointDB) FinalizedCheckpoint(ctx context.Context) (*ethpb.Checkpoint, error) {
|
||||
if f.finalized != nil {
|
||||
return f.finalized, nil
|
||||
}
|
||||
return f.ReadOnlyDatabaseWithSeqNum.FinalizedCheckpoint(ctx)
|
||||
}
|
||||
|
||||
@@ -77,12 +77,12 @@ func InitializeDataMaps() {
|
||||
},
|
||||
bytesutil.ToBytes4(params.BeaconConfig().ElectraForkVersion): func() (interfaces.ReadOnlySignedBeaconBlock, error) {
|
||||
return blocks.NewSignedBeaconBlock(
|
||||
ðpb.SignedBeaconBlockElectra{Block: ðpb.BeaconBlockElectra{Body: ðpb.BeaconBlockBodyElectra{ExecutionPayload: &enginev1.ExecutionPayloadDeneb{}}}},
|
||||
ðpb.SignedBeaconBlockElectra{Block: ðpb.BeaconBlockElectra{Body: ðpb.BeaconBlockBodyElectra{ExecutionPayload: &enginev1.ExecutionPayloadDeneb{}, ExecutionRequests: &enginev1.ExecutionRequests{}}}},
|
||||
)
|
||||
},
|
||||
bytesutil.ToBytes4(params.BeaconConfig().FuluForkVersion): func() (interfaces.ReadOnlySignedBeaconBlock, error) {
|
||||
return blocks.NewSignedBeaconBlock(
|
||||
ðpb.SignedBeaconBlockFulu{Block: ðpb.BeaconBlockElectra{Body: ðpb.BeaconBlockBodyElectra{ExecutionPayload: &enginev1.ExecutionPayloadDeneb{}}}},
|
||||
ðpb.SignedBeaconBlockFulu{Block: ðpb.BeaconBlockElectra{Body: ðpb.BeaconBlockBodyElectra{ExecutionPayload: &enginev1.ExecutionPayloadDeneb{}, ExecutionRequests: &enginev1.ExecutionRequests{}}}},
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -47,6 +47,10 @@ func TestLightClientHandler_GetLightClientBootstrap(t *testing.T) {
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
for _, testVersion := range version.All()[1:] {
|
||||
if testVersion == version.Gloas {
|
||||
// TODO(16027): Unskip light client tests for Gloas
|
||||
continue
|
||||
}
|
||||
t.Run(version.String(testVersion), func(t *testing.T) {
|
||||
l := util.NewTestLightClient(t, testVersion)
|
||||
|
||||
@@ -178,6 +182,10 @@ func TestLightClientHandler_GetLightClientByRange(t *testing.T) {
|
||||
|
||||
t.Run("can save retrieve", func(t *testing.T) {
|
||||
for _, testVersion := range version.All()[1:] {
|
||||
if testVersion == version.Gloas {
|
||||
// TODO(16027): Unskip light client tests for Gloas
|
||||
continue
|
||||
}
|
||||
t.Run(version.String(testVersion), func(t *testing.T) {
|
||||
|
||||
slot := primitives.Slot(params.BeaconConfig().VersionToForkEpochMap()[testVersion] * primitives.Epoch(config.SlotsPerEpoch)).Add(1)
|
||||
@@ -732,6 +740,10 @@ func TestLightClientHandler_GetLightClientFinalityUpdate(t *testing.T) {
|
||||
})
|
||||
|
||||
for _, testVersion := range version.All()[1:] {
|
||||
if testVersion == version.Gloas {
|
||||
// TODO(16027): Unskip light client tests for Gloas
|
||||
continue
|
||||
}
|
||||
t.Run(version.String(testVersion), func(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
@@ -827,6 +839,10 @@ func TestLightClientHandler_GetLightClientOptimisticUpdate(t *testing.T) {
|
||||
})
|
||||
|
||||
for _, testVersion := range version.All()[1:] {
|
||||
if testVersion == version.Gloas {
|
||||
// TODO(16027): Unskip light client tests for Gloas
|
||||
continue
|
||||
}
|
||||
t.Run(version.String(testVersion), func(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
l := util.NewTestLightClient(t, testVersion)
|
||||
|
||||
@@ -99,6 +99,7 @@ go_test(
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
|
||||
"@org_uber_go_mock//gomock:go_default_library",
|
||||
],
|
||||
|
||||
@@ -835,12 +835,17 @@ func (s *Server) PrepareBeaconProposer(w http.ResponseWriter, r *http.Request) {
|
||||
s.TrackedValidatorsCache.Set(val)
|
||||
validatorIndices = append(validatorIndices, primitives.ValidatorIndex(validatorIndex))
|
||||
}
|
||||
|
||||
if len(validatorIndices) == 0 {
|
||||
return
|
||||
}
|
||||
log.WithFields(logrus.Fields{
|
||||
"validatorIndices": validatorIndices,
|
||||
}).Info("Updated fee recipient addresses")
|
||||
|
||||
log := log.WithField("validatorCount", len(validatorIndices))
|
||||
if logrus.GetLevel() >= logrus.TraceLevel {
|
||||
log = log.WithField("validatorIndices", validatorIndices)
|
||||
}
|
||||
|
||||
log.Debug("Updated fee recipient addresses")
|
||||
}
|
||||
|
||||
// GetAttesterDuties requests the beacon node to provide a set of attestation duties,
|
||||
|
||||
@@ -44,6 +44,7 @@ import (
|
||||
"github.com/OffchainLabs/prysm/v7/time/slots"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
)
|
||||
|
||||
@@ -2854,6 +2855,8 @@ func TestPrepareBeaconProposer(t *testing.T) {
|
||||
|
||||
func TestProposer_PrepareBeaconProposerOverlapping(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
|
||||
db := dbutil.SetupDB(t)
|
||||
|
||||
// New validator
|
||||
|
||||
@@ -547,11 +547,19 @@ func (vs *Server) PrepareBeaconProposer(
|
||||
vs.TrackedValidatorsCache.Set(val)
|
||||
validatorIndices = append(validatorIndices, r.ValidatorIndex)
|
||||
}
|
||||
if len(validatorIndices) != 0 {
|
||||
log.WithFields(logrus.Fields{
|
||||
"validatorCount": len(validatorIndices),
|
||||
}).Debug("Updated fee recipient addresses for validator indices")
|
||||
|
||||
if len(validatorIndices) == 0 {
|
||||
return &emptypb.Empty{}, nil
|
||||
|
||||
}
|
||||
|
||||
log := log.WithField("validatorCount", len(validatorIndices))
|
||||
if logrus.GetLevel() >= logrus.TraceLevel {
|
||||
log = log.WithField("validatorIndices", validatorIndices)
|
||||
}
|
||||
|
||||
log.Debug("Updated fee recipient addresses")
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
@@ -3162,6 +3163,8 @@ func TestProposer_PrepareBeaconProposer(t *testing.T) {
|
||||
|
||||
func TestProposer_PrepareBeaconProposerOverlapping(t *testing.T) {
|
||||
hook := logTest.NewGlobal()
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
|
||||
db := dbutil.SetupDB(t)
|
||||
ctx := t.Context()
|
||||
proposerServer := &Server{
|
||||
@@ -3178,13 +3181,13 @@ func TestProposer_PrepareBeaconProposerOverlapping(t *testing.T) {
|
||||
}
|
||||
_, err := proposerServer.PrepareBeaconProposer(ctx, req)
|
||||
require.NoError(t, err)
|
||||
require.LogsContain(t, hook, "Updated fee recipient addresses for validator indices")
|
||||
require.LogsContain(t, hook, "Updated fee recipient addresses")
|
||||
|
||||
// Same validator
|
||||
hook.Reset()
|
||||
_, err = proposerServer.PrepareBeaconProposer(ctx, req)
|
||||
require.NoError(t, err)
|
||||
require.LogsContain(t, hook, "Updated fee recipient addresses for validator indices")
|
||||
require.LogsContain(t, hook, "Updated fee recipient addresses")
|
||||
|
||||
// Same validator with different fee recipient
|
||||
hook.Reset()
|
||||
@@ -3196,7 +3199,7 @@ func TestProposer_PrepareBeaconProposerOverlapping(t *testing.T) {
|
||||
}
|
||||
_, err = proposerServer.PrepareBeaconProposer(ctx, req)
|
||||
require.NoError(t, err)
|
||||
require.LogsContain(t, hook, "Updated fee recipient addresses for validator indices")
|
||||
require.LogsContain(t, hook, "Updated fee recipient addresses")
|
||||
|
||||
// More than one validator
|
||||
hook.Reset()
|
||||
@@ -3209,13 +3212,13 @@ func TestProposer_PrepareBeaconProposerOverlapping(t *testing.T) {
|
||||
}
|
||||
_, err = proposerServer.PrepareBeaconProposer(ctx, req)
|
||||
require.NoError(t, err)
|
||||
require.LogsContain(t, hook, "Updated fee recipient addresses for validator indices")
|
||||
require.LogsContain(t, hook, "Updated fee recipient addresses")
|
||||
|
||||
// Same validators
|
||||
hook.Reset()
|
||||
_, err = proposerServer.PrepareBeaconProposer(ctx, req)
|
||||
require.NoError(t, err)
|
||||
require.LogsContain(t, hook, "Updated fee recipient addresses for validator indices")
|
||||
require.LogsContain(t, hook, "Updated fee recipient addresses")
|
||||
}
|
||||
|
||||
func BenchmarkServer_PrepareBeaconProposer(b *testing.B) {
|
||||
|
||||
@@ -23,6 +23,7 @@ go_library(
|
||||
"getters_sync_committee.go",
|
||||
"getters_validator.go",
|
||||
"getters_withdrawal.go",
|
||||
"gloas.go",
|
||||
"hasher.go",
|
||||
"multi_value_slices.go",
|
||||
"proofs.go",
|
||||
|
||||
@@ -70,6 +70,14 @@ type BeaconState struct {
|
||||
pendingConsolidations []*ethpb.PendingConsolidation // pending_consolidations: List[PendingConsolidation, PENDING_CONSOLIDATIONS_LIMIT]
|
||||
proposerLookahead []primitives.ValidatorIndex // proposer_look_ahead: List[uint64, (MIN_LOOKAHEAD + 1)*SLOTS_PER_EPOCH]
|
||||
|
||||
// Gloas fields
|
||||
latestExecutionPayloadBid *ethpb.ExecutionPayloadBid
|
||||
executionPayloadAvailability []byte
|
||||
builderPendingPayments []*ethpb.BuilderPendingPayment
|
||||
builderPendingWithdrawals []*ethpb.BuilderPendingWithdrawal
|
||||
latestBlockHash []byte
|
||||
latestWithdrawalsRoot []byte
|
||||
|
||||
id uint64
|
||||
lock sync.RWMutex
|
||||
dirtyFields map[types.FieldIndex]bool
|
||||
@@ -125,6 +133,12 @@ type beaconStateMarshalable struct {
|
||||
PendingPartialWithdrawals []*ethpb.PendingPartialWithdrawal `json:"pending_partial_withdrawals" yaml:"pending_partial_withdrawals"`
|
||||
PendingConsolidations []*ethpb.PendingConsolidation `json:"pending_consolidations" yaml:"pending_consolidations"`
|
||||
ProposerLookahead []primitives.ValidatorIndex `json:"proposer_look_ahead" yaml:"proposer_look_ahead"`
|
||||
LatestExecutionPayloadBid *ethpb.ExecutionPayloadBid `json:"latest_execution_payload_bid" yaml:"latest_execution_payload_bid"`
|
||||
ExecutionPayloadAvailability []byte `json:"execution_payload_availability" yaml:"execution_payload_availability"`
|
||||
BuilderPendingPayments []*ethpb.BuilderPendingPayment `json:"builder_pending_payments" yaml:"builder_pending_payments"`
|
||||
BuilderPendingWithdrawals []*ethpb.BuilderPendingWithdrawal `json:"builder_pending_withdrawals" yaml:"builder_pending_withdrawals"`
|
||||
LatestBlockHash []byte `json:"latest_block_hash" yaml:"latest_block_hash"`
|
||||
LatestWithdrawalsRoot []byte `json:"latest_withdrawals_root" yaml:"latest_withdrawals_root"`
|
||||
}
|
||||
|
||||
func (b *BeaconState) MarshalJSON() ([]byte, error) {
|
||||
@@ -179,6 +193,12 @@ func (b *BeaconState) MarshalJSON() ([]byte, error) {
|
||||
PendingPartialWithdrawals: b.pendingPartialWithdrawals,
|
||||
PendingConsolidations: b.pendingConsolidations,
|
||||
ProposerLookahead: b.proposerLookahead,
|
||||
LatestExecutionPayloadBid: b.latestExecutionPayloadBid,
|
||||
ExecutionPayloadAvailability: b.executionPayloadAvailability,
|
||||
BuilderPendingPayments: b.builderPendingPayments,
|
||||
BuilderPendingWithdrawals: b.builderPendingWithdrawals,
|
||||
LatestBlockHash: b.latestBlockHash,
|
||||
LatestWithdrawalsRoot: b.latestWithdrawalsRoot,
|
||||
}
|
||||
return json.Marshal(marshalable)
|
||||
}
|
||||
|
||||
@@ -259,6 +259,57 @@ func (b *BeaconState) ToProtoUnsafe() any {
|
||||
PendingConsolidations: b.pendingConsolidations,
|
||||
ProposerLookahead: lookahead,
|
||||
}
|
||||
case version.Gloas:
|
||||
lookahead := make([]uint64, len(b.proposerLookahead))
|
||||
for i, v := range b.proposerLookahead {
|
||||
lookahead[i] = uint64(v)
|
||||
}
|
||||
|
||||
return ðpb.BeaconStateGloas{
|
||||
GenesisTime: b.genesisTime,
|
||||
GenesisValidatorsRoot: gvrCopy[:],
|
||||
Slot: b.slot,
|
||||
Fork: b.fork,
|
||||
LatestBlockHeader: b.latestBlockHeader,
|
||||
BlockRoots: br,
|
||||
StateRoots: sr,
|
||||
HistoricalRoots: b.historicalRoots.Slice(),
|
||||
Eth1Data: b.eth1Data,
|
||||
Eth1DataVotes: b.eth1DataVotes,
|
||||
Eth1DepositIndex: b.eth1DepositIndex,
|
||||
Validators: vals,
|
||||
Balances: bals,
|
||||
RandaoMixes: rm,
|
||||
Slashings: b.slashings,
|
||||
PreviousEpochParticipation: b.previousEpochParticipation,
|
||||
CurrentEpochParticipation: b.currentEpochParticipation,
|
||||
JustificationBits: b.justificationBits,
|
||||
PreviousJustifiedCheckpoint: b.previousJustifiedCheckpoint,
|
||||
CurrentJustifiedCheckpoint: b.currentJustifiedCheckpoint,
|
||||
FinalizedCheckpoint: b.finalizedCheckpoint,
|
||||
InactivityScores: inactivityScores,
|
||||
CurrentSyncCommittee: b.currentSyncCommittee,
|
||||
NextSyncCommittee: b.nextSyncCommittee,
|
||||
LatestExecutionPayloadBid: b.latestExecutionPayloadBid,
|
||||
NextWithdrawalIndex: b.nextWithdrawalIndex,
|
||||
NextWithdrawalValidatorIndex: b.nextWithdrawalValidatorIndex,
|
||||
HistoricalSummaries: b.historicalSummaries,
|
||||
DepositRequestsStartIndex: b.depositRequestsStartIndex,
|
||||
DepositBalanceToConsume: b.depositBalanceToConsume,
|
||||
ExitBalanceToConsume: b.exitBalanceToConsume,
|
||||
EarliestExitEpoch: b.earliestExitEpoch,
|
||||
ConsolidationBalanceToConsume: b.consolidationBalanceToConsume,
|
||||
EarliestConsolidationEpoch: b.earliestConsolidationEpoch,
|
||||
PendingDeposits: b.pendingDeposits,
|
||||
PendingPartialWithdrawals: b.pendingPartialWithdrawals,
|
||||
PendingConsolidations: b.pendingConsolidations,
|
||||
ProposerLookahead: lookahead,
|
||||
ExecutionPayloadAvailability: b.executionPayloadAvailability,
|
||||
BuilderPendingPayments: b.builderPendingPayments,
|
||||
BuilderPendingWithdrawals: b.builderPendingWithdrawals,
|
||||
LatestBlockHash: b.latestBlockHash,
|
||||
LatestWithdrawalsRoot: b.latestWithdrawalsRoot,
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@@ -510,6 +561,57 @@ func (b *BeaconState) ToProto() any {
|
||||
PendingConsolidations: b.pendingConsolidationsVal(),
|
||||
ProposerLookahead: lookahead,
|
||||
}
|
||||
case version.Gloas:
|
||||
lookahead := make([]uint64, len(b.proposerLookahead))
|
||||
for i, v := range b.proposerLookahead {
|
||||
lookahead[i] = uint64(v)
|
||||
}
|
||||
|
||||
return ðpb.BeaconStateGloas{
|
||||
GenesisTime: b.genesisTime,
|
||||
GenesisValidatorsRoot: gvrCopy[:],
|
||||
Slot: b.slot,
|
||||
Fork: b.forkVal(),
|
||||
LatestBlockHeader: b.latestBlockHeaderVal(),
|
||||
BlockRoots: br,
|
||||
StateRoots: sr,
|
||||
HistoricalRoots: b.historicalRoots.Slice(),
|
||||
Eth1Data: b.eth1DataVal(),
|
||||
Eth1DataVotes: b.eth1DataVotesVal(),
|
||||
Eth1DepositIndex: b.eth1DepositIndex,
|
||||
Validators: b.validatorsVal(),
|
||||
Balances: b.balancesVal(),
|
||||
RandaoMixes: rm,
|
||||
Slashings: b.slashingsVal(),
|
||||
PreviousEpochParticipation: b.previousEpochParticipationVal(),
|
||||
CurrentEpochParticipation: b.currentEpochParticipationVal(),
|
||||
JustificationBits: b.justificationBitsVal(),
|
||||
PreviousJustifiedCheckpoint: b.previousJustifiedCheckpointVal(),
|
||||
CurrentJustifiedCheckpoint: b.currentJustifiedCheckpointVal(),
|
||||
FinalizedCheckpoint: b.finalizedCheckpointVal(),
|
||||
InactivityScores: b.inactivityScoresVal(),
|
||||
CurrentSyncCommittee: b.currentSyncCommitteeVal(),
|
||||
NextSyncCommittee: b.nextSyncCommitteeVal(),
|
||||
LatestExecutionPayloadBid: b.latestExecutionPayloadBid.Copy(),
|
||||
NextWithdrawalIndex: b.nextWithdrawalIndex,
|
||||
NextWithdrawalValidatorIndex: b.nextWithdrawalValidatorIndex,
|
||||
HistoricalSummaries: b.historicalSummariesVal(),
|
||||
DepositRequestsStartIndex: b.depositRequestsStartIndex,
|
||||
DepositBalanceToConsume: b.depositBalanceToConsume,
|
||||
ExitBalanceToConsume: b.exitBalanceToConsume,
|
||||
EarliestExitEpoch: b.earliestExitEpoch,
|
||||
ConsolidationBalanceToConsume: b.consolidationBalanceToConsume,
|
||||
EarliestConsolidationEpoch: b.earliestConsolidationEpoch,
|
||||
PendingDeposits: b.pendingDepositsVal(),
|
||||
PendingPartialWithdrawals: b.pendingPartialWithdrawalsVal(),
|
||||
PendingConsolidations: b.pendingConsolidationsVal(),
|
||||
ProposerLookahead: lookahead,
|
||||
ExecutionPayloadAvailability: b.executionPayloadAvailabilityVal(),
|
||||
BuilderPendingPayments: b.builderPendingPaymentsVal(),
|
||||
BuilderPendingWithdrawals: b.builderPendingWithdrawalsVal(),
|
||||
LatestBlockHash: b.latestBlockHashVal(),
|
||||
LatestWithdrawalsRoot: b.latestWithdrawalsRootVal(),
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
74
beacon-chain/state/state-native/gloas.go
Normal file
74
beacon-chain/state/state-native/gloas.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package state_native
|
||||
|
||||
import (
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
// executionPayloadAvailabilityVal returns a copy of the execution payload availability.
|
||||
// This assumes that a lock is already held on BeaconState.
|
||||
func (b *BeaconState) executionPayloadAvailabilityVal() []byte {
|
||||
if b.executionPayloadAvailability == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
availability := make([]byte, len(b.executionPayloadAvailability))
|
||||
copy(availability, b.executionPayloadAvailability)
|
||||
|
||||
return availability
|
||||
}
|
||||
|
||||
// builderPendingPaymentsVal returns a copy of the builder pending payments.
|
||||
// This assumes that a lock is already held on BeaconState.
|
||||
func (b *BeaconState) builderPendingPaymentsVal() []*ethpb.BuilderPendingPayment {
|
||||
if b.builderPendingPayments == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
payments := make([]*ethpb.BuilderPendingPayment, len(b.builderPendingPayments))
|
||||
for i, payment := range b.builderPendingPayments {
|
||||
payments[i] = payment.Copy()
|
||||
}
|
||||
|
||||
return payments
|
||||
}
|
||||
|
||||
// builderPendingWithdrawalsVal returns a copy of the builder pending withdrawals.
|
||||
// This assumes that a lock is already held on BeaconState.
|
||||
func (b *BeaconState) builderPendingWithdrawalsVal() []*ethpb.BuilderPendingWithdrawal {
|
||||
if b.builderPendingWithdrawals == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
withdrawals := make([]*ethpb.BuilderPendingWithdrawal, len(b.builderPendingWithdrawals))
|
||||
for i, withdrawal := range b.builderPendingWithdrawals {
|
||||
withdrawals[i] = withdrawal.Copy()
|
||||
}
|
||||
|
||||
return withdrawals
|
||||
}
|
||||
|
||||
// latestBlockHashVal returns a copy of the latest block hash.
|
||||
// This assumes that a lock is already held on BeaconState.
|
||||
func (b *BeaconState) latestBlockHashVal() []byte {
|
||||
if b.latestBlockHash == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
hash := make([]byte, len(b.latestBlockHash))
|
||||
copy(hash, b.latestBlockHash)
|
||||
|
||||
return hash
|
||||
}
|
||||
|
||||
// latestWithdrawalsRootVal returns a copy of the latest withdrawals root.
|
||||
// This assumes that a lock is already held on BeaconState.
|
||||
func (b *BeaconState) latestWithdrawalsRootVal() []byte {
|
||||
if b.latestWithdrawalsRoot == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
root := make([]byte, len(b.latestWithdrawalsRoot))
|
||||
copy(root, b.latestWithdrawalsRoot)
|
||||
|
||||
return root
|
||||
}
|
||||
@@ -43,6 +43,8 @@ func ComputeFieldRootsWithHasher(ctx context.Context, state *BeaconState) ([][]b
|
||||
fieldRoots = make([][]byte, params.BeaconConfig().BeaconStateElectraFieldCount)
|
||||
case version.Fulu:
|
||||
fieldRoots = make([][]byte, params.BeaconConfig().BeaconStateFuluFieldCount)
|
||||
case version.Gloas:
|
||||
fieldRoots = make([][]byte, params.BeaconConfig().BeaconStateGloasFieldCount)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown state version %s", version.String(state.version))
|
||||
}
|
||||
@@ -245,7 +247,7 @@ func ComputeFieldRootsWithHasher(ctx context.Context, state *BeaconState) ([][]b
|
||||
fieldRoots[types.LatestExecutionPayloadHeaderCapella.RealPosition()] = executionPayloadRoot[:]
|
||||
}
|
||||
|
||||
if state.version >= version.Deneb {
|
||||
if state.version >= version.Deneb && state.version < version.Gloas {
|
||||
// Execution payload root.
|
||||
executionPayloadRoot, err := state.latestExecutionPayloadHeaderDeneb.HashTreeRoot()
|
||||
if err != nil {
|
||||
@@ -254,6 +256,16 @@ func ComputeFieldRootsWithHasher(ctx context.Context, state *BeaconState) ([][]b
|
||||
fieldRoots[types.LatestExecutionPayloadHeaderDeneb.RealPosition()] = executionPayloadRoot[:]
|
||||
}
|
||||
|
||||
if state.version >= version.Gloas {
|
||||
// Execution payload bid root for Gloas.
|
||||
bidRoot, err := state.latestExecutionPayloadBid.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fieldRoots[types.LatestExecutionPayloadBid.RealPosition()] = bidRoot[:]
|
||||
}
|
||||
|
||||
if state.version >= version.Capella {
|
||||
// Next withdrawal index root.
|
||||
nextWithdrawalIndexRoot := make([]byte, 32)
|
||||
@@ -328,5 +340,34 @@ func ComputeFieldRootsWithHasher(ctx context.Context, state *BeaconState) ([][]b
|
||||
}
|
||||
fieldRoots[types.ProposerLookahead.RealPosition()] = proposerLookaheadRoot[:]
|
||||
}
|
||||
|
||||
if state.version >= version.Gloas {
|
||||
epaRoot, err := stateutil.ExecutionPayloadAvailabilityRoot(state.executionPayloadAvailability)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not compute execution payload availability merkleization")
|
||||
}
|
||||
|
||||
fieldRoots[types.ExecutionPayloadAvailability.RealPosition()] = epaRoot[:]
|
||||
|
||||
bppRoot, err := stateutil.BuilderPendingPaymentsRoot(state.builderPendingPayments)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not compute builder pending payments merkleization")
|
||||
}
|
||||
|
||||
fieldRoots[types.BuilderPendingPayments.RealPosition()] = bppRoot[:]
|
||||
|
||||
bpwRoot, err := stateutil.BuilderPendingWithdrawalsRoot(state.builderPendingWithdrawals)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not compute builder pending withdrawals merkleization")
|
||||
}
|
||||
|
||||
fieldRoots[types.BuilderPendingWithdrawals.RealPosition()] = bpwRoot[:]
|
||||
|
||||
lbhRoot := bytesutil.ToBytes32(state.latestBlockHash)
|
||||
fieldRoots[types.LatestBlockHash.RealPosition()] = lbhRoot[:]
|
||||
|
||||
lwrRoot := bytesutil.ToBytes32(state.latestWithdrawalsRoot)
|
||||
fieldRoots[types.LatestWithdrawalsRoot.RealPosition()] = lwrRoot[:]
|
||||
}
|
||||
return fieldRoots, nil
|
||||
}
|
||||
|
||||
@@ -79,24 +79,25 @@ var (
|
||||
|
||||
bellatrixFields = append(altairFields, types.LatestExecutionPayloadHeader)
|
||||
|
||||
capellaFields = append(
|
||||
altairFields,
|
||||
types.LatestExecutionPayloadHeaderCapella,
|
||||
withdrawalAndHistoricalSummaryFields = []types.FieldIndex{
|
||||
types.NextWithdrawalIndex,
|
||||
types.NextWithdrawalValidatorIndex,
|
||||
types.HistoricalSummaries,
|
||||
)
|
||||
}
|
||||
|
||||
denebFields = append(
|
||||
capellaFields = slices.Concat(
|
||||
altairFields,
|
||||
types.LatestExecutionPayloadHeaderDeneb,
|
||||
types.NextWithdrawalIndex,
|
||||
types.NextWithdrawalValidatorIndex,
|
||||
types.HistoricalSummaries,
|
||||
[]types.FieldIndex{types.LatestExecutionPayloadHeaderCapella},
|
||||
withdrawalAndHistoricalSummaryFields,
|
||||
)
|
||||
|
||||
electraFields = append(
|
||||
denebFields,
|
||||
denebFields = slices.Concat(
|
||||
altairFields,
|
||||
[]types.FieldIndex{types.LatestExecutionPayloadHeaderDeneb},
|
||||
withdrawalAndHistoricalSummaryFields,
|
||||
)
|
||||
|
||||
electraAdditionalFields = []types.FieldIndex{
|
||||
types.DepositRequestsStartIndex,
|
||||
types.DepositBalanceToConsume,
|
||||
types.ExitBalanceToConsume,
|
||||
@@ -106,12 +107,34 @@ var (
|
||||
types.PendingDeposits,
|
||||
types.PendingPartialWithdrawals,
|
||||
types.PendingConsolidations,
|
||||
}
|
||||
|
||||
electraFields = slices.Concat(
|
||||
denebFields,
|
||||
electraAdditionalFields,
|
||||
)
|
||||
|
||||
fuluFields = append(
|
||||
electraFields,
|
||||
types.ProposerLookahead,
|
||||
)
|
||||
|
||||
gloasAdditionalFields = []types.FieldIndex{
|
||||
types.ExecutionPayloadAvailability,
|
||||
types.BuilderPendingPayments,
|
||||
types.BuilderPendingWithdrawals,
|
||||
types.LatestBlockHash,
|
||||
types.LatestWithdrawalsRoot,
|
||||
}
|
||||
|
||||
gloasFields = slices.Concat(
|
||||
altairFields,
|
||||
[]types.FieldIndex{types.LatestExecutionPayloadBid},
|
||||
withdrawalAndHistoricalSummaryFields,
|
||||
electraAdditionalFields,
|
||||
[]types.FieldIndex{types.ProposerLookahead},
|
||||
gloasAdditionalFields,
|
||||
)
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -122,6 +145,7 @@ const (
|
||||
denebSharedFieldRefCount = 7
|
||||
electraSharedFieldRefCount = 10
|
||||
fuluSharedFieldRefCount = 11
|
||||
gloasSharedFieldRefCount = 12 // Adds PendingBuilderWithdrawal to the shared-ref set and LatestExecutionPayloadHeader is removed
|
||||
)
|
||||
|
||||
// InitializeFromProtoPhase0 the beacon state from a protobuf representation.
|
||||
@@ -159,6 +183,11 @@ func InitializeFromProtoFulu(st *ethpb.BeaconStateFulu) (state.BeaconState, erro
|
||||
return InitializeFromProtoUnsafeFulu(proto.Clone(st).(*ethpb.BeaconStateFulu))
|
||||
}
|
||||
|
||||
// InitializeFromProtoGloas the beacon state from a protobuf representation.
|
||||
func InitializeFromProtoGloas(st *ethpb.BeaconStateGloas) (state.BeaconState, error) {
|
||||
return InitializeFromProtoUnsafeGloas(proto.Clone(st).(*ethpb.BeaconStateGloas))
|
||||
}
|
||||
|
||||
// InitializeFromProtoUnsafePhase0 directly uses the beacon state protobuf fields
|
||||
// and sets them as fields of the BeaconState type.
|
||||
func InitializeFromProtoUnsafePhase0(st *ethpb.BeaconState) (state.BeaconState, error) {
|
||||
@@ -736,6 +765,111 @@ func InitializeFromProtoUnsafeFulu(st *ethpb.BeaconStateFulu) (state.BeaconState
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// InitializeFromProtoUnsafeGloas directly uses the beacon state protobuf fields
|
||||
// and sets them as fields of the BeaconState type.
|
||||
func InitializeFromProtoUnsafeGloas(st *ethpb.BeaconStateGloas) (state.BeaconState, error) {
|
||||
if st == nil {
|
||||
return nil, errors.New("received nil state")
|
||||
}
|
||||
|
||||
hRoots := customtypes.HistoricalRoots(make([][32]byte, len(st.HistoricalRoots)))
|
||||
for i, r := range st.HistoricalRoots {
|
||||
hRoots[i] = bytesutil.ToBytes32(r)
|
||||
}
|
||||
|
||||
proposerLookahead := make([]primitives.ValidatorIndex, len(st.ProposerLookahead))
|
||||
for i, v := range st.ProposerLookahead {
|
||||
proposerLookahead[i] = primitives.ValidatorIndex(v)
|
||||
}
|
||||
|
||||
fieldCount := params.BeaconConfig().BeaconStateGloasFieldCount
|
||||
b := &BeaconState{
|
||||
version: version.Gloas,
|
||||
genesisTime: st.GenesisTime,
|
||||
genesisValidatorsRoot: bytesutil.ToBytes32(st.GenesisValidatorsRoot),
|
||||
slot: st.Slot,
|
||||
fork: st.Fork,
|
||||
latestBlockHeader: st.LatestBlockHeader,
|
||||
historicalRoots: hRoots,
|
||||
eth1Data: st.Eth1Data,
|
||||
eth1DataVotes: st.Eth1DataVotes,
|
||||
eth1DepositIndex: st.Eth1DepositIndex,
|
||||
slashings: st.Slashings,
|
||||
previousEpochParticipation: st.PreviousEpochParticipation,
|
||||
currentEpochParticipation: st.CurrentEpochParticipation,
|
||||
justificationBits: st.JustificationBits,
|
||||
previousJustifiedCheckpoint: st.PreviousJustifiedCheckpoint,
|
||||
currentJustifiedCheckpoint: st.CurrentJustifiedCheckpoint,
|
||||
finalizedCheckpoint: st.FinalizedCheckpoint,
|
||||
currentSyncCommittee: st.CurrentSyncCommittee,
|
||||
nextSyncCommittee: st.NextSyncCommittee,
|
||||
nextWithdrawalIndex: st.NextWithdrawalIndex,
|
||||
nextWithdrawalValidatorIndex: st.NextWithdrawalValidatorIndex,
|
||||
historicalSummaries: st.HistoricalSummaries,
|
||||
depositRequestsStartIndex: st.DepositRequestsStartIndex,
|
||||
depositBalanceToConsume: st.DepositBalanceToConsume,
|
||||
exitBalanceToConsume: st.ExitBalanceToConsume,
|
||||
earliestExitEpoch: st.EarliestExitEpoch,
|
||||
consolidationBalanceToConsume: st.ConsolidationBalanceToConsume,
|
||||
earliestConsolidationEpoch: st.EarliestConsolidationEpoch,
|
||||
pendingDeposits: st.PendingDeposits,
|
||||
pendingPartialWithdrawals: st.PendingPartialWithdrawals,
|
||||
pendingConsolidations: st.PendingConsolidations,
|
||||
proposerLookahead: proposerLookahead,
|
||||
latestExecutionPayloadBid: st.LatestExecutionPayloadBid,
|
||||
executionPayloadAvailability: st.ExecutionPayloadAvailability,
|
||||
builderPendingPayments: st.BuilderPendingPayments,
|
||||
builderPendingWithdrawals: st.BuilderPendingWithdrawals,
|
||||
latestBlockHash: st.LatestBlockHash,
|
||||
latestWithdrawalsRoot: st.LatestWithdrawalsRoot,
|
||||
dirtyFields: make(map[types.FieldIndex]bool, fieldCount),
|
||||
dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount),
|
||||
stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount),
|
||||
rebuildTrie: make(map[types.FieldIndex]bool, fieldCount),
|
||||
valMapHandler: stateutil.NewValMapHandler(st.Validators),
|
||||
}
|
||||
|
||||
b.blockRootsMultiValue = NewMultiValueBlockRoots(st.BlockRoots)
|
||||
b.stateRootsMultiValue = NewMultiValueStateRoots(st.StateRoots)
|
||||
b.randaoMixesMultiValue = NewMultiValueRandaoMixes(st.RandaoMixes)
|
||||
b.balancesMultiValue = NewMultiValueBalances(st.Balances)
|
||||
b.validatorsMultiValue = NewMultiValueValidators(st.Validators)
|
||||
b.inactivityScoresMultiValue = NewMultiValueInactivityScores(st.InactivityScores)
|
||||
b.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, gloasSharedFieldRefCount)
|
||||
|
||||
for _, f := range gloasFields {
|
||||
b.dirtyFields[f] = true
|
||||
b.rebuildTrie[f] = true
|
||||
b.dirtyIndices[f] = []uint64{}
|
||||
|
||||
trie, err := fieldtrie.NewFieldTrie(f, types.BasicArray, nil, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b.stateFieldLeaves[f] = trie
|
||||
}
|
||||
|
||||
// Initialize field reference tracking for shared data.
|
||||
b.sharedFieldReferences[types.HistoricalRoots] = stateutil.NewRef(1)
|
||||
b.sharedFieldReferences[types.Eth1DataVotes] = stateutil.NewRef(1)
|
||||
b.sharedFieldReferences[types.Slashings] = stateutil.NewRef(1)
|
||||
b.sharedFieldReferences[types.PreviousEpochParticipationBits] = stateutil.NewRef(1)
|
||||
b.sharedFieldReferences[types.CurrentEpochParticipationBits] = stateutil.NewRef(1)
|
||||
b.sharedFieldReferences[types.HistoricalSummaries] = stateutil.NewRef(1)
|
||||
b.sharedFieldReferences[types.PendingDeposits] = stateutil.NewRef(1)
|
||||
b.sharedFieldReferences[types.PendingPartialWithdrawals] = stateutil.NewRef(1)
|
||||
b.sharedFieldReferences[types.PendingConsolidations] = stateutil.NewRef(1)
|
||||
b.sharedFieldReferences[types.ProposerLookahead] = stateutil.NewRef(1)
|
||||
b.sharedFieldReferences[types.BuilderPendingWithdrawals] = stateutil.NewRef(1) // New in Gloas.
|
||||
|
||||
state.Count.Inc()
|
||||
// Finalizer runs when dst is being destroyed in garbage collection.
|
||||
runtime.SetFinalizer(b, finalizerCleanup)
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Copy returns a deep copy of the beacon state.
|
||||
func (b *BeaconState) Copy() state.BeaconState {
|
||||
b.lock.RLock()
|
||||
@@ -757,6 +891,8 @@ func (b *BeaconState) Copy() state.BeaconState {
|
||||
fieldCount = params.BeaconConfig().BeaconStateElectraFieldCount
|
||||
case version.Fulu:
|
||||
fieldCount = params.BeaconConfig().BeaconStateFuluFieldCount
|
||||
case version.Gloas:
|
||||
fieldCount = params.BeaconConfig().BeaconStateGloasFieldCount
|
||||
}
|
||||
|
||||
dst := &BeaconState{
|
||||
@@ -811,6 +947,12 @@ func (b *BeaconState) Copy() state.BeaconState {
|
||||
latestExecutionPayloadHeader: b.latestExecutionPayloadHeader.Copy(),
|
||||
latestExecutionPayloadHeaderCapella: b.latestExecutionPayloadHeaderCapella.Copy(),
|
||||
latestExecutionPayloadHeaderDeneb: b.latestExecutionPayloadHeaderDeneb.Copy(),
|
||||
latestExecutionPayloadBid: b.latestExecutionPayloadBid.Copy(),
|
||||
executionPayloadAvailability: b.executionPayloadAvailabilityVal(),
|
||||
builderPendingPayments: b.builderPendingPaymentsVal(),
|
||||
builderPendingWithdrawals: b.builderPendingWithdrawalsVal(),
|
||||
latestBlockHash: b.latestBlockHashVal(),
|
||||
latestWithdrawalsRoot: b.latestWithdrawalsRootVal(),
|
||||
|
||||
id: types.Enumerator.Inc(),
|
||||
|
||||
@@ -847,6 +989,8 @@ func (b *BeaconState) Copy() state.BeaconState {
|
||||
dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, electraSharedFieldRefCount)
|
||||
case version.Fulu:
|
||||
dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, fuluSharedFieldRefCount)
|
||||
case version.Gloas:
|
||||
dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, gloasSharedFieldRefCount)
|
||||
}
|
||||
|
||||
for field, ref := range b.sharedFieldReferences {
|
||||
@@ -942,6 +1086,8 @@ func (b *BeaconState) initializeMerkleLayers(ctx context.Context) error {
|
||||
b.dirtyFields = make(map[types.FieldIndex]bool, params.BeaconConfig().BeaconStateElectraFieldCount)
|
||||
case version.Fulu:
|
||||
b.dirtyFields = make(map[types.FieldIndex]bool, params.BeaconConfig().BeaconStateFuluFieldCount)
|
||||
case version.Gloas:
|
||||
b.dirtyFields = make(map[types.FieldIndex]bool, params.BeaconConfig().BeaconStateGloasFieldCount)
|
||||
default:
|
||||
return fmt.Errorf("unknown state version (%s) when computing dirty fields in merklization", version.String(b.version))
|
||||
}
|
||||
@@ -1180,6 +1326,19 @@ func (b *BeaconState) rootSelector(ctx context.Context, field types.FieldIndex)
|
||||
return stateutil.PendingConsolidationsRoot(b.pendingConsolidations)
|
||||
case types.ProposerLookahead:
|
||||
return stateutil.ProposerLookaheadRoot(b.proposerLookahead)
|
||||
case types.LatestExecutionPayloadBid:
|
||||
return b.latestExecutionPayloadBid.HashTreeRoot()
|
||||
case types.ExecutionPayloadAvailability:
|
||||
return stateutil.ExecutionPayloadAvailabilityRoot(b.executionPayloadAvailability)
|
||||
|
||||
case types.BuilderPendingPayments:
|
||||
return stateutil.BuilderPendingPaymentsRoot(b.builderPendingPayments)
|
||||
case types.BuilderPendingWithdrawals:
|
||||
return stateutil.BuilderPendingWithdrawalsRoot(b.builderPendingWithdrawals)
|
||||
case types.LatestBlockHash:
|
||||
return bytesutil.ToBytes32(b.latestBlockHash), nil
|
||||
case types.LatestWithdrawalsRoot:
|
||||
return bytesutil.ToBytes32(b.latestWithdrawalsRoot), nil
|
||||
}
|
||||
return [32]byte{}, errors.New("invalid field index provided")
|
||||
}
|
||||
|
||||
@@ -88,6 +88,8 @@ func (f FieldIndex) String() string {
|
||||
return "latestExecutionPayloadHeaderCapella"
|
||||
case LatestExecutionPayloadHeaderDeneb:
|
||||
return "latestExecutionPayloadHeaderDeneb"
|
||||
case LatestExecutionPayloadBid:
|
||||
return "latestExecutionPayloadBid"
|
||||
case NextWithdrawalIndex:
|
||||
return "nextWithdrawalIndex"
|
||||
case NextWithdrawalValidatorIndex:
|
||||
@@ -114,6 +116,16 @@ func (f FieldIndex) String() string {
|
||||
return "pendingConsolidations"
|
||||
case ProposerLookahead:
|
||||
return "proposerLookahead"
|
||||
case ExecutionPayloadAvailability:
|
||||
return "executionPayloadAvailability"
|
||||
case BuilderPendingPayments:
|
||||
return "builderPendingPayments"
|
||||
case BuilderPendingWithdrawals:
|
||||
return "builderPendingWithdrawals"
|
||||
case LatestBlockHash:
|
||||
return "latestBlockHash"
|
||||
case LatestWithdrawalsRoot:
|
||||
return "latestWithdrawalsRoot"
|
||||
default:
|
||||
return fmt.Sprintf("unknown field index number: %d", f)
|
||||
}
|
||||
@@ -171,7 +183,7 @@ func (f FieldIndex) RealPosition() int {
|
||||
return 22
|
||||
case NextSyncCommittee:
|
||||
return 23
|
||||
case LatestExecutionPayloadHeader, LatestExecutionPayloadHeaderCapella, LatestExecutionPayloadHeaderDeneb:
|
||||
case LatestExecutionPayloadHeader, LatestExecutionPayloadHeaderCapella, LatestExecutionPayloadHeaderDeneb, LatestExecutionPayloadBid:
|
||||
return 24
|
||||
case NextWithdrawalIndex:
|
||||
return 25
|
||||
@@ -199,6 +211,16 @@ func (f FieldIndex) RealPosition() int {
|
||||
return 36
|
||||
case ProposerLookahead:
|
||||
return 37
|
||||
case ExecutionPayloadAvailability:
|
||||
return 38
|
||||
case BuilderPendingPayments:
|
||||
return 39
|
||||
case BuilderPendingWithdrawals:
|
||||
return 40
|
||||
case LatestBlockHash:
|
||||
return 41
|
||||
case LatestWithdrawalsRoot:
|
||||
return 42
|
||||
default:
|
||||
return -1
|
||||
}
|
||||
@@ -251,6 +273,7 @@ const (
|
||||
LatestExecutionPayloadHeader
|
||||
LatestExecutionPayloadHeaderCapella
|
||||
LatestExecutionPayloadHeaderDeneb
|
||||
LatestExecutionPayloadBid // Gloas: EIP-7732
|
||||
NextWithdrawalIndex
|
||||
NextWithdrawalValidatorIndex
|
||||
HistoricalSummaries
|
||||
@@ -264,6 +287,11 @@ const (
|
||||
PendingPartialWithdrawals // Electra: EIP-7251
|
||||
PendingConsolidations // Electra: EIP-7251
|
||||
ProposerLookahead // Fulu: EIP-7917
|
||||
ExecutionPayloadAvailability // Gloas: EIP-7732
|
||||
BuilderPendingPayments // Gloas: EIP-7732
|
||||
BuilderPendingWithdrawals // Gloas: EIP-7732
|
||||
LatestBlockHash // Gloas: EIP-7732
|
||||
LatestWithdrawalsRoot // Gloas: EIP-7732
|
||||
)
|
||||
|
||||
// Enumerator keeps track of the number of states created since the node's start.
|
||||
|
||||
@@ -4,7 +4,10 @@ go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"block_header_root.go",
|
||||
"builder_pending_payments_root.go",
|
||||
"builder_pending_withdrawals_root.go",
|
||||
"eth1_root.go",
|
||||
"execution_payload_availability_root.go",
|
||||
"field_root_attestation.go",
|
||||
"field_root_eth1.go",
|
||||
"field_root_validator.go",
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package stateutil
|
||||
|
||||
import (
|
||||
"github.com/OffchainLabs/prysm/v7/encoding/ssz"
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
// BuilderPendingPaymentsRoot computes the merkle root of a slice of BuilderPendingPayment.
|
||||
func BuilderPendingPaymentsRoot(slice []*ethpb.BuilderPendingPayment) ([32]byte, error) {
|
||||
roots := make([][32]byte, len(slice))
|
||||
|
||||
for i, payment := range slice {
|
||||
r, err := payment.HashTreeRoot()
|
||||
if err != nil {
|
||||
return [32]byte{}, err
|
||||
}
|
||||
|
||||
roots[i] = r
|
||||
}
|
||||
|
||||
return ssz.MerkleizeVector(roots, uint64(len(roots))), nil
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package stateutil
|
||||
|
||||
import (
|
||||
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
|
||||
"github.com/OffchainLabs/prysm/v7/encoding/ssz"
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
// BuilderPendingWithdrawalsRoot computes the SSZ root of a slice of BuilderPendingWithdrawal.
|
||||
func BuilderPendingWithdrawalsRoot(slice []*ethpb.BuilderPendingWithdrawal) ([32]byte, error) {
|
||||
return ssz.SliceRoot(slice, fieldparams.BuilderPendingWithdrawalsLimit)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package stateutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/encoding/ssz"
|
||||
)
|
||||
|
||||
// ExecutionPayloadAvailabilityRoot computes the merkle root of an execution payload availability bitvector.
|
||||
func ExecutionPayloadAvailabilityRoot(bitvector []byte) ([32]byte, error) {
|
||||
chunkCount := (len(bitvector) + 31) / 32
|
||||
chunks := make([][32]byte, chunkCount)
|
||||
|
||||
for i := range chunks {
|
||||
start := i * 32
|
||||
end := min(start+32, len(bitvector))
|
||||
copy(chunks[i][:], bitvector[start:end])
|
||||
}
|
||||
|
||||
root, err := ssz.BitwiseMerkleize(chunks, uint64(len(chunks)), uint64(len(chunks)))
|
||||
if err != nil {
|
||||
return [32]byte{}, fmt.Errorf("could not merkleize execution payload availability: %w", err)
|
||||
}
|
||||
return root, nil
|
||||
}
|
||||
@@ -80,17 +80,10 @@ func (s *Service) updateCustodyInfoIfNeeded() error {
|
||||
return errors.Wrap(err, "p2p update custody info")
|
||||
}
|
||||
|
||||
// Update the p2p earliest available slot metric
|
||||
earliestAvailableSlotP2P.Set(float64(storedEarliestSlot))
|
||||
|
||||
dbEarliestSlot, _, err := s.cfg.beaconDB.UpdateCustodyInfo(s.ctx, storedEarliestSlot, storedGroupCount)
|
||||
if err != nil {
|
||||
if _, _, err := s.cfg.beaconDB.UpdateCustodyInfo(s.ctx, storedEarliestSlot, storedGroupCount); err != nil {
|
||||
return errors.Wrap(err, "beacon db update custody info")
|
||||
}
|
||||
|
||||
// Update the DB earliest available slot metric
|
||||
earliestAvailableSlotDB.Set(float64(dbEarliestSlot))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -133,6 +133,12 @@ var (
|
||||
Help: "Time to verify gossiped attestations",
|
||||
},
|
||||
)
|
||||
attestationVerificationGossipSummary = promauto.NewSummary(
|
||||
prometheus.SummaryOpts{
|
||||
Name: "gossip_attestation_verification_milliseconds",
|
||||
Help: "Time to verify gossiped attestations",
|
||||
},
|
||||
)
|
||||
blockVerificationGossipSummary = promauto.NewSummary(
|
||||
prometheus.SummaryOpts{
|
||||
Name: "gossip_block_verification_milliseconds",
|
||||
@@ -230,17 +236,6 @@ var (
|
||||
Buckets: []float64{100, 250, 500, 750, 1000, 1500, 2000, 4000, 8000, 12000, 16000},
|
||||
},
|
||||
)
|
||||
|
||||
// Custody earliest available slot metrics
|
||||
earliestAvailableSlotP2P = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "custody_earliest_available_slot_p2p",
|
||||
Help: "The earliest available slot tracked by the p2p service for custody purposes",
|
||||
})
|
||||
|
||||
earliestAvailableSlotDB = promauto.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "custody_earliest_available_slot_db",
|
||||
Help: "The earliest available slot tracked by the database for custody purposes",
|
||||
})
|
||||
)
|
||||
|
||||
func (s *Service) updateMetrics() {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"reflect"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/blocks"
|
||||
@@ -41,6 +42,11 @@ func (s *Service) validateCommitteeIndexBeaconAttestation(
|
||||
pid peer.ID,
|
||||
msg *pubsub.Message,
|
||||
) (pubsub.ValidationResult, error) {
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
attestationVerificationGossipSummary.Observe(float64(time.Since(start).Milliseconds()))
|
||||
}()
|
||||
|
||||
if pid == s.cfg.p2p.PeerID() {
|
||||
return pubsub.ValidationAccept, nil
|
||||
}
|
||||
|
||||
@@ -75,17 +75,8 @@ func (s *Service) validateBeaconBlockPubSub(ctx context.Context, pid peer.ID, ms
|
||||
return pubsub.ValidationReject, errors.New("block.Block is nil")
|
||||
}
|
||||
|
||||
// Broadcast the block on both block and operation feeds to notify other services in the beacon node
|
||||
// Broadcast the block on a feed to notify other services in the beacon node
|
||||
// of a received block (even if it does not process correctly through a state transition).
|
||||
if s.cfg.operationNotifier != nil {
|
||||
s.cfg.operationNotifier.OperationFeed().Send(&feed.Event{
|
||||
Type: operation.BlockGossipReceived,
|
||||
Data: &operation.BlockGossipReceivedData{
|
||||
SignedBlock: blk,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
s.cfg.blockNotifier.BlockFeed().Send(&feed.Event{
|
||||
Type: blockfeed.ReceivedBlock,
|
||||
Data: &blockfeed.ReceivedBlockData{
|
||||
@@ -247,6 +238,16 @@ func (s *Service) validateBeaconBlockPubSub(ctx context.Context, pid peer.ID, ms
|
||||
|
||||
blockArrivalGossipSummary.Observe(float64(sinceSlotStartTime.Milliseconds()))
|
||||
blockVerificationGossipSummary.Observe(float64(validationTime.Milliseconds()))
|
||||
|
||||
if s.cfg.operationNotifier != nil {
|
||||
s.cfg.operationNotifier.OperationFeed().Send(&feed.Event{
|
||||
Type: operation.BlockGossipReceived,
|
||||
Data: &operation.BlockGossipReceivedData{
|
||||
SignedBlock: blk,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return pubsub.ValidationAccept, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/async/abool"
|
||||
mock "github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/testing"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/feed"
|
||||
opfeed "github.com/OffchainLabs/prysm/v7/beacon-chain/core/feed/operation"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/helpers"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/signing"
|
||||
coreTime "github.com/OffchainLabs/prysm/v7/beacon-chain/core/time"
|
||||
@@ -79,17 +81,21 @@ func TestValidateBeaconBlockPubSub_InvalidSignature(t *testing.T) {
|
||||
}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
stateGen: stateGen,
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
operationNotifier: chainService.OperationNotifier(),
|
||||
stateGen: stateGen,
|
||||
},
|
||||
seenBlockCache: lruwrpr.New(10),
|
||||
badBlockCache: lruwrpr.New(10),
|
||||
}
|
||||
opChannel := make(chan *feed.Event, 1)
|
||||
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
|
||||
defer opSub.Unsubscribe()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = p.Encoding().EncodeGossip(buf, msg)
|
||||
@@ -108,6 +114,13 @@ func TestValidateBeaconBlockPubSub_InvalidSignature(t *testing.T) {
|
||||
require.ErrorContains(t, "invalid signature", err)
|
||||
result := res == pubsub.ValidationReject
|
||||
assert.Equal(t, true, result)
|
||||
|
||||
select {
|
||||
case event := <-opChannel:
|
||||
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
|
||||
default:
|
||||
// this case is needed, otherwise the test will never finish
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateBeaconBlockPubSub_InvalidSignature_MarksBlockAsBad(t *testing.T) {
|
||||
@@ -145,17 +158,21 @@ func TestValidateBeaconBlockPubSub_InvalidSignature_MarksBlockAsBad(t *testing.T
|
||||
}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
stateGen: stateGen,
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
operationNotifier: chainService.OperationNotifier(),
|
||||
stateGen: stateGen,
|
||||
},
|
||||
seenBlockCache: lruwrpr.New(10),
|
||||
badBlockCache: lruwrpr.New(10),
|
||||
}
|
||||
opChannel := make(chan *feed.Event, 1)
|
||||
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
|
||||
defer opSub.Unsubscribe()
|
||||
|
||||
blockRoot, err := msg.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
@@ -183,6 +200,13 @@ func TestValidateBeaconBlockPubSub_InvalidSignature_MarksBlockAsBad(t *testing.T
|
||||
|
||||
// Verify block is now marked as bad after invalid signature
|
||||
assert.Equal(t, true, r.hasBadBlock(blockRoot), "block should be marked as bad after invalid signature")
|
||||
|
||||
select {
|
||||
case event := <-opChannel:
|
||||
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
|
||||
default:
|
||||
// this case is needed, otherwise the test will never finish
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateBeaconBlockPubSub_BlockAlreadyPresentInDB(t *testing.T) {
|
||||
@@ -198,16 +222,20 @@ func TestValidateBeaconBlockPubSub_BlockAlreadyPresentInDB(t *testing.T) {
|
||||
chainService := &mock.ChainService{Genesis: time.Now()}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
operationNotifier: chainService.OperationNotifier(),
|
||||
},
|
||||
seenBlockCache: lruwrpr.New(10),
|
||||
badBlockCache: lruwrpr.New(10),
|
||||
}
|
||||
opChannel := make(chan *feed.Event, 1)
|
||||
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
|
||||
defer opSub.Unsubscribe()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err := p.Encoding().EncodeGossip(buf, msg)
|
||||
@@ -226,6 +254,13 @@ func TestValidateBeaconBlockPubSub_BlockAlreadyPresentInDB(t *testing.T) {
|
||||
res, err := r.validateBeaconBlockPubSub(ctx, "", m)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, res, pubsub.ValidationIgnore, "block present in DB should be ignored")
|
||||
|
||||
select {
|
||||
case event := <-opChannel:
|
||||
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
|
||||
default:
|
||||
// this case is needed, otherwise the test will never finish
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateBeaconBlockPubSub_CanRecoverStateSummary(t *testing.T) {
|
||||
@@ -260,19 +295,24 @@ func TestValidateBeaconBlockPubSub_CanRecoverStateSummary(t *testing.T) {
|
||||
}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
stateGen: stateGen,
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
operationNotifier: chainService.OperationNotifier(),
|
||||
stateGen: stateGen,
|
||||
},
|
||||
seenBlockCache: lruwrpr.New(10),
|
||||
badBlockCache: lruwrpr.New(10),
|
||||
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
|
||||
seenPendingBlocks: make(map[[32]byte]bool),
|
||||
}
|
||||
opChannel := make(chan *feed.Event, 1)
|
||||
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
|
||||
defer opSub.Unsubscribe()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = p.Encoding().EncodeGossip(buf, msg)
|
||||
require.NoError(t, err)
|
||||
@@ -291,6 +331,17 @@ func TestValidateBeaconBlockPubSub_CanRecoverStateSummary(t *testing.T) {
|
||||
result := res == pubsub.ValidationAccept
|
||||
assert.Equal(t, true, result)
|
||||
assert.NotNil(t, m.ValidatorData, "Decoded message was not set on the message validator data")
|
||||
|
||||
blockGossipFound := false
|
||||
select {
|
||||
case event := <-opChannel:
|
||||
if event.Type == opfeed.BlockGossipReceived {
|
||||
blockGossipFound = true
|
||||
}
|
||||
default:
|
||||
// this case is needed, otherwise the test will never finish
|
||||
}
|
||||
assert.Equal(t, true, blockGossipFound, "BlockGossipReceived event should be sent")
|
||||
}
|
||||
|
||||
func TestValidateBeaconBlockPubSub_IsInCache(t *testing.T) {
|
||||
@@ -326,19 +377,24 @@ func TestValidateBeaconBlockPubSub_IsInCache(t *testing.T) {
|
||||
}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
stateGen: stateGen,
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
operationNotifier: chainService.OperationNotifier(),
|
||||
stateGen: stateGen,
|
||||
},
|
||||
seenBlockCache: lruwrpr.New(10),
|
||||
badBlockCache: lruwrpr.New(10),
|
||||
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
|
||||
seenPendingBlocks: make(map[[32]byte]bool),
|
||||
}
|
||||
opChannel := make(chan *feed.Event, 1)
|
||||
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
|
||||
defer opSub.Unsubscribe()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = p.Encoding().EncodeGossip(buf, msg)
|
||||
require.NoError(t, err)
|
||||
@@ -357,6 +413,17 @@ func TestValidateBeaconBlockPubSub_IsInCache(t *testing.T) {
|
||||
result := res == pubsub.ValidationAccept
|
||||
assert.Equal(t, true, result)
|
||||
assert.NotNil(t, m.ValidatorData, "Decoded message was not set on the message validator data")
|
||||
|
||||
blockGossipFound := false
|
||||
select {
|
||||
case event := <-opChannel:
|
||||
if event.Type == opfeed.BlockGossipReceived {
|
||||
blockGossipFound = true
|
||||
}
|
||||
default:
|
||||
// this case is needed, otherwise the test will never finish
|
||||
}
|
||||
assert.Equal(t, true, blockGossipFound, "BlockGossipReceived event should be sent")
|
||||
}
|
||||
|
||||
func TestValidateBeaconBlockPubSub_ValidProposerSignature(t *testing.T) {
|
||||
@@ -392,19 +459,24 @@ func TestValidateBeaconBlockPubSub_ValidProposerSignature(t *testing.T) {
|
||||
}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
stateGen: stateGen,
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
operationNotifier: chainService.OperationNotifier(),
|
||||
stateGen: stateGen,
|
||||
},
|
||||
seenBlockCache: lruwrpr.New(10),
|
||||
badBlockCache: lruwrpr.New(10),
|
||||
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
|
||||
seenPendingBlocks: make(map[[32]byte]bool),
|
||||
}
|
||||
opChannel := make(chan *feed.Event, 1)
|
||||
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
|
||||
defer opSub.Unsubscribe()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = p.Encoding().EncodeGossip(buf, msg)
|
||||
require.NoError(t, err)
|
||||
@@ -423,6 +495,17 @@ func TestValidateBeaconBlockPubSub_ValidProposerSignature(t *testing.T) {
|
||||
result := res == pubsub.ValidationAccept
|
||||
assert.Equal(t, true, result)
|
||||
assert.NotNil(t, m.ValidatorData, "Decoded message was not set on the message validator data")
|
||||
|
||||
blockGossipFound := false
|
||||
select {
|
||||
case event := <-opChannel:
|
||||
if event.Type == opfeed.BlockGossipReceived {
|
||||
blockGossipFound = true
|
||||
}
|
||||
default:
|
||||
// this case is needed, otherwise the test will never finish
|
||||
}
|
||||
assert.Equal(t, true, blockGossipFound, "BlockGossipReceived event should be sent")
|
||||
}
|
||||
|
||||
func TestValidateBeaconBlockPubSub_WithLookahead(t *testing.T) {
|
||||
@@ -460,13 +543,14 @@ func TestValidateBeaconBlockPubSub_WithLookahead(t *testing.T) {
|
||||
}}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
stateGen: stateGen,
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
operationNotifier: chainService.OperationNotifier(),
|
||||
stateGen: stateGen,
|
||||
},
|
||||
seenBlockCache: lruwrpr.New(10),
|
||||
badBlockCache: lruwrpr.New(10),
|
||||
@@ -474,6 +558,10 @@ func TestValidateBeaconBlockPubSub_WithLookahead(t *testing.T) {
|
||||
seenPendingBlocks: make(map[[32]byte]bool),
|
||||
subHandler: newSubTopicHandler(),
|
||||
}
|
||||
opChannel := make(chan *feed.Event, 1)
|
||||
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
|
||||
defer opSub.Unsubscribe()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = p.Encoding().EncodeGossip(buf, msg)
|
||||
require.NoError(t, err)
|
||||
@@ -492,6 +580,17 @@ func TestValidateBeaconBlockPubSub_WithLookahead(t *testing.T) {
|
||||
result := res == pubsub.ValidationAccept
|
||||
assert.Equal(t, true, result)
|
||||
assert.NotNil(t, m.ValidatorData, "Decoded message was not set on the message validator data")
|
||||
|
||||
blockGossipFound := false
|
||||
select {
|
||||
case event := <-opChannel:
|
||||
if event.Type == opfeed.BlockGossipReceived {
|
||||
blockGossipFound = true
|
||||
}
|
||||
default:
|
||||
// this case is needed, otherwise the test will never finish
|
||||
}
|
||||
assert.Equal(t, true, blockGossipFound, "BlockGossipReceived event should be sent")
|
||||
}
|
||||
|
||||
func TestValidateBeaconBlockPubSub_AdvanceEpochsForState(t *testing.T) {
|
||||
@@ -529,19 +628,24 @@ func TestValidateBeaconBlockPubSub_AdvanceEpochsForState(t *testing.T) {
|
||||
}}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
stateGen: stateGen,
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
operationNotifier: chainService.OperationNotifier(),
|
||||
stateGen: stateGen,
|
||||
},
|
||||
seenBlockCache: lruwrpr.New(10),
|
||||
badBlockCache: lruwrpr.New(10),
|
||||
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
|
||||
seenPendingBlocks: make(map[[32]byte]bool),
|
||||
}
|
||||
opChannel := make(chan *feed.Event, 1)
|
||||
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
|
||||
defer opSub.Unsubscribe()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = p.Encoding().EncodeGossip(buf, msg)
|
||||
require.NoError(t, err)
|
||||
@@ -560,6 +664,17 @@ func TestValidateBeaconBlockPubSub_AdvanceEpochsForState(t *testing.T) {
|
||||
result := res == pubsub.ValidationAccept
|
||||
assert.Equal(t, true, result)
|
||||
assert.NotNil(t, m.ValidatorData, "Decoded message was not set on the message validator data")
|
||||
|
||||
blockGossipFound := false
|
||||
select {
|
||||
case event := <-opChannel:
|
||||
if event.Type == opfeed.BlockGossipReceived {
|
||||
blockGossipFound = true
|
||||
}
|
||||
default:
|
||||
// this case is needed, otherwise the test will never finish
|
||||
}
|
||||
assert.Equal(t, true, blockGossipFound, "BlockGossipReceived event should be sent")
|
||||
}
|
||||
|
||||
func TestValidateBeaconBlockPubSub_Syncing(t *testing.T) {
|
||||
@@ -580,13 +695,17 @@ func TestValidateBeaconBlockPubSub_Syncing(t *testing.T) {
|
||||
}}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: true},
|
||||
chain: chainService,
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: true},
|
||||
chain: chainService,
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
operationNotifier: chainService.OperationNotifier(),
|
||||
},
|
||||
}
|
||||
opChannel := make(chan *feed.Event, 1)
|
||||
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
|
||||
defer opSub.Unsubscribe()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = p.Encoding().EncodeGossip(buf, msg)
|
||||
@@ -601,6 +720,13 @@ func TestValidateBeaconBlockPubSub_Syncing(t *testing.T) {
|
||||
res, err := r.validateBeaconBlockPubSub(ctx, "", m)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, res, pubsub.ValidationIgnore, "block is ignored until fully synced")
|
||||
|
||||
select {
|
||||
case event := <-opChannel:
|
||||
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
|
||||
default:
|
||||
// this case is needed, otherwise the test will never finish
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateBeaconBlockPubSub_IgnoreAndQueueBlocksFromNearFuture(t *testing.T) {
|
||||
@@ -636,13 +762,14 @@ func TestValidateBeaconBlockPubSub_IgnoreAndQueueBlocksFromNearFuture(t *testing
|
||||
State: beaconState}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
p2p: p,
|
||||
beaconDB: db,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
stateGen: stateGen,
|
||||
p2p: p,
|
||||
beaconDB: db,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
operationNotifier: chainService.OperationNotifier(),
|
||||
stateGen: stateGen,
|
||||
},
|
||||
chainStarted: abool.New(),
|
||||
seenBlockCache: lruwrpr.New(10),
|
||||
@@ -650,6 +777,9 @@ func TestValidateBeaconBlockPubSub_IgnoreAndQueueBlocksFromNearFuture(t *testing
|
||||
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
|
||||
seenPendingBlocks: make(map[[32]byte]bool),
|
||||
}
|
||||
opChannel := make(chan *feed.Event, 1)
|
||||
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
|
||||
defer opSub.Unsubscribe()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = p.Encoding().EncodeGossip(buf, msg)
|
||||
@@ -670,6 +800,13 @@ func TestValidateBeaconBlockPubSub_IgnoreAndQueueBlocksFromNearFuture(t *testing
|
||||
|
||||
// check if the block is inserted in the Queue
|
||||
assert.Equal(t, true, len(r.pendingBlocksInCache(msg.Block.Slot)) == 1)
|
||||
|
||||
select {
|
||||
case event := <-opChannel:
|
||||
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
|
||||
default:
|
||||
// this case is needed, otherwise the test will never finish
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateBeaconBlockPubSub_RejectBlocksFromFuture(t *testing.T) {
|
||||
@@ -688,12 +825,13 @@ func TestValidateBeaconBlockPubSub_RejectBlocksFromFuture(t *testing.T) {
|
||||
chainService := &mock.ChainService{Genesis: time.Now()}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
p2p: p,
|
||||
beaconDB: db,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
p2p: p,
|
||||
beaconDB: db,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
operationNotifier: chainService.OperationNotifier(),
|
||||
},
|
||||
chainStarted: abool.New(),
|
||||
seenBlockCache: lruwrpr.New(10),
|
||||
@@ -701,6 +839,9 @@ func TestValidateBeaconBlockPubSub_RejectBlocksFromFuture(t *testing.T) {
|
||||
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
|
||||
seenPendingBlocks: make(map[[32]byte]bool),
|
||||
}
|
||||
opChannel := make(chan *feed.Event, 1)
|
||||
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
|
||||
defer opSub.Unsubscribe()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = p.Encoding().EncodeGossip(buf, msg)
|
||||
@@ -718,6 +859,13 @@ func TestValidateBeaconBlockPubSub_RejectBlocksFromFuture(t *testing.T) {
|
||||
res, err := r.validateBeaconBlockPubSub(ctx, "", m)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, res, pubsub.ValidationIgnore, "block from the future should be ignored")
|
||||
|
||||
select {
|
||||
case event := <-opChannel:
|
||||
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
|
||||
default:
|
||||
// this case is needed, otherwise the test will never finish
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateBeaconBlockPubSub_RejectBlocksFromThePast(t *testing.T) {
|
||||
@@ -742,16 +890,20 @@ func TestValidateBeaconBlockPubSub_RejectBlocksFromThePast(t *testing.T) {
|
||||
}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
operationNotifier: chainService.OperationNotifier(),
|
||||
},
|
||||
seenBlockCache: lruwrpr.New(10),
|
||||
badBlockCache: lruwrpr.New(10),
|
||||
}
|
||||
opChannel := make(chan *feed.Event, 1)
|
||||
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
|
||||
defer opSub.Unsubscribe()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = p.Encoding().EncodeGossip(buf, msg)
|
||||
@@ -769,6 +921,13 @@ func TestValidateBeaconBlockPubSub_RejectBlocksFromThePast(t *testing.T) {
|
||||
res, err := r.validateBeaconBlockPubSub(ctx, "", m)
|
||||
require.ErrorContains(t, "greater or equal to block slot", err)
|
||||
assert.Equal(t, res, pubsub.ValidationIgnore, "block from the past should be ignored")
|
||||
|
||||
select {
|
||||
case event := <-opChannel:
|
||||
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
|
||||
default:
|
||||
// this case is needed, otherwise the test will never finish
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateBeaconBlockPubSub_SeenProposerSlot(t *testing.T) {
|
||||
@@ -813,19 +972,23 @@ func TestValidateBeaconBlockPubSub_SeenProposerSlot(t *testing.T) {
|
||||
}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
slashingPool: slashingPool,
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
operationNotifier: chainService.OperationNotifier(),
|
||||
slashingPool: slashingPool,
|
||||
},
|
||||
seenBlockCache: lruwrpr.New(10),
|
||||
badBlockCache: lruwrpr.New(10),
|
||||
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
|
||||
seenPendingBlocks: make(map[[32]byte]bool),
|
||||
}
|
||||
opChannel := make(chan *feed.Event, 1)
|
||||
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
|
||||
defer opSub.Unsubscribe()
|
||||
|
||||
// Mark the proposer/slot as seen
|
||||
r.setSeenBlockIndexSlot(msg.Block.Slot, msg.Block.ProposerIndex)
|
||||
@@ -853,6 +1016,13 @@ func TestValidateBeaconBlockPubSub_SeenProposerSlot(t *testing.T) {
|
||||
|
||||
// Verify no slashings were created
|
||||
assert.Equal(t, 0, len(slashingPool.PendingPropSlashings), "Expected no slashings for same signature")
|
||||
|
||||
select {
|
||||
case event := <-opChannel:
|
||||
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
|
||||
default:
|
||||
// this case is needed, otherwise the test will never finish
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateBeaconBlockPubSub_FilterByFinalizedEpoch(t *testing.T) {
|
||||
@@ -875,17 +1045,21 @@ func TestValidateBeaconBlockPubSub_FilterByFinalizedEpoch(t *testing.T) {
|
||||
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
chain: chain,
|
||||
clock: startup.NewClock(chain.Genesis, chain.ValidatorsRoot),
|
||||
blockNotifier: chain.BlockNotifier(),
|
||||
attPool: attestations.NewPool(),
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
chain: chain,
|
||||
clock: startup.NewClock(chain.Genesis, chain.ValidatorsRoot),
|
||||
blockNotifier: chain.BlockNotifier(),
|
||||
operationNotifier: chain.OperationNotifier(),
|
||||
attPool: attestations.NewPool(),
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
},
|
||||
seenBlockCache: lruwrpr.New(10),
|
||||
badBlockCache: lruwrpr.New(10),
|
||||
}
|
||||
opChannel := make(chan *feed.Event, 1)
|
||||
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
|
||||
defer opSub.Unsubscribe()
|
||||
|
||||
b := util.NewBeaconBlock()
|
||||
b.Block.Slot = 1
|
||||
@@ -922,6 +1096,13 @@ func TestValidateBeaconBlockPubSub_FilterByFinalizedEpoch(t *testing.T) {
|
||||
res, err = r.validateBeaconBlockPubSub(t.Context(), "", m)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, pubsub.ValidationIgnore, res)
|
||||
|
||||
select {
|
||||
case event := <-opChannel:
|
||||
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
|
||||
default:
|
||||
// this case is needed, otherwise the test will never finish
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateBeaconBlockPubSub_ParentNotFinalizedDescendant(t *testing.T) {
|
||||
@@ -960,19 +1141,24 @@ func TestValidateBeaconBlockPubSub_ParentNotFinalizedDescendant(t *testing.T) {
|
||||
}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
stateGen: stateGen,
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
operationNotifier: chainService.OperationNotifier(),
|
||||
stateGen: stateGen,
|
||||
},
|
||||
seenBlockCache: lruwrpr.New(10),
|
||||
badBlockCache: lruwrpr.New(10),
|
||||
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
|
||||
seenPendingBlocks: make(map[[32]byte]bool),
|
||||
}
|
||||
opChannel := make(chan *feed.Event, 1)
|
||||
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
|
||||
defer opSub.Unsubscribe()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = p.Encoding().EncodeGossip(buf, msg)
|
||||
require.NoError(t, err)
|
||||
@@ -989,6 +1175,13 @@ func TestValidateBeaconBlockPubSub_ParentNotFinalizedDescendant(t *testing.T) {
|
||||
res, err := r.validateBeaconBlockPubSub(ctx, "", m)
|
||||
assert.Equal(t, pubsub.ValidationReject, res, "Wrong validation result returned")
|
||||
require.ErrorContains(t, "not descendant of finalized checkpoint", err)
|
||||
|
||||
select {
|
||||
case event := <-opChannel:
|
||||
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
|
||||
default:
|
||||
// this case is needed, otherwise the test will never finish
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateBeaconBlockPubSub_InvalidParentBlock(t *testing.T) {
|
||||
@@ -1026,19 +1219,24 @@ func TestValidateBeaconBlockPubSub_InvalidParentBlock(t *testing.T) {
|
||||
}}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
stateGen: stateGen,
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
operationNotifier: chainService.OperationNotifier(),
|
||||
stateGen: stateGen,
|
||||
},
|
||||
seenBlockCache: lruwrpr.New(10),
|
||||
badBlockCache: lruwrpr.New(10),
|
||||
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
|
||||
seenPendingBlocks: make(map[[32]byte]bool),
|
||||
}
|
||||
opChannel := make(chan *feed.Event, 1)
|
||||
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
|
||||
defer opSub.Unsubscribe()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = p.Encoding().EncodeGossip(buf, msg)
|
||||
require.NoError(t, err)
|
||||
@@ -1088,6 +1286,13 @@ func TestValidateBeaconBlockPubSub_InvalidParentBlock(t *testing.T) {
|
||||
require.ErrorContains(t, "has an invalid parent", err)
|
||||
// Expect block with bad parent to fail too
|
||||
assert.Equal(t, res, pubsub.ValidationReject, "block with invalid parent should be ignored")
|
||||
|
||||
select {
|
||||
case event := <-opChannel:
|
||||
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
|
||||
default:
|
||||
// this case is needed, otherwise the test will never finish
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateBeaconBlockPubSub_InsertValidPendingBlock(t *testing.T) {
|
||||
@@ -1120,19 +1325,24 @@ func TestValidateBeaconBlockPubSub_InsertValidPendingBlock(t *testing.T) {
|
||||
}}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
stateGen: stateGen,
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
operationNotifier: chainService.OperationNotifier(),
|
||||
stateGen: stateGen,
|
||||
},
|
||||
seenBlockCache: lruwrpr.New(10),
|
||||
badBlockCache: lruwrpr.New(10),
|
||||
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
|
||||
seenPendingBlocks: make(map[[32]byte]bool),
|
||||
}
|
||||
opChannel := make(chan *feed.Event, 1)
|
||||
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
|
||||
defer opSub.Unsubscribe()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = p.Encoding().EncodeGossip(buf, msg)
|
||||
require.NoError(t, err)
|
||||
@@ -1152,6 +1362,13 @@ func TestValidateBeaconBlockPubSub_InsertValidPendingBlock(t *testing.T) {
|
||||
bRoot, err = msg.Block.HashTreeRoot()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, true, r.seenPendingBlocks[bRoot])
|
||||
|
||||
select {
|
||||
case event := <-opChannel:
|
||||
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
|
||||
default:
|
||||
// this case is needed, otherwise the test will never finish
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateBeaconBlockPubSub_RejectBlocksFromBadParent(t *testing.T) {
|
||||
@@ -1203,19 +1420,24 @@ func TestValidateBeaconBlockPubSub_RejectBlocksFromBadParent(t *testing.T) {
|
||||
}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
stateGen: stateGen,
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
operationNotifier: chainService.OperationNotifier(),
|
||||
stateGen: stateGen,
|
||||
},
|
||||
seenBlockCache: lruwrpr.New(10),
|
||||
badBlockCache: lruwrpr.New(10),
|
||||
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
|
||||
seenPendingBlocks: make(map[[32]byte]bool),
|
||||
}
|
||||
opChannel := make(chan *feed.Event, 1)
|
||||
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
|
||||
defer opSub.Unsubscribe()
|
||||
|
||||
r.setBadBlock(ctx, bytesutil.ToBytes32(msg.Block.ParentRoot))
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
@@ -1234,6 +1456,13 @@ func TestValidateBeaconBlockPubSub_RejectBlocksFromBadParent(t *testing.T) {
|
||||
res, err := r.validateBeaconBlockPubSub(ctx, "", m)
|
||||
assert.ErrorContains(t, "invalid parent", err)
|
||||
assert.Equal(t, res, pubsub.ValidationReject)
|
||||
|
||||
select {
|
||||
case event := <-opChannel:
|
||||
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
|
||||
default:
|
||||
// this case is needed, otherwise the test will never finish
|
||||
}
|
||||
}
|
||||
|
||||
func TestService_setBadBlock_DoesntSetWithContextErr(t *testing.T) {
|
||||
@@ -1311,17 +1540,21 @@ func TestValidateBeaconBlockPubSub_ValidExecutionPayload(t *testing.T) {
|
||||
}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
stateGen: stateGen,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
operationNotifier: chainService.OperationNotifier(),
|
||||
stateGen: stateGen,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
},
|
||||
seenBlockCache: lruwrpr.New(10),
|
||||
badBlockCache: lruwrpr.New(10),
|
||||
}
|
||||
opChannel := make(chan *feed.Event, 1)
|
||||
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
|
||||
defer opSub.Unsubscribe()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = p.Encoding().EncodeGossip(buf, msg)
|
||||
@@ -1342,6 +1575,17 @@ func TestValidateBeaconBlockPubSub_ValidExecutionPayload(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
result := res == pubsub.ValidationAccept
|
||||
require.Equal(t, true, result)
|
||||
|
||||
blockGossipFound := false
|
||||
select {
|
||||
case event := <-opChannel:
|
||||
if event.Type == opfeed.BlockGossipReceived {
|
||||
blockGossipFound = true
|
||||
}
|
||||
default:
|
||||
// this case is needed, otherwise the test will never finish
|
||||
}
|
||||
assert.Equal(t, true, blockGossipFound, "BlockGossipReceived event should be sent")
|
||||
}
|
||||
|
||||
func TestValidateBeaconBlockPubSub_InvalidPayloadTimestamp(t *testing.T) {
|
||||
@@ -1383,17 +1627,21 @@ func TestValidateBeaconBlockPubSub_InvalidPayloadTimestamp(t *testing.T) {
|
||||
}}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
stateGen: stateGen,
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
operationNotifier: chainService.OperationNotifier(),
|
||||
stateGen: stateGen,
|
||||
},
|
||||
seenBlockCache: lruwrpr.New(10),
|
||||
badBlockCache: lruwrpr.New(10),
|
||||
}
|
||||
opChannel := make(chan *feed.Event, 1)
|
||||
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
|
||||
defer opSub.Unsubscribe()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = p.Encoding().EncodeGossip(buf, msg)
|
||||
@@ -1413,6 +1661,13 @@ func TestValidateBeaconBlockPubSub_InvalidPayloadTimestamp(t *testing.T) {
|
||||
require.NotNil(t, err)
|
||||
result := res == pubsub.ValidationReject
|
||||
assert.Equal(t, true, result)
|
||||
|
||||
select {
|
||||
case event := <-opChannel:
|
||||
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
|
||||
default:
|
||||
// this case is needed, otherwise the test will never finish
|
||||
}
|
||||
}
|
||||
|
||||
func Test_validateBellatrixBeaconBlock(t *testing.T) {
|
||||
@@ -1549,17 +1804,21 @@ func Test_validateBeaconBlockProcessingWhenParentIsOptimistic(t *testing.T) {
|
||||
}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
stateGen: stateGen,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
beaconDB: db,
|
||||
p2p: p,
|
||||
initialSync: &mockSync.Sync{IsSyncing: false},
|
||||
chain: chainService,
|
||||
blockNotifier: chainService.BlockNotifier(),
|
||||
operationNotifier: chainService.OperationNotifier(),
|
||||
stateGen: stateGen,
|
||||
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
|
||||
},
|
||||
seenBlockCache: lruwrpr.New(10),
|
||||
badBlockCache: lruwrpr.New(10),
|
||||
}
|
||||
opChannel := make(chan *feed.Event, 1)
|
||||
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
|
||||
defer opSub.Unsubscribe()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = p.Encoding().EncodeGossip(buf, msg)
|
||||
@@ -1580,6 +1839,17 @@ func Test_validateBeaconBlockProcessingWhenParentIsOptimistic(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
result := res == pubsub.ValidationAccept
|
||||
assert.Equal(t, true, result)
|
||||
|
||||
blockGossipFound := false
|
||||
select {
|
||||
case event := <-opChannel:
|
||||
if event.Type == opfeed.BlockGossipReceived {
|
||||
blockGossipFound = true
|
||||
}
|
||||
default:
|
||||
// this case is needed, otherwise the test will never finish
|
||||
}
|
||||
assert.Equal(t, true, blockGossipFound, "BlockGossipReceived event should be sent")
|
||||
}
|
||||
|
||||
func Test_getBlockFields(t *testing.T) {
|
||||
|
||||
3
changelog/Forostovec_fix-execution-option.md
Normal file
3
changelog/Forostovec_fix-execution-option.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## Changed
|
||||
|
||||
- Avoid redundant WithHttpEndpoint when JWT is provided
|
||||
3
changelog/bastin_has-state-using-diff.md
Normal file
3
changelog/bastin_has-state-using-diff.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Added
|
||||
|
||||
- Integrate state-diff into `HasState()`.
|
||||
3
changelog/bastin_refactor-slot-by-blkroot.md
Normal file
3
changelog/bastin_refactor-slot-by-blkroot.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Ignored
|
||||
|
||||
- Refactor finding slot by block root using state summary and block to its own function.
|
||||
3
changelog/bastin_state-diff-anchor-slot-bug.md
Normal file
3
changelog/bastin_state-diff-anchor-slot-bug.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Fixed
|
||||
|
||||
- Fix state diff repetitive anchor slot bug.
|
||||
4
changelog/bastin_state-diff-configs.md
Normal file
4
changelog/bastin_state-diff-configs.md
Normal file
@@ -0,0 +1,4 @@
|
||||
### Added
|
||||
|
||||
- Add initial configs for the state-diff feature.
|
||||
- Add kv functions for the state-diff feature.
|
||||
3
changelog/bastin_state-read-diff.md
Normal file
3
changelog/bastin_state-read-diff.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Added
|
||||
|
||||
- Integrate state-diff into `State()`.
|
||||
@@ -0,0 +1,2 @@
|
||||
### Added
|
||||
- prometheus metric `gossip_attestation_verification_milliseconds` to track attestation gossip topic validation latency.
|
||||
4
changelog/chris-downgrade-log-level.md
Normal file
4
changelog/chris-downgrade-log-level.md
Normal file
@@ -0,0 +1,4 @@
|
||||
### Changed
|
||||
|
||||
- Downgraded log level from INFO to DEBUG on PrepareBeaconProposer updated fee recipients.
|
||||
- Change the logging behaviour of Updated fee recipients to only log count of validators at Debug level and all validator indices at Trace level.
|
||||
3
changelog/late-block-dont-fire-attribute.md
Normal file
3
changelog/late-block-dont-fire-attribute.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Changed
|
||||
|
||||
- Stop emitting payload attribute events during late block handling when we are not proposing the next slot
|
||||
2
changelog/muzry-fix-jwt-length.md
Normal file
2
changelog/muzry-fix-jwt-length.md
Normal file
@@ -0,0 +1,2 @@
|
||||
### Fixed
|
||||
- Check the JWT secret length is exactly 256 bits (32 bytes) as per Engine API specification
|
||||
3
changelog/radek_init-gossip-execution-requests.md
Normal file
3
changelog/radek_init-gossip-execution-requests.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Changed
|
||||
|
||||
- Initialize the `ExecutionRequests` field in gossip block map.
|
||||
3
changelog/radek_move-block-gossip-event.md
Normal file
3
changelog/radek_move-block-gossip-event.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Fixed
|
||||
|
||||
- Move `BlockGossipReceived` event to the end of gossip validation.
|
||||
3
changelog/radek_remove-cross-validator.md
Normal file
3
changelog/radek_remove-cross-validator.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Removed
|
||||
|
||||
- Remove validator cross-client from end-to-end tests.
|
||||
3
changelog/sashaodessa_patch-1.md
Normal file
3
changelog/sashaodessa_patch-1.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Ignored
|
||||
|
||||
- Replace fixed sleep delays with active polling in prometheus service test to improve test reliability.
|
||||
3
changelog/satushh-revert-eas-metric.md
Normal file
3
changelog/satushh-revert-eas-metric.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Removed
|
||||
|
||||
- Reverted the eas metric as it currently has a bug. Will be fixed later.
|
||||
3
changelog/terence_guard-unreleased-forks.md
Normal file
3
changelog/terence_guard-unreleased-forks.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Added
|
||||
|
||||
- Add supported version for fork versions
|
||||
3
changelog/ttsao_add-gloas-beacon-state.md
Normal file
3
changelog/ttsao_add-gloas-beacon-state.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Added
|
||||
|
||||
- Implement Gloas state
|
||||
3
changelog/ttsao_implement-gloas-fork-blocks.md
Normal file
3
changelog/ttsao_implement-gloas-fork-blocks.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Added
|
||||
|
||||
- Implement Gloas fork support in consensus-types/blocks with factory methods, getters, setters, and proto handling
|
||||
3
changelog/ttsao_p2p-stategen.md
Normal file
3
changelog/ttsao_p2p-stategen.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Ignored
|
||||
|
||||
- P2p: wire stategen into service for last finalized state and use it for active validator count
|
||||
@@ -24,12 +24,13 @@ func FlagOptions(c *cli.Context) ([]execution.Option, error) {
|
||||
}
|
||||
headers := strings.Split(c.String(flags.ExecutionEngineHeaders.Name), ",")
|
||||
opts := []execution.Option{
|
||||
execution.WithHttpEndpoint(endpoint),
|
||||
execution.WithEth1HeaderRequestLimit(c.Uint64(flags.Eth1HeaderReqLimit.Name)),
|
||||
execution.WithHeaders(headers),
|
||||
}
|
||||
if len(jwtSecret) > 0 {
|
||||
opts = append(opts, execution.WithHttpEndpointAndJWTSecret(endpoint, jwtSecret))
|
||||
} else {
|
||||
opts = append(opts, execution.WithHttpEndpoint(endpoint))
|
||||
}
|
||||
return opts, nil
|
||||
}
|
||||
@@ -41,7 +42,7 @@ func FlagOptions(c *cli.Context) ([]execution.Option, error) {
|
||||
//
|
||||
// The secret must be stored as a hex-encoded string within a file in the filesystem.
|
||||
// If the --jwt-secret flag is provided to Prysm, but the file cannot be read, or does not contain a hex-encoded
|
||||
// key of at least 256 bits, the client should treat this as an error and abort the startup.
|
||||
// key of 256 bits, the client should treat this as an error and abort the startup.
|
||||
func parseJWTSecretFromFile(c *cli.Context) ([]byte, error) {
|
||||
jwtSecretFile := c.String(flags.ExecutionJWTSecretFlag.Name)
|
||||
if jwtSecretFile == "" {
|
||||
@@ -59,8 +60,8 @@ func parseJWTSecretFromFile(c *cli.Context) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(secret) < 32 {
|
||||
return nil, errors.New("provided JWT secret should be a hex string of at least 32 bytes")
|
||||
if len(secret) != 32 {
|
||||
return nil, errors.New("provided JWT secret should be a hex string of 32 bytes")
|
||||
}
|
||||
log.Infof("Finished reading JWT secret from %s", jwtSecretFile)
|
||||
return secret, nil
|
||||
|
||||
@@ -63,7 +63,19 @@ func Test_parseJWTSecretFromFile(t *testing.T) {
|
||||
set.String(flags.ExecutionJWTSecretFlag.Name, fullPath, "")
|
||||
ctx := cli.NewContext(&app, set, nil)
|
||||
_, err := parseJWTSecretFromFile(ctx)
|
||||
require.ErrorContains(t, "should be a hex string of at least 32 bytes", err)
|
||||
require.ErrorContains(t, "should be a hex string of 32 bytes", err)
|
||||
})
|
||||
t.Run("more than 32 bytes", func(t *testing.T) {
|
||||
app := cli.App{}
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
fullPath := filepath.Join(t.TempDir(), "foohex")
|
||||
secret := bytesutil.PadTo([]byte("foo"), 33)
|
||||
hexData := fmt.Sprintf("%#x", secret)
|
||||
require.NoError(t, file.WriteFile(fullPath, []byte(hexData)))
|
||||
set.String(flags.ExecutionJWTSecretFlag.Name, fullPath, "")
|
||||
ctx := cli.NewContext(&app, set, nil)
|
||||
_, err := parseJWTSecretFromFile(ctx)
|
||||
require.ErrorContains(t, "should be a hex string of 32 bytes", err)
|
||||
})
|
||||
t.Run("bad data", func(t *testing.T) {
|
||||
app := cli.App{}
|
||||
|
||||
@@ -18,7 +18,9 @@ go_library(
|
||||
],
|
||||
deps = [
|
||||
"//cmd:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_urfave_cli_v2//:go_default_library",
|
||||
],
|
||||
@@ -26,7 +28,13 @@ go_library(
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["api_module_test.go"],
|
||||
srcs = [
|
||||
"api_module_test.go",
|
||||
"config_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = ["//testing/assert:go_default_library"],
|
||||
deps = [
|
||||
"//testing/assert:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -345,4 +345,10 @@ var (
|
||||
Usage: "Maximum number of signatures to batch verify at once for beacon attestation p2p gossip.",
|
||||
Value: 1000,
|
||||
}
|
||||
// StateDiffExponents defines the state diff tree hierarchy levels.
|
||||
StateDiffExponents = &cli.IntSliceFlag{
|
||||
Name: "state-diff-exponents",
|
||||
Usage: "A comma-separated list of exponents (of 2) in decreasing order, defining the state diff hierarchy levels. The last exponent must be greater than or equal to 5.",
|
||||
Value: cli.NewIntSlice(21, 18, 16, 13, 11, 9, 5),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -2,9 +2,13 @@ package flags
|
||||
|
||||
import (
|
||||
"github.com/OffchainLabs/prysm/v7/cmd"
|
||||
"github.com/OffchainLabs/prysm/v7/config/features"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
const maxStateDiffExponents = 30
|
||||
|
||||
// GlobalFlags specifies all the global flags for the
|
||||
// beacon node.
|
||||
type GlobalFlags struct {
|
||||
@@ -19,6 +23,7 @@ type GlobalFlags struct {
|
||||
BlobBatchLimitBurstFactor int
|
||||
DataColumnBatchLimit int
|
||||
DataColumnBatchLimitBurstFactor int
|
||||
StateDiffExponents []int
|
||||
}
|
||||
|
||||
var globalConfig *GlobalFlags
|
||||
@@ -38,7 +43,7 @@ func Init(c *GlobalFlags) {
|
||||
|
||||
// ConfigureGlobalFlags initializes the global config.
|
||||
// based on the provided cli context.
|
||||
func ConfigureGlobalFlags(ctx *cli.Context) {
|
||||
func ConfigureGlobalFlags(ctx *cli.Context) error {
|
||||
cfg := &GlobalFlags{}
|
||||
|
||||
if ctx.Bool(SubscribeToAllSubnets.Name) {
|
||||
@@ -51,6 +56,18 @@ func ConfigureGlobalFlags(ctx *cli.Context) {
|
||||
cfg.SubscribeAllDataSubnets = true
|
||||
}
|
||||
|
||||
// State-diff-exponents
|
||||
cfg.StateDiffExponents = ctx.IntSlice(StateDiffExponents.Name)
|
||||
if features.Get().EnableStateDiff {
|
||||
if err := validateStateDiffExponents(cfg.StateDiffExponents); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if ctx.IsSet(StateDiffExponents.Name) {
|
||||
log.Warn("--state-diff-exponents is set but --enable-state-diff is not; the value will be ignored.")
|
||||
}
|
||||
}
|
||||
|
||||
cfg.BlockBatchLimit = ctx.Int(BlockBatchLimit.Name)
|
||||
cfg.BlockBatchLimitBurstFactor = ctx.Int(BlockBatchLimitBurstFactor.Name)
|
||||
cfg.BlobBatchLimit = ctx.Int(BlobBatchLimit.Name)
|
||||
@@ -63,6 +80,7 @@ func ConfigureGlobalFlags(ctx *cli.Context) {
|
||||
configureMinimumPeers(ctx, cfg)
|
||||
|
||||
Init(cfg)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MaxDialIsActive checks if the user has enabled the max dial flag.
|
||||
@@ -78,3 +96,26 @@ func configureMinimumPeers(ctx *cli.Context, cfg *GlobalFlags) {
|
||||
cfg.MinimumSyncPeers = maxPeers
|
||||
}
|
||||
}
|
||||
|
||||
// validateStateDiffExponents validates the provided exponents for state diffs with these constraints in mind:
|
||||
// - Must contain between 1 and 15 values.
|
||||
// - Exponents must be in strictly decreasing order.
|
||||
// - Every exponent must be <= 30. (2^30 slots is more than 300 years at 12s slots)
|
||||
// - The last (smallest) exponent must be >= 5. (This ensures diffs are at least 1 epoch apart)
|
||||
func validateStateDiffExponents(exponents []int) error {
|
||||
length := len(exponents)
|
||||
if length == 0 || length > 15 {
|
||||
return errors.New("state diff exponents must contain between 1 and 15 values")
|
||||
}
|
||||
if exponents[length-1] < 5 {
|
||||
return errors.New("the last state diff exponent must be at least 5")
|
||||
}
|
||||
prev := maxStateDiffExponents + 1
|
||||
for _, exp := range exponents {
|
||||
if exp >= prev {
|
||||
return errors.New("state diff exponents must be in strictly decreasing order, and each exponent must be <= 30")
|
||||
}
|
||||
prev = exp
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
39
cmd/beacon-chain/flags/config_test.go
Normal file
39
cmd/beacon-chain/flags/config_test.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package flags
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/testing/require"
|
||||
)
|
||||
|
||||
func TestValidateStateDiffExponents(t *testing.T) {
|
||||
tests := []struct {
|
||||
exponents []int
|
||||
wantErr bool
|
||||
errMsg string
|
||||
}{
|
||||
{exponents: []int{0, 1, 2}, wantErr: true, errMsg: "at least 5"},
|
||||
{exponents: []int{1, 2, 3}, wantErr: true, errMsg: "at least 5"},
|
||||
{exponents: []int{9, 8, 4}, wantErr: true, errMsg: "at least 5"},
|
||||
{exponents: []int{3, 4, 5}, wantErr: true, errMsg: "decreasing"},
|
||||
{exponents: []int{15, 14, 14, 12, 11}, wantErr: true, errMsg: "decreasing"},
|
||||
{exponents: []int{15, 14, 13, 12, 11}, wantErr: false},
|
||||
{exponents: []int{21, 18, 16, 13, 11, 9, 5}, wantErr: false},
|
||||
{exponents: []int{30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 18, 16, 13, 11, 9, 5}, wantErr: true, errMsg: "between 1 and 15 values"},
|
||||
{exponents: []int{}, wantErr: true, errMsg: "between 1 and 15 values"},
|
||||
{exponents: []int{30, 18, 16, 13, 11, 9, 5}, wantErr: false},
|
||||
{exponents: []int{31, 18, 16, 13, 11, 9, 5}, wantErr: true, errMsg: "<= 30"},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||
err := validateStateDiffExponents(tt.exponents)
|
||||
if tt.wantErr {
|
||||
require.ErrorContains(t, tt.errMsg, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,8 @@ type Flags struct {
|
||||
EnableExperimentalAttestationPool bool // EnableExperimentalAttestationPool enables an experimental attestation pool design.
|
||||
DisableDutiesV2 bool // DisableDutiesV2 sets validator client to use the get Duties endpoint
|
||||
EnableWeb bool // EnableWeb enables the webui on the validator client
|
||||
EnableStateDiff bool // EnableStateDiff enables the experimental state diff feature for the beacon node.
|
||||
|
||||
// Logging related toggles.
|
||||
DisableGRPCConnectionLogs bool // Disables logging when a new grpc client has connected.
|
||||
EnableFullSSZDataLogging bool // Enables logging for full ssz data on rejected gossip messages
|
||||
@@ -284,6 +286,16 @@ func ConfigureBeaconChain(ctx *cli.Context) error {
|
||||
cfg.DisableLastEpochTargets = true
|
||||
}
|
||||
|
||||
if ctx.IsSet(EnableStateDiff.Name) {
|
||||
logEnabled(EnableStateDiff)
|
||||
cfg.EnableStateDiff = true
|
||||
|
||||
if ctx.IsSet(enableHistoricalSpaceRepresentation.Name) {
|
||||
log.Warn("--enable-state-diff is enabled, ignoring --enable-historical-space-representation flag.")
|
||||
cfg.EnableHistoricalSpaceRepresentation = false
|
||||
}
|
||||
}
|
||||
|
||||
cfg.AggregateIntervals = [3]time.Duration{aggregateFirstInterval.Value, aggregateSecondInterval.Value, aggregateThirdInterval.Value}
|
||||
Init(cfg)
|
||||
return nil
|
||||
|
||||
@@ -172,6 +172,10 @@ var (
|
||||
Name: "enable-experimental-attestation-pool",
|
||||
Usage: "Enables an experimental attestation pool design.",
|
||||
}
|
||||
EnableStateDiff = &cli.BoolFlag{
|
||||
Name: "enable-state-diff",
|
||||
Usage: "Enables the experimental state diff feature.",
|
||||
}
|
||||
// forceHeadFlag is a flag to force the head of the beacon chain to a specific block.
|
||||
forceHeadFlag = &cli.StringFlag{
|
||||
Name: "sync-from",
|
||||
|
||||
@@ -44,6 +44,7 @@ const (
|
||||
MaxAttesterSlashingsElectra = 1 // Maximum number of attester slashings in a block.
|
||||
MaxRandomByte = uint64(1<<8 - 1) // MaxRandomByte defines max for a random byte using for proposer and sync committee sampling.
|
||||
MaxRandomValueElectra = uint64(1<<16 - 1) // MaxRandomValueElectra defines max for a random value using for proposer and sync committee sampling.
|
||||
BuilderPendingWithdrawalsLimit = 1048576 // Maximum number of builder pending withdrawals.
|
||||
|
||||
// Introduced in Fulu network upgrade.
|
||||
NumberOfColumns = 128 // NumberOfColumns refers to the specified number of data columns that can exist in a network.
|
||||
|
||||
@@ -44,6 +44,7 @@ const (
|
||||
MaxAttesterSlashingsElectra = 1 // Maximum number of attester slashings in a block.
|
||||
MaxRandomByte = uint64(1<<8 - 1) // Maximum value for a random value using for proposer and sync committee sampling.
|
||||
MaxRandomValueElectra = uint64(1<<16 - 1) // Maximum value for a random value using for proposer and sync committee sampling.
|
||||
BuilderPendingWithdrawalsLimit = 1048576 // Maximum number of builder pending withdrawals.
|
||||
|
||||
// Introduced in Fulu network upgrade.
|
||||
NumberOfColumns = 128 // NumberOfColumns refers to the specified number of data columns that can exist in a network.
|
||||
|
||||
@@ -162,6 +162,7 @@ type BeaconChainConfig struct {
|
||||
BeaconStateDenebFieldCount int // BeaconStateDenebFieldCount defines how many fields are in beacon state post upgrade to Deneb.
|
||||
BeaconStateElectraFieldCount int // BeaconStateElectraFieldCount defines how many fields are in beacon state post upgrade to Electra.
|
||||
BeaconStateFuluFieldCount int // BeaconStateFuluFieldCount defines how many fields are in beacon state post upgrade to Fulu.
|
||||
BeaconStateGloasFieldCount int // BeaconStateGloasFieldCount defines how many fields are in beacon state post upgrade to Gloas.
|
||||
|
||||
// Slasher constants.
|
||||
WeakSubjectivityPeriod primitives.Epoch // WeakSubjectivityPeriod defines the time period expressed in number of epochs were proof of stake network should validate block headers and attestations for slashable events.
|
||||
|
||||
@@ -206,6 +206,7 @@ var mainnetBeaconConfig = &BeaconChainConfig{
|
||||
BeaconStateDenebFieldCount: 28,
|
||||
BeaconStateElectraFieldCount: 37,
|
||||
BeaconStateFuluFieldCount: 38,
|
||||
BeaconStateGloasFieldCount: 43,
|
||||
|
||||
// Slasher related values.
|
||||
WeakSubjectivityPeriod: 54000,
|
||||
|
||||
@@ -53,6 +53,7 @@ go_test(
|
||||
"roblob_test.go",
|
||||
"roblock_test.go",
|
||||
"rodatacolumn_test.go",
|
||||
"setters_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
|
||||
@@ -82,6 +82,8 @@ func NewSignedBeaconBlock(i any) (interfaces.SignedBeaconBlock, error) {
|
||||
return initBlindedSignedBlockFromProtoFulu(b)
|
||||
case *eth.GenericSignedBeaconBlock_BlindedFulu:
|
||||
return initBlindedSignedBlockFromProtoFulu(b.BlindedFulu)
|
||||
case *eth.SignedBeaconBlockGloas:
|
||||
return initSignedBlockFromProtoGloas(b)
|
||||
default:
|
||||
return nil, errors.Wrapf(ErrUnsupportedSignedBeaconBlock, "unable to create block from type %T", i)
|
||||
}
|
||||
@@ -138,6 +140,8 @@ func NewBeaconBlock(i any) (interfaces.ReadOnlyBeaconBlock, error) {
|
||||
return initBlindedBlockFromProtoFulu(b)
|
||||
case *eth.GenericBeaconBlock_BlindedFulu:
|
||||
return initBlindedBlockFromProtoFulu(b.BlindedFulu)
|
||||
case *eth.BeaconBlockGloas:
|
||||
return initBlockFromProtoGloas(b)
|
||||
default:
|
||||
return nil, errors.Wrapf(errUnsupportedBeaconBlock, "unable to create block from type %T", i)
|
||||
}
|
||||
@@ -168,6 +172,8 @@ func NewBeaconBlockBody(i any) (interfaces.ReadOnlyBeaconBlockBody, error) {
|
||||
return initBlockBodyFromProtoElectra(b)
|
||||
case *eth.BlindedBeaconBlockBodyElectra:
|
||||
return initBlindedBlockBodyFromProtoElectra(b)
|
||||
case *eth.BeaconBlockBodyGloas:
|
||||
return initBlockBodyFromProtoGloas(b)
|
||||
default:
|
||||
return nil, errors.Wrapf(errUnsupportedBeaconBlockBody, "unable to create block body from type %T", i)
|
||||
}
|
||||
@@ -260,6 +266,12 @@ func BuildSignedBeaconBlock(blk interfaces.ReadOnlyBeaconBlock, signature []byte
|
||||
return nil, errIncorrectBlockVersion
|
||||
}
|
||||
return NewSignedBeaconBlock(ð.SignedBeaconBlockFulu{Block: pb, Signature: signature})
|
||||
case version.Gloas:
|
||||
pb, ok := pb.(*eth.BeaconBlockGloas)
|
||||
if !ok {
|
||||
return nil, errIncorrectBlockVersion
|
||||
}
|
||||
return NewSignedBeaconBlock(ð.SignedBeaconBlockGloas{Block: pb, Signature: signature})
|
||||
default:
|
||||
return nil, errUnsupportedBeaconBlock
|
||||
}
|
||||
|
||||
@@ -161,6 +161,18 @@ func Test_NewSignedBeaconBlock(t *testing.T) {
|
||||
assert.Equal(t, version.Deneb, b.Version())
|
||||
assert.Equal(t, true, b.IsBlinded())
|
||||
})
|
||||
t.Run("SignedBeaconBlockGloas", func(t *testing.T) {
|
||||
pb := ð.SignedBeaconBlockGloas{
|
||||
Block: ð.BeaconBlockGloas{
|
||||
Body: ð.BeaconBlockBodyGloas{},
|
||||
},
|
||||
Signature: []byte("sig"),
|
||||
}
|
||||
b, err := NewSignedBeaconBlock(pb)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, version.Gloas, b.Version())
|
||||
assert.Equal(t, false, b.IsBlinded())
|
||||
})
|
||||
t.Run("nil", func(t *testing.T) {
|
||||
_, err := NewSignedBeaconBlock(nil)
|
||||
assert.ErrorContains(t, "received nil object", err)
|
||||
@@ -276,6 +288,13 @@ func Test_NewBeaconBlock(t *testing.T) {
|
||||
assert.Equal(t, version.Deneb, b.Version())
|
||||
assert.Equal(t, true, b.IsBlinded())
|
||||
})
|
||||
t.Run("BeaconBlockGloas", func(t *testing.T) {
|
||||
pb := ð.BeaconBlockGloas{Body: ð.BeaconBlockBodyGloas{}}
|
||||
b, err := NewBeaconBlock(pb)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, version.Gloas, b.Version())
|
||||
assert.Equal(t, false, b.IsBlinded())
|
||||
})
|
||||
t.Run("nil", func(t *testing.T) {
|
||||
_, err := NewBeaconBlock(nil)
|
||||
assert.ErrorContains(t, "received nil object", err)
|
||||
@@ -354,6 +373,15 @@ func Test_NewBeaconBlockBody(t *testing.T) {
|
||||
assert.Equal(t, version.Deneb, b.version)
|
||||
assert.Equal(t, true, b.IsBlinded())
|
||||
})
|
||||
t.Run("BeaconBlockBodyGloas", func(t *testing.T) {
|
||||
pb := ð.BeaconBlockBodyGloas{}
|
||||
i, err := NewBeaconBlockBody(pb)
|
||||
require.NoError(t, err)
|
||||
b, ok := i.(*BeaconBlockBody)
|
||||
require.Equal(t, true, ok)
|
||||
assert.Equal(t, version.Gloas, b.version)
|
||||
assert.Equal(t, false, b.IsBlinded())
|
||||
})
|
||||
t.Run("nil", func(t *testing.T) {
|
||||
_, err := NewBeaconBlockBody(nil)
|
||||
assert.ErrorContains(t, "received nil object", err)
|
||||
@@ -425,6 +453,14 @@ func Test_BuildSignedBeaconBlock(t *testing.T) {
|
||||
assert.Equal(t, version.Deneb, sb.Version())
|
||||
assert.Equal(t, true, sb.IsBlinded())
|
||||
})
|
||||
t.Run("Gloas", func(t *testing.T) {
|
||||
b := &BeaconBlock{version: version.Gloas, body: &BeaconBlockBody{version: version.Gloas}}
|
||||
sb, err := BuildSignedBeaconBlock(b, sig[:])
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, sig, sb.Signature())
|
||||
assert.Equal(t, version.Gloas, sb.Version())
|
||||
assert.Equal(t, false, sb.IsBlinded())
|
||||
})
|
||||
}
|
||||
|
||||
func TestBuildSignedBeaconBlockFromExecutionPayload(t *testing.T) {
|
||||
@@ -535,4 +571,21 @@ func TestBuildSignedBeaconBlockFromExecutionPayload(t *testing.T) {
|
||||
require.DeepEqual(t, uint64(123), payload.ExcessBlobGas)
|
||||
require.DeepEqual(t, uint64(321), payload.BlobGasUsed)
|
||||
})
|
||||
t.Run("gloas execution unsupported", func(t *testing.T) {
|
||||
base := &SignedBeaconBlock{
|
||||
version: version.Gloas,
|
||||
block: &BeaconBlock{version: version.Gloas, body: &BeaconBlockBody{version: version.Gloas}},
|
||||
}
|
||||
blinded := &testBlindedSignedBeaconBlock{SignedBeaconBlock: base}
|
||||
_, err := BuildSignedBeaconBlockFromExecutionPayload(blinded, nil)
|
||||
require.ErrorContains(t, "Execution is not supported for gloas", err)
|
||||
})
|
||||
}
|
||||
|
||||
type testBlindedSignedBeaconBlock struct {
|
||||
*SignedBeaconBlock
|
||||
}
|
||||
|
||||
func (b *testBlindedSignedBeaconBlock) IsBlinded() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -80,6 +80,8 @@ func (b *SignedBeaconBlock) Copy() (interfaces.SignedBeaconBlock, error) {
|
||||
return initBlindedSignedBlockFromProtoFulu(pb.(*eth.SignedBlindedBeaconBlockFulu).Copy())
|
||||
}
|
||||
return initSignedBlockFromProtoFulu(pb.(*eth.SignedBeaconBlockFulu).Copy())
|
||||
case version.Gloas:
|
||||
return initSignedBlockFromProtoGloas(eth.CopySignedBeaconBlockGloas(pb.(*eth.SignedBeaconBlockGloas)))
|
||||
default:
|
||||
return nil, errIncorrectBlockVersion
|
||||
}
|
||||
@@ -157,6 +159,9 @@ func (b *SignedBeaconBlock) PbGenericBlock() (*eth.GenericSignedBeaconBlock, err
|
||||
return ð.GenericSignedBeaconBlock{
|
||||
Block: ð.GenericSignedBeaconBlock_Fulu{Fulu: bc},
|
||||
}, nil
|
||||
case version.Gloas:
|
||||
// Gloas doesn't support GenericSignedBeaconBlock yet
|
||||
return nil, errors.New("Gloas blocks don't support GenericSignedBeaconBlock conversion")
|
||||
default:
|
||||
return nil, errIncorrectBlockVersion
|
||||
}
|
||||
@@ -164,7 +169,7 @@ func (b *SignedBeaconBlock) PbGenericBlock() (*eth.GenericSignedBeaconBlock, err
|
||||
|
||||
// ToBlinded converts a non-blinded block to its blinded equivalent.
|
||||
func (b *SignedBeaconBlock) ToBlinded() (interfaces.ReadOnlySignedBeaconBlock, error) {
|
||||
if b.version < version.Bellatrix {
|
||||
if b.version < version.Bellatrix || b.version >= version.Gloas {
|
||||
return nil, ErrUnsupportedVersion
|
||||
}
|
||||
if b.IsBlinded() {
|
||||
@@ -376,7 +381,7 @@ func (b *SignedBeaconBlock) Version() int {
|
||||
|
||||
// IsBlinded metadata on whether a block is blinded
|
||||
func (b *SignedBeaconBlock) IsBlinded() bool {
|
||||
return b.version >= version.Bellatrix && b.block.body.executionPayload == nil
|
||||
return b.version < version.Gloas && b.version >= version.Bellatrix && b.block.body.executionPayload == nil
|
||||
}
|
||||
|
||||
// Header converts the underlying protobuf object from blinded block to header format.
|
||||
@@ -437,6 +442,8 @@ func (b *SignedBeaconBlock) MarshalSSZ() ([]byte, error) {
|
||||
return pb.(*eth.SignedBlindedBeaconBlockFulu).MarshalSSZ()
|
||||
}
|
||||
return pb.(*eth.SignedBeaconBlockFulu).MarshalSSZ()
|
||||
case version.Gloas:
|
||||
return pb.(*eth.SignedBeaconBlockGloas).MarshalSSZ()
|
||||
default:
|
||||
return []byte{}, errIncorrectBlockVersion
|
||||
}
|
||||
@@ -479,6 +486,8 @@ func (b *SignedBeaconBlock) MarshalSSZTo(dst []byte) ([]byte, error) {
|
||||
return pb.(*eth.SignedBlindedBeaconBlockFulu).MarshalSSZTo(dst)
|
||||
}
|
||||
return pb.(*eth.SignedBeaconBlockFulu).MarshalSSZTo(dst)
|
||||
case version.Gloas:
|
||||
return pb.(*eth.SignedBeaconBlockGloas).MarshalSSZTo(dst)
|
||||
default:
|
||||
return []byte{}, errIncorrectBlockVersion
|
||||
}
|
||||
@@ -526,6 +535,8 @@ func (b *SignedBeaconBlock) SizeSSZ() int {
|
||||
return pb.(*eth.SignedBlindedBeaconBlockFulu).SizeSSZ()
|
||||
}
|
||||
return pb.(*eth.SignedBeaconBlockFulu).SizeSSZ()
|
||||
case version.Gloas:
|
||||
return pb.(*eth.SignedBeaconBlockGloas).SizeSSZ()
|
||||
default:
|
||||
panic(incorrectBlockVersion)
|
||||
}
|
||||
@@ -666,6 +677,16 @@ func (b *SignedBeaconBlock) UnmarshalSSZ(buf []byte) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case version.Gloas:
|
||||
pb := ð.SignedBeaconBlockGloas{}
|
||||
err := pb.UnmarshalSSZ(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newBlock, err = initSignedBlockFromProtoGloas(pb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errIncorrectBlockVersion
|
||||
}
|
||||
@@ -705,7 +726,7 @@ func (b *BeaconBlock) IsNil() bool {
|
||||
|
||||
// IsBlinded checks if the beacon block is a blinded block.
|
||||
func (b *BeaconBlock) IsBlinded() bool {
|
||||
return b.version >= version.Bellatrix && b.body.executionPayload == nil
|
||||
return b.version < version.Gloas && b.version >= version.Bellatrix && b.body.executionPayload == nil
|
||||
}
|
||||
|
||||
// Version of the underlying protobuf object.
|
||||
@@ -749,6 +770,9 @@ func (b *BeaconBlock) HashTreeRoot() ([field_params.RootLength]byte, error) {
|
||||
return pb.(*eth.BlindedBeaconBlockFulu).HashTreeRoot()
|
||||
}
|
||||
return pb.(*eth.BeaconBlockElectra).HashTreeRoot()
|
||||
case version.Gloas:
|
||||
return pb.(*eth.BeaconBlockGloas).HashTreeRoot()
|
||||
|
||||
default:
|
||||
return [field_params.RootLength]byte{}, errIncorrectBlockVersion
|
||||
}
|
||||
@@ -790,6 +814,8 @@ func (b *BeaconBlock) HashTreeRootWith(h *ssz.Hasher) error {
|
||||
return pb.(*eth.BlindedBeaconBlockFulu).HashTreeRootWith(h)
|
||||
}
|
||||
return pb.(*eth.BeaconBlockElectra).HashTreeRootWith(h)
|
||||
case version.Gloas:
|
||||
return pb.(*eth.BeaconBlockGloas).HashTreeRootWith(h)
|
||||
default:
|
||||
return errIncorrectBlockVersion
|
||||
}
|
||||
@@ -832,6 +858,8 @@ func (b *BeaconBlock) MarshalSSZ() ([]byte, error) {
|
||||
return pb.(*eth.BlindedBeaconBlockFulu).MarshalSSZ()
|
||||
}
|
||||
return pb.(*eth.BeaconBlockElectra).MarshalSSZ()
|
||||
case version.Gloas:
|
||||
return pb.(*eth.BeaconBlockGloas).MarshalSSZ()
|
||||
default:
|
||||
return []byte{}, errIncorrectBlockVersion
|
||||
}
|
||||
@@ -874,6 +902,8 @@ func (b *BeaconBlock) MarshalSSZTo(dst []byte) ([]byte, error) {
|
||||
return pb.(*eth.BlindedBeaconBlockFulu).MarshalSSZTo(dst)
|
||||
}
|
||||
return pb.(*eth.BeaconBlockElectra).MarshalSSZTo(dst)
|
||||
case version.Gloas:
|
||||
return pb.(*eth.BeaconBlockGloas).MarshalSSZTo(dst)
|
||||
default:
|
||||
return []byte{}, errIncorrectBlockVersion
|
||||
}
|
||||
@@ -921,6 +951,8 @@ func (b *BeaconBlock) SizeSSZ() int {
|
||||
return pb.(*eth.BlindedBeaconBlockFulu).SizeSSZ()
|
||||
}
|
||||
return pb.(*eth.BeaconBlockElectra).SizeSSZ()
|
||||
case version.Gloas:
|
||||
return pb.(*eth.BeaconBlockGloas).SizeSSZ()
|
||||
default:
|
||||
panic(incorrectBodyVersion)
|
||||
}
|
||||
@@ -1061,6 +1093,16 @@ func (b *BeaconBlock) UnmarshalSSZ(buf []byte) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case version.Gloas:
|
||||
pb := ð.BeaconBlockGloas{}
|
||||
if err := pb.UnmarshalSSZ(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
var err error
|
||||
newBlock, err = initBlockFromProtoGloas(pb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errIncorrectBlockVersion
|
||||
}
|
||||
@@ -1200,15 +1242,13 @@ func (b *BeaconBlockBody) SyncAggregate() (*eth.SyncAggregate, error) {
|
||||
|
||||
// Execution returns the execution payload of the block body.
|
||||
func (b *BeaconBlockBody) Execution() (interfaces.ExecutionData, error) {
|
||||
switch b.version {
|
||||
case version.Phase0, version.Altair:
|
||||
if b.version <= version.Altair || b.version >= version.Gloas {
|
||||
return nil, consensus_types.ErrNotSupported("Execution", b.version)
|
||||
default:
|
||||
if b.IsBlinded() {
|
||||
return b.executionPayloadHeader, nil
|
||||
}
|
||||
return b.executionPayload, nil
|
||||
}
|
||||
if b.IsBlinded() {
|
||||
return b.executionPayloadHeader, nil
|
||||
}
|
||||
return b.executionPayload, nil
|
||||
}
|
||||
|
||||
func (b *BeaconBlockBody) BLSToExecutionChanges() ([]*eth.SignedBLSToExecutionChange, error) {
|
||||
@@ -1233,12 +1273,28 @@ func (b *BeaconBlockBody) BlobKzgCommitments() ([][]byte, error) {
|
||||
|
||||
// ExecutionRequests returns the execution requests
|
||||
func (b *BeaconBlockBody) ExecutionRequests() (*enginev1.ExecutionRequests, error) {
|
||||
if b.version < version.Electra {
|
||||
if b.version < version.Electra || b.version >= version.Gloas {
|
||||
return nil, consensus_types.ErrNotSupported("ExecutionRequests", b.version)
|
||||
}
|
||||
return b.executionRequests, nil
|
||||
}
|
||||
|
||||
// PayloadAttestations returns the payload attestations in the block.
|
||||
func (b *BeaconBlockBody) PayloadAttestations() ([]*eth.PayloadAttestation, error) {
|
||||
if b.version >= version.Gloas {
|
||||
return b.payloadAttestations, nil
|
||||
}
|
||||
return nil, consensus_types.ErrNotSupported("PayloadAttestations", b.version)
|
||||
}
|
||||
|
||||
// SignedExecutionPayloadBid returns the signed execution payload header in the block.
|
||||
func (b *BeaconBlockBody) SignedExecutionPayloadBid() (*eth.SignedExecutionPayloadBid, error) {
|
||||
if b.version >= version.Gloas {
|
||||
return b.signedExecutionPayloadBid, nil
|
||||
}
|
||||
return nil, consensus_types.ErrNotSupported("SignedExecutionPayloadBid", b.version)
|
||||
}
|
||||
|
||||
// Version returns the version of the beacon block body
|
||||
func (b *BeaconBlockBody) Version() int {
|
||||
return b.version
|
||||
@@ -1280,6 +1336,8 @@ func (b *BeaconBlockBody) HashTreeRoot() ([field_params.RootLength]byte, error)
|
||||
return pb.(*eth.BlindedBeaconBlockBodyElectra).HashTreeRoot()
|
||||
}
|
||||
return pb.(*eth.BeaconBlockBodyElectra).HashTreeRoot()
|
||||
case version.Gloas:
|
||||
return pb.(*eth.BeaconBlockBodyGloas).HashTreeRoot()
|
||||
default:
|
||||
return [field_params.RootLength]byte{}, errIncorrectBodyVersion
|
||||
}
|
||||
@@ -1287,5 +1345,5 @@ func (b *BeaconBlockBody) HashTreeRoot() ([field_params.RootLength]byte, error)
|
||||
|
||||
// IsBlinded checks if the beacon block body is a blinded block body.
|
||||
func (b *BeaconBlockBody) IsBlinded() bool {
|
||||
return b.version >= version.Bellatrix && b.executionPayload == nil
|
||||
return b.version < version.Gloas && b.version >= version.Bellatrix && b.executionPayload == nil
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ package blocks
|
||||
import (
|
||||
"testing"
|
||||
|
||||
bitfield "github.com/OffchainLabs/go-bitfield"
|
||||
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
|
||||
consensus_types "github.com/OffchainLabs/prysm/v7/consensus-types"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
|
||||
@@ -73,14 +75,54 @@ func Test_SignedBeaconBlock_IsNil(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_SignedBeaconBlock_Copy(t *testing.T) {
|
||||
bb := &BeaconBlockBody{}
|
||||
b := &BeaconBlock{body: bb}
|
||||
sb := &SignedBeaconBlock{block: b}
|
||||
cp, err := sb.Copy()
|
||||
require.NoError(t, err)
|
||||
assert.NotEqual(t, cp, sb)
|
||||
assert.NotEqual(t, cp.Block(), sb.block)
|
||||
assert.NotEqual(t, cp.Block().Body(), sb.block.body)
|
||||
t.Run("basic", func(t *testing.T) {
|
||||
bb := &BeaconBlockBody{}
|
||||
b := &BeaconBlock{body: bb}
|
||||
sb := &SignedBeaconBlock{block: b}
|
||||
cp, err := sb.Copy()
|
||||
require.NoError(t, err)
|
||||
assert.NotEqual(t, cp, sb)
|
||||
assert.NotEqual(t, cp.Block(), sb.block)
|
||||
assert.NotEqual(t, cp.Block().Body(), sb.block.body)
|
||||
})
|
||||
|
||||
t.Run("gloas deep copy", func(t *testing.T) {
|
||||
payload := []*eth.PayloadAttestation{{Signature: []byte{0x01}}}
|
||||
payloadBid := ð.SignedExecutionPayloadBid{Signature: []byte{0x02}}
|
||||
sb := &SignedBeaconBlock{
|
||||
version: version.Gloas,
|
||||
block: &BeaconBlock{
|
||||
version: version.Gloas,
|
||||
body: &BeaconBlockBody{
|
||||
version: version.Gloas,
|
||||
payloadAttestations: payload,
|
||||
signedExecutionPayloadBid: payloadBid,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cpIntf, err := sb.Copy()
|
||||
require.NoError(t, err)
|
||||
|
||||
cp, ok := cpIntf.(*SignedBeaconBlock)
|
||||
require.Equal(t, true, ok)
|
||||
assert.NotEqual(t, sb, cp)
|
||||
require.Equal(t, version.Gloas, cp.version)
|
||||
|
||||
att, err := cp.Block().Body().PayloadAttestations()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, payload, att)
|
||||
origAttSig := att[0].Signature[0]
|
||||
payload[0].Signature[0] ^= 0xFF
|
||||
require.Equal(t, origAttSig, att[0].Signature[0])
|
||||
|
||||
bid, err := cp.Block().Body().SignedExecutionPayloadBid()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, payloadBid, bid)
|
||||
origBidSig := bid.Signature[0]
|
||||
payloadBid.Signature[0] ^= 0xFF
|
||||
require.Equal(t, origBidSig, bid.Signature[0])
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SignedBeaconBlock_Version(t *testing.T) {
|
||||
@@ -122,6 +164,16 @@ func Test_SignedBeaconBlock_Header(t *testing.T) {
|
||||
assert.DeepEqual(t, expectedHTR[:], h.Header.BodyRoot)
|
||||
}
|
||||
|
||||
func Test_SignedBeaconBlock_PbGenericBlockGloasUnsupported(t *testing.T) {
|
||||
sb := &SignedBeaconBlock{
|
||||
version: version.Gloas,
|
||||
block: &BeaconBlock{version: version.Gloas, body: &BeaconBlockBody{version: version.Gloas}},
|
||||
}
|
||||
|
||||
_, err := sb.PbGenericBlock()
|
||||
require.ErrorContains(t, "Gloas blocks don't support GenericSignedBeaconBlock conversion", err)
|
||||
}
|
||||
|
||||
func Test_SignedBeaconBlock_UnmarshalSSZ(t *testing.T) {
|
||||
pb := hydrateSignedBeaconBlock()
|
||||
buf, err := pb.MarshalSSZ()
|
||||
@@ -190,6 +242,17 @@ func Test_BeaconBlock_IsBlinded(t *testing.T) {
|
||||
|
||||
b1 := &SignedBeaconBlock{version: version.Bellatrix, block: &BeaconBlock{body: &BeaconBlockBody{executionPayloadHeader: executionPayloadHeader{}}}}
|
||||
assert.Equal(t, true, b1.IsBlinded())
|
||||
|
||||
t.Run("gloas never blinded", func(t *testing.T) {
|
||||
sb := &SignedBeaconBlock{version: version.Gloas, block: &BeaconBlock{body: &BeaconBlockBody{version: version.Gloas}}}
|
||||
assert.Equal(t, false, sb.IsBlinded())
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SignedBeaconBlock_ToBlinded_GloasUnsupported(t *testing.T) {
|
||||
sb := &SignedBeaconBlock{version: version.Gloas, block: &BeaconBlock{version: version.Gloas, body: &BeaconBlockBody{version: version.Gloas}}}
|
||||
_, err := sb.ToBlinded()
|
||||
require.ErrorIs(t, err, ErrUnsupportedVersion)
|
||||
}
|
||||
|
||||
func Test_BeaconBlock_Version(t *testing.T) {
|
||||
@@ -324,6 +387,46 @@ func Test_BeaconBlockBody_Deposits(t *testing.T) {
|
||||
assert.DeepSSZEqual(t, d, bb.Block().Body().Deposits())
|
||||
}
|
||||
|
||||
func Test_BeaconBlockBody_PayloadAttestations(t *testing.T) {
|
||||
t.Run("unsupported before gloas", func(t *testing.T) {
|
||||
bb := &BeaconBlockBody{version: version.Fulu}
|
||||
_, err := bb.PayloadAttestations()
|
||||
require.ErrorIs(t, err, consensus_types.ErrUnsupportedField)
|
||||
})
|
||||
|
||||
t.Run("gloas returns payload", func(t *testing.T) {
|
||||
payload := []*eth.PayloadAttestation{{Signature: []byte{0x01}}}
|
||||
sb := &SignedBeaconBlock{
|
||||
version: version.Gloas,
|
||||
block: &BeaconBlock{version: version.Gloas, body: &BeaconBlockBody{version: version.Gloas}},
|
||||
}
|
||||
require.NoError(t, sb.SetPayloadAttestations(payload))
|
||||
got, err := sb.Block().Body().PayloadAttestations()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, payload, got)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_BeaconBlockBody_SignedExecutionPayloadBid(t *testing.T) {
|
||||
t.Run("unsupported before gloas", func(t *testing.T) {
|
||||
bb := &BeaconBlockBody{version: version.Fulu}
|
||||
_, err := bb.SignedExecutionPayloadBid()
|
||||
require.ErrorIs(t, err, consensus_types.ErrUnsupportedField)
|
||||
})
|
||||
|
||||
t.Run("gloas returns bid", func(t *testing.T) {
|
||||
bid := ð.SignedExecutionPayloadBid{Signature: []byte{0xFF}}
|
||||
sb := &SignedBeaconBlock{
|
||||
version: version.Gloas,
|
||||
block: &BeaconBlock{version: version.Gloas, body: &BeaconBlockBody{version: version.Gloas}},
|
||||
}
|
||||
require.NoError(t, sb.SetSignedExecutionPayloadBid(bid))
|
||||
got, err := sb.Block().Body().SignedExecutionPayloadBid()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, bid, got)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_BeaconBlockBody_VoluntaryExits(t *testing.T) {
|
||||
ve := make([]*eth.SignedVoluntaryExit, 0)
|
||||
bb := &SignedBeaconBlock{block: &BeaconBlock{body: &BeaconBlockBody{}}}
|
||||
@@ -400,6 +503,32 @@ func Test_BeaconBlockBody_Execution(t *testing.T) {
|
||||
gas, err = eDenebHeader.ExcessBlobGas()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, gas, uint64(223))
|
||||
|
||||
bb = &SignedBeaconBlock{version: version.Gloas, block: &BeaconBlock{version: version.Gloas, body: &BeaconBlockBody{version: version.Gloas}}}
|
||||
_, err = bb.Block().Body().Execution()
|
||||
require.ErrorIs(t, err, consensus_types.ErrUnsupportedField)
|
||||
}
|
||||
|
||||
func Test_BeaconBlockBody_ExecutionRequests(t *testing.T) {
|
||||
t.Run("unsupported before Electra", func(t *testing.T) {
|
||||
bb := &BeaconBlockBody{version: version.Deneb}
|
||||
_, err := bb.ExecutionRequests()
|
||||
require.ErrorIs(t, err, consensus_types.ErrUnsupportedField)
|
||||
})
|
||||
|
||||
t.Run("electra returns requests", func(t *testing.T) {
|
||||
reqs := &pb.ExecutionRequests{}
|
||||
bb := &BeaconBlockBody{version: version.Electra, executionRequests: reqs}
|
||||
result, err := bb.ExecutionRequests()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, reqs, result)
|
||||
})
|
||||
|
||||
t.Run("unsupported for Gloas", func(t *testing.T) {
|
||||
bb := &BeaconBlockBody{version: version.Gloas}
|
||||
_, err := bb.ExecutionRequests()
|
||||
require.ErrorIs(t, err, consensus_types.ErrUnsupportedField)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_BeaconBlockBody_HashTreeRoot(t *testing.T) {
|
||||
@@ -413,6 +542,17 @@ func Test_BeaconBlockBody_HashTreeRoot(t *testing.T) {
|
||||
assert.DeepEqual(t, expectedHTR, actualHTR)
|
||||
}
|
||||
|
||||
func Test_BeaconBlockBody_HashTreeRootGloas(t *testing.T) {
|
||||
pb := hydrateBeaconBlockBodyGloas()
|
||||
expectedHTR, err := pb.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
b, err := initBlockBodyFromProtoGloas(pb)
|
||||
require.NoError(t, err)
|
||||
actualHTR, err := b.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, expectedHTR, actualHTR)
|
||||
}
|
||||
|
||||
func hydrateSignedBeaconBlock() *eth.SignedBeaconBlock {
|
||||
return ð.SignedBeaconBlock{
|
||||
Signature: make([]byte, fieldparams.BLSSignatureLength),
|
||||
@@ -509,6 +649,43 @@ func hydrateBeaconBlockBodyCapella() *eth.BeaconBlockBodyCapella {
|
||||
}
|
||||
}
|
||||
|
||||
func hydrateBeaconBlockBodyGloas() *eth.BeaconBlockBodyGloas {
|
||||
bits := bitfield.NewBitvector512()
|
||||
bits.SetBitAt(0, true)
|
||||
|
||||
return ð.BeaconBlockBodyGloas{
|
||||
RandaoReveal: make([]byte, fieldparams.BLSSignatureLength),
|
||||
Graffiti: make([]byte, fieldparams.RootLength),
|
||||
Eth1Data: ð.Eth1Data{
|
||||
DepositRoot: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
},
|
||||
SyncAggregate: ð.SyncAggregate{
|
||||
SyncCommitteeBits: make([]byte, fieldparams.SyncAggregateSyncCommitteeBytesLength),
|
||||
SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength),
|
||||
},
|
||||
SignedExecutionPayloadBid: ð.SignedExecutionPayloadBid{
|
||||
Message: ð.ExecutionPayloadBid{
|
||||
ParentBlockHash: make([]byte, fieldparams.RootLength),
|
||||
ParentBlockRoot: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
FeeRecipient: make([]byte, 20),
|
||||
BlobKzgCommitmentsRoot: make([]byte, fieldparams.RootLength),
|
||||
},
|
||||
Signature: make([]byte, fieldparams.BLSSignatureLength),
|
||||
},
|
||||
PayloadAttestations: []*eth.PayloadAttestation{
|
||||
{
|
||||
AggregationBits: bits,
|
||||
Data: ð.PayloadAttestationData{
|
||||
BeaconBlockRoot: make([]byte, fieldparams.RootLength),
|
||||
},
|
||||
Signature: make([]byte, fieldparams.BLSSignatureLength),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func hydrateBeaconBlockBodyDeneb() *eth.BeaconBlockBodyDeneb {
|
||||
return ð.BeaconBlockBodyDeneb{
|
||||
RandaoReveal: make([]byte, fieldparams.BLSSignatureLength),
|
||||
|
||||
@@ -185,6 +185,19 @@ func (b *SignedBeaconBlock) Proto() (proto.Message, error) { // nolint:gocognit
|
||||
Block: block,
|
||||
Signature: b.signature[:],
|
||||
}, nil
|
||||
case version.Gloas:
|
||||
var block *eth.BeaconBlockGloas
|
||||
if blockMessage != nil {
|
||||
var ok bool
|
||||
block, ok = blockMessage.(*eth.BeaconBlockGloas)
|
||||
if !ok {
|
||||
return nil, errIncorrectBlockVersion
|
||||
}
|
||||
}
|
||||
return ð.SignedBeaconBlockGloas{
|
||||
Block: block,
|
||||
Signature: b.signature[:],
|
||||
}, nil
|
||||
default:
|
||||
return nil, errors.New("unsupported signed beacon block version")
|
||||
}
|
||||
@@ -399,6 +412,22 @@ func (b *BeaconBlock) Proto() (proto.Message, error) { // nolint:gocognit
|
||||
StateRoot: b.stateRoot[:],
|
||||
Body: body,
|
||||
}, nil
|
||||
case version.Gloas:
|
||||
var body *eth.BeaconBlockBodyGloas
|
||||
if bodyMessage != nil {
|
||||
var ok bool
|
||||
body, ok = bodyMessage.(*eth.BeaconBlockBodyGloas)
|
||||
if !ok {
|
||||
return nil, errIncorrectBodyVersion
|
||||
}
|
||||
}
|
||||
return ð.BeaconBlockGloas{
|
||||
Slot: b.slot,
|
||||
ProposerIndex: b.proposerIndex,
|
||||
ParentRoot: b.parentRoot[:],
|
||||
StateRoot: b.stateRoot[:],
|
||||
Body: body,
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported beacon block version: %s", version.String(b.version))
|
||||
}
|
||||
@@ -668,6 +697,21 @@ func (b *BeaconBlockBody) Proto() (proto.Message, error) {
|
||||
BlobKzgCommitments: b.blobKzgCommitments,
|
||||
ExecutionRequests: b.executionRequests,
|
||||
}, nil
|
||||
case version.Gloas:
|
||||
return ð.BeaconBlockBodyGloas{
|
||||
RandaoReveal: b.randaoReveal[:],
|
||||
Eth1Data: b.eth1Data,
|
||||
Graffiti: b.graffiti[:],
|
||||
ProposerSlashings: b.proposerSlashings,
|
||||
AttesterSlashings: b.attesterSlashingsElectra,
|
||||
Attestations: b.attestationsElectra,
|
||||
Deposits: b.deposits,
|
||||
VoluntaryExits: b.voluntaryExits,
|
||||
SyncAggregate: b.syncAggregate,
|
||||
BlsToExecutionChanges: b.blsToExecutionChanges,
|
||||
SignedExecutionPayloadBid: b.signedExecutionPayloadBid,
|
||||
PayloadAttestations: b.payloadAttestations,
|
||||
}, nil
|
||||
default:
|
||||
return nil, errors.New("unsupported beacon block body version")
|
||||
}
|
||||
@@ -1477,3 +1521,67 @@ func initBlindedBlockBodyFromProtoFulu(pb *eth.BlindedBeaconBlockBodyElectra) (*
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Gloas
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
func initSignedBlockFromProtoGloas(pb *eth.SignedBeaconBlockGloas) (*SignedBeaconBlock, error) {
|
||||
if pb == nil {
|
||||
return nil, errNilBlock
|
||||
}
|
||||
|
||||
block, err := initBlockFromProtoGloas(pb.Block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b := &SignedBeaconBlock{
|
||||
version: version.Gloas,
|
||||
block: block,
|
||||
signature: bytesutil.ToBytes96(pb.Signature),
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func initBlockFromProtoGloas(pb *eth.BeaconBlockGloas) (*BeaconBlock, error) {
|
||||
if pb == nil {
|
||||
return nil, errNilBlock
|
||||
}
|
||||
|
||||
body, err := initBlockBodyFromProtoGloas(pb.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b := &BeaconBlock{
|
||||
version: version.Gloas,
|
||||
slot: pb.Slot,
|
||||
proposerIndex: pb.ProposerIndex,
|
||||
parentRoot: bytesutil.ToBytes32(pb.ParentRoot),
|
||||
stateRoot: bytesutil.ToBytes32(pb.StateRoot),
|
||||
body: body,
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func initBlockBodyFromProtoGloas(pb *eth.BeaconBlockBodyGloas) (*BeaconBlockBody, error) {
|
||||
if pb == nil {
|
||||
return nil, errNilBlockBody
|
||||
}
|
||||
|
||||
b := &BeaconBlockBody{
|
||||
version: version.Gloas,
|
||||
randaoReveal: bytesutil.ToBytes96(pb.RandaoReveal),
|
||||
eth1Data: pb.Eth1Data,
|
||||
graffiti: bytesutil.ToBytes32(pb.Graffiti),
|
||||
proposerSlashings: pb.ProposerSlashings,
|
||||
attesterSlashingsElectra: pb.AttesterSlashings,
|
||||
attestationsElectra: pb.Attestations,
|
||||
deposits: pb.Deposits,
|
||||
voluntaryExits: pb.VoluntaryExits,
|
||||
syncAggregate: pb.SyncAggregate,
|
||||
blsToExecutionChanges: pb.BlsToExecutionChanges,
|
||||
signedExecutionPayloadBid: pb.SignedExecutionPayloadBid,
|
||||
payloadAttestations: pb.PayloadAttestations,
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
@@ -1792,6 +1792,61 @@ func bodyBlindedElectra(t *testing.T) *BeaconBlockBody {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignedBeaconBlockProtoGloas(t *testing.T) {
|
||||
payload := []*eth.PayloadAttestation{{Signature: []byte{0x01}}}
|
||||
bid := ð.SignedExecutionPayloadBid{Signature: []byte{0x02}}
|
||||
sb := &SignedBeaconBlock{
|
||||
version: version.Gloas,
|
||||
block: &BeaconBlock{
|
||||
version: version.Gloas,
|
||||
body: &BeaconBlockBody{
|
||||
version: version.Gloas,
|
||||
payloadAttestations: payload,
|
||||
signedExecutionPayloadBid: bid,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
msg, err := sb.Proto()
|
||||
require.NoError(t, err)
|
||||
gloas, ok := msg.(*eth.SignedBeaconBlockGloas)
|
||||
require.Equal(t, true, ok)
|
||||
require.DeepEqual(t, payload, gloas.Block.Body.PayloadAttestations)
|
||||
require.DeepEqual(t, bid, gloas.Block.Body.SignedExecutionPayloadBid)
|
||||
}
|
||||
|
||||
func TestInitSignedBlockFromProtoGloas(t *testing.T) {
|
||||
bits := bitfield.NewBitvector512()
|
||||
bits.SetBitAt(0, true)
|
||||
pb := ð.SignedBeaconBlockGloas{
|
||||
Block: ð.BeaconBlockGloas{
|
||||
Body: ð.BeaconBlockBodyGloas{
|
||||
PayloadAttestations: []*eth.PayloadAttestation{
|
||||
{
|
||||
AggregationBits: bits,
|
||||
Signature: []byte{0x01},
|
||||
},
|
||||
},
|
||||
SignedExecutionPayloadBid: ð.SignedExecutionPayloadBid{Signature: []byte{0x02}},
|
||||
},
|
||||
},
|
||||
Signature: []byte{0x03},
|
||||
}
|
||||
|
||||
sb, err := initSignedBlockFromProtoGloas(pb)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, version.Gloas, sb.Version())
|
||||
|
||||
gotPayload, err := sb.Block().Body().PayloadAttestations()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(gotPayload))
|
||||
require.DeepEqual(t, pb.Block.Body.PayloadAttestations, gotPayload)
|
||||
|
||||
gotBid, err := sb.Block().Body().SignedExecutionPayloadBid()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, pb.Block.Body.SignedExecutionPayloadBid, gotBid)
|
||||
}
|
||||
|
||||
func getFields() fields {
|
||||
b20 := make([]byte, 20)
|
||||
b48 := make([]byte, 48)
|
||||
|
||||
@@ -144,7 +144,7 @@ func (b *SignedBeaconBlock) SetSyncAggregate(s *eth.SyncAggregate) error {
|
||||
// SetExecution sets the execution payload of the block body.
|
||||
// This function is not thread safe, it is only used during block creation.
|
||||
func (b *SignedBeaconBlock) SetExecution(e interfaces.ExecutionData) error {
|
||||
if b.version == version.Phase0 || b.version == version.Altair {
|
||||
if b.version == version.Phase0 || b.version == version.Altair || b.version >= version.Gloas {
|
||||
return consensus_types.ErrNotSupported("Execution", b.version)
|
||||
}
|
||||
if e.IsBlinded() {
|
||||
@@ -176,9 +176,27 @@ func (b *SignedBeaconBlock) SetBlobKzgCommitments(c [][]byte) error {
|
||||
|
||||
// SetExecutionRequests sets the execution requests in the block.
|
||||
func (b *SignedBeaconBlock) SetExecutionRequests(req *enginev1.ExecutionRequests) error {
|
||||
if b.version < version.Electra {
|
||||
if b.version < version.Electra || b.version >= version.Gloas {
|
||||
return consensus_types.ErrNotSupported("SetExecutionRequests", b.version)
|
||||
}
|
||||
b.block.body.executionRequests = req
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetPayloadAttestations sets the payload attestations in the block.
|
||||
func (b *SignedBeaconBlock) SetPayloadAttestations(pa []*eth.PayloadAttestation) error {
|
||||
if b.version < version.Gloas {
|
||||
return consensus_types.ErrNotSupported("SetPayloadAttestations", b.version)
|
||||
}
|
||||
b.block.body.payloadAttestations = pa
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetSignedExecutionPayloadBid sets the signed execution payload header in the block.
|
||||
func (b *SignedBeaconBlock) SetSignedExecutionPayloadBid(header *eth.SignedExecutionPayloadBid) error {
|
||||
if b.version < version.Gloas {
|
||||
return consensus_types.ErrNotSupported("SetSignedExecutionPayloadBid", b.version)
|
||||
}
|
||||
b.block.body.signedExecutionPayloadBid = header
|
||||
return nil
|
||||
}
|
||||
|
||||
107
consensus-types/blocks/setters_test.go
Normal file
107
consensus-types/blocks/setters_test.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package blocks
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
bitfield "github.com/OffchainLabs/go-bitfield"
|
||||
consensus_types "github.com/OffchainLabs/prysm/v7/consensus-types"
|
||||
enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1"
|
||||
eth "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
"github.com/OffchainLabs/prysm/v7/runtime/version"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/require"
|
||||
)
|
||||
|
||||
func TestSignedBeaconBlock_SetPayloadAttestations(t *testing.T) {
|
||||
t.Run("rejects pre-Gloas versions", func(t *testing.T) {
|
||||
sb := newTestSignedBeaconBlock(version.Fulu)
|
||||
payload := []*eth.PayloadAttestation{{}}
|
||||
|
||||
err := sb.SetPayloadAttestations(payload)
|
||||
|
||||
require.ErrorIs(t, err, consensus_types.ErrUnsupportedField)
|
||||
require.IsNil(t, sb.block.body.payloadAttestations)
|
||||
})
|
||||
|
||||
t.Run("sets payload attestations for Gloas", func(t *testing.T) {
|
||||
sb := newTestSignedBeaconBlock(version.Gloas)
|
||||
payload := []*eth.PayloadAttestation{
|
||||
{
|
||||
AggregationBits: bitfield.NewBitvector512(),
|
||||
Data: ð.PayloadAttestationData{
|
||||
BeaconBlockRoot: []byte{0x01, 0x02},
|
||||
PayloadPresent: true,
|
||||
BlobDataAvailable: true,
|
||||
},
|
||||
Signature: []byte{0x03},
|
||||
},
|
||||
}
|
||||
|
||||
err := sb.SetPayloadAttestations(payload)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, payload, sb.block.body.payloadAttestations)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSignedBeaconBlock_SetSignedExecutionPayloadBid(t *testing.T) {
|
||||
t.Run("rejects pre-Gloas versions", func(t *testing.T) {
|
||||
sb := newTestSignedBeaconBlock(version.Fulu)
|
||||
payloadBid := ð.SignedExecutionPayloadBid{}
|
||||
|
||||
err := sb.SetSignedExecutionPayloadBid(payloadBid)
|
||||
|
||||
require.ErrorIs(t, err, consensus_types.ErrUnsupportedField)
|
||||
require.IsNil(t, sb.block.body.signedExecutionPayloadBid)
|
||||
})
|
||||
|
||||
t.Run("sets signed execution payload bid for Gloas", func(t *testing.T) {
|
||||
sb := newTestSignedBeaconBlock(version.Gloas)
|
||||
payloadBid := ð.SignedExecutionPayloadBid{
|
||||
Message: ð.ExecutionPayloadBid{
|
||||
ParentBlockHash: []byte{0xaa},
|
||||
BlockHash: []byte{0xbb},
|
||||
FeeRecipient: []byte{0xcc},
|
||||
},
|
||||
Signature: []byte{0xdd},
|
||||
}
|
||||
|
||||
err := sb.SetSignedExecutionPayloadBid(payloadBid)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, payloadBid, sb.block.body.signedExecutionPayloadBid)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSignedBeaconBlock_SetExecution(t *testing.T) {
|
||||
t.Run("rejects Gloas version", func(t *testing.T) {
|
||||
sb := newTestSignedBeaconBlock(version.Gloas)
|
||||
payload := &enginev1.ExecutionPayload{}
|
||||
wrapped, err := WrappedExecutionPayload(payload)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = sb.SetExecution(wrapped)
|
||||
require.ErrorIs(t, err, consensus_types.ErrUnsupportedField)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSignedBeaconBlock_SetExecutionRequests(t *testing.T) {
|
||||
t.Run("rejects Gloas version", func(t *testing.T) {
|
||||
sb := newTestSignedBeaconBlock(version.Gloas)
|
||||
requests := &enginev1.ExecutionRequests{}
|
||||
|
||||
err := sb.SetExecutionRequests(requests)
|
||||
require.ErrorIs(t, err, consensus_types.ErrUnsupportedField)
|
||||
})
|
||||
}
|
||||
|
||||
func newTestSignedBeaconBlock(ver int) *SignedBeaconBlock {
|
||||
return &SignedBeaconBlock{
|
||||
version: ver,
|
||||
block: &BeaconBlock{
|
||||
version: ver,
|
||||
body: &BeaconBlockBody{
|
||||
version: ver,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -40,23 +40,25 @@ var (
|
||||
|
||||
// BeaconBlockBody is the main beacon block body structure. It can represent any block type.
|
||||
type BeaconBlockBody struct {
|
||||
version int
|
||||
randaoReveal [field_params.BLSSignatureLength]byte
|
||||
eth1Data *eth.Eth1Data
|
||||
graffiti [field_params.RootLength]byte
|
||||
proposerSlashings []*eth.ProposerSlashing
|
||||
attesterSlashings []*eth.AttesterSlashing
|
||||
attesterSlashingsElectra []*eth.AttesterSlashingElectra
|
||||
attestations []*eth.Attestation
|
||||
attestationsElectra []*eth.AttestationElectra
|
||||
deposits []*eth.Deposit
|
||||
voluntaryExits []*eth.SignedVoluntaryExit
|
||||
syncAggregate *eth.SyncAggregate
|
||||
executionPayload interfaces.ExecutionData
|
||||
executionPayloadHeader interfaces.ExecutionData
|
||||
blsToExecutionChanges []*eth.SignedBLSToExecutionChange
|
||||
blobKzgCommitments [][]byte
|
||||
executionRequests *enginev1.ExecutionRequests
|
||||
version int
|
||||
randaoReveal [field_params.BLSSignatureLength]byte
|
||||
eth1Data *eth.Eth1Data
|
||||
graffiti [field_params.RootLength]byte
|
||||
proposerSlashings []*eth.ProposerSlashing
|
||||
attesterSlashings []*eth.AttesterSlashing
|
||||
attesterSlashingsElectra []*eth.AttesterSlashingElectra
|
||||
attestations []*eth.Attestation
|
||||
attestationsElectra []*eth.AttestationElectra
|
||||
deposits []*eth.Deposit
|
||||
voluntaryExits []*eth.SignedVoluntaryExit
|
||||
syncAggregate *eth.SyncAggregate
|
||||
executionPayload interfaces.ExecutionData
|
||||
executionPayloadHeader interfaces.ExecutionData
|
||||
blsToExecutionChanges []*eth.SignedBLSToExecutionChange
|
||||
blobKzgCommitments [][]byte
|
||||
executionRequests *enginev1.ExecutionRequests
|
||||
signedExecutionPayloadBid *eth.SignedExecutionPayloadBid
|
||||
payloadAttestations []*eth.PayloadAttestation
|
||||
}
|
||||
|
||||
var _ interfaces.ReadOnlyBeaconBlockBody = &BeaconBlockBody{}
|
||||
|
||||
@@ -69,6 +69,8 @@ type ReadOnlyBeaconBlockBody interface {
|
||||
BLSToExecutionChanges() ([]*ethpb.SignedBLSToExecutionChange, error)
|
||||
BlobKzgCommitments() ([][]byte, error)
|
||||
ExecutionRequests() (*enginev1.ExecutionRequests, error)
|
||||
PayloadAttestations() ([]*ethpb.PayloadAttestation, error)
|
||||
SignedExecutionPayloadBid() (*ethpb.SignedExecutionPayloadBid, error)
|
||||
}
|
||||
|
||||
type SignedBeaconBlock interface {
|
||||
@@ -91,6 +93,8 @@ type SignedBeaconBlock interface {
|
||||
SetSlot(slot primitives.Slot)
|
||||
SetSignature(sig []byte)
|
||||
SetExecutionRequests(er *enginev1.ExecutionRequests) error
|
||||
SetPayloadAttestations(pa []*ethpb.PayloadAttestation) error
|
||||
SetSignedExecutionPayloadBid(header *ethpb.SignedExecutionPayloadBid) error
|
||||
Unblind(e ExecutionData) error
|
||||
}
|
||||
|
||||
|
||||
@@ -276,6 +276,14 @@ func (b *BeaconBlockBody) ExecutionRequests() (*enginev1.ExecutionRequests, erro
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (b *BeaconBlockBody) PayloadAttestations() ([]*eth.PayloadAttestation, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (b *BeaconBlockBody) SignedExecutionPayloadBid() (*eth.SignedExecutionPayloadBid, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (b *BeaconBlockBody) Attestations() []eth.Att {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
@@ -172,6 +172,18 @@ func (cf *VersionedUnmarshaler) UnmarshalBeaconState(marshaled []byte) (s state.
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to init state trie from state, detected fork=%s", forkName)
|
||||
}
|
||||
case version.Gloas:
|
||||
st := ðpb.BeaconStateGloas{}
|
||||
|
||||
err = st.UnmarshalSSZ(marshaled)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to unmarshal state, detected fork=%s", forkName)
|
||||
}
|
||||
|
||||
s, err = state_native.InitializeFromProtoUnsafeGloas(st)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to init state trie from state, detected fork=%s", forkName)
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to initialize BeaconState for fork version=%s", forkName)
|
||||
}
|
||||
|
||||
@@ -26,8 +26,21 @@ func TestLifecycle(t *testing.T) {
|
||||
port := 1000 + rand.Intn(1000)
|
||||
prometheusService := NewService(t.Context(), fmt.Sprintf(":%d", port), nil)
|
||||
prometheusService.Start()
|
||||
// Give service time to start.
|
||||
time.Sleep(time.Second)
|
||||
// Actively wait until the service responds on /metrics (faster and less flaky than a fixed sleep)
|
||||
deadline := time.Now().Add(3 * time.Second)
|
||||
for {
|
||||
if time.Now().After(deadline) {
|
||||
t.Fatalf("metrics endpoint not ready within timeout")
|
||||
}
|
||||
resp, err := http.Get(fmt.Sprintf("http://localhost:%d/metrics", port))
|
||||
if err == nil {
|
||||
_ = resp.Body.Close()
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
break
|
||||
}
|
||||
}
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Query the service to ensure it really started.
|
||||
resp, err := http.Get(fmt.Sprintf("http://localhost:%d/metrics", port))
|
||||
@@ -36,8 +49,18 @@ func TestLifecycle(t *testing.T) {
|
||||
|
||||
err = prometheusService.Stop()
|
||||
require.NoError(t, err)
|
||||
// Give service time to stop.
|
||||
time.Sleep(time.Second)
|
||||
// Actively wait until the service stops responding on /metrics
|
||||
deadline = time.Now().Add(3 * time.Second)
|
||||
for {
|
||||
if time.Now().After(deadline) {
|
||||
t.Fatalf("metrics endpoint still reachable after timeout")
|
||||
}
|
||||
_, err = http.Get(fmt.Sprintf("http://localhost:%d/metrics", port))
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Query the service to ensure it really stopped.
|
||||
_, err = http.Get(fmt.Sprintf("http://localhost:%d/metrics", port))
|
||||
|
||||
288
proto/engine/v1/gloas.pb.go
generated
Executable file
288
proto/engine/v1/gloas.pb.go
generated
Executable file
@@ -0,0 +1,288 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.3
|
||||
// protoc v3.21.7
|
||||
// source: proto/engine/v1/gloas.proto
|
||||
|
||||
package enginev1
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
|
||||
github_com_OffchainLabs_prysm_v6_consensus_types_primitives "github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
_ "github.com/OffchainLabs/prysm/v7/proto/eth/ext"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type ExecutionPayloadEnvelope struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Payload *ExecutionPayloadDeneb `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"`
|
||||
ExecutionRequests *ExecutionRequests `protobuf:"bytes,2,opt,name=execution_requests,json=executionRequests,proto3" json:"execution_requests,omitempty"`
|
||||
BuilderIndex github_com_OffchainLabs_prysm_v6_consensus_types_primitives.ValidatorIndex `protobuf:"varint,3,opt,name=builder_index,json=builderIndex,proto3" json:"builder_index,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/primitives.ValidatorIndex"`
|
||||
BeaconBlockRoot []byte `protobuf:"bytes,4,opt,name=beacon_block_root,json=beaconBlockRoot,proto3" json:"beacon_block_root,omitempty" ssz-size:"32"`
|
||||
Slot github_com_OffchainLabs_prysm_v6_consensus_types_primitives.Slot `protobuf:"varint,5,opt,name=slot,proto3" json:"slot,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/primitives.Slot"`
|
||||
BlobKzgCommitments [][]byte `protobuf:"bytes,6,rep,name=blob_kzg_commitments,json=blobKzgCommitments,proto3" json:"blob_kzg_commitments,omitempty" ssz-max:"4096" ssz-size:"?,48"`
|
||||
StateRoot []byte `protobuf:"bytes,7,opt,name=state_root,json=stateRoot,proto3" json:"state_root,omitempty" ssz-size:"32"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ExecutionPayloadEnvelope) Reset() {
|
||||
*x = ExecutionPayloadEnvelope{}
|
||||
mi := &file_proto_engine_v1_gloas_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ExecutionPayloadEnvelope) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ExecutionPayloadEnvelope) ProtoMessage() {}
|
||||
|
||||
func (x *ExecutionPayloadEnvelope) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_engine_v1_gloas_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ExecutionPayloadEnvelope.ProtoReflect.Descriptor instead.
|
||||
func (*ExecutionPayloadEnvelope) Descriptor() ([]byte, []int) {
|
||||
return file_proto_engine_v1_gloas_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *ExecutionPayloadEnvelope) GetPayload() *ExecutionPayloadDeneb {
|
||||
if x != nil {
|
||||
return x.Payload
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ExecutionPayloadEnvelope) GetExecutionRequests() *ExecutionRequests {
|
||||
if x != nil {
|
||||
return x.ExecutionRequests
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ExecutionPayloadEnvelope) GetBuilderIndex() github_com_OffchainLabs_prysm_v6_consensus_types_primitives.ValidatorIndex {
|
||||
if x != nil {
|
||||
return x.BuilderIndex
|
||||
}
|
||||
return github_com_OffchainLabs_prysm_v6_consensus_types_primitives.ValidatorIndex(0)
|
||||
}
|
||||
|
||||
func (x *ExecutionPayloadEnvelope) GetBeaconBlockRoot() []byte {
|
||||
if x != nil {
|
||||
return x.BeaconBlockRoot
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ExecutionPayloadEnvelope) GetSlot() github_com_OffchainLabs_prysm_v6_consensus_types_primitives.Slot {
|
||||
if x != nil {
|
||||
return x.Slot
|
||||
}
|
||||
return github_com_OffchainLabs_prysm_v6_consensus_types_primitives.Slot(0)
|
||||
}
|
||||
|
||||
func (x *ExecutionPayloadEnvelope) GetBlobKzgCommitments() [][]byte {
|
||||
if x != nil {
|
||||
return x.BlobKzgCommitments
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ExecutionPayloadEnvelope) GetStateRoot() []byte {
|
||||
if x != nil {
|
||||
return x.StateRoot
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SignedExecutionPayloadEnvelope struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Message *ExecutionPayloadEnvelope `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
|
||||
Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty" ssz-size:"96"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *SignedExecutionPayloadEnvelope) Reset() {
|
||||
*x = SignedExecutionPayloadEnvelope{}
|
||||
mi := &file_proto_engine_v1_gloas_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *SignedExecutionPayloadEnvelope) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SignedExecutionPayloadEnvelope) ProtoMessage() {}
|
||||
|
||||
func (x *SignedExecutionPayloadEnvelope) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_engine_v1_gloas_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SignedExecutionPayloadEnvelope.ProtoReflect.Descriptor instead.
|
||||
func (*SignedExecutionPayloadEnvelope) Descriptor() ([]byte, []int) {
|
||||
return file_proto_engine_v1_gloas_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *SignedExecutionPayloadEnvelope) GetMessage() *ExecutionPayloadEnvelope {
|
||||
if x != nil {
|
||||
return x.Message
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SignedExecutionPayloadEnvelope) GetSignature() []byte {
|
||||
if x != nil {
|
||||
return x.Signature
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_proto_engine_v1_gloas_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_proto_engine_v1_gloas_proto_rawDesc = []byte{
|
||||
0x0a, 0x1b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2f, 0x76,
|
||||
0x31, 0x2f, 0x67, 0x6c, 0x6f, 0x61, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x65,
|
||||
0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76,
|
||||
0x31, 0x1a, 0x26, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2f,
|
||||
0x76, 0x31, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x67,
|
||||
0x69, 0x6e, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1d, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6c, 0x65, 0x63, 0x74,
|
||||
0x72, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f,
|
||||
0x65, 0x74, 0x68, 0x2f, 0x65, 0x78, 0x74, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa3, 0x04, 0x0a, 0x18, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f,
|
||||
0x70, 0x65, 0x12, 0x43, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65,
|
||||
0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x07,
|
||||
0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x54, 0x0a, 0x12, 0x65, 0x78, 0x65, 0x63, 0x75,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65,
|
||||
0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x52, 0x11, 0x65, 0x78, 0x65, 0x63,
|
||||
0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x73, 0x0a,
|
||||
0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03,
|
||||
0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
|
||||
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62,
|
||||
0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x36, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69,
|
||||
0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49,
|
||||
0x6e, 0x64, 0x65, 0x78, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x64,
|
||||
0x65, 0x78, 0x12, 0x32, 0x0a, 0x11, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x5f, 0x62, 0x6c, 0x6f,
|
||||
0x63, 0x6b, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a,
|
||||
0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f,
|
||||
0x63, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x05,
|
||||
0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
|
||||
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62,
|
||||
0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x36, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69,
|
||||
0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74,
|
||||
0x12, 0x42, 0x0a, 0x14, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x6b, 0x7a, 0x67, 0x5f, 0x63, 0x6f, 0x6d,
|
||||
0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x10,
|
||||
0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36,
|
||||
0x52, 0x12, 0x62, 0x6c, 0x6f, 0x62, 0x4b, 0x7a, 0x67, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d,
|
||||
0x65, 0x6e, 0x74, 0x73, 0x12, 0x25, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f,
|
||||
0x6f, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32,
|
||||
0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x8e, 0x01, 0x0a, 0x1e,
|
||||
0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50,
|
||||
0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x46,
|
||||
0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x2c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e,
|
||||
0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61,
|
||||
0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x07, 0x6d,
|
||||
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74,
|
||||
0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39,
|
||||
0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x3b, 0x5a, 0x39,
|
||||
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68,
|
||||
0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x36,
|
||||
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31,
|
||||
0x3b, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_proto_engine_v1_gloas_proto_rawDescOnce sync.Once
|
||||
file_proto_engine_v1_gloas_proto_rawDescData = file_proto_engine_v1_gloas_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_proto_engine_v1_gloas_proto_rawDescGZIP() []byte {
|
||||
file_proto_engine_v1_gloas_proto_rawDescOnce.Do(func() {
|
||||
file_proto_engine_v1_gloas_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_engine_v1_gloas_proto_rawDescData)
|
||||
})
|
||||
return file_proto_engine_v1_gloas_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_proto_engine_v1_gloas_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_proto_engine_v1_gloas_proto_goTypes = []any{
|
||||
(*ExecutionPayloadEnvelope)(nil), // 0: ethereum.engine.v1.ExecutionPayloadEnvelope
|
||||
(*SignedExecutionPayloadEnvelope)(nil), // 1: ethereum.engine.v1.SignedExecutionPayloadEnvelope
|
||||
(*ExecutionPayloadDeneb)(nil), // 2: ethereum.engine.v1.ExecutionPayloadDeneb
|
||||
(*ExecutionRequests)(nil), // 3: ethereum.engine.v1.ExecutionRequests
|
||||
}
|
||||
var file_proto_engine_v1_gloas_proto_depIdxs = []int32{
|
||||
2, // 0: ethereum.engine.v1.ExecutionPayloadEnvelope.payload:type_name -> ethereum.engine.v1.ExecutionPayloadDeneb
|
||||
3, // 1: ethereum.engine.v1.ExecutionPayloadEnvelope.execution_requests:type_name -> ethereum.engine.v1.ExecutionRequests
|
||||
0, // 2: ethereum.engine.v1.SignedExecutionPayloadEnvelope.message:type_name -> ethereum.engine.v1.ExecutionPayloadEnvelope
|
||||
3, // [3:3] is the sub-list for method output_type
|
||||
3, // [3:3] is the sub-list for method input_type
|
||||
3, // [3:3] is the sub-list for extension type_name
|
||||
3, // [3:3] is the sub-list for extension extendee
|
||||
0, // [0:3] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_proto_engine_v1_gloas_proto_init() }
|
||||
func file_proto_engine_v1_gloas_proto_init() {
|
||||
if File_proto_engine_v1_gloas_proto != nil {
|
||||
return
|
||||
}
|
||||
file_proto_engine_v1_execution_engine_proto_init()
|
||||
file_proto_engine_v1_electra_proto_init()
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_proto_engine_v1_gloas_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_proto_engine_v1_gloas_proto_goTypes,
|
||||
DependencyIndexes: file_proto_engine_v1_gloas_proto_depIdxs,
|
||||
MessageInfos: file_proto_engine_v1_gloas_proto_msgTypes,
|
||||
}.Build()
|
||||
File_proto_engine_v1_gloas_proto = out.File
|
||||
file_proto_engine_v1_gloas_proto_rawDesc = nil
|
||||
file_proto_engine_v1_gloas_proto_goTypes = nil
|
||||
file_proto_engine_v1_gloas_proto_depIdxs = nil
|
||||
}
|
||||
@@ -371,6 +371,7 @@ go_library(
|
||||
"beacon_block.go",
|
||||
"cloners.go",
|
||||
"eip_7521.go",
|
||||
"gloas.go",
|
||||
"log.go",
|
||||
"sync_committee_mainnet.go",
|
||||
"sync_committee_minimal.go", # keep
|
||||
|
||||
@@ -61,3 +61,116 @@ func CopySyncCommitteeContribution(c *SyncCommitteeContribution) *SyncCommitteeC
|
||||
Signature: bytesutil.SafeCopyBytes(c.Signature),
|
||||
}
|
||||
}
|
||||
|
||||
// CopySignedBeaconBlockGloas copies the provided signed beacon block Gloas object.
|
||||
func CopySignedBeaconBlockGloas(sb *SignedBeaconBlockGloas) *SignedBeaconBlockGloas {
|
||||
if sb == nil {
|
||||
return nil
|
||||
}
|
||||
return &SignedBeaconBlockGloas{
|
||||
Block: copyBeaconBlockGloas(sb.Block),
|
||||
Signature: bytesutil.SafeCopyBytes(sb.Signature),
|
||||
}
|
||||
}
|
||||
|
||||
// copyBeaconBlockGloas copies the provided beacon block Gloas object.
|
||||
func copyBeaconBlockGloas(b *BeaconBlockGloas) *BeaconBlockGloas {
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
return &BeaconBlockGloas{
|
||||
Slot: b.Slot,
|
||||
ProposerIndex: b.ProposerIndex,
|
||||
ParentRoot: bytesutil.SafeCopyBytes(b.ParentRoot),
|
||||
StateRoot: bytesutil.SafeCopyBytes(b.StateRoot),
|
||||
Body: copyBeaconBlockBodyGloas(b.Body),
|
||||
}
|
||||
}
|
||||
|
||||
// copyPayloadAttestation copies the provided payload attestation object.
|
||||
func copyPayloadAttestation(pa *PayloadAttestation) *PayloadAttestation {
|
||||
if pa == nil {
|
||||
return nil
|
||||
}
|
||||
copied := &PayloadAttestation{
|
||||
AggregationBits: pa.AggregationBits,
|
||||
Signature: bytesutil.SafeCopyBytes(pa.Signature),
|
||||
}
|
||||
if pa.Data != nil {
|
||||
copied.Data = &PayloadAttestationData{
|
||||
BeaconBlockRoot: bytesutil.SafeCopyBytes(pa.Data.BeaconBlockRoot),
|
||||
Slot: pa.Data.Slot,
|
||||
PayloadPresent: pa.Data.PayloadPresent,
|
||||
BlobDataAvailable: pa.Data.BlobDataAvailable,
|
||||
}
|
||||
}
|
||||
return copied
|
||||
}
|
||||
|
||||
// copyPayloadAttestations copies a slice of payload attestations.
|
||||
func copyPayloadAttestations(pas []*PayloadAttestation) []*PayloadAttestation {
|
||||
if len(pas) == 0 {
|
||||
return nil
|
||||
}
|
||||
copied := make([]*PayloadAttestation, len(pas))
|
||||
for i, pa := range pas {
|
||||
copied[i] = copyPayloadAttestation(pa)
|
||||
}
|
||||
return copied
|
||||
}
|
||||
|
||||
// copySignedExecutionPayloadBid copies the provided signed execution payload header.
|
||||
func copySignedExecutionPayloadBid(header *SignedExecutionPayloadBid) *SignedExecutionPayloadBid {
|
||||
if header == nil {
|
||||
return nil
|
||||
}
|
||||
copied := &SignedExecutionPayloadBid{
|
||||
Signature: bytesutil.SafeCopyBytes(header.Signature),
|
||||
}
|
||||
if header.Message != nil {
|
||||
copied.Message = &ExecutionPayloadBid{
|
||||
ParentBlockHash: bytesutil.SafeCopyBytes(header.Message.ParentBlockHash),
|
||||
ParentBlockRoot: bytesutil.SafeCopyBytes(header.Message.ParentBlockRoot),
|
||||
BlockHash: bytesutil.SafeCopyBytes(header.Message.BlockHash),
|
||||
FeeRecipient: bytesutil.SafeCopyBytes(header.Message.FeeRecipient),
|
||||
GasLimit: header.Message.GasLimit,
|
||||
BuilderIndex: header.Message.BuilderIndex,
|
||||
Slot: header.Message.Slot,
|
||||
Value: header.Message.Value,
|
||||
BlobKzgCommitmentsRoot: bytesutil.SafeCopyBytes(header.Message.BlobKzgCommitmentsRoot),
|
||||
}
|
||||
}
|
||||
return copied
|
||||
}
|
||||
|
||||
// copyBeaconBlockBodyGloas copies the provided beacon block body Gloas object.
|
||||
func copyBeaconBlockBodyGloas(body *BeaconBlockBodyGloas) *BeaconBlockBodyGloas {
|
||||
if body == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
copied := &BeaconBlockBodyGloas{
|
||||
RandaoReveal: bytesutil.SafeCopyBytes(body.RandaoReveal),
|
||||
Graffiti: bytesutil.SafeCopyBytes(body.Graffiti),
|
||||
}
|
||||
|
||||
if body.Eth1Data != nil {
|
||||
copied.Eth1Data = body.Eth1Data.Copy()
|
||||
}
|
||||
|
||||
if body.SyncAggregate != nil {
|
||||
copied.SyncAggregate = body.SyncAggregate.Copy()
|
||||
}
|
||||
|
||||
copied.ProposerSlashings = CopySlice(body.ProposerSlashings)
|
||||
copied.AttesterSlashings = CopySlice(body.AttesterSlashings)
|
||||
copied.Attestations = CopySlice(body.Attestations)
|
||||
copied.Deposits = CopySlice(body.Deposits)
|
||||
copied.VoluntaryExits = CopySlice(body.VoluntaryExits)
|
||||
copied.BlsToExecutionChanges = CopySlice(body.BlsToExecutionChanges)
|
||||
|
||||
copied.SignedExecutionPayloadBid = copySignedExecutionPayloadBid(body.SignedExecutionPayloadBid)
|
||||
copied.PayloadAttestations = copyPayloadAttestations(body.PayloadAttestations)
|
||||
|
||||
return copied
|
||||
}
|
||||
|
||||
@@ -5,9 +5,12 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
bitfield "github.com/OffchainLabs/go-bitfield"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1"
|
||||
v1alpha1 "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/assert"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func TestCopySignedBeaconBlock(t *testing.T) {
|
||||
@@ -100,6 +103,49 @@ func TestCopySyncCommitteeContribution(t *testing.T) {
|
||||
assert.NotEmpty(t, got, "Copied sync committee contribution has empty fields")
|
||||
}
|
||||
|
||||
func TestCopySignedBeaconBlockGloasNil(t *testing.T) {
|
||||
if got := v1alpha1.CopySignedBeaconBlockGloas(nil); got != nil {
|
||||
t.Fatalf("CopySignedBeaconBlockGloas(nil) = %v, want nil", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopySignedBeaconBlockGloasDeepCopy(t *testing.T) {
|
||||
original := genSignedBeaconBlockGloas()
|
||||
|
||||
copied := v1alpha1.CopySignedBeaconBlockGloas(original)
|
||||
if copied == original {
|
||||
t.Fatalf("CopySignedBeaconBlockGloas returned the original pointer")
|
||||
}
|
||||
if !reflect.DeepEqual(copied, original) {
|
||||
t.Fatalf("CopySignedBeaconBlockGloas() = %v, want %v", copied, original)
|
||||
}
|
||||
|
||||
want := proto.Clone(copied).(*v1alpha1.SignedBeaconBlockGloas)
|
||||
|
||||
original.Signature[0] ^= 0xFF
|
||||
original.Block.ParentRoot[0] ^= 0xFF
|
||||
original.Block.StateRoot[0] ^= 0xFF
|
||||
original.Block.Body.RandaoReveal[0] ^= 0xFF
|
||||
original.Block.Body.Graffiti[0] ^= 0xFF
|
||||
original.Block.Body.SyncAggregate.SyncCommitteeSignature[0] ^= 0xFF
|
||||
original.Block.Body.SignedExecutionPayloadBid.Signature[0] ^= 0xFF
|
||||
original.Block.Body.SignedExecutionPayloadBid.Message.BlockHash[0] ^= 0xFF
|
||||
original.Block.Body.PayloadAttestations[0].Signature[0] ^= 0xFF
|
||||
original.Block.Body.PayloadAttestations[0].Data.BeaconBlockRoot[0] ^= 0xFF
|
||||
original.Block.Body.PayloadAttestations = append(original.Block.Body.PayloadAttestations, &v1alpha1.PayloadAttestation{})
|
||||
original.Block.Body.BlsToExecutionChanges[0].Message.ValidatorIndex++
|
||||
|
||||
if !reflect.DeepEqual(want, copied) {
|
||||
t.Fatalf("copy mutated after modifying source: got %v, want %v", copied, want)
|
||||
}
|
||||
if copied.Block == original.Block {
|
||||
t.Fatal("expected copied block pointer to differ from original")
|
||||
}
|
||||
if copied.Block.Body == original.Block.Body {
|
||||
t.Fatal("expected copied block body pointer to differ from original")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyPendingAttestationSlice(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -1125,3 +1171,78 @@ func genConsolidationRequest() *enginev1.ConsolidationRequest {
|
||||
TargetPubkey: bytes(48),
|
||||
}
|
||||
}
|
||||
|
||||
func genSignedBeaconBlockGloas() *v1alpha1.SignedBeaconBlockGloas {
|
||||
return &v1alpha1.SignedBeaconBlockGloas{
|
||||
Block: genBeaconBlockGloas(),
|
||||
Signature: bytes(96),
|
||||
}
|
||||
}
|
||||
|
||||
func genBeaconBlockGloas() *v1alpha1.BeaconBlockGloas {
|
||||
return &v1alpha1.BeaconBlockGloas{
|
||||
Slot: primitives.Slot(rand.Uint64()),
|
||||
ProposerIndex: primitives.ValidatorIndex(rand.Uint64()),
|
||||
ParentRoot: bytes(32),
|
||||
StateRoot: bytes(32),
|
||||
Body: genBeaconBlockBodyGloas(),
|
||||
}
|
||||
}
|
||||
|
||||
func genBeaconBlockBodyGloas() *v1alpha1.BeaconBlockBodyGloas {
|
||||
return &v1alpha1.BeaconBlockBodyGloas{
|
||||
RandaoReveal: bytes(96),
|
||||
Eth1Data: genEth1Data(),
|
||||
Graffiti: bytes(32),
|
||||
ProposerSlashings: genProposerSlashings(3),
|
||||
AttesterSlashings: genAttesterSlashingsElectra(3),
|
||||
Attestations: genAttestationsElectra(3),
|
||||
Deposits: genDeposits(3),
|
||||
VoluntaryExits: genSignedVoluntaryExits(3),
|
||||
SyncAggregate: genSyncAggregate(),
|
||||
BlsToExecutionChanges: genBLSToExecutionChanges(2),
|
||||
SignedExecutionPayloadBid: genSignedExecutionPayloadBidGloas(),
|
||||
PayloadAttestations: genPayloadAttestations(2),
|
||||
}
|
||||
}
|
||||
|
||||
func genSignedExecutionPayloadBidGloas() *v1alpha1.SignedExecutionPayloadBid {
|
||||
return &v1alpha1.SignedExecutionPayloadBid{
|
||||
Message: genExecutionPayloadBidGloas(),
|
||||
Signature: bytes(96),
|
||||
}
|
||||
}
|
||||
|
||||
func genExecutionPayloadBidGloas() *v1alpha1.ExecutionPayloadBid {
|
||||
return &v1alpha1.ExecutionPayloadBid{
|
||||
ParentBlockHash: bytes(32),
|
||||
ParentBlockRoot: bytes(32),
|
||||
BlockHash: bytes(32),
|
||||
FeeRecipient: bytes(20),
|
||||
GasLimit: rand.Uint64(),
|
||||
BuilderIndex: primitives.ValidatorIndex(rand.Uint64()),
|
||||
Slot: primitives.Slot(rand.Uint64()),
|
||||
Value: primitives.Gwei(rand.Uint64()),
|
||||
BlobKzgCommitmentsRoot: bytes(32),
|
||||
}
|
||||
}
|
||||
|
||||
func genPayloadAttestations(num int) []*v1alpha1.PayloadAttestation {
|
||||
pas := make([]*v1alpha1.PayloadAttestation, num)
|
||||
for i := range pas {
|
||||
bits := bitfield.NewBitvector512()
|
||||
bits.SetBitAt(uint64(i%512), true)
|
||||
|
||||
pas[i] = &v1alpha1.PayloadAttestation{
|
||||
AggregationBits: bits,
|
||||
Data: &v1alpha1.PayloadAttestationData{
|
||||
BeaconBlockRoot: bytes(32),
|
||||
Slot: primitives.Slot(rand.Uint64()),
|
||||
PayloadPresent: i%2 == 0,
|
||||
BlobDataAvailable: i%2 == 1,
|
||||
},
|
||||
Signature: bytes(96),
|
||||
}
|
||||
}
|
||||
return pas
|
||||
}
|
||||
|
||||
47
proto/prysm/v1alpha1/gloas.go
Normal file
47
proto/prysm/v1alpha1/gloas.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package eth
|
||||
|
||||
import (
|
||||
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
|
||||
)
|
||||
|
||||
// Copy creates a deep copy of ExecutionPayloadBid.
|
||||
func (header *ExecutionPayloadBid) Copy() *ExecutionPayloadBid {
|
||||
if header == nil {
|
||||
return nil
|
||||
}
|
||||
return &ExecutionPayloadBid{
|
||||
ParentBlockHash: bytesutil.SafeCopyBytes(header.ParentBlockHash),
|
||||
ParentBlockRoot: bytesutil.SafeCopyBytes(header.ParentBlockRoot),
|
||||
BlockHash: bytesutil.SafeCopyBytes(header.BlockHash),
|
||||
FeeRecipient: bytesutil.SafeCopyBytes(header.FeeRecipient),
|
||||
GasLimit: header.GasLimit,
|
||||
BuilderIndex: header.BuilderIndex,
|
||||
Slot: header.Slot,
|
||||
Value: header.Value,
|
||||
BlobKzgCommitmentsRoot: bytesutil.SafeCopyBytes(header.BlobKzgCommitmentsRoot),
|
||||
}
|
||||
}
|
||||
|
||||
// Copy creates a deep copy of BuilderPendingWithdrawal.
|
||||
func (withdrawal *BuilderPendingWithdrawal) Copy() *BuilderPendingWithdrawal {
|
||||
if withdrawal == nil {
|
||||
return nil
|
||||
}
|
||||
return &BuilderPendingWithdrawal{
|
||||
FeeRecipient: bytesutil.SafeCopyBytes(withdrawal.FeeRecipient),
|
||||
Amount: withdrawal.Amount,
|
||||
BuilderIndex: withdrawal.BuilderIndex,
|
||||
WithdrawableEpoch: withdrawal.WithdrawableEpoch,
|
||||
}
|
||||
}
|
||||
|
||||
// Copy creates a deep copy of BuilderPendingPayment.
|
||||
func (payment *BuilderPendingPayment) Copy() *BuilderPendingPayment {
|
||||
if payment == nil {
|
||||
return nil
|
||||
}
|
||||
return &BuilderPendingPayment{
|
||||
Weight: payment.Weight,
|
||||
Withdrawal: payment.Withdrawal.Copy(),
|
||||
}
|
||||
}
|
||||
168
proto/prysm/v1alpha1/gloas_test.go
Normal file
168
proto/prysm/v1alpha1/gloas_test.go
Normal file
@@ -0,0 +1,168 @@
|
||||
package eth
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
)
|
||||
|
||||
func TestExecutionPayloadBid_Copy(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
bid *ExecutionPayloadBid
|
||||
}{
|
||||
{
|
||||
name: "nil bid",
|
||||
bid: nil,
|
||||
},
|
||||
{
|
||||
name: "empty bid",
|
||||
bid: &ExecutionPayloadBid{},
|
||||
},
|
||||
{
|
||||
name: "fully populated bid",
|
||||
bid: &ExecutionPayloadBid{
|
||||
ParentBlockHash: []byte("parent_block_hash_32_bytes_long!"),
|
||||
ParentBlockRoot: []byte("parent_block_root_32_bytes_long!"),
|
||||
BlockHash: []byte("block_hash_32_bytes_are_long!!"),
|
||||
FeeRecipient: []byte("fee_recipient_20_byt"),
|
||||
GasLimit: 15000000,
|
||||
BuilderIndex: primitives.ValidatorIndex(42),
|
||||
Slot: primitives.Slot(12345),
|
||||
Value: 1000000000000000000,
|
||||
BlobKzgCommitmentsRoot: []byte("blob_kzg_commitments_32_bytes!!"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
copied := tt.bid.Copy()
|
||||
if tt.bid == nil {
|
||||
if copied != nil {
|
||||
t.Errorf("Copy() of nil should return nil, got %v", copied)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(tt.bid, copied) {
|
||||
t.Errorf("Copy() = %v, want %v", copied, tt.bid)
|
||||
}
|
||||
|
||||
if len(tt.bid.ParentBlockHash) > 0 {
|
||||
tt.bid.ParentBlockHash[0] = 0xFF
|
||||
if copied.ParentBlockHash[0] == 0xFF {
|
||||
t.Error("Copy() did not create deep copy of ParentBlockHash")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilderPendingWithdrawal_Copy(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
withdrawal *BuilderPendingWithdrawal
|
||||
}{
|
||||
{
|
||||
name: "nil withdrawal",
|
||||
withdrawal: nil,
|
||||
},
|
||||
{
|
||||
name: "empty withdrawal",
|
||||
withdrawal: &BuilderPendingWithdrawal{},
|
||||
},
|
||||
{
|
||||
name: "fully populated withdrawal",
|
||||
withdrawal: &BuilderPendingWithdrawal{
|
||||
FeeRecipient: []byte("fee_recipient_20_byt"),
|
||||
Amount: primitives.Gwei(5000000000),
|
||||
BuilderIndex: primitives.ValidatorIndex(123),
|
||||
WithdrawableEpoch: primitives.Epoch(456),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
copied := tt.withdrawal.Copy()
|
||||
if tt.withdrawal == nil {
|
||||
if copied != nil {
|
||||
t.Errorf("Copy() of nil should return nil, got %v", copied)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(tt.withdrawal, copied) {
|
||||
t.Errorf("Copy() = %v, want %v", copied, tt.withdrawal)
|
||||
}
|
||||
|
||||
// Verify deep copy by modifying original
|
||||
if len(tt.withdrawal.FeeRecipient) > 0 {
|
||||
tt.withdrawal.FeeRecipient[0] = 0xFF
|
||||
if copied.FeeRecipient[0] == 0xFF {
|
||||
t.Error("Copy() did not create deep copy of FeeRecipient")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilderPendingPayment_Copy(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
payment *BuilderPendingPayment
|
||||
}{
|
||||
{
|
||||
name: "nil payment",
|
||||
payment: nil,
|
||||
},
|
||||
{
|
||||
name: "empty payment",
|
||||
payment: &BuilderPendingPayment{},
|
||||
},
|
||||
{
|
||||
name: "payment with nil withdrawal",
|
||||
payment: &BuilderPendingPayment{
|
||||
Weight: primitives.Gwei(1000),
|
||||
Withdrawal: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fully populated payment",
|
||||
payment: &BuilderPendingPayment{
|
||||
Weight: primitives.Gwei(2500),
|
||||
Withdrawal: &BuilderPendingWithdrawal{
|
||||
FeeRecipient: []byte("test_recipient_20byt"),
|
||||
Amount: primitives.Gwei(10000),
|
||||
BuilderIndex: primitives.ValidatorIndex(789),
|
||||
WithdrawableEpoch: primitives.Epoch(999),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
copied := tt.payment.Copy()
|
||||
if tt.payment == nil {
|
||||
if copied != nil {
|
||||
t.Errorf("Copy() of nil should return nil, got %v", copied)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(tt.payment, copied) {
|
||||
t.Errorf("Copy() = %v, want %v", copied, tt.payment)
|
||||
}
|
||||
|
||||
if tt.payment.Withdrawal != nil && len(tt.payment.Withdrawal.FeeRecipient) > 0 {
|
||||
tt.payment.Withdrawal.FeeRecipient[0] = 0xFF
|
||||
if copied.Withdrawal != nil && len(copied.Withdrawal.FeeRecipient) > 0 && copied.Withdrawal.FeeRecipient[0] == 0xFF {
|
||||
t.Error("Copy() did not create deep copy of nested Withdrawal.FeeRecipient")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,11 @@ go_library(
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["fork_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = ["@com_github_stretchr_testify//assert:go_default_library"],
|
||||
deps = [
|
||||
":go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"@com_github_stretchr_testify//assert:go_default_library",
|
||||
"@com_github_stretchr_testify//require:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -14,6 +14,7 @@ const (
|
||||
Deneb
|
||||
Electra
|
||||
Fulu
|
||||
Gloas
|
||||
)
|
||||
|
||||
var versionToString = map[int]string{
|
||||
@@ -24,11 +25,19 @@ var versionToString = map[int]string{
|
||||
Deneb: "deneb",
|
||||
Electra: "electra",
|
||||
Fulu: "fulu",
|
||||
Gloas: "gloas",
|
||||
}
|
||||
|
||||
// stringToVersion and allVersions are populated in init()
|
||||
var stringToVersion = map[string]int{}
|
||||
var allVersions []int
|
||||
var supportedVersions []int
|
||||
|
||||
// unsupportedVersions contains fork versions that exist in the enums but are not yet
|
||||
// enabled on any supported network. These versions are removed from All().
|
||||
var unsupportedVersions = map[int]struct{}{
|
||||
Gloas: {},
|
||||
}
|
||||
|
||||
// ErrUnrecognizedVersionName means a string does not match the list of canonical version names.
|
||||
var ErrUnrecognizedVersionName = errors.New("version name doesn't map to a known value in the enum")
|
||||
@@ -52,9 +61,15 @@ func String(version int) string {
|
||||
return name
|
||||
}
|
||||
|
||||
// All returns a list of all known fork versions.
|
||||
// All returns a list of all supported fork versions.
|
||||
func All() []int {
|
||||
return allVersions
|
||||
return supportedVersions
|
||||
}
|
||||
|
||||
// IsUnsupported reports whether the provided version is currently gate-kept.
|
||||
func IsUnsupported(version int) bool {
|
||||
_, ok := unsupportedVersions[version]
|
||||
return ok
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -66,4 +81,12 @@ func init() {
|
||||
i++
|
||||
}
|
||||
sort.Ints(allVersions)
|
||||
|
||||
supportedVersions = make([]int, 0, len(allVersions))
|
||||
for _, v := range allVersions {
|
||||
if _, skip := unsupportedVersions[v]; skip {
|
||||
continue
|
||||
}
|
||||
supportedVersions = append(supportedVersions, v)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package version
|
||||
package version_test
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
"github.com/OffchainLabs/prysm/v7/runtime/version"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestVersionString(t *testing.T) {
|
||||
@@ -16,18 +20,18 @@ func TestVersionString(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "phase0",
|
||||
version: Phase0,
|
||||
version: version.Phase0,
|
||||
want: "phase0",
|
||||
},
|
||||
{
|
||||
name: "altair",
|
||||
version: Altair,
|
||||
version: version.Altair,
|
||||
want: "altair",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := String(tt.version); got != tt.want {
|
||||
if got := version.String(tt.version); got != tt.want {
|
||||
t.Errorf("String() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
@@ -35,7 +39,7 @@ func TestVersionString(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestVersionSorting(t *testing.T) {
|
||||
versions := All()
|
||||
versions := version.All()
|
||||
expected := slices.Clone(versions)
|
||||
sort.Ints(expected)
|
||||
tests := []struct {
|
||||
@@ -54,3 +58,74 @@ func TestVersionSorting(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnsupportedVersionsExcludedFromAll(t *testing.T) {
|
||||
for _, v := range unsupportedVersions() {
|
||||
assert.NotContains(t, version.All(), v, "unsupported version %s should not be returned by version.All()", version.String(v))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnsupportedVersionsAreNotScheduledOnTestnets(t *testing.T) {
|
||||
unsupported := unsupportedVersions()
|
||||
if len(unsupported) == 0 {
|
||||
t.Skip("no unsupported versions defined")
|
||||
}
|
||||
|
||||
testnetConfigs := []*params.BeaconChainConfig{
|
||||
params.HoleskyConfig(),
|
||||
params.SepoliaConfig(),
|
||||
params.HoodiConfig(),
|
||||
}
|
||||
|
||||
for _, v := range unsupported {
|
||||
for _, cfg := range testnetConfigs {
|
||||
epoch := forkEpochForVersion(cfg, v)
|
||||
require.Equalf(
|
||||
t,
|
||||
cfg.FarFutureEpoch,
|
||||
epoch,
|
||||
"unsupported version %s should not be scheduled on %s (epoch=%d)",
|
||||
version.String(v),
|
||||
cfg.ConfigName,
|
||||
epoch,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func forkEpochForVersion(cfg *params.BeaconChainConfig, v int) primitives.Epoch {
|
||||
switch v {
|
||||
case version.Phase0:
|
||||
return cfg.GenesisEpoch
|
||||
case version.Altair:
|
||||
return cfg.AltairForkEpoch
|
||||
case version.Bellatrix:
|
||||
return cfg.BellatrixForkEpoch
|
||||
case version.Capella:
|
||||
return cfg.CapellaForkEpoch
|
||||
case version.Deneb:
|
||||
return cfg.DenebForkEpoch
|
||||
case version.Electra:
|
||||
return cfg.ElectraForkEpoch
|
||||
case version.Fulu:
|
||||
return cfg.FuluForkEpoch
|
||||
default:
|
||||
if version.IsUnsupported(v) {
|
||||
return cfg.FarFutureEpoch
|
||||
}
|
||||
panic("forkEpochForVersion missing version " + version.String(v))
|
||||
}
|
||||
}
|
||||
|
||||
func unsupportedVersions() []int {
|
||||
var unsupportedVersions []int
|
||||
for v := 0; ; v++ {
|
||||
if version.String(v) == "unknown version" {
|
||||
break
|
||||
}
|
||||
if version.IsUnsupported(v) {
|
||||
unsupportedVersions = append(unsupportedVersions, v)
|
||||
}
|
||||
}
|
||||
return unsupportedVersions
|
||||
}
|
||||
|
||||
@@ -261,11 +261,15 @@ func (c *componentHandler) printPIDs(logger func(string, ...any)) {
|
||||
|
||||
msg += "This test PID: " + strconv.Itoa(os.Getpid()) + " (parent=" + strconv.Itoa(os.Getppid()) + ")\n"
|
||||
|
||||
// Beacon chain nodes
|
||||
msg += fmt.Sprintf("Beacon chain nodes: %v\n", PIDsFromMultiComponentRunner(c.beaconNodes))
|
||||
// Validator nodes
|
||||
msg += fmt.Sprintf("Prysm beacon chain nodes: %v\n", PIDsFromMultiComponentRunner(c.beaconNodes))
|
||||
msg += fmt.Sprintf("Prysm validators: %v\n", PIDsFromMultiComponentRunner(c.validatorNodes))
|
||||
if c.lighthouseBeaconNodes != nil {
|
||||
msg += fmt.Sprintf("Lighthouse beacon chain nodes: %v\n", PIDsFromMultiComponentRunner(c.lighthouseBeaconNodes))
|
||||
}
|
||||
if c.lighthouseValidatorNodes != nil {
|
||||
msg += fmt.Sprintf("Lighthouse validators: %v\n", PIDsFromMultiComponentRunner(c.lighthouseValidatorNodes))
|
||||
}
|
||||
msg += fmt.Sprintf("Validators: %v\n", PIDsFromMultiComponentRunner(c.validatorNodes))
|
||||
// ETH1 nodes
|
||||
msg += fmt.Sprintf("ETH1 nodes: %v\n", PIDsFromMultiComponentRunner(c.eth1Nodes))
|
||||
|
||||
logger(msg)
|
||||
|
||||
@@ -249,6 +249,10 @@ func (node *LighthouseBeaconNode) Stop() error {
|
||||
return node.cmd.Process.Kill()
|
||||
}
|
||||
|
||||
func (node *LighthouseBeaconNode) UnderlyingProcess() *os.Process {
|
||||
return node.cmd.Process
|
||||
}
|
||||
|
||||
func (node *LighthouseBeaconNode) createTestnetDir(ctx context.Context, index int) (string, error) {
|
||||
testNetDir := e2e.TestParams.TestPath + fmt.Sprintf("/lighthouse-testnet-%d", index)
|
||||
configPath := filepath.Join(testNetDir, "config.yaml")
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user