mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-02-12 22:15:07 -05:00
Compare commits
3 Commits
hdiff-rest
...
GetVersion
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
295dbaf85c | ||
|
|
fea9930c49 | ||
|
|
3b0f34a79f |
@@ -63,6 +63,19 @@ type PeerCount struct {
|
||||
Connected string `json:"connected"`
|
||||
Disconnecting string `json:"disconnecting"`
|
||||
}
|
||||
type GetVersionV2Response struct {
|
||||
Data *VersionV2 `json:"data"`
|
||||
}
|
||||
type VersionV2 struct {
|
||||
BeaconNode *ClientVersionV1 `json:"beacon_node"`
|
||||
ExecutionClient *ClientVersionV1 `json:"execution_client,omitempty"`
|
||||
}
|
||||
type ClientVersionV1 struct {
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Commit string `json:"commit"`
|
||||
}
|
||||
|
||||
type GetVersionResponse struct {
|
||||
Data *Version `json:"data"`
|
||||
|
||||
@@ -27,7 +27,6 @@ go_library(
|
||||
"receive_blob.go",
|
||||
"receive_block.go",
|
||||
"receive_data_column.go",
|
||||
"receive_payload_attestation_message.go",
|
||||
"service.go",
|
||||
"setup_forkchoice.go",
|
||||
"tracked_proposer.go",
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
// PayloadAttestationReceiver interface defines the methods of chain service for receiving
|
||||
// validated payload attestation messages.
|
||||
type PayloadAttestationReceiver interface {
|
||||
ReceivePayloadAttestationMessage(context.Context, *ethpb.PayloadAttestationMessage) error
|
||||
}
|
||||
|
||||
// ReceivePayloadAttestationMessage accepts a payload attestation message.
|
||||
func (s *Service) ReceivePayloadAttestationMessage(ctx context.Context, a *ethpb.PayloadAttestationMessage) error {
|
||||
// TODO: Handle payload attestation message processing once Gloas is fully wired.
|
||||
return nil
|
||||
}
|
||||
@@ -757,11 +757,6 @@ func (c *ChainService) ReceiveDataColumns(dcs []blocks.VerifiedRODataColumn) err
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReceivePayloadAttestationMessage implements the same method in the chain service.
|
||||
func (c *ChainService) ReceivePayloadAttestationMessage(_ context.Context, _ *ethpb.PayloadAttestationMessage) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DependentRootForEpoch mocks the same method in the chain service
|
||||
func (c *ChainService) DependentRootForEpoch(_ [32]byte, _ primitives.Epoch) ([32]byte, error) {
|
||||
return c.TargetRoot, nil
|
||||
|
||||
2
beacon-chain/cache/BUILD.bazel
vendored
2
beacon-chain/cache/BUILD.bazel
vendored
@@ -17,7 +17,6 @@ go_library(
|
||||
"error.go",
|
||||
"interfaces.go",
|
||||
"log.go",
|
||||
"payload_attestation.go",
|
||||
"payload_id.go",
|
||||
"proposer_indices.go",
|
||||
"proposer_indices_disabled.go", # keep
|
||||
@@ -77,7 +76,6 @@ go_test(
|
||||
"checkpoint_state_test.go",
|
||||
"committee_fuzz_test.go",
|
||||
"committee_test.go",
|
||||
"payload_attestation_test.go",
|
||||
"payload_id_test.go",
|
||||
"private_access_test.go",
|
||||
"proposer_indices_test.go",
|
||||
|
||||
53
beacon-chain/cache/payload_attestation.go
vendored
53
beacon-chain/cache/payload_attestation.go
vendored
@@ -1,53 +0,0 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
)
|
||||
|
||||
// PayloadAttestationCache tracks seen payload attestation messages for a single slot.
|
||||
type PayloadAttestationCache struct {
|
||||
slot primitives.Slot
|
||||
seen map[primitives.ValidatorIndex]struct{}
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// Seen returns true if a vote for the given slot has already been
|
||||
// processed for this validator index.
|
||||
func (p *PayloadAttestationCache) Seen(slot primitives.Slot, idx primitives.ValidatorIndex) bool {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
if p.slot != slot {
|
||||
return false
|
||||
}
|
||||
if p.seen == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := p.seen[idx]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Add marks the given slot and validator index as seen.
|
||||
// This function assumes that the message has already been validated.
|
||||
func (p *PayloadAttestationCache) Add(slot primitives.Slot, idx primitives.ValidatorIndex) error {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
if p.slot != slot {
|
||||
p.slot = slot
|
||||
p.seen = make(map[primitives.ValidatorIndex]struct{})
|
||||
}
|
||||
if p.seen == nil {
|
||||
p.seen = make(map[primitives.ValidatorIndex]struct{})
|
||||
}
|
||||
p.seen[idx] = struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clear clears the internal cache.
|
||||
func (p *PayloadAttestationCache) Clear() {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.slot = 0
|
||||
p.seen = nil
|
||||
}
|
||||
48
beacon-chain/cache/payload_attestation_test.go
vendored
48
beacon-chain/cache/payload_attestation_test.go
vendored
@@ -1,48 +0,0 @@
|
||||
package cache_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/cache"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPayloadAttestationCache_SeenAndAdd(t *testing.T) {
|
||||
var c cache.PayloadAttestationCache
|
||||
slot1 := primitives.Slot(1)
|
||||
slot2 := primitives.Slot(2)
|
||||
idx1 := primitives.ValidatorIndex(3)
|
||||
idx2 := primitives.ValidatorIndex(4)
|
||||
|
||||
require.False(t, c.Seen(slot1, idx1))
|
||||
|
||||
require.NoError(t, c.Add(slot1, idx1))
|
||||
require.True(t, c.Seen(slot1, idx1))
|
||||
require.False(t, c.Seen(slot1, idx2))
|
||||
require.False(t, c.Seen(slot2, idx1))
|
||||
|
||||
require.NoError(t, c.Add(slot1, idx2))
|
||||
require.True(t, c.Seen(slot1, idx1))
|
||||
require.True(t, c.Seen(slot1, idx2))
|
||||
|
||||
require.NoError(t, c.Add(slot2, idx1))
|
||||
require.True(t, c.Seen(slot2, idx1))
|
||||
require.False(t, c.Seen(slot1, idx1))
|
||||
require.False(t, c.Seen(slot1, idx2))
|
||||
}
|
||||
|
||||
func TestPayloadAttestationCache_Clear(t *testing.T) {
|
||||
var c cache.PayloadAttestationCache
|
||||
slot := primitives.Slot(10)
|
||||
idx := primitives.ValidatorIndex(42)
|
||||
|
||||
require.NoError(t, c.Add(slot, idx))
|
||||
require.True(t, c.Seen(slot, idx))
|
||||
|
||||
c.Clear()
|
||||
require.False(t, c.Seen(slot, idx))
|
||||
|
||||
require.NoError(t, c.Add(slot, idx))
|
||||
require.True(t, c.Seen(slot, idx))
|
||||
}
|
||||
@@ -77,7 +77,7 @@ func ProcessPayloadAttestations(ctx context.Context, st state.BeaconState, body
|
||||
|
||||
// indexedPayloadAttestation converts a payload attestation into its indexed form.
|
||||
func indexedPayloadAttestation(ctx context.Context, st state.ReadOnlyBeaconState, att *eth.PayloadAttestation) (*consensus_types.IndexedPayloadAttestation, error) {
|
||||
committee, err := PayloadCommittee(ctx, st, att.Data.Slot)
|
||||
committee, err := payloadCommittee(ctx, st, att.Data.Slot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -96,7 +96,7 @@ func indexedPayloadAttestation(ctx context.Context, st state.ReadOnlyBeaconState
|
||||
}, nil
|
||||
}
|
||||
|
||||
// PayloadCommittee returns the payload timeliness committee for a given slot for the state.
|
||||
// payloadCommittee returns the payload timeliness committee for a given slot for the state.
|
||||
//
|
||||
// <spec fn="get_ptc" fork="gloas" hash="ae15f761">
|
||||
// def get_ptc(state: BeaconState, slot: Slot) -> Vector[ValidatorIndex, PTC_SIZE]:
|
||||
@@ -115,7 +115,7 @@ func indexedPayloadAttestation(ctx context.Context, st state.ReadOnlyBeaconState
|
||||
// state, indices, seed, size=PTC_SIZE, shuffle_indices=False
|
||||
// )
|
||||
// </spec>
|
||||
func PayloadCommittee(ctx context.Context, st state.ReadOnlyBeaconState, slot primitives.Slot) ([]primitives.ValidatorIndex, error) {
|
||||
func payloadCommittee(ctx context.Context, st state.ReadOnlyBeaconState, slot primitives.Slot) ([]primitives.ValidatorIndex, error) {
|
||||
epoch := slots.ToEpoch(slot)
|
||||
seed, err := ptcSeed(st, epoch, slot)
|
||||
if err != nil {
|
||||
|
||||
@@ -26,15 +26,6 @@ var ErrNotFoundMetadataSeqNum = errors.Wrap(ErrNotFound, "metadata sequence numb
|
||||
// but the database was created without state-diff support.
|
||||
var ErrStateDiffIncompatible = errors.New("state-diff feature enabled but database was created without state-diff support")
|
||||
|
||||
// ErrStateDiffCorrupted is returned when state-diff metadata or data is missing or invalid.
|
||||
var ErrStateDiffCorrupted = errors.New("state-diff database corrupted")
|
||||
|
||||
// ErrStateDiffExponentMismatch is returned when configured exponents differ from stored metadata.
|
||||
var ErrStateDiffExponentMismatch = errors.New("state-diff exponents mismatch")
|
||||
|
||||
// ErrStateDiffMissingSnapshot is returned when the offset snapshot is missing.
|
||||
var ErrStateDiffMissingSnapshot = errors.New("state-diff offset snapshot missing")
|
||||
|
||||
var errEmptyBlockSlice = errors.New("[]blocks.ROBlock is empty")
|
||||
var errIncorrectBlockParent = errors.New("unexpected missing or forked blocks in a []ROBlock")
|
||||
var errFinalizedChildNotFound = errors.New("unable to find finalized root descending from backfill batch")
|
||||
|
||||
@@ -7,11 +7,9 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/db/iface"
|
||||
"github.com/OffchainLabs/prysm/v7/cmd/beacon-chain/flags"
|
||||
"github.com/OffchainLabs/prysm/v7/config/features"
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
|
||||
@@ -23,7 +21,6 @@ import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
prombolt "github.com/prysmaticlabs/prombbolt"
|
||||
logrus "github.com/sirupsen/logrus"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
@@ -226,42 +223,8 @@ func (kv *Store) startStateDiff(ctx context.Context) error {
|
||||
}
|
||||
|
||||
if hasOffset {
|
||||
storedExponents, err := kv.loadStateDiffExponents()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: state-diff metadata missing or invalid; re-sync required: %v", ErrStateDiffCorrupted, err)
|
||||
}
|
||||
currentExponents := flags.Get().StateDiffExponents
|
||||
if !slices.Equal(storedExponents, currentExponents) {
|
||||
return errors.Wrapf(
|
||||
ErrStateDiffExponentMismatch,
|
||||
"state-diff exponents changed; database incompatible. "+
|
||||
"Database was initialized with: %v. "+
|
||||
"Current configuration: %v. "+
|
||||
"Options: use original exponents (--state-diff-exponents=%s) or delete database and re-sync from genesis/checkpoint.",
|
||||
storedExponents,
|
||||
currentExponents,
|
||||
formatStateDiffExponents(storedExponents),
|
||||
)
|
||||
}
|
||||
offset, err := kv.loadOffset()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cache, err := populateStateDiffCacheFromDB(kv, offset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
kv.stateDiffCache = cache
|
||||
if flags.Get().StateDiffValidateOnStartup {
|
||||
if err := validateStateDiffCache(ctx, kv, cache); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
log.WithFields(logrus.Fields{
|
||||
"offset": offset,
|
||||
"exponents": storedExponents,
|
||||
}).Info("State-diff cache initialized from existing database")
|
||||
return nil
|
||||
// Existing state-diff database - restarts not yet supported.
|
||||
return errors.New("restarting with existing state-diff database not yet supported")
|
||||
}
|
||||
|
||||
// Check if this is a new database (no head block).
|
||||
|
||||
@@ -3,15 +3,12 @@ package kv
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/cmd/beacon-chain/flags"
|
||||
"github.com/OffchainLabs/prysm/v7/config/features"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
|
||||
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"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
@@ -30,108 +27,6 @@ func setupDB(t testing.TB) *Store {
|
||||
return db
|
||||
}
|
||||
|
||||
func TestStartStateDiff_ExponentMismatch(t *testing.T) {
|
||||
resetCfg := features.InitWithReset(&features.Flags{EnableStateDiff: true})
|
||||
defer resetCfg()
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
store := setupDB(t)
|
||||
require.NoError(t, store.db.Update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(stateDiffBucket)
|
||||
if bucket == nil {
|
||||
return bolt.ErrBucketNotFound
|
||||
}
|
||||
offsetBytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(offsetBytes, 0)
|
||||
if err := bucket.Put(offsetKey, offsetBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
encoded, err := encodeStateDiffExponents([]int{20, 10})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return bucket.Put(exponentsKey, encoded)
|
||||
}))
|
||||
|
||||
ctx := t.Context()
|
||||
err := store.startStateDiff(ctx)
|
||||
require.ErrorContains(t, "state-diff exponents changed", err)
|
||||
}
|
||||
|
||||
func TestStartStateDiff_MissingOffsetSnapshot(t *testing.T) {
|
||||
resetCfg := features.InitWithReset(&features.Flags{EnableStateDiff: true})
|
||||
defer resetCfg()
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
store := setupDB(t)
|
||||
require.NoError(t, store.db.Update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(stateDiffBucket)
|
||||
if bucket == nil {
|
||||
return bolt.ErrBucketNotFound
|
||||
}
|
||||
offsetBytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(offsetBytes, 0)
|
||||
if err := bucket.Put(offsetKey, offsetBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
encoded, err := encodeStateDiffExponents(flags.Get().StateDiffExponents)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return bucket.Put(exponentsKey, encoded)
|
||||
}))
|
||||
|
||||
ctx := t.Context()
|
||||
err := store.startStateDiff(ctx)
|
||||
require.ErrorContains(t, "missing offset snapshot", err)
|
||||
}
|
||||
|
||||
func TestStartStateDiff_ValidateOnStartup(t *testing.T) {
|
||||
resetCfg := features.InitWithReset(&features.Flags{EnableStateDiff: true})
|
||||
defer resetCfg()
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
globalFlags := flags.GlobalFlags{
|
||||
StateDiffExponents: flags.Get().StateDiffExponents,
|
||||
StateDiffValidateOnStartup: true,
|
||||
}
|
||||
flags.Init(&globalFlags)
|
||||
|
||||
store := setupDB(t)
|
||||
require.NoError(t, store.db.Update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(stateDiffBucket)
|
||||
if bucket == nil {
|
||||
return bolt.ErrBucketNotFound
|
||||
}
|
||||
st, _ := createState(t, 0, version.Phase0)
|
||||
stateBytes, err := st.MarshalSSZ()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
enc, err := addKey(st.Version(), stateBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
offsetBytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(offsetBytes, 0)
|
||||
if err := bucket.Put(offsetKey, offsetBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
encoded, err := encodeStateDiffExponents(flags.Get().StateDiffExponents)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := bucket.Put(exponentsKey, encoded); err != nil {
|
||||
return err
|
||||
}
|
||||
key := makeKeyForStateDiffTree(0, 0)
|
||||
return bucket.Put(key, enc)
|
||||
}))
|
||||
|
||||
err := store.startStateDiff(t.Context())
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_setupBlockStorageType(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
t.Run("fresh database with feature enabled to store full blocks should store full blocks", func(t *testing.T) {
|
||||
|
||||
@@ -1048,15 +1048,10 @@ func (s *Store) isStateValidatorMigrationOver() (bool, error) {
|
||||
}
|
||||
|
||||
func (s *Store) getStateUsingStateDiff(ctx context.Context, blockRoot [32]byte) (state.BeaconState, error) {
|
||||
stateSummary, err := s.StateSummary(ctx, blockRoot)
|
||||
slot, err := s.SlotByBlockRoot(ctx, blockRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if stateSummary == nil {
|
||||
return nil, ErrNotFoundState
|
||||
}
|
||||
|
||||
slot := stateSummary.Slot
|
||||
|
||||
if uint64(slot) < s.getOffset() {
|
||||
return nil, ErrSlotBeforeOffset
|
||||
@@ -1070,33 +1065,14 @@ func (s *Store) getStateUsingStateDiff(ctx context.Context, blockRoot [32]byte)
|
||||
return nil, errors.New("state not found")
|
||||
}
|
||||
|
||||
blk, err := s.Block(ctx, blockRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if blk != nil && !blk.IsNil() {
|
||||
stateRoot, err := st.HashTreeRoot(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if stateRoot != blk.Block().StateRoot() {
|
||||
return nil, errors.Wrap(ErrNotFoundState, "state root mismatch for block")
|
||||
}
|
||||
}
|
||||
|
||||
return st, nil
|
||||
}
|
||||
|
||||
func (s *Store) hasStateUsingStateDiff(ctx context.Context, blockRoot [32]byte) (bool, error) {
|
||||
stateSummary, err := s.StateSummary(ctx, blockRoot)
|
||||
slot, err := s.SlotByBlockRoot(ctx, blockRoot)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if stateSummary == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
slot := stateSummary.Slot
|
||||
|
||||
if uint64(slot) < s.getOffset() {
|
||||
return false, ErrSlotBeforeOffset
|
||||
|
||||
@@ -133,9 +133,6 @@ func (s *Store) saveHdiff(lvl int, anchor, st state.ReadOnlyBeaconState) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := s.stateDiffCache.setLevelHasData(lvl); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -175,9 +172,6 @@ func (s *Store) saveFullSnapshot(st state.ReadOnlyBeaconState) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.stateDiffCache.setLevelHasData(0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,131 +1,19 @@
|
||||
package kv
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
||||
"github.com/OffchainLabs/prysm/v7/cmd/beacon-chain/flags"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
pkgerrors "github.com/pkg/errors"
|
||||
"go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
type stateDiffCache struct {
|
||||
sync.RWMutex
|
||||
anchors []state.ReadOnlyBeaconState
|
||||
levelsWithData []bool
|
||||
offset uint64
|
||||
}
|
||||
|
||||
func populateStateDiffCacheFromDB(s *Store, offset uint64) (*stateDiffCache, error) {
|
||||
cache := &stateDiffCache{
|
||||
anchors: make([]state.ReadOnlyBeaconState, len(flags.Get().StateDiffExponents)-1),
|
||||
levelsWithData: make([]bool, len(flags.Get().StateDiffExponents)),
|
||||
offset: offset,
|
||||
}
|
||||
|
||||
if err := s.db.View(func(tx *bbolt.Tx) error {
|
||||
bucket := tx.Bucket(stateDiffBucket)
|
||||
if bucket == nil {
|
||||
return bbolt.ErrBucketNotFound
|
||||
}
|
||||
for level := range cache.levelsWithData {
|
||||
if level == 0 {
|
||||
if bucket.Get(makeKeyForStateDiffTree(0, offset)) != nil {
|
||||
cache.levelsWithData[level] = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
cursor := bucket.Cursor()
|
||||
prefix := []byte{byte(level)}
|
||||
key, _ := cursor.Seek(prefix)
|
||||
if key != nil && key[0] == byte(level) {
|
||||
slot, ok := slotFromStateDiffKey(key)
|
||||
if !ok {
|
||||
return ErrStateDiffCorrupted
|
||||
}
|
||||
if slot < offset {
|
||||
return ErrStateDiffCorrupted
|
||||
}
|
||||
if level == 0 && slot != offset {
|
||||
return ErrStateDiffCorrupted
|
||||
}
|
||||
if computeLevel(offset, primitives.Slot(slot)) != level {
|
||||
return ErrStateDiffCorrupted
|
||||
}
|
||||
cache.levelsWithData[level] = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
anchor0, err := s.getFullSnapshot(offset)
|
||||
if err != nil {
|
||||
return nil, pkgerrors.Wrapf(ErrStateDiffMissingSnapshot, "state diff cache: missing offset snapshot at %d", offset)
|
||||
}
|
||||
cache.anchors[0] = anchor0
|
||||
cache.levelsWithData[0] = true
|
||||
|
||||
return cache, nil
|
||||
}
|
||||
|
||||
func validateStateDiffCache(ctx context.Context, s *Store, cache *stateDiffCache) error {
|
||||
for level, hasData := range cache.levelsWithData {
|
||||
if !hasData || level == 0 {
|
||||
continue
|
||||
}
|
||||
maxSlot, err := latestSlotForLevel(s, level)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := s.stateByDiff(ctx, primitives.Slot(maxSlot)); err != nil {
|
||||
return pkgerrors.Wrapf(ErrStateDiffCorrupted, "state diff validation failed for level %d slot %d: %v", level, maxSlot, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func latestSlotForLevel(s *Store, level int) (uint64, error) {
|
||||
var maxSlot uint64
|
||||
found := false
|
||||
err := s.db.View(func(tx *bbolt.Tx) error {
|
||||
bucket := tx.Bucket(stateDiffBucket)
|
||||
if bucket == nil {
|
||||
return bbolt.ErrBucketNotFound
|
||||
}
|
||||
cursor := bucket.Cursor()
|
||||
prefix := []byte{byte(level)}
|
||||
for key, _ := cursor.Seek(prefix); key != nil && key[0] == byte(level); key, _ = cursor.Next() {
|
||||
slot, ok := slotFromStateDiffKey(key)
|
||||
if !ok {
|
||||
return ErrStateDiffCorrupted
|
||||
}
|
||||
if !found || slot > maxSlot {
|
||||
maxSlot = slot
|
||||
found = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !found {
|
||||
return 0, ErrStateDiffCorrupted
|
||||
}
|
||||
return maxSlot, nil
|
||||
}
|
||||
|
||||
func slotFromStateDiffKey(key []byte) (uint64, bool) {
|
||||
if len(key) < 9 {
|
||||
return 0, false
|
||||
}
|
||||
return binary.LittleEndian.Uint64(key[1:9]), true
|
||||
anchors []state.ReadOnlyBeaconState
|
||||
offset uint64
|
||||
}
|
||||
|
||||
func newStateDiffCache(s *Store) (*stateDiffCache, error) {
|
||||
@@ -149,9 +37,8 @@ func newStateDiffCache(s *Store) (*stateDiffCache, error) {
|
||||
}
|
||||
|
||||
return &stateDiffCache{
|
||||
anchors: make([]state.ReadOnlyBeaconState, len(flags.Get().StateDiffExponents)-1), // -1 because last level doesn't need to be cached
|
||||
levelsWithData: make([]bool, len(flags.Get().StateDiffExponents)),
|
||||
offset: offset,
|
||||
anchors: make([]state.ReadOnlyBeaconState, len(flags.Get().StateDiffExponents)-1), // -1 because last level doesn't need to be cached
|
||||
offset: offset,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -171,25 +58,6 @@ func (c *stateDiffCache) setAnchor(level int, anchor state.ReadOnlyBeaconState)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *stateDiffCache) levelHasData(level int) bool {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
if level < 0 || level >= len(c.levelsWithData) {
|
||||
return false
|
||||
}
|
||||
return c.levelsWithData[level]
|
||||
}
|
||||
|
||||
func (c *stateDiffCache) setLevelHasData(level int) error {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if level < 0 || level >= len(c.levelsWithData) {
|
||||
return errors.New("state diff cache: level data index out of range")
|
||||
}
|
||||
c.levelsWithData[level] = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *stateDiffCache) getOffset() uint64 {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
||||
statenative "github.com/OffchainLabs/prysm/v7/beacon-chain/state/state-native"
|
||||
@@ -22,78 +21,9 @@ import (
|
||||
|
||||
var (
|
||||
offsetKey = []byte("offset")
|
||||
exponentsKey = []byte("exponents")
|
||||
ErrSlotBeforeOffset = errors.New("slot is before state-diff root offset")
|
||||
)
|
||||
|
||||
func encodeStateDiffExponents(exponents []int) ([]byte, error) {
|
||||
if len(exponents) == 0 {
|
||||
return nil, errors.New("state diff exponents cannot be empty")
|
||||
}
|
||||
if len(exponents) > 255 {
|
||||
return nil, fmt.Errorf("state diff exponents length %d exceeds max 255", len(exponents))
|
||||
}
|
||||
encoded := make([]byte, len(exponents)+1)
|
||||
encoded[0] = byte(len(exponents))
|
||||
for i, exp := range exponents {
|
||||
if exp < 2 || exp > flags.MaxStateDiffExponent {
|
||||
return nil, fmt.Errorf("state diff exponent %d out of range for encoding", exp)
|
||||
}
|
||||
encoded[i+1] = byte(exp)
|
||||
}
|
||||
return encoded, nil
|
||||
}
|
||||
|
||||
func decodeStateDiffExponents(encoded []byte) ([]int, error) {
|
||||
if len(encoded) == 0 {
|
||||
return nil, errors.New("state diff exponents missing length prefix")
|
||||
}
|
||||
count := int(encoded[0])
|
||||
if count == 0 {
|
||||
return nil, errors.New("state diff exponents length cannot be zero")
|
||||
}
|
||||
if len(encoded) != count+1 {
|
||||
return nil, fmt.Errorf("state diff exponents length mismatch: expected %d got %d", count, len(encoded)-1)
|
||||
}
|
||||
exponents := make([]int, count)
|
||||
for i := range count {
|
||||
exponents[i] = int(encoded[i+1])
|
||||
}
|
||||
return exponents, nil
|
||||
}
|
||||
|
||||
func formatStateDiffExponents(exponents []int) string {
|
||||
if len(exponents) == 0 {
|
||||
return ""
|
||||
}
|
||||
parts := make([]string, len(exponents))
|
||||
for i, exp := range exponents {
|
||||
parts[i] = fmt.Sprintf("%d", exp)
|
||||
}
|
||||
return strings.Join(parts, ",")
|
||||
}
|
||||
|
||||
func (s *Store) loadStateDiffExponents() ([]int, error) {
|
||||
var encoded []byte
|
||||
err := s.db.View(func(tx *bbolt.Tx) error {
|
||||
bucket := tx.Bucket(stateDiffBucket)
|
||||
if bucket == nil {
|
||||
return bbolt.ErrBucketNotFound
|
||||
}
|
||||
value := bucket.Get(exponentsKey)
|
||||
if value == nil {
|
||||
return errors.New("state diff exponents not found")
|
||||
}
|
||||
encoded = make([]byte, len(value))
|
||||
copy(encoded, value)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decodeStateDiffExponents(encoded)
|
||||
}
|
||||
|
||||
func makeKeyForStateDiffTree(level int, slot uint64) []byte {
|
||||
buf := make([]byte, 16)
|
||||
buf[0] = byte(level)
|
||||
@@ -194,29 +124,6 @@ func (s *Store) getOffset() uint64 {
|
||||
return s.stateDiffCache.getOffset()
|
||||
}
|
||||
|
||||
func (s *Store) loadOffset() (uint64, 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(offsetKey)
|
||||
if offsetBytes == nil {
|
||||
return errors.New("state diff offset not found")
|
||||
}
|
||||
if len(offsetBytes) != 8 {
|
||||
return fmt.Errorf("state diff offset has invalid length %d", len(offsetBytes))
|
||||
}
|
||||
offset = binary.LittleEndian.Uint64(offsetBytes)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return offset, nil
|
||||
}
|
||||
|
||||
// hasStateDiffOffset checks if the state-diff offset has been set in the database.
|
||||
// This is used to detect if an existing database has state-diff enabled.
|
||||
func (s *Store) hasStateDiffOffset() (bool, error) {
|
||||
@@ -246,13 +153,8 @@ func (s *Store) initializeStateDiff(slot primitives.Slot, initialState state.Rea
|
||||
return nil
|
||||
}
|
||||
}
|
||||
exponentsBytes, err := encodeStateDiffExponents(flags.Get().StateDiffExponents)
|
||||
if err != nil {
|
||||
return pkgerrors.Wrap(err, "failed to encode state diff exponents")
|
||||
}
|
||||
|
||||
// Write metadata directly to the database (without using cache which doesn't exist yet).
|
||||
err = s.db.Update(func(tx *bbolt.Tx) error {
|
||||
// Write offset directly to the database (without using cache which doesn't exist yet).
|
||||
err := s.db.Update(func(tx *bbolt.Tx) error {
|
||||
bucket := tx.Bucket(stateDiffBucket)
|
||||
if bucket == nil {
|
||||
return bbolt.ErrBucketNotFound
|
||||
@@ -260,10 +162,7 @@ func (s *Store) initializeStateDiff(slot primitives.Slot, initialState state.Rea
|
||||
|
||||
offsetBytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(offsetBytes, uint64(slot))
|
||||
if err := bucket.Put(offsetKey, offsetBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
return bucket.Put(exponentsKey, exponentsBytes)
|
||||
return bucket.Put(offsetKey, offsetBytes)
|
||||
})
|
||||
if err != nil {
|
||||
return pkgerrors.Wrap(err, "failed to set offset")
|
||||
@@ -387,20 +286,15 @@ func (s *Store) getBaseAndDiffChain(offset uint64, slot primitives.Slot) (state.
|
||||
}
|
||||
|
||||
var diffChainItems []diffItem
|
||||
lastSeenAnchorRelSlot := baseAnchorSlot - offset
|
||||
lastSeenAnchorSlot := baseAnchorSlot
|
||||
for i, exp := range exponents[1 : lvl+1] {
|
||||
span := math.PowerOf2(uint64(exp))
|
||||
diffSlot := rel / span * span
|
||||
if diffSlot == lastSeenAnchorRelSlot {
|
||||
if diffSlot == lastSeenAnchorSlot {
|
||||
continue
|
||||
}
|
||||
level := i + 1
|
||||
if s.stateDiffCache != nil && !s.stateDiffCache.levelHasData(level) {
|
||||
lastSeenAnchorRelSlot = diffSlot
|
||||
continue
|
||||
}
|
||||
diffChainItems = append(diffChainItems, diffItem{level: level, slot: diffSlot + offset})
|
||||
lastSeenAnchorRelSlot = diffSlot
|
||||
diffChainItems = append(diffChainItems, diffItem{level: i + 1, slot: diffSlot + offset})
|
||||
lastSeenAnchorSlot = diffSlot
|
||||
}
|
||||
|
||||
baseSnapshot, err := s.getFullSnapshot(baseAnchorSlot)
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
||||
"github.com/OffchainLabs/prysm/v7/cmd/beacon-chain/flags"
|
||||
"github.com/OffchainLabs/prysm/v7/config/features"
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
"github.com/OffchainLabs/prysm/v7/math"
|
||||
@@ -35,81 +34,6 @@ func TestStateDiff_LoadOrInitOffset(t *testing.T) {
|
||||
require.Equal(t, uint64(10), offset)
|
||||
}
|
||||
|
||||
func TestStateDiff_LoadOffset(t *testing.T) {
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
db := setupDB(t)
|
||||
_, err := db.loadOffset()
|
||||
require.ErrorContains(t, "offset not found", err)
|
||||
|
||||
err = setOffsetInDB(db, 10)
|
||||
require.NoError(t, err)
|
||||
offset, err := db.loadOffset()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(10), offset)
|
||||
}
|
||||
|
||||
func TestStateDiff_EncodeDecodeExponents(t *testing.T) {
|
||||
t.Run("roundtrip", func(t *testing.T) {
|
||||
exponents := []int{21, 18, 16, 13}
|
||||
encoded, err := encodeStateDiffExponents(exponents)
|
||||
require.NoError(t, err)
|
||||
decoded, err := decodeStateDiffExponents(encoded)
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, exponents, decoded)
|
||||
})
|
||||
|
||||
t.Run("encode-empty", func(t *testing.T) {
|
||||
_, err := encodeStateDiffExponents(nil)
|
||||
require.ErrorContains(t, "cannot be empty", err)
|
||||
})
|
||||
|
||||
t.Run("encode-negative", func(t *testing.T) {
|
||||
_, err := encodeStateDiffExponents([]int{21, -1})
|
||||
require.ErrorContains(t, "out of range", err)
|
||||
})
|
||||
|
||||
t.Run("encode-too-large", func(t *testing.T) {
|
||||
_, err := encodeStateDiffExponents([]int{flags.MaxStateDiffExponent + 1})
|
||||
require.ErrorContains(t, "out of range", err)
|
||||
})
|
||||
|
||||
t.Run("decode-empty", func(t *testing.T) {
|
||||
_, err := decodeStateDiffExponents(nil)
|
||||
require.ErrorContains(t, "missing length prefix", err)
|
||||
})
|
||||
|
||||
t.Run("decode-zero-length", func(t *testing.T) {
|
||||
_, err := decodeStateDiffExponents([]byte{0})
|
||||
require.ErrorContains(t, "length cannot be zero", err)
|
||||
})
|
||||
|
||||
t.Run("decode-length-mismatch", func(t *testing.T) {
|
||||
_, err := decodeStateDiffExponents([]byte{2, 10})
|
||||
require.ErrorContains(t, "length mismatch", err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStateDiff_InitializeStoresExponents(t *testing.T) {
|
||||
setDefaultStateDiffExponents()
|
||||
resetCfg := features.InitWithReset(&features.Flags{EnableStateDiff: true})
|
||||
defer resetCfg()
|
||||
|
||||
db := setupDB(t)
|
||||
st, _ := createState(t, 0, version.Phase0)
|
||||
require.NoError(t, db.initializeStateDiff(0, st))
|
||||
|
||||
stored, err := db.loadStateDiffExponents()
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, flags.Get().StateDiffExponents, stored)
|
||||
}
|
||||
|
||||
func TestStateDiff_LoadExponentsMissing(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
_, err := db.loadStateDiffExponents()
|
||||
require.ErrorContains(t, "exponents not found", err)
|
||||
}
|
||||
|
||||
func TestStateDiff_ComputeLevel(t *testing.T) {
|
||||
db := setupDB(t)
|
||||
setDefaultStateDiffExponents()
|
||||
@@ -230,124 +154,6 @@ func TestStateDiff_SaveFullSnapshot(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateDiff_StateByDiff_NonZeroOffsetSkipsRedundantLevelDiff(t *testing.T) {
|
||||
setStateDiffExponents([]int{6, 5, 4})
|
||||
|
||||
db := setupDB(t)
|
||||
|
||||
offset := uint64(1000)
|
||||
require.NoError(t, setOffsetInDB(db, offset))
|
||||
|
||||
stOffset, _ := createState(t, primitives.Slot(offset), version.Phase0)
|
||||
require.NoError(t, db.saveStateByDiff(context.Background(), stOffset))
|
||||
|
||||
st32, _ := createState(t, primitives.Slot(offset+32), version.Phase0)
|
||||
require.NoError(t, db.saveStateByDiff(context.Background(), st32))
|
||||
|
||||
st64, _ := createState(t, primitives.Slot(offset+64), version.Phase0)
|
||||
require.NoError(t, db.saveStateByDiff(context.Background(), st64))
|
||||
|
||||
st80, _ := createState(t, primitives.Slot(offset+80), version.Phase0)
|
||||
require.NoError(t, db.saveStateByDiff(context.Background(), st80))
|
||||
|
||||
readSt, err := db.stateByDiff(context.Background(), primitives.Slot(offset+80))
|
||||
require.NoError(t, err)
|
||||
|
||||
stWantSSZ, err := st80.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
stGotSSZ, err := readSt.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, stWantSSZ, stGotSSZ)
|
||||
}
|
||||
|
||||
func TestStateDiff_PopulateStateDiffCacheFromDB(t *testing.T) {
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
db := setupDB(t)
|
||||
_, err := populateStateDiffCacheFromDB(db, 0)
|
||||
require.ErrorContains(t, "missing offset snapshot", err)
|
||||
|
||||
st, _ := createState(t, 0, version.Phase0)
|
||||
require.NoError(t, setOffsetInDB(db, 0))
|
||||
require.NoError(t, db.saveStateByDiff(context.Background(), st))
|
||||
|
||||
err = db.db.Update(func(tx *bbolt.Tx) error {
|
||||
bucket := tx.Bucket(stateDiffBucket)
|
||||
if bucket == nil {
|
||||
return bbolt.ErrBucketNotFound
|
||||
}
|
||||
key := makeKeyForStateDiffTree(2, math.PowerOf2(16))
|
||||
return bucket.Put(append(key, stateSuffix...), []byte{1})
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
cache, err := populateStateDiffCacheFromDB(db, 0)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, cache)
|
||||
require.Equal(t, uint64(0), cache.getOffset())
|
||||
require.NotNil(t, cache.getAnchor(0))
|
||||
require.Equal(t, true, cache.levelHasData(0))
|
||||
require.Equal(t, false, cache.levelHasData(1))
|
||||
require.Equal(t, true, cache.levelHasData(2))
|
||||
}
|
||||
|
||||
func TestStateDiff_PopulateStateDiffCacheFromDB_InvalidLevelKey(t *testing.T) {
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
db := setupDB(t)
|
||||
st, _ := createState(t, 0, version.Phase0)
|
||||
require.NoError(t, setOffsetInDB(db, 0))
|
||||
require.NoError(t, db.saveStateByDiff(context.Background(), st))
|
||||
|
||||
require.NoError(t, db.db.Update(func(tx *bbolt.Tx) error {
|
||||
bucket := tx.Bucket(stateDiffBucket)
|
||||
if bucket == nil {
|
||||
return bbolt.ErrBucketNotFound
|
||||
}
|
||||
key := makeKeyForStateDiffTree(2, 1)
|
||||
return bucket.Put(append(key, stateSuffix...), []byte{1})
|
||||
}))
|
||||
|
||||
_, err := populateStateDiffCacheFromDB(db, 0)
|
||||
require.ErrorIs(t, ErrStateDiffCorrupted, err)
|
||||
}
|
||||
|
||||
func TestStateDiff_GetBaseAndDiffChainSkipsEmptyLevels(t *testing.T) {
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
db := setupDB(t)
|
||||
require.NoError(t, setOffsetInDB(db, 0))
|
||||
st, _ := createState(t, 0, version.Phase0)
|
||||
require.NoError(t, db.saveFullSnapshot(st))
|
||||
|
||||
cache, err := populateStateDiffCacheFromDB(db, 0)
|
||||
require.NoError(t, err)
|
||||
cache.levelsWithData[0] = true
|
||||
cache.levelsWithData[1] = false
|
||||
cache.levelsWithData[2] = true
|
||||
db.stateDiffCache = cache
|
||||
|
||||
slot := primitives.Slot(math.PowerOf2(18) + math.PowerOf2(16))
|
||||
key := makeKeyForStateDiffTree(2, uint64(slot))
|
||||
require.NoError(t, db.db.Update(func(tx *bbolt.Tx) error {
|
||||
bucket := tx.Bucket(stateDiffBucket)
|
||||
if bucket == nil {
|
||||
return bbolt.ErrBucketNotFound
|
||||
}
|
||||
if err := bucket.Put(append(key, stateSuffix...), []byte{1}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := bucket.Put(append(key, validatorSuffix...), []byte{2}); err != nil {
|
||||
return err
|
||||
}
|
||||
return bucket.Put(append(key, balancesSuffix...), []byte{3})
|
||||
}))
|
||||
|
||||
_, diffChain, err := db.getBaseAndDiffChain(0, slot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(diffChain))
|
||||
}
|
||||
|
||||
func TestStateDiff_SaveAndReadFullSnapshot(t *testing.T) {
|
||||
setDefaultStateDiffExponents()
|
||||
|
||||
@@ -853,10 +659,8 @@ func setOffsetInDB(s *Store, offset uint64) error {
|
||||
}
|
||||
|
||||
func setDefaultStateDiffExponents() {
|
||||
setStateDiffExponents([]int{21, 18, 16, 13, 11, 9, 5})
|
||||
}
|
||||
|
||||
func setStateDiffExponents(exponents []int) {
|
||||
globalFlags := flags.GlobalFlags{StateDiffExponents: exponents}
|
||||
globalFlags := flags.GlobalFlags{
|
||||
StateDiffExponents: []int{21, 18, 16, 13, 11, 9, 5},
|
||||
}
|
||||
flags.Init(&globalFlags)
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"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"
|
||||
)
|
||||
|
||||
@@ -1348,7 +1349,7 @@ func TestStore_CanSaveRetrieveStateUsingStateDiff(t *testing.T) {
|
||||
|
||||
readSt, err := db.State(context.Background(), [32]byte{'A'})
|
||||
require.IsNil(t, readSt)
|
||||
require.ErrorIs(t, err, ErrNotFoundState)
|
||||
require.ErrorContains(t, "neither state summary nor block found", err)
|
||||
})
|
||||
|
||||
t.Run("Slot not in tree", func(t *testing.T) {
|
||||
@@ -1476,8 +1477,14 @@ func TestStore_CanSaveRetrieveStateUsingStateDiff(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
readSt, err := db.State(context.Background(), r)
|
||||
require.ErrorIs(t, err, ErrNotFoundState)
|
||||
require.IsNil(t, readSt)
|
||||
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)
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1571,8 +1578,14 @@ func TestStore_CanSaveRetrieveStateUsingStateDiff(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
readSt, err := db.State(context.Background(), r)
|
||||
require.ErrorIs(t, err, ErrNotFoundState)
|
||||
require.IsNil(t, readSt)
|
||||
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)
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1581,6 +1594,7 @@ func TestStore_CanSaveRetrieveStateUsingStateDiff(t *testing.T) {
|
||||
|
||||
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
|
||||
@@ -1593,6 +1607,7 @@ func TestStore_HasStateUsingStateDiff(t *testing.T) {
|
||||
|
||||
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) {
|
||||
|
||||
@@ -25,6 +25,7 @@ go_library(
|
||||
"//testing/spectest:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//api/server/structs:go_default_library",
|
||||
"//beacon-chain/blockchain/kzg:go_default_library",
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/cache/depositsnapshot:go_default_library",
|
||||
@@ -99,6 +100,7 @@ go_test(
|
||||
data = glob(["testdata/**"]),
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//api/server/structs:go_default_library",
|
||||
"//async/event:go_default_library",
|
||||
"//beacon-chain/blockchain/kzg:go_default_library",
|
||||
"//beacon-chain/blockchain/testing:go_default_library",
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/api/server/structs"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/kzg"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/peerdas"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/execution/types"
|
||||
@@ -99,6 +100,8 @@ const (
|
||||
GetBlobsV1 = "engine_getBlobsV1"
|
||||
// GetBlobsV2 request string for JSON-RPC.
|
||||
GetBlobsV2 = "engine_getBlobsV2"
|
||||
// GetClientVersionV1 is the JSON-RPC method that identifies the execution client.
|
||||
GetClientVersionV1 = "engine_getClientVersionV1"
|
||||
// Defines the seconds before timing out engine endpoints with non-block execution semantics.
|
||||
defaultEngineTimeout = time.Second
|
||||
)
|
||||
@@ -135,6 +138,7 @@ type EngineCaller interface {
|
||||
GetPayload(ctx context.Context, payloadId [8]byte, slot primitives.Slot) (*blocks.GetPayloadResponse, error)
|
||||
ExecutionBlockByHash(ctx context.Context, hash common.Hash, withTxs bool) (*pb.ExecutionBlock, error)
|
||||
GetTerminalBlockHash(ctx context.Context, transitionTime uint64) ([]byte, bool, error)
|
||||
GetClientVersionV1(ctx context.Context) ([]*structs.ClientVersionV1, error)
|
||||
}
|
||||
|
||||
var ErrEmptyBlockHash = errors.New("Block hash is empty 0x0000...")
|
||||
@@ -553,6 +557,39 @@ func (s *Service) GetBlobsV2(ctx context.Context, versionedHashes []common.Hash)
|
||||
return result, handleRPCError(err)
|
||||
}
|
||||
|
||||
func (s *Service) GetClientVersionV1(ctx context.Context) ([]*structs.ClientVersionV1, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.GetClientVersionV1")
|
||||
defer span.End()
|
||||
|
||||
commit := version.GitCommit()
|
||||
if len(commit) >= 8 {
|
||||
commit = commit[:8]
|
||||
}
|
||||
|
||||
var result []*structs.ClientVersionV1
|
||||
err := s.rpcClient.CallContext(
|
||||
ctx,
|
||||
&result,
|
||||
GetClientVersionV1,
|
||||
structs.ClientVersionV1{
|
||||
Code: "PM",
|
||||
Name: "Prysm",
|
||||
Version: version.SemanticVersion(),
|
||||
Commit: commit,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, handleRPCError(err)
|
||||
}
|
||||
|
||||
if len(result) == 0 {
|
||||
return nil, errors.New("execution client returned no result")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ReconstructFullBlock takes in a blinded beacon block and reconstructs
|
||||
// a beacon block with a full execution payload via the engine API.
|
||||
func (s *Service) ReconstructFullBlock(
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/api/server/structs"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/kzg"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/peerdas"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/db/filesystem"
|
||||
@@ -999,6 +1000,99 @@ func TestClient_HTTP(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.DeepEqual(t, want, resp)
|
||||
})
|
||||
t.Run(GetClientVersionV1, func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
want any
|
||||
resp []*structs.ClientVersionV1
|
||||
hasError bool
|
||||
errMsg string
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
want: []*structs.ClientVersionV1{{
|
||||
Code: "GE",
|
||||
Name: "go-ethereum",
|
||||
Version: "1.15.11-stable",
|
||||
Commit: "36b2371c",
|
||||
}},
|
||||
resp: []*structs.ClientVersionV1{{
|
||||
Code: "GE",
|
||||
Name: "go-ethereum",
|
||||
Version: "1.15.11-stable",
|
||||
Commit: "36b2371c",
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "empty response",
|
||||
want: []*structs.ClientVersionV1{},
|
||||
hasError: true,
|
||||
errMsg: "execution client returned no result",
|
||||
},
|
||||
{
|
||||
name: "RPC error",
|
||||
want: "brokenMsg",
|
||||
hasError: true,
|
||||
errMsg: "unexpected error in JSON-RPC",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
defer func() {
|
||||
require.NoError(t, r.Body.Close())
|
||||
}()
|
||||
enc, err := io.ReadAll(r.Body)
|
||||
require.NoError(t, err)
|
||||
jsonRequestString := string(enc)
|
||||
|
||||
// We expect the JSON string RPC request contains the right method name.
|
||||
require.Equal(t, true, strings.Contains(
|
||||
jsonRequestString, GetClientVersionV1,
|
||||
))
|
||||
require.Equal(t, true, strings.Contains(
|
||||
jsonRequestString, "\"code\":\"PM\"",
|
||||
))
|
||||
require.Equal(t, true, strings.Contains(
|
||||
jsonRequestString, "\"name\":\"Prysm\"",
|
||||
))
|
||||
require.Equal(t, true, strings.Contains(
|
||||
jsonRequestString, fmt.Sprintf("\"version\":\"%s\"", version.SemanticVersion()),
|
||||
))
|
||||
require.Equal(t, true, strings.Contains(
|
||||
jsonRequestString, fmt.Sprintf("\"commit\":\"%s\"", version.GitCommit()[:8]),
|
||||
))
|
||||
resp := map[string]any{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": tc.want,
|
||||
}
|
||||
err = json.NewEncoder(w).Encode(resp)
|
||||
require.NoError(t, err)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
rpcClient, err := rpc.DialHTTP(srv.URL)
|
||||
require.NoError(t, err)
|
||||
defer rpcClient.Close()
|
||||
|
||||
service := &Service{}
|
||||
service.rpcClient = rpcClient
|
||||
|
||||
// We call the RPC method via HTTP and expect a proper result.
|
||||
resp, err := service.GetClientVersionV1(ctx)
|
||||
if tc.hasError {
|
||||
require.NotNil(t, err)
|
||||
require.ErrorContains(t, tc.errMsg, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.DeepEqual(t, tc.resp, resp)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestReconstructFullBellatrixBlock(t *testing.T) {
|
||||
|
||||
@@ -13,6 +13,7 @@ go_library(
|
||||
"//visibility:public",
|
||||
],
|
||||
deps = [
|
||||
"//api/server/structs:go_default_library",
|
||||
"//async/event:go_default_library",
|
||||
"//beacon-chain/core/peerdas:go_default_library",
|
||||
"//beacon-chain/execution/types:go_default_library",
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"math/big"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/api/server/structs"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/peerdas"
|
||||
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
@@ -42,6 +43,8 @@ type EngineClient struct {
|
||||
ErrorBlobSidecars error
|
||||
DataColumnSidecars []blocks.VerifiedRODataColumn
|
||||
ErrorDataColumnSidecars error
|
||||
ClientVersion []*structs.ClientVersionV1
|
||||
ErrorClientVersion error
|
||||
}
|
||||
|
||||
// NewPayload --
|
||||
@@ -173,3 +176,8 @@ func (e *EngineClient) GetTerminalBlockHash(ctx context.Context, transitionTime
|
||||
blk = parentBlk
|
||||
}
|
||||
}
|
||||
|
||||
// GetClientVersionV1 --
|
||||
func (e *EngineClient) GetClientVersionV1(context.Context) ([]*structs.ClientVersionV1, error) {
|
||||
return e.ClientVersion, e.ErrorClientVersion
|
||||
}
|
||||
|
||||
@@ -550,12 +550,6 @@ func openDB(ctx context.Context, dbPath string, clearer *dbClearer) (*kv.Store,
|
||||
cfg := features.Get()
|
||||
cfg.EnableStateDiff = false
|
||||
features.Init(cfg)
|
||||
} else if errors.Is(err, kv.ErrStateDiffExponentMismatch) {
|
||||
log.WithError(err).Error("State-diff configuration mismatch; restart aborted. Use the stored exponents or re-sync the database.")
|
||||
return nil, err
|
||||
} else if errors.Is(err, kv.ErrStateDiffMissingSnapshot) || errors.Is(err, kv.ErrStateDiffCorrupted) {
|
||||
log.WithError(err).Error("State-diff database corrupted; restart aborted. Delete database and re-sync from genesis/checkpoint.")
|
||||
return nil, err
|
||||
} else if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not create database at %s", dbPath)
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ var gossipTopicMappings = map[string]func() proto.Message{
|
||||
LightClientOptimisticUpdateTopicFormat: func() proto.Message { return ðpb.LightClientOptimisticUpdateAltair{} },
|
||||
LightClientFinalityUpdateTopicFormat: func() proto.Message { return ðpb.LightClientFinalityUpdateAltair{} },
|
||||
DataColumnSubnetTopicFormat: func() proto.Message { return ðpb.DataColumnSidecar{} },
|
||||
PayloadAttestationMessageTopicFormat: func() proto.Message { return ðpb.PayloadAttestationMessage{} },
|
||||
}
|
||||
|
||||
// GossipTopicMappings is a function to return the assigned data type
|
||||
@@ -145,7 +144,4 @@ func init() {
|
||||
|
||||
// Specially handle Fulu objects.
|
||||
GossipTypeMapping[reflect.TypeFor[*ethpb.SignedBeaconBlockFulu]()] = BlockSubnetTopicFormat
|
||||
|
||||
// Payload attestation messages.
|
||||
GossipTypeMapping[reflect.TypeFor[*ethpb.PayloadAttestationMessage]()] = PayloadAttestationMessageTopicFormat
|
||||
}
|
||||
|
||||
@@ -46,8 +46,6 @@ const (
|
||||
GossipLightClientOptimisticUpdateMessage = "light_client_optimistic_update"
|
||||
// GossipDataColumnSidecarMessage is the name for the data column sidecar message type.
|
||||
GossipDataColumnSidecarMessage = "data_column_sidecar"
|
||||
// GossipPayloadAttestationMessage is the name for the payload attestation message type.
|
||||
GossipPayloadAttestationMessage = "payload_attestation_message"
|
||||
|
||||
// Topic Formats
|
||||
//
|
||||
@@ -77,8 +75,6 @@ const (
|
||||
LightClientOptimisticUpdateTopicFormat = GossipProtocolAndDigest + GossipLightClientOptimisticUpdateMessage
|
||||
// DataColumnSubnetTopicFormat is the topic format for the data column subnet.
|
||||
DataColumnSubnetTopicFormat = GossipProtocolAndDigest + GossipDataColumnSidecarMessage + "_%d"
|
||||
// PayloadAttestationMessageTopicFormat is the topic format for payload attestation messages.
|
||||
PayloadAttestationMessageTopicFormat = GossipProtocolAndDigest + GossipPayloadAttestationMessage
|
||||
)
|
||||
|
||||
// topic is a struct representing a single gossipsub topic.
|
||||
@@ -145,7 +141,7 @@ func (s *Service) allTopics() []topic {
|
||||
cfg := params.BeaconConfig()
|
||||
// bellatrix: no special topics; electra: blobs topics handled all together
|
||||
genesis, altair, capella := cfg.GenesisEpoch, cfg.AltairForkEpoch, cfg.CapellaForkEpoch
|
||||
deneb, fulu, gloas, future := cfg.DenebForkEpoch, cfg.FuluForkEpoch, cfg.GloasForkEpoch, cfg.FarFutureEpoch
|
||||
deneb, fulu, future := cfg.DenebForkEpoch, cfg.FuluForkEpoch, cfg.FarFutureEpoch
|
||||
// Templates are starter topics - they have a placeholder digest and the subnet is set to the maximum value
|
||||
// for the subnet (see how this is used in allSubnetsBelow). These are not directly returned by the method,
|
||||
// they are copied and modified for each digest where they apply based on the start and end epochs.
|
||||
@@ -162,7 +158,6 @@ func (s *Service) allTopics() []topic {
|
||||
newTopic(altair, future, empty, GossipLightClientOptimisticUpdateMessage),
|
||||
newTopic(altair, future, empty, GossipLightClientFinalityUpdateMessage),
|
||||
newTopic(capella, future, empty, GossipBlsToExecutionChangeMessage),
|
||||
newTopic(gloas, future, empty, GossipPayloadAttestationMessage),
|
||||
}
|
||||
last := params.GetNetworkScheduleEntry(genesis)
|
||||
schedule := []params.NetworkScheduleEntry{last}
|
||||
|
||||
@@ -405,6 +405,7 @@ func (s *Service) nodeEndpoints() []endpoint {
|
||||
MetadataProvider: s.cfg.MetadataProvider,
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
ExecutionChainInfoFetcher: s.cfg.ExecutionChainInfoFetcher,
|
||||
ExecutionEngineCaller: s.cfg.ExecutionEngineCaller,
|
||||
}
|
||||
|
||||
const namespace = "node"
|
||||
@@ -469,6 +470,16 @@ func (s *Service) nodeEndpoints() []endpoint {
|
||||
handler: server.GetVersion,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v2/node/version",
|
||||
name: namespace + ".GetVersionV2",
|
||||
middleware: []middleware.Middleware{
|
||||
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
|
||||
middleware.AcceptEncodingHeaderHandler(),
|
||||
},
|
||||
handler: server.GetVersionV2,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/node/health",
|
||||
name: namespace + ".GetHealth",
|
||||
|
||||
@@ -86,6 +86,7 @@ func Test_endpoints(t *testing.T) {
|
||||
"/eth/v1/node/peers/{peer_id}": {http.MethodGet},
|
||||
"/eth/v1/node/peer_count": {http.MethodGet},
|
||||
"/eth/v1/node/version": {http.MethodGet},
|
||||
"/eth/v2/node/version": {http.MethodGet},
|
||||
"/eth/v1/node/syncing": {http.MethodGet},
|
||||
"/eth/v1/node/health": {http.MethodGet},
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ go_library(
|
||||
srcs = [
|
||||
"handlers.go",
|
||||
"handlers_peers.go",
|
||||
"log.go",
|
||||
"server.go",
|
||||
],
|
||||
importpath = "github.com/OffchainLabs/prysm/v7/beacon-chain/rpc/eth/node",
|
||||
@@ -30,6 +31,7 @@ go_library(
|
||||
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
|
||||
"@com_github_libp2p_go_libp2p//core/peer:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@org_golang_google_grpc//:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -44,6 +46,7 @@ go_test(
|
||||
deps = [
|
||||
"//api/server/structs:go_default_library",
|
||||
"//beacon-chain/blockchain/testing:go_default_library",
|
||||
"//beacon-chain/execution/testing:go_default_library",
|
||||
"//beacon-chain/p2p:go_default_library",
|
||||
"//beacon-chain/p2p/peers:go_default_library",
|
||||
"//beacon-chain/p2p/testing:go_default_library",
|
||||
|
||||
@@ -103,6 +103,8 @@ func (s *Server) GetIdentity(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// GetVersion requests that the beacon node identify information about its implementation in a
|
||||
// format similar to a HTTP User-Agent field.
|
||||
//
|
||||
// Deprecated: in favour of GetVersionV2.
|
||||
func (*Server) GetVersion(w http.ResponseWriter, r *http.Request) {
|
||||
_, span := trace.StartSpan(r.Context(), "node.GetVersion")
|
||||
defer span.End()
|
||||
@@ -116,6 +118,38 @@ func (*Server) GetVersion(w http.ResponseWriter, r *http.Request) {
|
||||
httputil.WriteJson(w, resp)
|
||||
}
|
||||
|
||||
// GetVersionV2 Retrieves structured information about the version of the beacon node and its attached
|
||||
// execution client in the same format as used on the Engine API
|
||||
func (s *Server) GetVersionV2(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := trace.StartSpan(r.Context(), "node.GetVersionV2")
|
||||
defer span.End()
|
||||
|
||||
var elData *structs.ClientVersionV1
|
||||
elDataList, err := s.ExecutionEngineCaller.GetClientVersionV1(ctx)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("endpoint", "GetVersionV2").Debug("Could not get execution client version")
|
||||
} else if len(elDataList) > 0 {
|
||||
elData = elDataList[0]
|
||||
}
|
||||
|
||||
commit := version.GitCommit()
|
||||
if len(commit) >= 8 {
|
||||
commit = commit[:8]
|
||||
}
|
||||
resp := &structs.GetVersionV2Response{
|
||||
Data: &structs.VersionV2{
|
||||
BeaconNode: &structs.ClientVersionV1{
|
||||
Code: "PM",
|
||||
Name: "Prysm",
|
||||
Version: version.SemanticVersion(),
|
||||
Commit: commit,
|
||||
},
|
||||
ExecutionClient: elData,
|
||||
},
|
||||
}
|
||||
httputil.WriteJson(w, resp)
|
||||
}
|
||||
|
||||
// GetHealth returns node health status in http status codes. Useful for load balancers.
|
||||
func (s *Server) GetHealth(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := trace.StartSpan(r.Context(), "node.GetHealth")
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/OffchainLabs/go-bitfield"
|
||||
"github.com/OffchainLabs/prysm/v7/api/server/structs"
|
||||
mock "github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/testing"
|
||||
mockengine "github.com/OffchainLabs/prysm/v7/beacon-chain/execution/testing"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/p2p"
|
||||
mockp2p "github.com/OffchainLabs/prysm/v7/beacon-chain/p2p/testing"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/rpc/testutil"
|
||||
@@ -90,6 +91,75 @@ func TestGetVersion(t *testing.T) {
|
||||
assert.StringContains(t, arch, resp.Data.Version)
|
||||
}
|
||||
|
||||
func TestGetVersionV2(t *testing.T) {
|
||||
t.Run("happy path", func(t *testing.T) {
|
||||
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/node/version", nil)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s := &Server{
|
||||
ExecutionEngineCaller: &mockengine.EngineClient{
|
||||
ClientVersion: []*structs.ClientVersionV1{{
|
||||
Code: "EL",
|
||||
Name: "ExecutionClient",
|
||||
Version: "v1.0.0",
|
||||
Commit: "abcdef12",
|
||||
}},
|
||||
},
|
||||
}
|
||||
s.GetVersionV2(writer, request)
|
||||
require.Equal(t, http.StatusOK, writer.Code)
|
||||
|
||||
resp := &structs.GetVersionV2Response{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
||||
require.NotNil(t, resp)
|
||||
require.NotNil(t, resp.Data)
|
||||
require.NotNil(t, resp.Data.BeaconNode)
|
||||
require.NotNil(t, resp.Data.ExecutionClient)
|
||||
require.Equal(t, "EL", resp.Data.ExecutionClient.Code)
|
||||
require.Equal(t, "ExecutionClient", resp.Data.ExecutionClient.Name)
|
||||
require.Equal(t, "v1.0.0", resp.Data.ExecutionClient.Version)
|
||||
require.Equal(t, "abcdef12", resp.Data.ExecutionClient.Commit)
|
||||
require.Equal(t, "PM", resp.Data.BeaconNode.Code)
|
||||
require.Equal(t, "Prysm", resp.Data.BeaconNode.Name)
|
||||
require.Equal(t, version.SemanticVersion(), resp.Data.BeaconNode.Version)
|
||||
require.Equal(t, true, len(resp.Data.BeaconNode.Commit) <= 8)
|
||||
})
|
||||
|
||||
t.Run("unhappy path", func(t *testing.T) {
|
||||
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/node/version", nil)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s := &Server{
|
||||
ExecutionEngineCaller: &mockengine.EngineClient{
|
||||
ClientVersion: nil,
|
||||
ErrorClientVersion: fmt.Errorf("error"),
|
||||
},
|
||||
}
|
||||
s.GetVersionV2(writer, request)
|
||||
require.Equal(t, http.StatusOK, writer.Code)
|
||||
|
||||
resp := &structs.GetVersionV2Response{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
||||
require.NotNil(t, resp)
|
||||
require.NotNil(t, resp.Data)
|
||||
require.NotNil(t, resp.Data.BeaconNode)
|
||||
require.Equal(t, true, resp.Data.ExecutionClient == nil)
|
||||
|
||||
// make sure there is no 'execution_client' field
|
||||
var payload map[string]any
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), &payload))
|
||||
data, ok := payload["data"].(map[string]any)
|
||||
require.Equal(t, true, ok)
|
||||
_, found := data["beacon_node"]
|
||||
require.Equal(t, true, found)
|
||||
_, found = data["execution_client"]
|
||||
require.Equal(t, false, found)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestGetHealth(t *testing.T) {
|
||||
checker := &syncmock.Sync{}
|
||||
optimisticFetcher := &mock.ChainService{Optimistic: false}
|
||||
|
||||
9
beacon-chain/rpc/eth/node/log.go
Normal file
9
beacon-chain/rpc/eth/node/log.go
Normal file
@@ -0,0 +1,9 @@
|
||||
// Code generated by hack/gen-logs.sh; DO NOT EDIT.
|
||||
// This file is created and regenerated automatically. Anything added here might get removed.
|
||||
package node
|
||||
|
||||
import "github.com/sirupsen/logrus"
|
||||
|
||||
// The prefix for logs from this package will be the text after the last slash in the package path.
|
||||
// If you wish to change this, you should add your desired name in the runtime/logging/logrus-prefixed-formatter/prefix-replacement.go file.
|
||||
var log = logrus.WithField("package", "beacon-chain/rpc/eth/node")
|
||||
@@ -26,4 +26,5 @@ type Server struct {
|
||||
GenesisTimeFetcher blockchain.TimeFetcher
|
||||
HeadFetcher blockchain.HeadFetcher
|
||||
ExecutionChainInfoFetcher execution.ChainInfoFetcher
|
||||
ExecutionEngineCaller execution.EngineCaller
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/helpers"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/time"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/db"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
|
||||
@@ -123,13 +122,8 @@ func (s *State) StateByRootInitialSync(ctx context.Context, blockRoot [32]byte)
|
||||
}
|
||||
|
||||
if s.beaconDB.HasState(ctx, blockRoot) {
|
||||
st, err := s.beaconDB.State(ctx, blockRoot)
|
||||
if err == nil {
|
||||
return st, nil
|
||||
}
|
||||
if !stderrors.Is(err, db.ErrNotFoundState) {
|
||||
return nil, errors.Wrap(err, "failed to retrieve init-sync state from db")
|
||||
}
|
||||
s, err := s.beaconDB.State(ctx, blockRoot)
|
||||
return s, errors.Wrap(err, "failed to retrieve init-sync state from db")
|
||||
}
|
||||
|
||||
startState, err := s.latestAncestor(ctx, blockRoot)
|
||||
@@ -219,13 +213,7 @@ func (s *State) loadStateByRoot(ctx context.Context, blockRoot [32]byte) (state.
|
||||
|
||||
// Short circuit if the state is already in the DB.
|
||||
if s.beaconDB.HasState(ctx, blockRoot) {
|
||||
st, err := s.beaconDB.State(ctx, blockRoot)
|
||||
if err == nil {
|
||||
return st, nil
|
||||
}
|
||||
if !stderrors.Is(err, db.ErrNotFoundState) {
|
||||
return nil, err
|
||||
}
|
||||
return s.beaconDB.State(ctx, blockRoot)
|
||||
}
|
||||
|
||||
summary, err := s.stateSummary(ctx, blockRoot)
|
||||
|
||||
@@ -185,71 +185,6 @@ type testSetupSlots struct {
|
||||
lastblock primitives.Slot
|
||||
}
|
||||
|
||||
type notFoundOnRootDB struct {
|
||||
db.NoHeadAccessDatabase
|
||||
target [32]byte
|
||||
}
|
||||
|
||||
func (d *notFoundOnRootDB) HasState(ctx context.Context, blockRoot [32]byte) bool {
|
||||
if blockRoot == d.target {
|
||||
return true
|
||||
}
|
||||
return d.NoHeadAccessDatabase.HasState(ctx, blockRoot)
|
||||
}
|
||||
|
||||
func (d *notFoundOnRootDB) State(ctx context.Context, blockRoot [32]byte) (state.BeaconState, error) {
|
||||
if blockRoot == d.target {
|
||||
return nil, db.ErrNotFoundState
|
||||
}
|
||||
return d.NoHeadAccessDatabase.State(ctx, blockRoot)
|
||||
}
|
||||
|
||||
func TestStateByRoot_FallsBackToReplayOnNotFoundStateFromDirectRead(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
beaconDB := testDB.SetupDB(t)
|
||||
|
||||
st9, _ := util.DeterministicGenesisState(t, 32)
|
||||
st9, err := ReplayProcessSlots(ctx, st9, 9)
|
||||
require.NoError(t, err)
|
||||
|
||||
hdr := st9.LatestBlockHeader()
|
||||
hdrRoot, err := hdr.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
st10 := st9.Copy()
|
||||
blk10 := util.NewBeaconBlock()
|
||||
blk10.Block.Slot = 10
|
||||
blk10.Block.ParentRoot = hdrRoot[:]
|
||||
idx10, err := helpers.BeaconProposerIndexAtSlot(ctx, st10, blk10.Block.Slot)
|
||||
require.NoError(t, err)
|
||||
blk10.Block.ProposerIndex = idx10
|
||||
ib10, err := blt.NewSignedBeaconBlock(blk10)
|
||||
require.NoError(t, err)
|
||||
|
||||
st10, err = executeStateTransitionStateGen(ctx, st10, ib10)
|
||||
require.NoError(t, err)
|
||||
st10Root, err := st10.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
blk10.Block.StateRoot = st10Root[:]
|
||||
|
||||
util.SaveBlock(t, ctx, beaconDB, blk10)
|
||||
require.NoError(t, beaconDB.SaveState(ctx, st9, hdrRoot))
|
||||
|
||||
ib10, err = blt.NewSignedBeaconBlock(blk10)
|
||||
require.NoError(t, err)
|
||||
rob10, err := blt.NewROBlock(ib10)
|
||||
require.NoError(t, err)
|
||||
|
||||
service := New(¬FoundOnRootDB{NoHeadAccessDatabase: beaconDB, target: rob10.Root()}, doublylinkedtree.New())
|
||||
|
||||
got, err := service.StateByRoot(ctx, rob10.Root())
|
||||
require.NoError(t, err)
|
||||
|
||||
gotRoot, err := got.HashTreeRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, st10Root, gotRoot)
|
||||
}
|
||||
|
||||
func TestLoadStateByRoot(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
persistEpochBoundary := func(r testChain, slot primitives.Slot) {
|
||||
|
||||
@@ -47,7 +47,6 @@ go_library(
|
||||
"subscriber_bls_to_execution_change.go",
|
||||
"subscriber_data_column_sidecar.go",
|
||||
"subscriber_handlers.go",
|
||||
"subscriber_payload_attestation.go",
|
||||
"subscriber_sync_committee_message.go",
|
||||
"subscriber_sync_contribution_proof.go",
|
||||
"subscription_topic_handler.go",
|
||||
@@ -59,7 +58,6 @@ go_library(
|
||||
"validate_bls_to_execution_change.go",
|
||||
"validate_data_column.go",
|
||||
"validate_light_client.go",
|
||||
"validate_payload_attestation.go",
|
||||
"validate_proposer_slashing.go",
|
||||
"validate_sync_committee_message.go",
|
||||
"validate_sync_contribution_proof.go",
|
||||
@@ -116,7 +114,6 @@ go_library(
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/blocks:go_default_library",
|
||||
"//consensus-types/interfaces:go_default_library",
|
||||
"//consensus-types/payload-attestation:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//consensus-types/wrapper:go_default_library",
|
||||
"//container/leaky-bucket:go_default_library",
|
||||
@@ -215,7 +212,6 @@ go_test(
|
||||
"validate_bls_to_execution_change_test.go",
|
||||
"validate_data_column_test.go",
|
||||
"validate_light_client_test.go",
|
||||
"validate_payload_attestation_test.go",
|
||||
"validate_proposer_slashing_test.go",
|
||||
"validate_sync_committee_message_test.go",
|
||||
"validate_sync_contribution_proof_test.go",
|
||||
@@ -268,7 +264,6 @@ go_test(
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/blocks:go_default_library",
|
||||
"//consensus-types/interfaces:go_default_library",
|
||||
"//consensus-types/payload-attestation:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//consensus-types/wrapper:go_default_library",
|
||||
"//container/leaky-bucket:go_default_library",
|
||||
|
||||
@@ -207,13 +207,6 @@ func WithTrackedValidatorsCache(c *cache.TrackedValidatorsCache) Option {
|
||||
}
|
||||
}
|
||||
|
||||
func WithPayloadAttestationCache(c *cache.PayloadAttestationCache) Option {
|
||||
return func(s *Service) error {
|
||||
s.payloadAttestationCache = c
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithSlasherEnabled configures the sync package to support slashing detection.
|
||||
func WithSlasherEnabled(enabled bool) Option {
|
||||
return func(s *Service) error {
|
||||
|
||||
@@ -38,7 +38,6 @@ import (
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
|
||||
payloadattestation "github.com/OffchainLabs/prysm/v7/consensus-types/payload-attestation"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
leakybucket "github.com/OffchainLabs/prysm/v7/container/leaky-bucket"
|
||||
"github.com/OffchainLabs/prysm/v7/crypto/rand"
|
||||
@@ -122,7 +121,6 @@ type blockchainService interface {
|
||||
blockchain.FinalizationFetcher
|
||||
blockchain.ForkFetcher
|
||||
blockchain.AttestationReceiver
|
||||
blockchain.PayloadAttestationReceiver
|
||||
blockchain.TimeFetcher
|
||||
blockchain.GenesisFetcher
|
||||
blockchain.CanonicalFetcher
|
||||
@@ -175,7 +173,6 @@ type Service struct {
|
||||
verifierWaiter *verification.InitializerWaiter
|
||||
newBlobVerifier verification.NewBlobVerifier
|
||||
newColumnsVerifier verification.NewDataColumnsVerifier
|
||||
newPayloadAttestationVerifier verification.NewPayloadAttestationMsgVerifier
|
||||
columnSidecarsExecSingleFlight singleflight.Group
|
||||
reconstructionSingleFlight singleflight.Group
|
||||
availableBlocker coverage.AvailableBlocker
|
||||
@@ -185,7 +182,6 @@ type Service struct {
|
||||
slasherEnabled bool
|
||||
lcStore *lightClient.Store
|
||||
dataColumnLogCh chan dataColumnLogEntry
|
||||
payloadAttestationCache *cache.PayloadAttestationCache
|
||||
digestActions perDigestSet
|
||||
subscriptionSpawner func(func()) // see Service.spawn for details
|
||||
}
|
||||
@@ -194,16 +190,15 @@ type Service struct {
|
||||
func NewService(ctx context.Context, opts ...Option) *Service {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
r := &Service{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
chainStarted: abool.New(),
|
||||
cfg: &config{clock: startup.NewClock(time.Unix(0, 0), [32]byte{})},
|
||||
slotToPendingBlocks: gcache.New(pendingBlockExpTime /* exp time */, 0 /* disable janitor */),
|
||||
seenPendingBlocks: make(map[[32]byte]bool),
|
||||
blkRootToPendingAtts: make(map[[32]byte][]any),
|
||||
dataColumnLogCh: make(chan dataColumnLogEntry, 1000),
|
||||
reconstructionRandGen: rand.NewGenerator(),
|
||||
payloadAttestationCache: &cache.PayloadAttestationCache{},
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
chainStarted: abool.New(),
|
||||
cfg: &config{clock: startup.NewClock(time.Unix(0, 0), [32]byte{})},
|
||||
slotToPendingBlocks: gcache.New(pendingBlockExpTime /* exp time */, 0 /* disable janitor */),
|
||||
seenPendingBlocks: make(map[[32]byte]bool),
|
||||
blkRootToPendingAtts: make(map[[32]byte][]any),
|
||||
dataColumnLogCh: make(chan dataColumnLogEntry, 1000),
|
||||
reconstructionRandGen: rand.NewGenerator(),
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
@@ -255,12 +250,6 @@ func newDataColumnsVerifierFromInitializer(ini *verification.Initializer) verifi
|
||||
}
|
||||
}
|
||||
|
||||
func newPayloadAttestationMessageFromInitializer(ini *verification.Initializer) verification.NewPayloadAttestationMsgVerifier {
|
||||
return func(pa payloadattestation.ROMessage, reqs []verification.Requirement) verification.PayloadAttestationMsgVerifier {
|
||||
return ini.NewPayloadAttestationMsgVerifier(pa, reqs)
|
||||
}
|
||||
}
|
||||
|
||||
// Start the regular sync service.
|
||||
func (s *Service) Start() {
|
||||
v, err := s.verifierWaiter.WaitForInitializer(s.ctx)
|
||||
@@ -270,7 +259,6 @@ func (s *Service) Start() {
|
||||
}
|
||||
s.newBlobVerifier = newBlobVerifierFromInitializer(v)
|
||||
s.newColumnsVerifier = newDataColumnsVerifierFromInitializer(v)
|
||||
s.newPayloadAttestationVerifier = newPayloadAttestationMessageFromInitializer(v)
|
||||
|
||||
go s.verifierRoutine()
|
||||
go s.startDiscoveryAndSubscriptions()
|
||||
|
||||
@@ -330,18 +330,6 @@ func (s *Service) registerSubscribers(nse params.NetworkScheduleEntry) bool {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// New gossip topic in Gloas.
|
||||
if params.BeaconConfig().GloasForkEpoch <= nse.Epoch {
|
||||
s.spawn(func() {
|
||||
s.subscribe(
|
||||
p2p.PayloadAttestationMessageTopicFormat,
|
||||
s.validatePayloadAttestation,
|
||||
s.payloadAttestationSubscriber,
|
||||
nse,
|
||||
)
|
||||
})
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
eth "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func (s *Service) payloadAttestationSubscriber(ctx context.Context, msg proto.Message) error {
|
||||
a, ok := msg.(*eth.PayloadAttestationMessage)
|
||||
if !ok {
|
||||
return errWrongMessage
|
||||
}
|
||||
|
||||
if err := s.payloadAttestationCache.Add(a.Data.Slot, a.ValidatorIndex); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.cfg.chain.ReceivePayloadAttestationMessage(ctx, a)
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/transition"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/p2p"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/verification"
|
||||
payloadattestation "github.com/OffchainLabs/prysm/v7/consensus-types/payload-attestation"
|
||||
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
|
||||
"github.com/OffchainLabs/prysm/v7/monitoring/tracing/trace"
|
||||
eth "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
"github.com/OffchainLabs/prysm/v7/time/slots"
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
errAlreadySeenPayloadAttestation = errors.New("payload attestation already seen for validator index")
|
||||
)
|
||||
|
||||
func (s *Service) validatePayloadAttestation(ctx context.Context, pid peer.ID, msg *pubsub.Message) (pubsub.ValidationResult, error) {
|
||||
if pid == s.cfg.p2p.PeerID() {
|
||||
return pubsub.ValidationAccept, nil
|
||||
}
|
||||
if s.cfg.initialSync.Syncing() {
|
||||
return pubsub.ValidationIgnore, nil
|
||||
}
|
||||
ctx, span := trace.StartSpan(ctx, "sync.validatePayloadAttestation")
|
||||
defer span.End()
|
||||
|
||||
if msg.Topic == nil {
|
||||
return pubsub.ValidationReject, p2p.ErrInvalidTopic
|
||||
}
|
||||
m, err := s.decodePubsubMessage(msg)
|
||||
if err != nil {
|
||||
return pubsub.ValidationReject, err
|
||||
}
|
||||
att, ok := m.(*eth.PayloadAttestationMessage)
|
||||
if !ok {
|
||||
return pubsub.ValidationReject, errWrongMessage
|
||||
}
|
||||
pa, err := payloadattestation.NewReadOnly(att)
|
||||
if err != nil {
|
||||
return pubsub.ValidationIgnore, err
|
||||
}
|
||||
v := s.newPayloadAttestationVerifier(pa, verification.GossipPayloadAttestationMessageRequirements)
|
||||
|
||||
// [IGNORE] The message's slot is for the current slot (with a MAXIMUM_GOSSIP_CLOCK_DISPARITY allowance),
|
||||
// i.e. data.slot == current_slot.
|
||||
if err := v.VerifyCurrentSlot(); err != nil {
|
||||
return pubsub.ValidationIgnore, err
|
||||
}
|
||||
|
||||
// [IGNORE] The payload_attestation_message is the first valid message received from the validator with
|
||||
// index payload_attestation_message.validator_index.
|
||||
if s.payloadAttestationCache.Seen(pa.Slot(), pa.ValidatorIndex()) {
|
||||
return pubsub.ValidationIgnore, errAlreadySeenPayloadAttestation
|
||||
}
|
||||
|
||||
// [IGNORE] The message's block data.beacon_block_root has been seen (via gossip or non-gossip sources)
|
||||
// (a client MAY queue attestation for processing once the block is retrieved. Note a client might want to request payload after).
|
||||
if err := v.VerifyBlockRootSeen(s.cfg.chain.InForkchoice); err != nil {
|
||||
// TODO: queue attestation
|
||||
return pubsub.ValidationIgnore, err
|
||||
}
|
||||
|
||||
// [REJECT] The message's block data.beacon_block_root passes validation.
|
||||
if err := v.VerifyBlockRootValid(s.hasBadBlock); err != nil {
|
||||
return pubsub.ValidationReject, err
|
||||
}
|
||||
|
||||
st, err := s.getPtcState(ctx, pa)
|
||||
if err != nil {
|
||||
return pubsub.ValidationIgnore, err
|
||||
}
|
||||
|
||||
// [REJECT] The message's validator index is within the payload committee in get_ptc(state, data.slot).
|
||||
// The state is the head state corresponding to processing the block up to the current slot.
|
||||
if err := v.VerifyValidatorInPTC(ctx, st); err != nil {
|
||||
return pubsub.ValidationReject, err
|
||||
}
|
||||
|
||||
// [REJECT] payload_attestation_message.signature is valid with respect to the validator's public key.
|
||||
if err := v.VerifySignature(st); err != nil {
|
||||
return pubsub.ValidationReject, err
|
||||
}
|
||||
|
||||
msg.ValidatorData = att
|
||||
|
||||
return pubsub.ValidationAccept, nil
|
||||
}
|
||||
|
||||
func (s *Service) getPtcState(ctx context.Context, pa payloadattestation.ROMessage) (state.ReadOnlyBeaconState, error) {
|
||||
blockRoot := pa.BeaconBlockRoot()
|
||||
blockSlot := pa.Slot()
|
||||
blockEpoch := slots.ToEpoch(blockSlot)
|
||||
headSlot := s.cfg.chain.HeadSlot()
|
||||
headEpoch := slots.ToEpoch(headSlot)
|
||||
headRoot, err := s.cfg.chain.HeadRoot(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if blockEpoch == headEpoch {
|
||||
if bytes.Equal(blockRoot[:], headRoot) {
|
||||
return s.cfg.chain.HeadStateReadOnly(ctx)
|
||||
}
|
||||
|
||||
headDependent, err := s.cfg.chain.DependentRootForEpoch(bytesutil.ToBytes32(headRoot), blockEpoch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockDependent, err := s.cfg.chain.DependentRootForEpoch(blockRoot, blockEpoch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bytes.Equal(headDependent[:], blockDependent[:]) {
|
||||
return s.cfg.chain.HeadStateReadOnly(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
headState, err := s.cfg.chain.HeadState(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return transition.ProcessSlotsUsingNextSlotCache(ctx, headState, headRoot, blockSlot)
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
mock "github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/testing"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/cache"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/p2p"
|
||||
p2ptest "github.com/OffchainLabs/prysm/v7/beacon-chain/p2p/testing"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/startup"
|
||||
mockSync "github.com/OffchainLabs/prysm/v7/beacon-chain/sync/initial-sync/testing"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/verification"
|
||||
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
payloadattestation "github.com/OffchainLabs/prysm/v7/consensus-types/payload-attestation"
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/require"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/util"
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
pb "github.com/libp2p/go-libp2p-pubsub/pb"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func TestValidatePayloadAttestationMessage_IncorrectTopic(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
p := p2ptest.NewTestP2P(t)
|
||||
chainService := &mock.ChainService{Genesis: time.Unix(time.Now().Unix()-int64(params.BeaconConfig().SecondsPerSlot), 0)}
|
||||
s := &Service{
|
||||
payloadAttestationCache: &cache.PayloadAttestationCache{},
|
||||
cfg: &config{chain: chainService, p2p: p, initialSync: &mockSync.Sync{}, clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot)}}
|
||||
|
||||
msg := util.HydratePayloadAttestation(ðpb.PayloadAttestation{}) // Using payload attestation for message should fail.
|
||||
buf := new(bytes.Buffer)
|
||||
_, err := p.Encoding().EncodeGossip(buf, msg)
|
||||
require.NoError(t, err)
|
||||
|
||||
topic := p2p.GossipTypeMapping[reflect.TypeFor[*ethpb.PayloadAttestation]()]
|
||||
digest, err := s.currentForkDigest()
|
||||
require.NoError(t, err)
|
||||
topic = s.addDigestToTopic(topic, digest)
|
||||
|
||||
result, err := s.validatePayloadAttestation(ctx, "", &pubsub.Message{
|
||||
Message: &pb.Message{
|
||||
Data: buf.Bytes(),
|
||||
Topic: &topic,
|
||||
}})
|
||||
require.ErrorContains(t, "extraction failed for topic", err)
|
||||
require.Equal(t, result, pubsub.ValidationReject)
|
||||
}
|
||||
|
||||
func TestValidatePayloadAttestationMessage_ErrorPathsWithMock(t *testing.T) {
|
||||
tests := []struct {
|
||||
error error
|
||||
verifier verification.NewPayloadAttestationMsgVerifier
|
||||
result pubsub.ValidationResult
|
||||
}{
|
||||
{
|
||||
error: errors.New("incorrect slot"),
|
||||
verifier: func(pa payloadattestation.ROMessage, reqs []verification.Requirement) verification.PayloadAttestationMsgVerifier {
|
||||
return &verification.MockPayloadAttestation{ErrIncorrectPayloadAttSlot: errors.New("incorrect slot")}
|
||||
},
|
||||
result: pubsub.ValidationIgnore,
|
||||
},
|
||||
{
|
||||
error: errors.New("block root seen"),
|
||||
verifier: func(pa payloadattestation.ROMessage, reqs []verification.Requirement) verification.PayloadAttestationMsgVerifier {
|
||||
return &verification.MockPayloadAttestation{ErrPayloadAttBlockRootNotSeen: errors.New("block root seen")}
|
||||
},
|
||||
result: pubsub.ValidationIgnore,
|
||||
},
|
||||
{
|
||||
error: errors.New("block root invalid"),
|
||||
verifier: func(pa payloadattestation.ROMessage, reqs []verification.Requirement) verification.PayloadAttestationMsgVerifier {
|
||||
return &verification.MockPayloadAttestation{ErrPayloadAttBlockRootInvalid: errors.New("block root invalid")}
|
||||
},
|
||||
result: pubsub.ValidationReject,
|
||||
},
|
||||
{
|
||||
error: errors.New("validator not in PTC"),
|
||||
verifier: func(pa payloadattestation.ROMessage, reqs []verification.Requirement) verification.PayloadAttestationMsgVerifier {
|
||||
return &verification.MockPayloadAttestation{ErrIncorrectPayloadAttValidator: errors.New("validator not in PTC")}
|
||||
},
|
||||
result: pubsub.ValidationReject,
|
||||
},
|
||||
{
|
||||
error: errors.New("incorrect signature"),
|
||||
verifier: func(pa payloadattestation.ROMessage, reqs []verification.Requirement) verification.PayloadAttestationMsgVerifier {
|
||||
return &verification.MockPayloadAttestation{ErrInvalidMessageSignature: errors.New("incorrect signature")}
|
||||
},
|
||||
result: pubsub.ValidationReject,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.error.Error(), func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
p := p2ptest.NewTestP2P(t)
|
||||
chainService := &mock.ChainService{Genesis: time.Unix(time.Now().Unix()-int64(params.BeaconConfig().SecondsPerSlot), 0)}
|
||||
s := &Service{
|
||||
payloadAttestationCache: &cache.PayloadAttestationCache{},
|
||||
cfg: &config{chain: chainService, p2p: p, initialSync: &mockSync.Sync{}, clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot)}}
|
||||
s.newPayloadAttestationVerifier = tt.verifier
|
||||
|
||||
msg := newPayloadAttestationMessage()
|
||||
buf := new(bytes.Buffer)
|
||||
_, err := p.Encoding().EncodeGossip(buf, msg)
|
||||
require.NoError(t, err)
|
||||
|
||||
topic := p2p.GossipTypeMapping[reflect.TypeFor[*ethpb.PayloadAttestationMessage]()]
|
||||
digest, err := s.currentForkDigest()
|
||||
require.NoError(t, err)
|
||||
topic = s.addDigestToTopic(topic, digest)
|
||||
|
||||
result, err := s.validatePayloadAttestation(ctx, "", &pubsub.Message{
|
||||
Message: &pb.Message{
|
||||
Data: buf.Bytes(),
|
||||
Topic: &topic,
|
||||
}})
|
||||
|
||||
require.ErrorContains(t, tt.error.Error(), err)
|
||||
require.Equal(t, result, tt.result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatePayloadAttestationMessage_Accept(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
p := p2ptest.NewTestP2P(t)
|
||||
chainService := &mock.ChainService{Genesis: time.Unix(time.Now().Unix()-int64(params.BeaconConfig().SecondsPerSlot), 0)}
|
||||
s := &Service{
|
||||
payloadAttestationCache: &cache.PayloadAttestationCache{},
|
||||
cfg: &config{chain: chainService, p2p: p, initialSync: &mockSync.Sync{}, clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot)}}
|
||||
s.newPayloadAttestationVerifier = func(pa payloadattestation.ROMessage, reqs []verification.Requirement) verification.PayloadAttestationMsgVerifier {
|
||||
return &verification.MockPayloadAttestation{}
|
||||
}
|
||||
|
||||
msg := newPayloadAttestationMessage()
|
||||
buf := new(bytes.Buffer)
|
||||
_, err := p.Encoding().EncodeGossip(buf, msg)
|
||||
require.NoError(t, err)
|
||||
|
||||
topic := p2p.GossipTypeMapping[reflect.TypeFor[*ethpb.PayloadAttestationMessage]()]
|
||||
digest, err := s.currentForkDigest()
|
||||
require.NoError(t, err)
|
||||
topic = s.addDigestToTopic(topic, digest)
|
||||
|
||||
result, err := s.validatePayloadAttestation(ctx, "", &pubsub.Message{
|
||||
Message: &pb.Message{
|
||||
Data: buf.Bytes(),
|
||||
Topic: &topic,
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, result, pubsub.ValidationAccept)
|
||||
}
|
||||
|
||||
func newPayloadAttestationMessage() *ethpb.PayloadAttestationMessage {
|
||||
return ðpb.PayloadAttestationMessage{
|
||||
ValidatorIndex: 0,
|
||||
Data: util.HydratePayloadAttestationData(ðpb.PayloadAttestationData{Slot: 1}),
|
||||
Signature: make([]byte, fieldparams.BLSSignatureLength),
|
||||
}
|
||||
}
|
||||
@@ -15,16 +15,12 @@ go_library(
|
||||
"log.go",
|
||||
"metrics.go",
|
||||
"mock.go",
|
||||
"payload_attestation.go",
|
||||
"payload_attestation_mock.go",
|
||||
"requirements.go",
|
||||
"result.go",
|
||||
],
|
||||
importpath = "github.com/OffchainLabs/prysm/v7/beacon-chain/verification",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//beacon-chain/blockchain/kzg:go_default_library",
|
||||
"//beacon-chain/core/gloas:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/core/peerdas:go_default_library",
|
||||
"//beacon-chain/core/signing:go_default_library",
|
||||
@@ -36,7 +32,6 @@ go_library(
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/blocks:go_default_library",
|
||||
"//consensus-types/payload-attestation:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
@@ -62,14 +57,12 @@ go_test(
|
||||
"data_column_test.go",
|
||||
"filesystem_test.go",
|
||||
"initializer_test.go",
|
||||
"payload_attestation_test.go",
|
||||
"result_test.go",
|
||||
"verification_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//beacon-chain/blockchain/kzg:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/core/peerdas:go_default_library",
|
||||
"//beacon-chain/core/signing:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
@@ -80,10 +73,8 @@ go_test(
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types/blocks:go_default_library",
|
||||
"//consensus-types/payload-attestation:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
"//crypto/bls/common:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//runtime/interop:go_default_library",
|
||||
|
||||
@@ -14,6 +14,24 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
RequireBlobIndexInBounds Requirement = iota
|
||||
RequireNotFromFutureSlot
|
||||
RequireSlotAboveFinalized
|
||||
RequireValidProposerSignature
|
||||
RequireSidecarParentSeen
|
||||
RequireSidecarParentValid
|
||||
RequireSidecarParentSlotLower
|
||||
RequireSidecarDescendsFromFinalized
|
||||
RequireSidecarInclusionProven
|
||||
RequireSidecarKzgProofVerified
|
||||
RequireSidecarProposerExpected
|
||||
|
||||
// Data columns specific.
|
||||
RequireValidFields
|
||||
RequireCorrectSubnet
|
||||
)
|
||||
|
||||
var allBlobSidecarRequirements = []Requirement{
|
||||
RequireBlobIndexInBounds,
|
||||
RequireNotFromFutureSlot,
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
|
||||
payloadattestation "github.com/OffchainLabs/prysm/v7/consensus-types/payload-attestation"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
"golang.org/x/sync/singleflight"
|
||||
@@ -87,16 +86,6 @@ func (ini *Initializer) NewDataColumnsVerifier(roDataColumns []blocks.RODataColu
|
||||
}
|
||||
}
|
||||
|
||||
// NewPayloadAttestationMsgVerifier creates a PayloadAttestationMsgVerifier for a single payload attestation message,
|
||||
// with the given set of requirements.
|
||||
func (ini *Initializer) NewPayloadAttestationMsgVerifier(pa payloadattestation.ROMessage, reqs []Requirement) *PayloadAttMsgVerifier {
|
||||
return &PayloadAttMsgVerifier{
|
||||
sharedResources: ini.shared,
|
||||
results: newResults(reqs...),
|
||||
pa: pa,
|
||||
}
|
||||
}
|
||||
|
||||
// InitializerWaiter provides an Initializer once all dependent resources are ready
|
||||
// via the WaitForInitializer method.
|
||||
type InitializerWaiter struct {
|
||||
|
||||
@@ -3,10 +3,8 @@ package verification
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
||||
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
|
||||
payloadattestation "github.com/OffchainLabs/prysm/v7/consensus-types/payload-attestation"
|
||||
)
|
||||
|
||||
// BlobVerifier defines the methods implemented by the ROBlobVerifier.
|
||||
@@ -56,18 +54,3 @@ type DataColumnsVerifier interface {
|
||||
// NewDataColumnsVerifier is a function signature that can be used to mock a setup where a
|
||||
// column verifier can be easily initialized.
|
||||
type NewDataColumnsVerifier func(dataColumns []blocks.RODataColumn, reqs []Requirement) DataColumnsVerifier
|
||||
|
||||
// PayloadAttestationMsgVerifier defines the methods implemented by the ROPayloadAttestation.
|
||||
type PayloadAttestationMsgVerifier interface {
|
||||
VerifyCurrentSlot() error
|
||||
VerifyBlockRootSeen(blockRootSeen func([32]byte) bool) error
|
||||
VerifyBlockRootValid(func([32]byte) bool) error
|
||||
VerifyValidatorInPTC(context.Context, state.ReadOnlyBeaconState) error
|
||||
VerifySignature(state.ReadOnlyBeaconState) error
|
||||
VerifiedPayloadAttestation() (payloadattestation.VerifiedROMessage, error)
|
||||
SatisfyRequirement(Requirement)
|
||||
}
|
||||
|
||||
// NewPayloadAttestationMsgVerifier is a function signature that can be used by code that needs to be
|
||||
// able to mock Initializer.NewPayloadAttestationMsgVerifier without complex setup.
|
||||
type NewPayloadAttestationMsgVerifier func(pa payloadattestation.ROMessage, reqs []Requirement) PayloadAttestationMsgVerifier
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
package verification
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/gloas"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/signing"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
payloadattestation "github.com/OffchainLabs/prysm/v7/consensus-types/payload-attestation"
|
||||
"github.com/OffchainLabs/prysm/v7/crypto/bls"
|
||||
"github.com/OffchainLabs/prysm/v7/time/slots"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// RequirementList defines a list of requirements.
|
||||
type RequirementList []Requirement
|
||||
|
||||
// PayloadAttGossipRequirements defines the list of requirements for gossip payload attestation messages.
|
||||
var PayloadAttGossipRequirements = []Requirement{
|
||||
RequireCurrentSlot,
|
||||
RequireMessageNotSeen,
|
||||
RequireValidatorInPTC,
|
||||
RequireBlockRootSeen,
|
||||
RequireBlockRootValid,
|
||||
RequireSignatureValid,
|
||||
}
|
||||
|
||||
// GossipPayloadAttestationMessageRequirements is a requirement list for gossip payload attestation messages.
|
||||
var GossipPayloadAttestationMessageRequirements = RequirementList(PayloadAttGossipRequirements)
|
||||
|
||||
var (
|
||||
ErrIncorrectPayloadAttSlot = errors.New("payload att slot does not match the current slot")
|
||||
ErrPayloadAttBlockRootNotSeen = errors.New("block root not seen")
|
||||
ErrPayloadAttBlockRootInvalid = errors.New("block root invalid")
|
||||
ErrIncorrectPayloadAttValidator = errors.New("validator not present in payload timeliness committee")
|
||||
ErrInvalidPayloadAttMessage = errors.New("invalid payload attestation message")
|
||||
)
|
||||
|
||||
var _ PayloadAttestationMsgVerifier = &PayloadAttMsgVerifier{}
|
||||
|
||||
// PayloadAttMsgVerifier is a read-only verifier for payload attestation messages.
|
||||
type PayloadAttMsgVerifier struct {
|
||||
*sharedResources
|
||||
results *results
|
||||
pa payloadattestation.ROMessage
|
||||
}
|
||||
|
||||
// VerifyCurrentSlot verifies if the current slot matches the expected slot.
|
||||
// Represents the following spec verification:
|
||||
// [IGNORE] data.slot is the current slot.
|
||||
func (v *PayloadAttMsgVerifier) VerifyCurrentSlot() (err error) {
|
||||
defer v.record(RequireCurrentSlot, &err)
|
||||
|
||||
currentSlot := v.clock.CurrentSlot()
|
||||
if v.pa.Slot() != currentSlot {
|
||||
return fmt.Errorf("%w: got %d want %d", ErrIncorrectPayloadAttSlot, v.pa.Slot(), currentSlot)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifyBlockRootSeen verifies if the block root has been seen before.
|
||||
// Represents the following spec verification:
|
||||
// [IGNORE] The attestation's data.beacon_block_root has been seen (via both gossip and non-gossip sources).
|
||||
func (v *PayloadAttMsgVerifier) VerifyBlockRootSeen(blockRootSeen func([32]byte) bool) (err error) {
|
||||
defer v.record(RequireBlockRootSeen, &err)
|
||||
if blockRootSeen != nil && blockRootSeen(v.pa.BeaconBlockRoot()) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("%w: root=%#x", ErrPayloadAttBlockRootNotSeen, v.pa.BeaconBlockRoot())
|
||||
}
|
||||
|
||||
// VerifyBlockRootValid verifies if the block root is valid.
|
||||
// Represents the following spec verification:
|
||||
// [REJECT] The beacon block with root data.beacon_block_root passes validation.
|
||||
func (v *PayloadAttMsgVerifier) VerifyBlockRootValid(badBlock func([32]byte) bool) (err error) {
|
||||
defer v.record(RequireBlockRootValid, &err)
|
||||
|
||||
if badBlock != nil && badBlock(v.pa.BeaconBlockRoot()) {
|
||||
return fmt.Errorf("%w: root=%#x", ErrPayloadAttBlockRootInvalid, v.pa.BeaconBlockRoot())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifyValidatorInPTC verifies if the validator is present.
|
||||
// Represents the following spec verification:
|
||||
// [REJECT] The validator index is within the payload committee in get_ptc(state, data.slot). For the current's slot head state.
|
||||
func (v *PayloadAttMsgVerifier) VerifyValidatorInPTC(ctx context.Context, st state.ReadOnlyBeaconState) (err error) {
|
||||
defer v.record(RequireValidatorInPTC, &err)
|
||||
|
||||
ptc, err := gloas.PayloadCommittee(ctx, st, v.pa.Slot())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if slices.Index(ptc, v.pa.ValidatorIndex()) == -1 {
|
||||
return fmt.Errorf("%w: validatorIndex=%d", ErrIncorrectPayloadAttValidator, v.pa.ValidatorIndex())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifySignature verifies the signature of the payload attestation message.
|
||||
// Represents the following spec verification:
|
||||
// [REJECT] The signature of payload_attestation_message.signature is valid with respect to the validator index.
|
||||
func (v *PayloadAttMsgVerifier) VerifySignature(st state.ReadOnlyBeaconState) (err error) {
|
||||
defer v.record(RequireSignatureValid, &err)
|
||||
|
||||
err = validatePayloadAttestationMessageSignature(st, v.pa)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifiedPayloadAttestation returns a verified payload attestation message by checking all requirements.
|
||||
func (v *PayloadAttMsgVerifier) VerifiedPayloadAttestation() (payloadattestation.VerifiedROMessage, error) {
|
||||
if v.results.allSatisfied() {
|
||||
return payloadattestation.NewVerifiedROMessage(v.pa), nil
|
||||
}
|
||||
return payloadattestation.VerifiedROMessage{}, ErrInvalidPayloadAttMessage
|
||||
}
|
||||
|
||||
// SatisfyRequirement allows the caller to manually mark a requirement as satisfied.
|
||||
func (v *PayloadAttMsgVerifier) SatisfyRequirement(req Requirement) {
|
||||
v.record(req, nil)
|
||||
}
|
||||
|
||||
// ValidatePayloadAttestationMessageSignature verifies the signature of a payload attestation message.
|
||||
func validatePayloadAttestationMessageSignature(st state.ReadOnlyBeaconState, payloadAtt payloadattestation.ROMessage) error {
|
||||
val, err := st.ValidatorAtIndex(payloadAtt.ValidatorIndex())
|
||||
if err != nil {
|
||||
return fmt.Errorf("validator %d: %w", payloadAtt.ValidatorIndex(), err)
|
||||
}
|
||||
|
||||
pub, err := bls.PublicKeyFromBytes(val.PublicKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("public key: %w", err)
|
||||
}
|
||||
|
||||
s := payloadAtt.Signature()
|
||||
sig, err := bls.SignatureFromBytes(s[:])
|
||||
if err != nil {
|
||||
return fmt.Errorf("signature bytes: %w", err)
|
||||
}
|
||||
|
||||
currentEpoch := slots.ToEpoch(st.Slot())
|
||||
domain, err := signing.Domain(st.Fork(), currentEpoch, params.BeaconConfig().DomainPTCAttester, st.GenesisValidatorsRoot())
|
||||
if err != nil {
|
||||
return fmt.Errorf("domain: %w", err)
|
||||
}
|
||||
|
||||
root, err := payloadAtt.SigningRoot(domain)
|
||||
if err != nil {
|
||||
return fmt.Errorf("signing root: %w", err)
|
||||
}
|
||||
|
||||
if !sig.Verify(pub, root[:]) {
|
||||
return fmt.Errorf("verify signature: %w", signing.ErrSigFailedToVerify)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// record records the result of a requirement verification.
|
||||
func (v *PayloadAttMsgVerifier) record(req Requirement, err *error) {
|
||||
if err == nil || *err == nil {
|
||||
v.results.record(req, nil)
|
||||
return
|
||||
}
|
||||
|
||||
v.results.record(req, *err)
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package verification
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
||||
payloadattestation "github.com/OffchainLabs/prysm/v7/consensus-types/payload-attestation"
|
||||
)
|
||||
|
||||
type MockPayloadAttestation struct {
|
||||
ErrIncorrectPayloadAttSlot error
|
||||
ErrIncorrectPayloadAttValidator error
|
||||
ErrPayloadAttBlockRootNotSeen error
|
||||
ErrPayloadAttBlockRootInvalid error
|
||||
ErrInvalidPayloadAttMessage error
|
||||
ErrInvalidMessageSignature error
|
||||
ErrUnsatisfiedRequirement error
|
||||
}
|
||||
|
||||
var _ PayloadAttestationMsgVerifier = &MockPayloadAttestation{}
|
||||
|
||||
func (m *MockPayloadAttestation) VerifyCurrentSlot() error {
|
||||
return m.ErrIncorrectPayloadAttSlot
|
||||
}
|
||||
|
||||
func (m *MockPayloadAttestation) VerifyValidatorInPTC(ctx context.Context, st state.ReadOnlyBeaconState) error {
|
||||
return m.ErrIncorrectPayloadAttValidator
|
||||
}
|
||||
|
||||
func (m *MockPayloadAttestation) VerifyBlockRootSeen(_ func([32]byte) bool) error {
|
||||
return m.ErrPayloadAttBlockRootNotSeen
|
||||
}
|
||||
|
||||
func (m *MockPayloadAttestation) VerifyBlockRootValid(func([32]byte) bool) error {
|
||||
return m.ErrPayloadAttBlockRootInvalid
|
||||
}
|
||||
|
||||
func (m *MockPayloadAttestation) VerifySignature(st state.ReadOnlyBeaconState) (err error) {
|
||||
return m.ErrInvalidMessageSignature
|
||||
}
|
||||
|
||||
func (m *MockPayloadAttestation) VerifiedPayloadAttestation() (payloadattestation.VerifiedROMessage, error) {
|
||||
return payloadattestation.VerifiedROMessage{}, nil
|
||||
}
|
||||
|
||||
func (m *MockPayloadAttestation) SatisfyRequirement(req Requirement) {}
|
||||
@@ -1,167 +0,0 @@
|
||||
package verification
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/helpers"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/signing"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/startup"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
payloadattestation "github.com/OffchainLabs/prysm/v7/consensus-types/payload-attestation"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
"github.com/OffchainLabs/prysm/v7/crypto/bls"
|
||||
"github.com/OffchainLabs/prysm/v7/crypto/bls/common"
|
||||
eth "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/require"
|
||||
testutil "github.com/OffchainLabs/prysm/v7/testing/util"
|
||||
"github.com/OffchainLabs/prysm/v7/time/slots"
|
||||
)
|
||||
|
||||
func TestPayloadAttestationVerifyCurrentSlot(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
now := time.Unix(1000, 0)
|
||||
genesis := now.Add(-time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second)
|
||||
clock := startup.NewClock(genesis, [32]byte{}, startup.WithNower(func() time.Time { return now }))
|
||||
ini := &Initializer{shared: &sharedResources{clock: clock}}
|
||||
|
||||
msg := newPayloadAttestationMessage(primitives.Slot(1), 0, bytes.Repeat([]byte{0x11}, 32))
|
||||
pa, err := payloadattestation.NewReadOnly(msg)
|
||||
require.NoError(t, err)
|
||||
v := ini.NewPayloadAttestationMsgVerifier(pa, GossipPayloadAttestationMessageRequirements)
|
||||
require.NoError(t, v.VerifyCurrentSlot())
|
||||
|
||||
msg = newPayloadAttestationMessage(primitives.Slot(2), 0, bytes.Repeat([]byte{0x11}, 32))
|
||||
pa, err = payloadattestation.NewReadOnly(msg)
|
||||
require.NoError(t, err)
|
||||
v = ini.NewPayloadAttestationMsgVerifier(pa, GossipPayloadAttestationMessageRequirements)
|
||||
require.ErrorIs(t, v.VerifyCurrentSlot(), ErrIncorrectPayloadAttSlot)
|
||||
}
|
||||
|
||||
func TestPayloadAttestationVerifyBlockRootSeenAndValid(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
ini := &Initializer{shared: &sharedResources{}}
|
||||
root := bytes.Repeat([]byte{0x22}, 32)
|
||||
var root32 [32]byte
|
||||
copy(root32[:], root)
|
||||
|
||||
msg := newPayloadAttestationMessage(primitives.Slot(1), 0, root)
|
||||
pa, err := payloadattestation.NewReadOnly(msg)
|
||||
require.NoError(t, err)
|
||||
v := ini.NewPayloadAttestationMsgVerifier(pa, GossipPayloadAttestationMessageRequirements)
|
||||
|
||||
require.NoError(t, v.VerifyBlockRootSeen(func(r [32]byte) bool { return r == root32 }))
|
||||
require.ErrorIs(t, v.VerifyBlockRootSeen(func([32]byte) bool { return false }), ErrPayloadAttBlockRootNotSeen)
|
||||
|
||||
require.NoError(t, v.VerifyBlockRootValid(func([32]byte) bool { return false }))
|
||||
require.ErrorIs(t, v.VerifyBlockRootValid(func([32]byte) bool { return true }), ErrPayloadAttBlockRootInvalid)
|
||||
}
|
||||
|
||||
func TestPayloadAttestationVerifyValidatorInPTC(t *testing.T) {
|
||||
setupPayloadAttTestConfig(t)
|
||||
|
||||
_, pk := newKey(t)
|
||||
st := newTestState(t, []*eth.Validator{activeValidator(pk)}, 1)
|
||||
msg := newPayloadAttestationMessage(primitives.Slot(1), 0, bytes.Repeat([]byte{0x33}, 32))
|
||||
pa, err := payloadattestation.NewReadOnly(msg)
|
||||
require.NoError(t, err)
|
||||
v := (&Initializer{shared: &sharedResources{}}).NewPayloadAttestationMsgVerifier(pa, GossipPayloadAttestationMessageRequirements)
|
||||
require.NoError(t, v.VerifyValidatorInPTC(context.Background(), st))
|
||||
|
||||
msg = newPayloadAttestationMessage(primitives.Slot(1), 1, bytes.Repeat([]byte{0x33}, 32))
|
||||
pa, err = payloadattestation.NewReadOnly(msg)
|
||||
require.NoError(t, err)
|
||||
v = (&Initializer{shared: &sharedResources{}}).NewPayloadAttestationMsgVerifier(pa, GossipPayloadAttestationMessageRequirements)
|
||||
require.ErrorIs(t, v.VerifyValidatorInPTC(context.Background(), st), ErrIncorrectPayloadAttValidator)
|
||||
}
|
||||
|
||||
func TestPayloadAttestationVerifySignature(t *testing.T) {
|
||||
setupPayloadAttTestConfig(t)
|
||||
|
||||
sk, pk := newKey(t)
|
||||
st := newTestState(t, []*eth.Validator{activeValidator(pk)}, 1)
|
||||
root := bytes.Repeat([]byte{0x44}, 32)
|
||||
data := ð.PayloadAttestationData{
|
||||
BeaconBlockRoot: root,
|
||||
Slot: 1,
|
||||
PayloadPresent: true,
|
||||
BlobDataAvailable: true,
|
||||
}
|
||||
msg := ð.PayloadAttestationMessage{
|
||||
ValidatorIndex: 0,
|
||||
Data: data,
|
||||
Signature: signPayloadAttestationMessage(t, st, data, sk),
|
||||
}
|
||||
pa, err := payloadattestation.NewReadOnly(msg)
|
||||
require.NoError(t, err)
|
||||
v := (&Initializer{shared: &sharedResources{}}).NewPayloadAttestationMsgVerifier(pa, GossipPayloadAttestationMessageRequirements)
|
||||
require.NoError(t, v.VerifySignature(st))
|
||||
|
||||
sk2, _ := newKey(t)
|
||||
msg.Signature = signPayloadAttestationMessage(t, st, data, sk2)
|
||||
pa, err = payloadattestation.NewReadOnly(msg)
|
||||
require.NoError(t, err)
|
||||
v = (&Initializer{shared: &sharedResources{}}).NewPayloadAttestationMsgVerifier(pa, GossipPayloadAttestationMessageRequirements)
|
||||
require.ErrorIs(t, v.VerifySignature(st), signing.ErrSigFailedToVerify)
|
||||
}
|
||||
|
||||
func newPayloadAttestationMessage(slot primitives.Slot, idx primitives.ValidatorIndex, root []byte) *eth.PayloadAttestationMessage {
|
||||
return ð.PayloadAttestationMessage{
|
||||
ValidatorIndex: idx,
|
||||
Data: ð.PayloadAttestationData{
|
||||
BeaconBlockRoot: root,
|
||||
Slot: slot,
|
||||
PayloadPresent: true,
|
||||
BlobDataAvailable: true,
|
||||
},
|
||||
Signature: []byte{0x01},
|
||||
}
|
||||
}
|
||||
|
||||
func newTestState(t *testing.T, vals []*eth.Validator, slot primitives.Slot) state.BeaconState {
|
||||
st, err := testutil.NewBeaconStateGloas()
|
||||
require.NoError(t, err)
|
||||
for _, v := range vals {
|
||||
require.NoError(t, st.AppendValidator(v))
|
||||
require.NoError(t, st.AppendBalance(v.EffectiveBalance))
|
||||
}
|
||||
require.NoError(t, st.SetSlot(slot))
|
||||
require.NoError(t, helpers.UpdateCommitteeCache(t.Context(), st, slots.ToEpoch(slot)))
|
||||
return st
|
||||
}
|
||||
|
||||
func setupPayloadAttTestConfig(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.SlotsPerEpoch = 1
|
||||
cfg.MaxEffectiveBalanceElectra = cfg.MaxEffectiveBalance
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
}
|
||||
|
||||
func activeValidator(pub []byte) *eth.Validator {
|
||||
return ð.Validator{
|
||||
PublicKey: pub,
|
||||
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
||||
WithdrawalCredentials: make([]byte, 32),
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
}
|
||||
}
|
||||
|
||||
func newKey(t *testing.T) (common.SecretKey, []byte) {
|
||||
sk, err := bls.RandKey()
|
||||
require.NoError(t, err)
|
||||
return sk, sk.PublicKey().Marshal()
|
||||
}
|
||||
|
||||
func signPayloadAttestationMessage(t *testing.T, st state.ReadOnlyBeaconState, data *eth.PayloadAttestationData, sk common.SecretKey) []byte {
|
||||
domain, err := signing.Domain(st.Fork(), slots.ToEpoch(st.Slot()), params.BeaconConfig().DomainPTCAttester, st.GenesisValidatorsRoot())
|
||||
require.NoError(t, err)
|
||||
root, err := signing.ComputeSigningRoot(data, domain)
|
||||
require.NoError(t, err)
|
||||
sig := sk.Sign(root[:])
|
||||
return sig.Marshal()
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package verification
|
||||
|
||||
const (
|
||||
RequireBlobIndexInBounds Requirement = iota
|
||||
RequireNotFromFutureSlot
|
||||
RequireSlotAboveFinalized
|
||||
RequireValidProposerSignature
|
||||
RequireSidecarParentSeen
|
||||
RequireSidecarParentValid
|
||||
RequireSidecarParentSlotLower
|
||||
RequireSidecarDescendsFromFinalized
|
||||
RequireSidecarInclusionProven
|
||||
RequireSidecarKzgProofVerified
|
||||
RequireSidecarProposerExpected
|
||||
|
||||
// Data columns specific.
|
||||
RequireValidFields
|
||||
RequireCorrectSubnet
|
||||
|
||||
// Payload attestation specific.
|
||||
RequireCurrentSlot
|
||||
RequireMessageNotSeen
|
||||
RequireValidatorInPTC
|
||||
RequireBlockRootSeen
|
||||
RequireBlockRootValid
|
||||
RequireSignatureValid
|
||||
)
|
||||
@@ -29,22 +29,6 @@ func (r Requirement) String() string {
|
||||
return "RequireSidecarKzgProofVerified"
|
||||
case RequireSidecarProposerExpected:
|
||||
return "RequireSidecarProposerExpected"
|
||||
case RequireValidFields:
|
||||
return "RequireValidFields"
|
||||
case RequireCorrectSubnet:
|
||||
return "RequireCorrectSubnet"
|
||||
case RequireCurrentSlot:
|
||||
return "RequireCurrentSlot"
|
||||
case RequireMessageNotSeen:
|
||||
return "RequireMessageNotSeen"
|
||||
case RequireValidatorInPTC:
|
||||
return "RequireValidatorInPTC"
|
||||
case RequireBlockRootSeen:
|
||||
return "RequireBlockRootSeen"
|
||||
case RequireBlockRootValid:
|
||||
return "RequireBlockRootValid"
|
||||
case RequireSignatureValid:
|
||||
return "RequireSignatureValid"
|
||||
default:
|
||||
return unknownRequirementName
|
||||
}
|
||||
|
||||
@@ -61,16 +61,3 @@ func TestAllBlobRequirementsHaveStrings(t *testing.T) {
|
||||
require.NotEqual(t, unknownRequirementName, allBlobSidecarRequirements[i].String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestPayloadAttestationRequirementsHaveStrings(t *testing.T) {
|
||||
blobReqs := make(map[Requirement]struct{}, len(allBlobSidecarRequirements))
|
||||
for i := range allBlobSidecarRequirements {
|
||||
blobReqs[allBlobSidecarRequirements[i]] = struct{}{}
|
||||
}
|
||||
for i := range PayloadAttGossipRequirements {
|
||||
req := PayloadAttGossipRequirements[i]
|
||||
require.NotEqual(t, unknownRequirementName, req.String())
|
||||
_, overlaps := blobReqs[req]
|
||||
require.Equal(t, false, overlaps)
|
||||
}
|
||||
}
|
||||
|
||||
3
changelog/bastin_get-version-v2.md
Normal file
3
changelog/bastin_get-version-v2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Added
|
||||
|
||||
- New beacon API endpoint `eth/v2/node/version`.
|
||||
@@ -1,3 +0,0 @@
|
||||
### Ignored
|
||||
|
||||
- improving maintainability and deduplication on get and post block parsing.
|
||||
@@ -1,3 +0,0 @@
|
||||
### Added
|
||||
|
||||
- Added support for Payload attestation gossip net in gloas
|
||||
@@ -356,11 +356,6 @@ var (
|
||||
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),
|
||||
}
|
||||
// StateDiffValidateOnStartup validates state diff data on startup.
|
||||
StateDiffValidateOnStartup = &cli.BoolFlag{
|
||||
Name: "disable-hdiff-validate-on-startup",
|
||||
Usage: "Disables state-diff validation on startup (enabled by default).",
|
||||
}
|
||||
// DisableEphemeralLogFile disables the 24 hour debug log file.
|
||||
DisableEphemeralLogFile = &cli.BoolFlag{
|
||||
Name: "disable-ephemeral-log-file",
|
||||
|
||||
@@ -9,25 +9,24 @@ import (
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
const MaxStateDiffExponent = 30
|
||||
const maxStateDiffExponents = 30
|
||||
|
||||
// GlobalFlags specifies all the global flags for the
|
||||
// beacon node.
|
||||
type GlobalFlags struct {
|
||||
StateDiffValidateOnStartup bool
|
||||
Supernode bool
|
||||
DisableGetBlobsV2 bool
|
||||
SemiSupernode bool
|
||||
SubscribeToAllSubnets bool
|
||||
BlobBatchLimitBurstFactor int
|
||||
DataColumnBatchLimit int
|
||||
BlockBatchLimit int
|
||||
MaxConcurrentDials int
|
||||
MinimumPeersPerSubnet int
|
||||
Supernode bool
|
||||
SemiSupernode bool
|
||||
DisableGetBlobsV2 bool
|
||||
MinimumSyncPeers int
|
||||
DataColumnBatchLimitBurstFactor int
|
||||
MinimumPeersPerSubnet int
|
||||
MaxConcurrentDials int
|
||||
BlockBatchLimit int
|
||||
BlockBatchLimitBurstFactor int
|
||||
BlobBatchLimit int
|
||||
BlobBatchLimitBurstFactor int
|
||||
DataColumnBatchLimit int
|
||||
DataColumnBatchLimitBurstFactor int
|
||||
StateDiffExponents []int
|
||||
}
|
||||
|
||||
@@ -81,7 +80,6 @@ func ConfigureGlobalFlags(ctx *cli.Context) error {
|
||||
|
||||
// State-diff-exponents
|
||||
cfg.StateDiffExponents = ctx.IntSlice(StateDiffExponents.Name)
|
||||
cfg.StateDiffValidateOnStartup = !ctx.Bool(StateDiffValidateOnStartup.Name)
|
||||
if features.Get().EnableStateDiff {
|
||||
if err := validateStateDiffExponents(cfg.StateDiffExponents); err != nil {
|
||||
return err
|
||||
@@ -90,9 +88,6 @@ func ConfigureGlobalFlags(ctx *cli.Context) error {
|
||||
if ctx.IsSet(StateDiffExponents.Name) {
|
||||
log.Warn("--state-diff-exponents is set but --enable-state-diff is not; the value will be ignored.")
|
||||
}
|
||||
if ctx.IsSet(StateDiffValidateOnStartup.Name) {
|
||||
log.Warn("--disable-hdiff-validate-on-startup is set but --enable-state-diff is not; the value will be ignored.")
|
||||
}
|
||||
}
|
||||
|
||||
cfg.BlockBatchLimit = ctx.Int(BlockBatchLimit.Name)
|
||||
@@ -137,7 +132,7 @@ func validateStateDiffExponents(exponents []int) error {
|
||||
if exponents[length-1] < 5 {
|
||||
return errors.New("the last state diff exponent must be at least 5")
|
||||
}
|
||||
prev := MaxStateDiffExponent + 1
|
||||
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")
|
||||
|
||||
@@ -161,7 +161,6 @@ var appFlags = []cli.Flag{
|
||||
dasFlags.BlobRetentionEpochFlag,
|
||||
flags.BatchVerifierLimit,
|
||||
flags.StateDiffExponents,
|
||||
flags.StateDiffValidateOnStartup,
|
||||
flags.DisableEphemeralLogFile,
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,6 @@ var appHelpFlagGroups = []flagGroup{
|
||||
flags.RPCPort,
|
||||
flags.BatchVerifierLimit,
|
||||
flags.StateDiffExponents,
|
||||
flags.StateDiffValidateOnStartup,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["readonly_message.go"],
|
||||
importpath = "github.com/OffchainLabs/prysm/v7/consensus-types/payload-attestation",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//beacon-chain/core/signing:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -1,87 +0,0 @@
|
||||
package payloadattestation
|
||||
|
||||
import (
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/signing"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
errNilPayloadAttMessage = errors.New("received nil payload attestation message")
|
||||
errNilPayloadAttData = errors.New("received nil payload attestation data")
|
||||
errNilPayloadAttSignature = errors.New("received nil payload attestation signature")
|
||||
)
|
||||
|
||||
// ROMessage represents a read-only payload attestation message.
|
||||
type ROMessage struct {
|
||||
m *ethpb.PayloadAttestationMessage
|
||||
}
|
||||
|
||||
// validatePayloadAtt checks if the given payload attestation message is valid.
|
||||
func validatePayloadAtt(m *ethpb.PayloadAttestationMessage) error {
|
||||
if m == nil {
|
||||
return errNilPayloadAttMessage
|
||||
}
|
||||
if m.Data == nil {
|
||||
return errNilPayloadAttData
|
||||
}
|
||||
if len(m.Signature) == 0 {
|
||||
return errNilPayloadAttSignature
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewReadOnly creates a new ReadOnly instance after validating the message.
|
||||
func NewReadOnly(m *ethpb.PayloadAttestationMessage) (ROMessage, error) {
|
||||
if err := validatePayloadAtt(m); err != nil {
|
||||
return ROMessage{}, err
|
||||
}
|
||||
return ROMessage{m}, nil
|
||||
}
|
||||
|
||||
// ValidatorIndex returns the validator index from the payload attestation message.
|
||||
func (r *ROMessage) ValidatorIndex() primitives.ValidatorIndex {
|
||||
return r.m.ValidatorIndex
|
||||
}
|
||||
|
||||
// Signature returns the signature from the payload attestation message.
|
||||
func (r *ROMessage) Signature() [96]byte {
|
||||
return bytesutil.ToBytes96(r.m.Signature)
|
||||
}
|
||||
|
||||
// BeaconBlockRoot returns the beacon block root from the payload attestation message.
|
||||
func (r *ROMessage) BeaconBlockRoot() [32]byte {
|
||||
return bytesutil.ToBytes32(r.m.Data.BeaconBlockRoot)
|
||||
}
|
||||
|
||||
// Slot returns the slot from the payload attestation message.
|
||||
func (r *ROMessage) Slot() primitives.Slot {
|
||||
return r.m.Data.Slot
|
||||
}
|
||||
|
||||
// PayloadPresent returns whether the payload was present.
|
||||
func (r *ROMessage) PayloadPresent() bool {
|
||||
return r.m.Data.PayloadPresent
|
||||
}
|
||||
|
||||
// BlobDataAvailable returns whether blob data was available.
|
||||
func (r *ROMessage) BlobDataAvailable() bool {
|
||||
return r.m.Data.BlobDataAvailable
|
||||
}
|
||||
|
||||
// SigningRoot returns the signing root from the payload attestation message.
|
||||
func (r *ROMessage) SigningRoot(domain []byte) ([32]byte, error) {
|
||||
return signing.ComputeSigningRoot(r.m.Data, domain)
|
||||
}
|
||||
|
||||
// VerifiedROMessage represents a verified read-only payload attestation message.
|
||||
type VerifiedROMessage struct {
|
||||
ROMessage
|
||||
}
|
||||
|
||||
// NewVerifiedROMessage creates a new VerifiedROMessage instance after validating the message.
|
||||
func NewVerifiedROMessage(r ROMessage) VerifiedROMessage {
|
||||
return VerifiedROMessage{r}
|
||||
}
|
||||
@@ -34,8 +34,17 @@ func SemanticVersion() string {
|
||||
return gitTag
|
||||
}
|
||||
|
||||
// GitCommit returns the current build commit hash.
|
||||
func GitCommit() string {
|
||||
return resolvedGitCommit()
|
||||
}
|
||||
|
||||
// BuildData returns the git tag and commit of the current build.
|
||||
func BuildData() string {
|
||||
return fmt.Sprintf("Prysm/%s/%s", gitTag, resolvedGitCommit())
|
||||
}
|
||||
|
||||
func resolvedGitCommit() string {
|
||||
// if doing a local build, these values are not interpolated
|
||||
if gitCommit == "{STABLE_GIT_COMMIT}" {
|
||||
commit, err := exec.Command("git", "rev-parse", "HEAD").Output()
|
||||
@@ -45,5 +54,5 @@ func BuildData() string {
|
||||
gitCommit = strings.TrimRight(string(commit), "\r\n")
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("Prysm/%s/%s", gitTag, gitCommit)
|
||||
return gitCommit
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ go_library(
|
||||
importpath = "github.com/OffchainLabs/prysm/v7/testing/spectest/shared/common/forkchoice",
|
||||
visibility = ["//testing/spectest:__subpackages__"],
|
||||
deps = [
|
||||
"//api/server/structs:go_default_library",
|
||||
"//beacon-chain/blockchain:go_default_library",
|
||||
"//beacon-chain/blockchain/kzg:go_default_library",
|
||||
"//beacon-chain/blockchain/testing:go_default_library",
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/api/server/structs"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/kzg"
|
||||
mock "github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/testing"
|
||||
@@ -141,3 +142,7 @@ func (m *engineMock) ExecutionBlockByHash(_ context.Context, hash common.Hash, _
|
||||
func (m *engineMock) GetTerminalBlockHash(context.Context, uint64) ([]byte, bool, error) {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
func (m *engineMock) GetClientVersionV1(context.Context) ([]*structs.ClientVersionV1, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -52,7 +52,6 @@ go_library(
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//consensus-types/validator:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//encoding/ssz:go_default_library",
|
||||
"//monitoring/tracing/trace:go_default_library",
|
||||
"//network/httputil:go_default_library",
|
||||
"//proto/engine/v1:go_default_library",
|
||||
|
||||
@@ -66,13 +66,12 @@ func (c *beaconApiValidatorClient) duties(ctx context.Context, in *ethpb.DutiesR
|
||||
}()
|
||||
|
||||
nextEpochDuties := ðpb.ValidatorDutiesContainer{}
|
||||
nextEpochErr := c.dutiesForEpoch(ctx, nextEpochDuties, in.Epoch+1, vals, fetchSyncDuties)
|
||||
|
||||
if currEpochErr := <-errCh; currEpochErr != nil {
|
||||
return nil, currEpochErr
|
||||
if err := c.dutiesForEpoch(ctx, nextEpochDuties, in.Epoch+1, vals, fetchSyncDuties); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get duties for next epoch `%d`", in.Epoch+1)
|
||||
}
|
||||
if nextEpochErr != nil {
|
||||
return nil, errors.Wrapf(nextEpochErr, "failed to get duties for next epoch `%d`", in.Epoch+1)
|
||||
|
||||
if err = <-errCh; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ðpb.ValidatorDutiesContainer{
|
||||
|
||||
@@ -55,153 +55,114 @@ func (c *beaconApiValidatorClient) beaconBlock(ctx context.Context, slot primiti
|
||||
}
|
||||
}
|
||||
|
||||
// sszBlockCodec defines SSZ unmarshalers for a fork's block and blinded block types.
|
||||
type sszBlockCodec struct {
|
||||
unmarshalBlock func([]byte) (*ethpb.GenericBeaconBlock, error)
|
||||
unmarshalBlinded func([]byte) (*ethpb.GenericBeaconBlock, error) // nil for Phase0/Altair
|
||||
}
|
||||
|
||||
type sszCodecEntry struct {
|
||||
minVersion int
|
||||
codec sszBlockCodec
|
||||
}
|
||||
|
||||
// sszCodecs is ordered descending by version so that unknown future versions
|
||||
// fall through to the latest known fork (matching the original if-cascade).
|
||||
var sszCodecs = []sszCodecEntry{
|
||||
{
|
||||
minVersion: version.Fulu,
|
||||
codec: sszBlockCodec{
|
||||
unmarshalBlock: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
|
||||
block := ðpb.BeaconBlockContentsFulu{}
|
||||
if err := block.UnmarshalSSZ(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Fulu{Fulu: block}}, nil
|
||||
},
|
||||
unmarshalBlinded: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
|
||||
blindedBlock := ðpb.BlindedBeaconBlockFulu{}
|
||||
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedFulu{BlindedFulu: blindedBlock}, IsBlinded: true}, nil
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
minVersion: version.Electra,
|
||||
codec: sszBlockCodec{
|
||||
unmarshalBlock: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
|
||||
block := ðpb.BeaconBlockContentsElectra{}
|
||||
if err := block.UnmarshalSSZ(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Electra{Electra: block}}, nil
|
||||
},
|
||||
unmarshalBlinded: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
|
||||
blindedBlock := ðpb.BlindedBeaconBlockElectra{}
|
||||
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedElectra{BlindedElectra: blindedBlock}, IsBlinded: true}, nil
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
minVersion: version.Deneb,
|
||||
codec: sszBlockCodec{
|
||||
unmarshalBlock: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
|
||||
block := ðpb.BeaconBlockContentsDeneb{}
|
||||
if err := block.UnmarshalSSZ(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Deneb{Deneb: block}}, nil
|
||||
},
|
||||
unmarshalBlinded: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
|
||||
blindedBlock := ðpb.BlindedBeaconBlockDeneb{}
|
||||
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedDeneb{BlindedDeneb: blindedBlock}, IsBlinded: true}, nil
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
minVersion: version.Capella,
|
||||
codec: sszBlockCodec{
|
||||
unmarshalBlock: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
|
||||
block := ðpb.BeaconBlockCapella{}
|
||||
if err := block.UnmarshalSSZ(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Capella{Capella: block}}, nil
|
||||
},
|
||||
unmarshalBlinded: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
|
||||
blindedBlock := ðpb.BlindedBeaconBlockCapella{}
|
||||
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedCapella{BlindedCapella: blindedBlock}, IsBlinded: true}, nil
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
minVersion: version.Bellatrix,
|
||||
codec: sszBlockCodec{
|
||||
unmarshalBlock: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
|
||||
block := ðpb.BeaconBlockBellatrix{}
|
||||
if err := block.UnmarshalSSZ(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Bellatrix{Bellatrix: block}}, nil
|
||||
},
|
||||
unmarshalBlinded: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
|
||||
blindedBlock := ðpb.BlindedBeaconBlockBellatrix{}
|
||||
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedBellatrix{BlindedBellatrix: blindedBlock}, IsBlinded: true}, nil
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
minVersion: version.Altair,
|
||||
codec: sszBlockCodec{
|
||||
unmarshalBlock: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
|
||||
block := ðpb.BeaconBlockAltair{}
|
||||
if err := block.UnmarshalSSZ(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Altair{Altair: block}}, nil
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
minVersion: version.Phase0,
|
||||
codec: sszBlockCodec{
|
||||
unmarshalBlock: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
|
||||
block := ðpb.BeaconBlock{}
|
||||
if err := block.UnmarshalSSZ(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Phase0{Phase0: block}}, nil
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func processBlockSSZResponse(ver int, data []byte, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
||||
for _, entry := range sszCodecs {
|
||||
if ver >= entry.minVersion {
|
||||
if isBlinded && entry.codec.unmarshalBlinded != nil {
|
||||
return entry.codec.unmarshalBlinded(data)
|
||||
}
|
||||
return entry.codec.unmarshalBlock(data)
|
||||
if ver >= version.Fulu {
|
||||
return processBlockSSZResponseFulu(data, isBlinded)
|
||||
}
|
||||
if ver >= version.Electra {
|
||||
return processBlockSSZResponseElectra(data, isBlinded)
|
||||
}
|
||||
if ver >= version.Deneb {
|
||||
return processBlockSSZResponseDeneb(data, isBlinded)
|
||||
}
|
||||
if ver >= version.Capella {
|
||||
return processBlockSSZResponseCapella(data, isBlinded)
|
||||
}
|
||||
if ver >= version.Bellatrix {
|
||||
return processBlockSSZResponseBellatrix(data, isBlinded)
|
||||
}
|
||||
if ver >= version.Altair {
|
||||
block := ðpb.BeaconBlockAltair{}
|
||||
if err := block.UnmarshalSSZ(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Altair{Altair: block}}, nil
|
||||
}
|
||||
if ver >= version.Phase0 {
|
||||
block := ðpb.BeaconBlock{}
|
||||
if err := block.UnmarshalSSZ(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Phase0{Phase0: block}}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unsupported block version %s", version.String(ver))
|
||||
}
|
||||
|
||||
func processBlockSSZResponseFulu(data []byte, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
||||
if isBlinded {
|
||||
blindedBlock := ðpb.BlindedBeaconBlockFulu{}
|
||||
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedFulu{BlindedFulu: blindedBlock}, IsBlinded: true}, nil
|
||||
}
|
||||
block := ðpb.BeaconBlockContentsFulu{}
|
||||
if err := block.UnmarshalSSZ(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Fulu{Fulu: block}}, nil
|
||||
}
|
||||
|
||||
func processBlockSSZResponseElectra(data []byte, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
||||
if isBlinded {
|
||||
blindedBlock := ðpb.BlindedBeaconBlockElectra{}
|
||||
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedElectra{BlindedElectra: blindedBlock}, IsBlinded: true}, nil
|
||||
}
|
||||
block := ðpb.BeaconBlockContentsElectra{}
|
||||
if err := block.UnmarshalSSZ(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Electra{Electra: block}}, nil
|
||||
}
|
||||
|
||||
func processBlockSSZResponseDeneb(data []byte, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
||||
if isBlinded {
|
||||
blindedBlock := ðpb.BlindedBeaconBlockDeneb{}
|
||||
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedDeneb{BlindedDeneb: blindedBlock}, IsBlinded: true}, nil
|
||||
}
|
||||
block := ðpb.BeaconBlockContentsDeneb{}
|
||||
if err := block.UnmarshalSSZ(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Deneb{Deneb: block}}, nil
|
||||
}
|
||||
|
||||
func processBlockSSZResponseCapella(data []byte, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
||||
if isBlinded {
|
||||
blindedBlock := ðpb.BlindedBeaconBlockCapella{}
|
||||
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedCapella{BlindedCapella: blindedBlock}, IsBlinded: true}, nil
|
||||
}
|
||||
block := ðpb.BeaconBlockCapella{}
|
||||
if err := block.UnmarshalSSZ(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Capella{Capella: block}}, nil
|
||||
}
|
||||
|
||||
func processBlockSSZResponseBellatrix(data []byte, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
||||
if isBlinded {
|
||||
blindedBlock := ðpb.BlindedBeaconBlockBellatrix{}
|
||||
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_BlindedBellatrix{BlindedBellatrix: blindedBlock}, IsBlinded: true}, nil
|
||||
}
|
||||
block := ðpb.BeaconBlockBellatrix{}
|
||||
if err := block.UnmarshalSSZ(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Bellatrix{Bellatrix: block}}, nil
|
||||
}
|
||||
|
||||
func convertBlockToGeneric(decoder *json.Decoder, dest ethpb.GenericConverter, version string, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
||||
typeName := version
|
||||
if isBlinded {
|
||||
@@ -219,52 +180,69 @@ func convertBlockToGeneric(decoder *json.Decoder, dest ethpb.GenericConverter, v
|
||||
return genericBlock, nil
|
||||
}
|
||||
|
||||
// jsonBlockTypes defines factory functions for creating block and blinded block structs for JSON decoding.
|
||||
type jsonBlockTypes struct {
|
||||
newBlock func() ethpb.GenericConverter
|
||||
newBlinded func() ethpb.GenericConverter // nil for Phase0/Altair
|
||||
}
|
||||
|
||||
var jsonBlockFactories = map[string]jsonBlockTypes{
|
||||
version.String(version.Phase0): {
|
||||
newBlock: func() ethpb.GenericConverter { return &structs.BeaconBlock{} },
|
||||
},
|
||||
version.String(version.Altair): {
|
||||
newBlock: func() ethpb.GenericConverter { return &structs.BeaconBlockAltair{} },
|
||||
},
|
||||
version.String(version.Bellatrix): {
|
||||
newBlock: func() ethpb.GenericConverter { return &structs.BeaconBlockBellatrix{} },
|
||||
newBlinded: func() ethpb.GenericConverter { return &structs.BlindedBeaconBlockBellatrix{} },
|
||||
},
|
||||
version.String(version.Capella): {
|
||||
newBlock: func() ethpb.GenericConverter { return &structs.BeaconBlockCapella{} },
|
||||
newBlinded: func() ethpb.GenericConverter { return &structs.BlindedBeaconBlockCapella{} },
|
||||
},
|
||||
version.String(version.Deneb): {
|
||||
newBlock: func() ethpb.GenericConverter { return &structs.BeaconBlockContentsDeneb{} },
|
||||
newBlinded: func() ethpb.GenericConverter { return &structs.BlindedBeaconBlockDeneb{} },
|
||||
},
|
||||
version.String(version.Electra): {
|
||||
newBlock: func() ethpb.GenericConverter { return &structs.BeaconBlockContentsElectra{} },
|
||||
newBlinded: func() ethpb.GenericConverter { return &structs.BlindedBeaconBlockElectra{} },
|
||||
},
|
||||
version.String(version.Fulu): {
|
||||
newBlock: func() ethpb.GenericConverter { return &structs.BeaconBlockContentsFulu{} },
|
||||
newBlinded: func() ethpb.GenericConverter { return &structs.BlindedBeaconBlockFulu{} },
|
||||
},
|
||||
}
|
||||
|
||||
func processBlockJSONResponse(ver string, isBlinded bool, decoder *json.Decoder) (*ethpb.GenericBeaconBlock, error) {
|
||||
if decoder == nil {
|
||||
return nil, errors.New("no produce block json decoder found")
|
||||
}
|
||||
|
||||
factory, ok := jsonBlockFactories[ver]
|
||||
if !ok {
|
||||
switch ver {
|
||||
case version.String(version.Phase0):
|
||||
return convertBlockToGeneric(decoder, &structs.BeaconBlock{}, version.String(version.Phase0), false)
|
||||
|
||||
case version.String(version.Altair):
|
||||
return convertBlockToGeneric(decoder, &structs.BeaconBlockAltair{}, "altair", false)
|
||||
|
||||
case version.String(version.Bellatrix):
|
||||
return processBellatrixBlock(decoder, isBlinded)
|
||||
|
||||
case version.String(version.Capella):
|
||||
return processCapellaBlock(decoder, isBlinded)
|
||||
|
||||
case version.String(version.Deneb):
|
||||
return processDenebBlock(decoder, isBlinded)
|
||||
|
||||
case version.String(version.Electra):
|
||||
return processElectraBlock(decoder, isBlinded)
|
||||
|
||||
case version.String(version.Fulu):
|
||||
return processFuluBlock(decoder, isBlinded)
|
||||
|
||||
default:
|
||||
return nil, errors.Errorf("unsupported consensus version `%s`", ver)
|
||||
}
|
||||
if isBlinded && factory.newBlinded != nil {
|
||||
return convertBlockToGeneric(decoder, factory.newBlinded(), ver, true)
|
||||
}
|
||||
return convertBlockToGeneric(decoder, factory.newBlock(), ver, false)
|
||||
}
|
||||
|
||||
func processBellatrixBlock(decoder *json.Decoder, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
||||
if isBlinded {
|
||||
return convertBlockToGeneric(decoder, &structs.BlindedBeaconBlockBellatrix{}, "bellatrix", true)
|
||||
}
|
||||
return convertBlockToGeneric(decoder, &structs.BeaconBlockBellatrix{}, "bellatrix", false)
|
||||
}
|
||||
|
||||
func processCapellaBlock(decoder *json.Decoder, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
||||
if isBlinded {
|
||||
return convertBlockToGeneric(decoder, &structs.BlindedBeaconBlockCapella{}, "capella", true)
|
||||
}
|
||||
return convertBlockToGeneric(decoder, &structs.BeaconBlockCapella{}, "capella", false)
|
||||
}
|
||||
|
||||
func processDenebBlock(decoder *json.Decoder, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
||||
if isBlinded {
|
||||
return convertBlockToGeneric(decoder, &structs.BlindedBeaconBlockDeneb{}, "deneb", true)
|
||||
}
|
||||
return convertBlockToGeneric(decoder, &structs.BeaconBlockContentsDeneb{}, "deneb", false)
|
||||
}
|
||||
|
||||
func processElectraBlock(decoder *json.Decoder, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
||||
if isBlinded {
|
||||
return convertBlockToGeneric(decoder, &structs.BlindedBeaconBlockElectra{}, "electra", true)
|
||||
}
|
||||
return convertBlockToGeneric(decoder, &structs.BeaconBlockContentsElectra{}, "electra", false)
|
||||
}
|
||||
|
||||
func processFuluBlock(decoder *json.Decoder, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
|
||||
if isBlinded {
|
||||
return convertBlockToGeneric(decoder, &structs.BlindedBeaconBlockFulu{}, "fulu", true)
|
||||
}
|
||||
return convertBlockToGeneric(decoder, &structs.BeaconBlockContentsFulu{}, "fulu", false)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/OffchainLabs/prysm/v7/api/server/structs"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
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/validator/client/beacon-api/mock"
|
||||
@@ -26,7 +25,7 @@ func TestGetBeaconBlock_RequestFailed(t *testing.T) {
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
@@ -150,7 +149,7 @@ func TestGetBeaconBlock_Error(t *testing.T) {
|
||||
|
||||
b, err := json.Marshal(resp)
|
||||
require.NoError(t, err)
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
@@ -186,7 +185,7 @@ func TestGetBeaconBlock_Phase0Valid(t *testing.T) {
|
||||
Data: bytes,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
@@ -209,25 +208,6 @@ func TestGetBeaconBlock_Phase0Valid(t *testing.T) {
|
||||
assert.DeepEqual(t, expectedBeaconBlock, beaconBlock)
|
||||
}
|
||||
|
||||
func TestSSZCodecs_OrderAndCoverage(t *testing.T) {
|
||||
versions := version.All()
|
||||
require.NotEmpty(t, versions)
|
||||
|
||||
expected := make([]int, 0, len(versions))
|
||||
for i := len(versions) - 1; i >= 0; i-- {
|
||||
expected = append(expected, versions[i])
|
||||
}
|
||||
|
||||
require.Equal(t, len(expected), len(sszCodecs))
|
||||
|
||||
for i, entry := range sszCodecs {
|
||||
assert.Equal(t, expected[i], entry.minVersion, "sszCodecs[%d] has wrong fork order", i)
|
||||
if i > 0 {
|
||||
require.Equal(t, true, entry.minVersion < sszCodecs[i-1].minVersion, "sszCodecs not strictly descending at index %d", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add SSZ test cases below this line
|
||||
|
||||
func TestGetBeaconBlock_SSZ_BellatrixValid(t *testing.T) {
|
||||
@@ -244,7 +224,7 @@ func TestGetBeaconBlock_SSZ_BellatrixValid(t *testing.T) {
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
@@ -286,7 +266,7 @@ func TestGetBeaconBlock_SSZ_BlindedBellatrixValid(t *testing.T) {
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
@@ -328,7 +308,7 @@ func TestGetBeaconBlock_SSZ_CapellaValid(t *testing.T) {
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
@@ -370,7 +350,7 @@ func TestGetBeaconBlock_SSZ_BlindedCapellaValid(t *testing.T) {
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
@@ -412,7 +392,7 @@ func TestGetBeaconBlock_SSZ_DenebValid(t *testing.T) {
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
@@ -454,7 +434,7 @@ func TestGetBeaconBlock_SSZ_BlindedDenebValid(t *testing.T) {
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
@@ -496,7 +476,7 @@ func TestGetBeaconBlock_SSZ_ElectraValid(t *testing.T) {
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
@@ -538,7 +518,7 @@ func TestGetBeaconBlock_SSZ_BlindedElectraValid(t *testing.T) {
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
@@ -566,90 +546,6 @@ func TestGetBeaconBlock_SSZ_BlindedElectraValid(t *testing.T) {
|
||||
assert.DeepEqual(t, expectedBeaconBlock, beaconBlock)
|
||||
}
|
||||
|
||||
func TestGetBeaconBlock_SSZ_FuluValid(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
proto := testhelpers.GenerateProtoFuluBeaconBlockContents()
|
||||
bytes, err := proto.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
|
||||
const slot = primitives.Slot(1)
|
||||
randaoReveal := []byte{2}
|
||||
graffiti := []byte{3}
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
).Return(
|
||||
bytes,
|
||||
http.Header{
|
||||
"Content-Type": []string{api.OctetStreamMediaType},
|
||||
api.VersionHeader: []string{"fulu"},
|
||||
api.ExecutionPayloadBlindedHeader: []string{"false"},
|
||||
},
|
||||
nil,
|
||||
).Times(1)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{handler: handler}
|
||||
beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedBeaconBlock := ðpb.GenericBeaconBlock{
|
||||
Block: ðpb.GenericBeaconBlock_Fulu{
|
||||
Fulu: proto,
|
||||
},
|
||||
IsBlinded: false,
|
||||
}
|
||||
|
||||
assert.DeepEqual(t, expectedBeaconBlock, beaconBlock)
|
||||
}
|
||||
|
||||
func TestGetBeaconBlock_SSZ_BlindedFuluValid(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
proto := testhelpers.GenerateProtoBlindedFuluBeaconBlock()
|
||||
bytes, err := proto.MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
|
||||
const slot = primitives.Slot(1)
|
||||
randaoReveal := []byte{2}
|
||||
graffiti := []byte{3}
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
).Return(
|
||||
bytes,
|
||||
http.Header{
|
||||
"Content-Type": []string{api.OctetStreamMediaType},
|
||||
api.VersionHeader: []string{"fulu"},
|
||||
api.ExecutionPayloadBlindedHeader: []string{"true"},
|
||||
},
|
||||
nil,
|
||||
).Times(1)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{handler: handler}
|
||||
beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedBeaconBlock := ðpb.GenericBeaconBlock{
|
||||
Block: ðpb.GenericBeaconBlock_BlindedFulu{
|
||||
BlindedFulu: proto,
|
||||
},
|
||||
IsBlinded: true,
|
||||
}
|
||||
|
||||
assert.DeepEqual(t, expectedBeaconBlock, beaconBlock)
|
||||
}
|
||||
|
||||
func TestGetBeaconBlock_SSZ_UnsupportedVersion(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
@@ -660,7 +556,7 @@ func TestGetBeaconBlock_SSZ_UnsupportedVersion(t *testing.T) {
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
@@ -693,7 +589,7 @@ func TestGetBeaconBlock_SSZ_InvalidBlindedHeader(t *testing.T) {
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
@@ -726,7 +622,7 @@ func TestGetBeaconBlock_SSZ_InvalidVersionHeader(t *testing.T) {
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
@@ -755,7 +651,7 @@ func TestGetBeaconBlock_SSZ_GetSSZError(t *testing.T) {
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
@@ -784,7 +680,7 @@ func TestGetBeaconBlock_SSZ_Phase0Valid(t *testing.T) {
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
@@ -826,7 +722,7 @@ func TestGetBeaconBlock_SSZ_AltairValid(t *testing.T) {
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
@@ -874,7 +770,7 @@ func TestGetBeaconBlock_AltairValid(t *testing.T) {
|
||||
Data: bytes,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
@@ -918,7 +814,7 @@ func TestGetBeaconBlock_BellatrixValid(t *testing.T) {
|
||||
Data: bytes,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
@@ -963,7 +859,7 @@ func TestGetBeaconBlock_BlindedBellatrixValid(t *testing.T) {
|
||||
Data: bytes,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
@@ -1008,7 +904,7 @@ func TestGetBeaconBlock_CapellaValid(t *testing.T) {
|
||||
Data: bytes,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
@@ -1053,7 +949,7 @@ func TestGetBeaconBlock_BlindedCapellaValid(t *testing.T) {
|
||||
Data: bytes,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
@@ -1077,96 +973,6 @@ func TestGetBeaconBlock_BlindedCapellaValid(t *testing.T) {
|
||||
assert.DeepEqual(t, expectedBeaconBlock, beaconBlock)
|
||||
}
|
||||
|
||||
func TestGetBeaconBlock_FuluValid(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
proto := testhelpers.GenerateProtoFuluBeaconBlockContents()
|
||||
block := testhelpers.GenerateJsonFuluBeaconBlockContents()
|
||||
bytes, err := json.Marshal(block)
|
||||
require.NoError(t, err)
|
||||
|
||||
const slot = primitives.Slot(1)
|
||||
randaoReveal := []byte{2}
|
||||
graffiti := []byte{3}
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
b, err := json.Marshal(structs.ProduceBlockV3Response{
|
||||
Version: "fulu",
|
||||
ExecutionPayloadBlinded: false,
|
||||
Data: bytes,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
).Return(
|
||||
b,
|
||||
http.Header{"Content-Type": []string{"application/json"}},
|
||||
nil,
|
||||
).Times(1)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{handler: handler}
|
||||
beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedBeaconBlock := ðpb.GenericBeaconBlock{
|
||||
Block: ðpb.GenericBeaconBlock_Fulu{
|
||||
Fulu: proto,
|
||||
},
|
||||
IsBlinded: false,
|
||||
}
|
||||
|
||||
assert.DeepEqual(t, expectedBeaconBlock, beaconBlock)
|
||||
}
|
||||
|
||||
func TestGetBeaconBlock_BlindedFuluValid(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
proto := testhelpers.GenerateProtoBlindedFuluBeaconBlock()
|
||||
block := testhelpers.GenerateJsonBlindedFuluBeaconBlock()
|
||||
bytes, err := json.Marshal(block)
|
||||
require.NoError(t, err)
|
||||
|
||||
const slot = primitives.Slot(1)
|
||||
randaoReveal := []byte{2}
|
||||
graffiti := []byte{3}
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
b, err := json.Marshal(structs.ProduceBlockV3Response{
|
||||
Version: "fulu",
|
||||
ExecutionPayloadBlinded: true,
|
||||
Data: bytes,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
).Return(
|
||||
b,
|
||||
http.Header{"Content-Type": []string{"application/json"}},
|
||||
nil,
|
||||
).Times(1)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{handler: handler}
|
||||
beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedBeaconBlock := ðpb.GenericBeaconBlock{
|
||||
Block: ðpb.GenericBeaconBlock_BlindedFulu{
|
||||
BlindedFulu: proto,
|
||||
},
|
||||
IsBlinded: true,
|
||||
}
|
||||
|
||||
assert.DeepEqual(t, expectedBeaconBlock, beaconBlock)
|
||||
}
|
||||
|
||||
func TestGetBeaconBlock_DenebValid(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
@@ -1188,7 +994,7 @@ func TestGetBeaconBlock_DenebValid(t *testing.T) {
|
||||
Data: bytes,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
@@ -1233,7 +1039,7 @@ func TestGetBeaconBlock_BlindedDenebValid(t *testing.T) {
|
||||
Data: bytes,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
@@ -1278,7 +1084,7 @@ func TestGetBeaconBlock_ElectraValid(t *testing.T) {
|
||||
Data: bytes,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
@@ -1323,7 +1129,7 @@ func TestGetBeaconBlock_BlindedElectraValid(t *testing.T) {
|
||||
Data: bytes,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
handler := mock.NewMockJsonRestHandler(ctrl)
|
||||
handler.EXPECT().GetSSZ(
|
||||
gomock.Any(),
|
||||
fmt.Sprintf("/eth/v3/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)),
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/api/server/structs"
|
||||
"github.com/OffchainLabs/prysm/v7/encoding/ssz"
|
||||
"github.com/OffchainLabs/prysm/v7/network/httputil"
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
"github.com/pkg/errors"
|
||||
@@ -22,128 +21,34 @@ type blockProcessingResult struct {
|
||||
marshalJSON func() ([]byte, error)
|
||||
}
|
||||
|
||||
type sszMarshaler interface {
|
||||
MarshalSSZ() ([]byte, error)
|
||||
}
|
||||
|
||||
func buildBlockResult(
|
||||
versionName string,
|
||||
blinded bool,
|
||||
sszObj sszMarshaler,
|
||||
rootObj ssz.Hashable,
|
||||
jsonFn func() ([]byte, error),
|
||||
) (*blockProcessingResult, error) {
|
||||
beaconBlockRoot, err := rootObj.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to compute block root for %s beacon block", versionName)
|
||||
}
|
||||
|
||||
marshaledSSZ, err := sszObj.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to serialize %s beacon block", versionName)
|
||||
}
|
||||
|
||||
return &blockProcessingResult{
|
||||
consensusVersion: versionName,
|
||||
blinded: blinded,
|
||||
beaconBlockRoot: beaconBlockRoot,
|
||||
marshalledSSZ: marshaledSSZ,
|
||||
marshalJSON: jsonFn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) proposeBeaconBlock(ctx context.Context, in *ethpb.GenericSignedBeaconBlock) (*ethpb.ProposeResponse, error) {
|
||||
var res *blockProcessingResult
|
||||
var err error
|
||||
switch blockType := in.Block.(type) {
|
||||
case *ethpb.GenericSignedBeaconBlock_Phase0:
|
||||
res, err = buildBlockResult("phase0", false, blockType.Phase0, blockType.Phase0.Block, func() ([]byte, error) {
|
||||
return json.Marshal(structs.SignedBeaconBlockPhase0FromConsensus(blockType.Phase0))
|
||||
})
|
||||
res, err = handlePhase0Block(blockType)
|
||||
case *ethpb.GenericSignedBeaconBlock_Altair:
|
||||
res, err = buildBlockResult("altair", false, blockType.Altair, blockType.Altair.Block, func() ([]byte, error) {
|
||||
return json.Marshal(structs.SignedBeaconBlockAltairFromConsensus(blockType.Altair))
|
||||
})
|
||||
res, err = handleAltairBlock(blockType)
|
||||
case *ethpb.GenericSignedBeaconBlock_Bellatrix:
|
||||
res, err = buildBlockResult("bellatrix", false, blockType.Bellatrix, blockType.Bellatrix.Block, func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBeaconBlockBellatrixFromConsensus(blockType.Bellatrix)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert bellatrix beacon block")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
})
|
||||
res, err = handleBellatrixBlock(blockType)
|
||||
case *ethpb.GenericSignedBeaconBlock_BlindedBellatrix:
|
||||
res, err = buildBlockResult("bellatrix", true, blockType.BlindedBellatrix, blockType.BlindedBellatrix.Block, func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBlindedBeaconBlockBellatrixFromConsensus(blockType.BlindedBellatrix)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert blinded bellatrix beacon block")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
})
|
||||
res, err = handleBlindedBellatrixBlock(blockType)
|
||||
case *ethpb.GenericSignedBeaconBlock_Capella:
|
||||
res, err = buildBlockResult("capella", false, blockType.Capella, blockType.Capella.Block, func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBeaconBlockCapellaFromConsensus(blockType.Capella)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert capella beacon block")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
})
|
||||
res, err = handleCapellaBlock(blockType)
|
||||
case *ethpb.GenericSignedBeaconBlock_BlindedCapella:
|
||||
res, err = buildBlockResult("capella", true, blockType.BlindedCapella, blockType.BlindedCapella.Block, func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBlindedBeaconBlockCapellaFromConsensus(blockType.BlindedCapella)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert blinded capella beacon block")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
})
|
||||
res, err = handleBlindedCapellaBlock(blockType)
|
||||
case *ethpb.GenericSignedBeaconBlock_Deneb:
|
||||
res, err = buildBlockResult("deneb", false, blockType.Deneb, blockType.Deneb.Block, func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBeaconBlockContentsDenebFromConsensus(blockType.Deneb)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert deneb beacon block contents")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
})
|
||||
res, err = handleDenebBlockContents(blockType)
|
||||
case *ethpb.GenericSignedBeaconBlock_BlindedDeneb:
|
||||
res, err = buildBlockResult("deneb", true, blockType.BlindedDeneb, blockType.BlindedDeneb, func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBlindedBeaconBlockDenebFromConsensus(blockType.BlindedDeneb)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert deneb blinded beacon block")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
})
|
||||
res, err = handleBlindedDenebBlock(blockType)
|
||||
case *ethpb.GenericSignedBeaconBlock_Electra:
|
||||
res, err = buildBlockResult("electra", false, blockType.Electra, blockType.Electra.Block, func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBeaconBlockContentsElectraFromConsensus(blockType.Electra)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert electra beacon block contents")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
})
|
||||
res, err = handleElectraBlockContents(blockType)
|
||||
case *ethpb.GenericSignedBeaconBlock_BlindedElectra:
|
||||
res, err = buildBlockResult("electra", true, blockType.BlindedElectra, blockType.BlindedElectra, func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBlindedBeaconBlockElectraFromConsensus(blockType.BlindedElectra)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert electra blinded beacon block")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
})
|
||||
res, err = handleBlindedElectraBlock(blockType)
|
||||
case *ethpb.GenericSignedBeaconBlock_Fulu:
|
||||
res, err = buildBlockResult("fulu", false, blockType.Fulu, blockType.Fulu.Block, func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBeaconBlockContentsFuluFromConsensus(blockType.Fulu)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert fulu beacon block contents")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
})
|
||||
res, err = handleFuluBlockContents(blockType)
|
||||
case *ethpb.GenericSignedBeaconBlock_BlindedFulu:
|
||||
res, err = buildBlockResult("fulu", true, blockType.BlindedFulu, blockType.BlindedFulu, func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBlindedBeaconBlockFuluFromConsensus(blockType.BlindedFulu)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert fulu blinded beacon block")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
})
|
||||
res, err = handleBlindedFuluBlock(blockType)
|
||||
default:
|
||||
return nil, errors.Errorf("unsupported block type %T", in.Block)
|
||||
}
|
||||
@@ -211,3 +116,357 @@ func (c *beaconApiValidatorClient) proposeBeaconBlock(ctx context.Context, in *e
|
||||
|
||||
return ðpb.ProposeResponse{BlockRoot: res.beaconBlockRoot[:]}, nil
|
||||
}
|
||||
|
||||
func handlePhase0Block(block *ethpb.GenericSignedBeaconBlock_Phase0) (*blockProcessingResult, error) {
|
||||
var res blockProcessingResult
|
||||
res.consensusVersion = "phase0"
|
||||
res.blinded = false
|
||||
|
||||
beaconBlockRoot, err := block.Phase0.Block.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compute block root for phase0 beacon block")
|
||||
}
|
||||
res.beaconBlockRoot = beaconBlockRoot
|
||||
|
||||
// Marshal SSZ
|
||||
ssz, err := block.Phase0.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to serialize block for phase0 beacon block")
|
||||
}
|
||||
res.marshalledSSZ = ssz
|
||||
|
||||
// Set up JSON marshalling function for fallback
|
||||
res.marshalJSON = func() ([]byte, error) {
|
||||
signedBlock := structs.SignedBeaconBlockPhase0FromConsensus(block.Phase0)
|
||||
return json.Marshal(signedBlock)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func handleAltairBlock(block *ethpb.GenericSignedBeaconBlock_Altair) (*blockProcessingResult, error) {
|
||||
var res blockProcessingResult
|
||||
res.consensusVersion = "altair"
|
||||
res.blinded = false
|
||||
|
||||
beaconBlockRoot, err := block.Altair.Block.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compute block root for altair beacon block")
|
||||
}
|
||||
res.beaconBlockRoot = beaconBlockRoot
|
||||
|
||||
// Marshal SSZ
|
||||
ssz, err := block.Altair.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to serialize block for altair beacon block")
|
||||
}
|
||||
res.marshalledSSZ = ssz
|
||||
|
||||
// Set up JSON marshalling function for fallback
|
||||
res.marshalJSON = func() ([]byte, error) {
|
||||
signedBlock := structs.SignedBeaconBlockAltairFromConsensus(block.Altair)
|
||||
return json.Marshal(signedBlock)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func handleBellatrixBlock(block *ethpb.GenericSignedBeaconBlock_Bellatrix) (*blockProcessingResult, error) {
|
||||
var res blockProcessingResult
|
||||
res.consensusVersion = "bellatrix"
|
||||
res.blinded = false
|
||||
|
||||
beaconBlockRoot, err := block.Bellatrix.Block.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compute block root for bellatrix beacon block")
|
||||
}
|
||||
res.beaconBlockRoot = beaconBlockRoot
|
||||
|
||||
// Marshal SSZ
|
||||
ssz, err := block.Bellatrix.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to serialize block for bellatrix beacon block")
|
||||
}
|
||||
res.marshalledSSZ = ssz
|
||||
|
||||
// Set up JSON marshalling function for fallback
|
||||
res.marshalJSON = func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBeaconBlockBellatrixFromConsensus(block.Bellatrix)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert bellatrix beacon block")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func handleBlindedBellatrixBlock(block *ethpb.GenericSignedBeaconBlock_BlindedBellatrix) (*blockProcessingResult, error) {
|
||||
var res blockProcessingResult
|
||||
res.consensusVersion = "bellatrix"
|
||||
res.blinded = true
|
||||
|
||||
beaconBlockRoot, err := block.BlindedBellatrix.Block.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compute block root for bellatrix beacon block")
|
||||
}
|
||||
res.beaconBlockRoot = beaconBlockRoot
|
||||
|
||||
// Marshal SSZ
|
||||
ssz, err := block.BlindedBellatrix.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to serialize block for bellatrix beacon block")
|
||||
}
|
||||
res.marshalledSSZ = ssz
|
||||
|
||||
// Set up JSON marshalling function for fallback
|
||||
res.marshalJSON = func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBlindedBeaconBlockBellatrixFromConsensus(block.BlindedBellatrix)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert blinded bellatrix beacon block")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func handleCapellaBlock(block *ethpb.GenericSignedBeaconBlock_Capella) (*blockProcessingResult, error) {
|
||||
var res blockProcessingResult
|
||||
res.consensusVersion = "capella"
|
||||
res.blinded = false
|
||||
|
||||
beaconBlockRoot, err := block.Capella.Block.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compute block root for capella beacon block")
|
||||
}
|
||||
res.beaconBlockRoot = beaconBlockRoot
|
||||
|
||||
// Marshal SSZ
|
||||
ssz, err := block.Capella.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to serialize capella beacon block")
|
||||
}
|
||||
res.marshalledSSZ = ssz
|
||||
|
||||
// Set up JSON marshalling function for fallback
|
||||
res.marshalJSON = func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBeaconBlockCapellaFromConsensus(block.Capella)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert capella beacon block")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func handleBlindedCapellaBlock(block *ethpb.GenericSignedBeaconBlock_BlindedCapella) (*blockProcessingResult, error) {
|
||||
var res blockProcessingResult
|
||||
res.consensusVersion = "capella"
|
||||
res.blinded = true
|
||||
|
||||
beaconBlockRoot, err := block.BlindedCapella.Block.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compute block root for blinded capella beacon block")
|
||||
}
|
||||
res.beaconBlockRoot = beaconBlockRoot
|
||||
|
||||
// Marshal SSZ
|
||||
ssz, err := block.BlindedCapella.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to serialize blinded capella beacon block")
|
||||
}
|
||||
res.marshalledSSZ = ssz
|
||||
|
||||
// Set up JSON marshalling function for fallback
|
||||
res.marshalJSON = func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBlindedBeaconBlockCapellaFromConsensus(block.BlindedCapella)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert blinded capella beacon block")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func handleDenebBlockContents(block *ethpb.GenericSignedBeaconBlock_Deneb) (*blockProcessingResult, error) {
|
||||
var res blockProcessingResult
|
||||
res.consensusVersion = "deneb"
|
||||
res.blinded = false
|
||||
|
||||
beaconBlockRoot, err := block.Deneb.Block.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compute block root for deneb beacon block")
|
||||
}
|
||||
res.beaconBlockRoot = beaconBlockRoot
|
||||
|
||||
// Marshal SSZ
|
||||
ssz, err := block.Deneb.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to serialize deneb beacon block")
|
||||
}
|
||||
res.marshalledSSZ = ssz
|
||||
|
||||
// Set up JSON marshalling function for fallback
|
||||
res.marshalJSON = func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBeaconBlockContentsDenebFromConsensus(block.Deneb)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert deneb beacon block contents")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func handleBlindedDenebBlock(block *ethpb.GenericSignedBeaconBlock_BlindedDeneb) (*blockProcessingResult, error) {
|
||||
var res blockProcessingResult
|
||||
res.consensusVersion = "deneb"
|
||||
res.blinded = true
|
||||
|
||||
beaconBlockRoot, err := block.BlindedDeneb.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compute block root for deneb blinded beacon block")
|
||||
}
|
||||
res.beaconBlockRoot = beaconBlockRoot
|
||||
|
||||
// Marshal SSZ
|
||||
ssz, err := block.BlindedDeneb.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to serialize blinded deneb beacon block")
|
||||
}
|
||||
res.marshalledSSZ = ssz
|
||||
|
||||
// Set up JSON marshalling function for fallback
|
||||
res.marshalJSON = func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBlindedBeaconBlockDenebFromConsensus(block.BlindedDeneb)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert deneb blinded beacon block")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func handleElectraBlockContents(block *ethpb.GenericSignedBeaconBlock_Electra) (*blockProcessingResult, error) {
|
||||
var res blockProcessingResult
|
||||
res.consensusVersion = "electra"
|
||||
res.blinded = false
|
||||
|
||||
beaconBlockRoot, err := block.Electra.Block.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compute block root for electra beacon block")
|
||||
}
|
||||
res.beaconBlockRoot = beaconBlockRoot
|
||||
|
||||
// Marshal SSZ
|
||||
ssz, err := block.Electra.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to serialize electra beacon block")
|
||||
}
|
||||
res.marshalledSSZ = ssz
|
||||
|
||||
// Set up JSON marshalling function for fallback
|
||||
res.marshalJSON = func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBeaconBlockContentsElectraFromConsensus(block.Electra)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert electra beacon block contents")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func handleBlindedElectraBlock(block *ethpb.GenericSignedBeaconBlock_BlindedElectra) (*blockProcessingResult, error) {
|
||||
var res blockProcessingResult
|
||||
res.consensusVersion = "electra"
|
||||
res.blinded = true
|
||||
|
||||
beaconBlockRoot, err := block.BlindedElectra.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compute block root for electra blinded beacon block")
|
||||
}
|
||||
res.beaconBlockRoot = beaconBlockRoot
|
||||
|
||||
// Marshal SSZ
|
||||
ssz, err := block.BlindedElectra.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to serialize blinded electra beacon block")
|
||||
}
|
||||
res.marshalledSSZ = ssz
|
||||
|
||||
// Set up JSON marshalling function for fallback
|
||||
res.marshalJSON = func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBlindedBeaconBlockElectraFromConsensus(block.BlindedElectra)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert electra blinded beacon block")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func handleFuluBlockContents(block *ethpb.GenericSignedBeaconBlock_Fulu) (*blockProcessingResult, error) {
|
||||
var res blockProcessingResult
|
||||
res.consensusVersion = "fulu"
|
||||
res.blinded = false
|
||||
|
||||
beaconBlockRoot, err := block.Fulu.Block.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compute block root for fulu beacon block")
|
||||
}
|
||||
res.beaconBlockRoot = beaconBlockRoot
|
||||
|
||||
// Marshal SSZ
|
||||
ssz, err := block.Fulu.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to serialize fulu beacon block")
|
||||
}
|
||||
res.marshalledSSZ = ssz
|
||||
|
||||
// Set up JSON marshalling function for fallback
|
||||
res.marshalJSON = func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBeaconBlockContentsFuluFromConsensus(block.Fulu)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert fulu beacon block contents")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func handleBlindedFuluBlock(block *ethpb.GenericSignedBeaconBlock_BlindedFulu) (*blockProcessingResult, error) {
|
||||
var res blockProcessingResult
|
||||
res.consensusVersion = "fulu"
|
||||
res.blinded = true
|
||||
|
||||
beaconBlockRoot, err := block.BlindedFulu.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compute block root for fulu blinded beacon block")
|
||||
}
|
||||
res.beaconBlockRoot = beaconBlockRoot
|
||||
|
||||
// Marshal SSZ
|
||||
ssz, err := block.BlindedFulu.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to serialize blinded fulu beacon block")
|
||||
}
|
||||
res.marshalledSSZ = ssz
|
||||
|
||||
// Set up JSON marshalling function for fallback
|
||||
res.marshalJSON = func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBlindedBeaconBlockFuluFromConsensus(block.BlindedFulu)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert fulu blinded beacon block")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
@@ -620,42 +620,6 @@ func TestProposeBeaconBlock_SSZFails_406_FallbackToJSON(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestProposeBeaconBlock_SSZFails_406_JSONFallbackFails(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
ctx := t.Context()
|
||||
handler := mock.NewMockHandler(ctrl)
|
||||
|
||||
handler.EXPECT().PostSSZ(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blocks",
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(
|
||||
nil, nil, &httputil.DefaultJsonError{
|
||||
Code: http.StatusNotAcceptable,
|
||||
Message: "SSZ not supported",
|
||||
},
|
||||
).Times(1)
|
||||
|
||||
handler.EXPECT().Post(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blocks",
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
nil,
|
||||
).Return(
|
||||
errors.New("json fallback failed"),
|
||||
).Times(1)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{handler: handler}
|
||||
_, err := validatorClient.proposeBeaconBlock(ctx, ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedPhase0Block(),
|
||||
})
|
||||
assert.ErrorContains(t, "failed to submit block via JSON fallback", err)
|
||||
}
|
||||
|
||||
func TestProposeBeaconBlock_SSZFails_Non406_NoFallback(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
@@ -712,41 +676,3 @@ func TestProposeBeaconBlock_SSZFails_Non406_NoFallback(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type badHashable struct{}
|
||||
|
||||
func (badHashable) HashTreeRoot() ([32]byte, error) {
|
||||
return [32]byte{}, errors.New("hash root error")
|
||||
}
|
||||
|
||||
type badMarshaler struct{}
|
||||
|
||||
func (badMarshaler) MarshalSSZ() ([]byte, error) {
|
||||
return nil, errors.New("marshal ssz error")
|
||||
}
|
||||
|
||||
type okMarshaler struct{}
|
||||
|
||||
func (okMarshaler) MarshalSSZ() ([]byte, error) {
|
||||
return []byte{1, 2, 3}, nil
|
||||
}
|
||||
|
||||
type okHashable struct{}
|
||||
|
||||
func (okHashable) HashTreeRoot() ([32]byte, error) {
|
||||
return [32]byte{1}, nil
|
||||
}
|
||||
|
||||
func TestBuildBlockResult_HashTreeRootError(t *testing.T) {
|
||||
_, err := buildBlockResult("phase0", false, okMarshaler{}, badHashable{}, func() ([]byte, error) {
|
||||
return []byte(`{}`), nil
|
||||
})
|
||||
assert.ErrorContains(t, "failed to compute block root for phase0 beacon block", err)
|
||||
}
|
||||
|
||||
func TestBuildBlockResult_MarshalSSZError(t *testing.T) {
|
||||
_, err := buildBlockResult("phase0", false, badMarshaler{}, okHashable{}, func() ([]byte, error) {
|
||||
return []byte(`{}`), nil
|
||||
})
|
||||
assert.ErrorContains(t, "failed to serialize phase0 beacon block", err)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ go_library(
|
||||
"capella_beacon_block_test_helpers.go",
|
||||
"deneb_beacon_block_test_helpers.go",
|
||||
"electra_beacon_block_test_helpers.go",
|
||||
"fulu_beacon_block_test_helpers.go",
|
||||
"phase0_beacon_block_test_helpers.go",
|
||||
"test_helpers.go",
|
||||
],
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
package test_helpers
|
||||
|
||||
import (
|
||||
"github.com/OffchainLabs/prysm/v7/api/server/structs"
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
func GenerateProtoFuluBeaconBlockContents() *ethpb.BeaconBlockContentsFulu {
|
||||
electra := GenerateProtoElectraBeaconBlockContents()
|
||||
return ðpb.BeaconBlockContentsFulu{
|
||||
Block: electra.Block,
|
||||
KzgProofs: electra.KzgProofs,
|
||||
Blobs: electra.Blobs,
|
||||
}
|
||||
}
|
||||
|
||||
func GenerateProtoBlindedFuluBeaconBlock() *ethpb.BlindedBeaconBlockFulu {
|
||||
electra := GenerateProtoBlindedElectraBeaconBlock()
|
||||
return ðpb.BlindedBeaconBlockFulu{
|
||||
Slot: electra.Slot,
|
||||
ProposerIndex: electra.ProposerIndex,
|
||||
ParentRoot: electra.ParentRoot,
|
||||
StateRoot: electra.StateRoot,
|
||||
Body: electra.Body,
|
||||
}
|
||||
}
|
||||
|
||||
func GenerateJsonFuluBeaconBlockContents() *structs.BeaconBlockContentsFulu {
|
||||
electra := GenerateJsonElectraBeaconBlockContents()
|
||||
return &structs.BeaconBlockContentsFulu{
|
||||
Block: electra.Block,
|
||||
KzgProofs: electra.KzgProofs,
|
||||
Blobs: electra.Blobs,
|
||||
}
|
||||
}
|
||||
|
||||
func GenerateJsonBlindedFuluBeaconBlock() *structs.BlindedBeaconBlockFulu {
|
||||
electra := GenerateJsonBlindedElectraBeaconBlock()
|
||||
return &structs.BlindedBeaconBlockFulu{
|
||||
Slot: electra.Slot,
|
||||
ProposerIndex: electra.ProposerIndex,
|
||||
ParentRoot: electra.ParentRoot,
|
||||
StateRoot: electra.StateRoot,
|
||||
Body: electra.Body,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user