Compare commits

..

1 Commits

Author SHA1 Message Date
terence
b619a9e059 Add gossip for payload attestation 2026-02-04 20:02:29 -08:00
29 changed files with 1373 additions and 649 deletions

View File

@@ -27,6 +27,7 @@ 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",

View File

@@ -0,0 +1,19 @@
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.
// TODO: implement actual processing; for now this is a stub for sync integration.
func (s *Service) ReceivePayloadAttestationMessage(ctx context.Context, a *ethpb.PayloadAttestationMessage) error {
return nil
}

View File

@@ -757,6 +757,11 @@ 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

View File

@@ -17,6 +17,7 @@ go_library(
"error.go",
"interfaces.go",
"log.go",
"payload_attestation.go",
"payload_id.go",
"proposer_indices.go",
"proposer_indices_disabled.go", # keep

View File

@@ -0,0 +1,62 @@
package cache
import (
"errors"
"sync"
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
eth "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
)
var errNilPayloadAttestationMessage = errors.New("nil Payload Attestation Message")
// PayloadAttestationCache tracks seen payload attestation messages for a single block root.
type PayloadAttestationCache struct {
root [32]byte
seen map[uint64]struct{}
sync.Mutex
}
// Seen returns true if a vote for the given beacon block root has already been
// processed for this validator index.
func (p *PayloadAttestationCache) Seen(root [32]byte, idx uint64) bool {
p.Lock()
defer p.Unlock()
if p.root != root {
return false
}
if p.seen == nil {
return false
}
_, ok := p.seen[idx]
return ok
}
// Add marks the given payload attestation message as seen for this validator index.
// This function assumes that the message has already been validated.
func (p *PayloadAttestationCache) Add(att *eth.PayloadAttestationMessage, idx uint64) error {
if att == nil || att.Data == nil || len(att.Data.BeaconBlockRoot) == 0 {
return errNilPayloadAttestationMessage
}
root := bytesutil.ToBytes32(att.Data.BeaconBlockRoot)
p.Lock()
defer p.Unlock()
if p.root != root {
p.root = root
p.seen = make(map[uint64]struct{})
}
if p.seen == nil {
p.seen = make(map[uint64]struct{})
}
p.seen[idx] = struct{}{}
return nil
}
// Clear clears the internal cache.
func (p *PayloadAttestationCache) Clear() {
p.Lock()
defer p.Unlock()
p.root = [32]byte{}
p.seen = nil
}

View File

@@ -156,6 +156,11 @@ func payloadCommittee(ctx context.Context, st state.ReadOnlyBeaconState, slot pr
return selected, nil
}
// PayloadTimelinessCommittee returns the payload timeliness committee for a given slot.
func PayloadTimelinessCommittee(ctx context.Context, st state.ReadOnlyBeaconState, slot primitives.Slot) ([]primitives.ValidatorIndex, error) {
return payloadCommittee(ctx, st, slot)
}
// ptcSeed computes the seed for the payload timeliness committee.
func ptcSeed(st state.ReadOnlyBeaconState, epoch primitives.Epoch, slot primitives.Slot) ([32]byte, error) {
seed, err := helpers.Seed(st, epoch, params.BeaconConfig().DomainPTCAttester)

View File

@@ -25,6 +25,7 @@ var gossipTopicMappings = map[string]func() proto.Message{
LightClientOptimisticUpdateTopicFormat: func() proto.Message { return &ethpb.LightClientOptimisticUpdateAltair{} },
LightClientFinalityUpdateTopicFormat: func() proto.Message { return &ethpb.LightClientFinalityUpdateAltair{} },
DataColumnSubnetTopicFormat: func() proto.Message { return &ethpb.DataColumnSidecar{} },
PayloadAttestationMessageTopicFormat: func() proto.Message { return &ethpb.PayloadAttestationMessage{} },
}
// GossipTopicMappings is a function to return the assigned data type
@@ -144,4 +145,7 @@ func init() {
// Specially handle Fulu objects.
GossipTypeMapping[reflect.TypeFor[*ethpb.SignedBeaconBlockFulu]()] = BlockSubnetTopicFormat
// Payload attestation messages.
GossipTypeMapping[reflect.TypeFor[*ethpb.PayloadAttestationMessage]()] = PayloadAttestationMessageTopicFormat
}

View File

@@ -46,6 +46,8 @@ 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
//
@@ -75,6 +77,8 @@ 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.
@@ -148,6 +152,7 @@ func (s *Service) allTopics() []topic {
empty := [4]byte{0, 0, 0, 0} // empty digest for templates, replaced by real digests in per-fork copies.
templates := []topic{
newTopic(genesis, future, empty, GossipBlockMessage),
newTopic(genesis, future, empty, GossipPayloadAttestationMessage),
newTopic(genesis, future, empty, GossipAggregateAndProofMessage),
newTopic(genesis, future, empty, GossipExitMessage),
newTopic(genesis, future, empty, GossipProposerSlashingMessage),

View File

@@ -20,6 +20,7 @@ go_library(
"metrics.go",
"once.go",
"options.go",
"payload_attestations.go",
"pending_attestations_queue.go",
"pending_blocks_queue.go",
"rate_limiter.go",
@@ -113,6 +114,7 @@ go_library(
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/epbs/payload-attestation:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//consensus-types/wrapper:go_default_library",
@@ -177,6 +179,7 @@ go_test(
"fork_watcher_test.go",
"kzg_batch_verifier_test.go",
"once_test.go",
"payload_attestations_test.go",
"pending_attestations_queue_bucket_test.go",
"pending_attestations_queue_test.go",
"pending_blocks_queue_test.go",
@@ -263,6 +266,7 @@ go_test(
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/epbs/payload-attestation:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//consensus-types/wrapper:go_default_library",

View File

@@ -207,6 +207,13 @@ func WithTrackedValidatorsCache(c *cache.TrackedValidatorsCache) Option {
}
}
func WithPayloadAttestationCache(r *cache.PayloadAttestationCache) Option {
return func(s *Service) error {
s.payloadAttestationCache = r
return nil
}
}
// WithSlasherEnabled configures the sync package to support slashing detection.
func WithSlasherEnabled(enabled bool) Option {
return func(s *Service) error {

View File

@@ -0,0 +1,94 @@
package sync
import (
"context"
"github.com/OffchainLabs/prysm/v7/beacon-chain/p2p"
"github.com/OffchainLabs/prysm/v7/beacon-chain/verification"
payloadattestation "github.com/OffchainLabs/prysm/v7/consensus-types/epbs/payload-attestation"
"github.com/OffchainLabs/prysm/v7/monitoring/tracing"
"github.com/OffchainLabs/prysm/v7/monitoring/tracing/trace"
eth "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
)
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 {
tracing.AnnotateError(span, err)
return pubsub.ValidationReject, err
}
att, ok := m.(*eth.PayloadAttestationMessage)
if !ok {
return pubsub.ValidationReject, errWrongMessage
}
pa, err := payloadattestation.NewReadOnly(att)
if err != nil {
log.WithError(err).Error("Failed to create read only payload attestation")
return pubsub.ValidationIgnore, err
}
v := s.newPayloadAttestationVerifier(pa, verification.GossipPayloadAttestationMessageRequirements)
if err := v.VerifyCurrentSlot(); err != nil {
return pubsub.ValidationIgnore, err
}
if err := v.VerifyPayloadStatus(); err != nil {
return pubsub.ValidationReject, err
}
if err := v.VerifyBlockRootSeen(s.hasBadBlock); err != nil {
return pubsub.ValidationIgnore, err
}
if err := v.VerifyBlockRootValid(s.hasBadBlock); err != nil {
return pubsub.ValidationReject, err
}
st, err := s.cfg.chain.HeadState(ctx)
if err != nil {
return pubsub.ValidationIgnore, err
}
if err := v.VerifyValidatorInPTC(ctx, st); err != nil {
return pubsub.ValidationReject, err
}
if err := v.VerifySignature(st); err != nil {
return pubsub.ValidationReject, err
}
if s.payloadAttestationCache.Seen(pa.BeaconBlockRoot(), uint64(pa.ValidatorIndex())) {
return pubsub.ValidationIgnore, errAlreadySeenPayloadAttestation
}
msg.ValidatorData = att
return pubsub.ValidationAccept, nil
}
func (s *Service) payloadAttestationSubscriber(ctx context.Context, msg proto.Message) error {
a, ok := msg.(*eth.PayloadAttestationMessage)
if !ok {
return errWrongMessage
}
return s.cfg.chain.ReceivePayloadAttestationMessage(ctx, a)
}

View File

@@ -0,0 +1,172 @@
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/epbs/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(&ethpb.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("incorrect status"),
verifier: func(pa payloadattestation.ROMessage, reqs []verification.Requirement) verification.PayloadAttestationMsgVerifier {
return &verification.MockPayloadAttestation{ErrIncorrectPayloadAttStatus: errors.New("incorrect status")}
},
result: pubsub.ValidationReject,
},
{
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 &ethpb.PayloadAttestationMessage{
ValidatorIndex: 0,
Data: util.HydratePayloadAttestationData(&ethpb.PayloadAttestationData{Slot: 1}),
Signature: make([]byte, fieldparams.BLSSignatureLength),
}
}

View File

@@ -37,6 +37,7 @@ import (
"github.com/OffchainLabs/prysm/v7/cmd/beacon-chain/flags"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
payloadattestation "github.com/OffchainLabs/prysm/v7/consensus-types/epbs/payload-attestation"
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
leakybucket "github.com/OffchainLabs/prysm/v7/container/leaky-bucket"
@@ -121,6 +122,7 @@ type blockchainService interface {
blockchain.FinalizationFetcher
blockchain.ForkFetcher
blockchain.AttestationReceiver
blockchain.PayloadAttestationReceiver
blockchain.TimeFetcher
blockchain.GenesisFetcher
blockchain.CanonicalFetcher
@@ -173,6 +175,7 @@ type Service struct {
verifierWaiter *verification.InitializerWaiter
newBlobVerifier verification.NewBlobVerifier
newColumnsVerifier verification.NewDataColumnsVerifier
newPayloadAttestationVerifier verification.NewPayloadAttestationMsgVerifier
columnSidecarsExecSingleFlight singleflight.Group
reconstructionSingleFlight singleflight.Group
availableBlocker coverage.AvailableBlocker
@@ -182,6 +185,7 @@ type Service struct {
slasherEnabled bool
lcStore *lightClient.Store
dataColumnLogCh chan dataColumnLogEntry
payloadAttestationCache *cache.PayloadAttestationCache
digestActions perDigestSet
subscriptionSpawner func(func()) // see Service.spawn for details
}
@@ -190,15 +194,16 @@ 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(),
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{},
}
for _, opt := range opts {
@@ -250,6 +255,12 @@ 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)
@@ -259,6 +270,7 @@ func (s *Service) Start() {
}
s.newBlobVerifier = newBlobVerifierFromInitializer(v)
s.newColumnsVerifier = newDataColumnsVerifierFromInitializer(v)
s.newPayloadAttestationVerifier = newPayloadAttestationMessageFromInitializer(v)
go s.verifierRoutine()
go s.startDiscoveryAndSubscriptions()

View File

@@ -7,20 +7,25 @@ go_library(
"blob.go",
"cache.go",
"data_column.go",
"epbs.go",
"error.go",
"fake.go",
"filesystem.go",
"initializer.go",
"initializer_epbs.go",
"interface.go",
"log.go",
"metrics.go",
"mock.go",
"payload_attestation.go",
"payload_attestation_mock.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",
@@ -32,6 +37,7 @@ go_library(
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/epbs/payload-attestation:go_default_library",
"//consensus-types/primitives:go_default_library",
"//crypto/bls:go_default_library",
"//encoding/bytesutil:go_default_library",

View File

@@ -0,0 +1,24 @@
package verification
import (
"context"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
payloadattestation "github.com/OffchainLabs/prysm/v7/consensus-types/epbs/payload-attestation"
)
// PayloadAttestationMsgVerifier defines the methods implemented by the ROPayloadAttestation.
type PayloadAttestationMsgVerifier interface {
VerifyCurrentSlot() error
VerifyPayloadStatus() error
VerifyBlockRootSeen(func([32]byte) bool) error
VerifyBlockRootValid(func([32]byte) bool) error
VerifyValidatorInPTC(context.Context, state.BeaconState) error
VerifySignature(state.BeaconState) 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

View File

@@ -0,0 +1,15 @@
package verification
import (
payloadattestation "github.com/OffchainLabs/prysm/v7/consensus-types/epbs/payload-attestation"
)
// 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,
}
}

View File

@@ -0,0 +1,218 @@
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/epbs/payload-attestation"
"github.com/OffchainLabs/prysm/v7/crypto/bls"
"github.com/OffchainLabs/prysm/v7/time/slots"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// RequirementList defines a list of requirements.
type RequirementList []Requirement
const (
RequireCurrentSlot Requirement = iota
RequireMessageNotSeen
RequireKnownPayloadStatus
RequireValidatorInPTC
RequireBlockRootSeen
RequireBlockRootValid
RequireSignatureValid
)
// PayloadAttGossipRequirements defines the list of requirements for gossip payload attestation messages.
var PayloadAttGossipRequirements = []Requirement{
RequireCurrentSlot,
RequireMessageNotSeen,
RequireKnownPayloadStatus,
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")
ErrIncorrectPayloadAttStatus = errors.New("unknown payload att status")
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)
if v.pa.Slot() != v.clock.CurrentSlot() {
log.WithFields(logFields(v.pa)).Errorf("does not match current slot %d", v.clock.CurrentSlot())
return ErrIncorrectPayloadAttSlot
}
return nil
}
// VerifyPayloadStatus verifies if the payload status is known.
// In Gloas, payload status is represented by booleans, so there is no invalid enum to reject.
func (v *PayloadAttMsgVerifier) VerifyPayloadStatus() (err error) {
defer v.record(RequireKnownPayloadStatus, &err)
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(parentSeen func([32]byte) bool) (err error) {
defer v.record(RequireBlockRootSeen, &err)
if parentSeen != nil && parentSeen(v.pa.BeaconBlockRoot()) {
return nil
}
log.WithFields(logFields(v.pa)).Error(ErrPayloadAttBlockRootNotSeen.Error())
return ErrPayloadAttBlockRootNotSeen
}
// 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()) {
log.WithFields(logFields(v.pa)).Error(ErrPayloadAttBlockRootInvalid.Error())
return ErrPayloadAttBlockRootInvalid
}
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.BeaconState) (err error) {
defer v.record(RequireValidatorInPTC, &err)
ptc, err := gloas.PayloadTimelinessCommittee(ctx, st, v.pa.Slot())
if err != nil {
return err
}
idx := slices.Index(ptc, v.pa.ValidatorIndex())
if idx == -1 {
log.WithFields(logFields(v.pa)).Error(ErrIncorrectPayloadAttValidator.Error())
return ErrIncorrectPayloadAttValidator
}
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.BeaconState) (err error) {
defer v.record(RequireSignatureValid, &err)
err = validatePayloadAttestationMessageSignature(st, v.pa)
if err != nil {
if errors.Is(err, signing.ErrSigFailedToVerify) {
log.WithFields(logFields(v.pa)).Error("Signature failed to validate")
} else {
log.WithFields(logFields(v.pa)).WithError(err).Error("Could not validate signature")
}
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.BeaconState, payloadAtt payloadattestation.ROMessage) error {
val, err := st.ValidatorAtIndex(payloadAtt.ValidatorIndex())
if err != nil {
return err
}
pub, err := bls.PublicKeyFromBytes(val.PublicKey)
if err != nil {
return err
}
s := payloadAtt.Signature()
sig, err := bls.SignatureFromBytes(s[:])
if err != nil {
return err
}
currentEpoch := slots.ToEpoch(st.Slot())
domain, err := signing.Domain(st.Fork(), currentEpoch, params.BeaconConfig().DomainPTCAttester, st.GenesisValidatorsRoot())
if err != nil {
return err
}
root, err := payloadAtt.SigningRoot(domain)
if err != nil {
return err
}
if !sig.Verify(pub, root[:]) {
return 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)
}
// logFields returns log fields for a ROMessage instance.
func logFields(payload payloadattestation.ROMessage) logrus.Fields {
return logrus.Fields{
"slot": payload.Slot(),
"validatorIndex": payload.ValidatorIndex(),
"signature": fmt.Sprintf("%#x", payload.Signature()),
"beaconBlockRoot": fmt.Sprintf("%#x", payload.BeaconBlockRoot()),
"payloadPresent": payload.PayloadPresent(),
"blobDataAvailable": payload.BlobDataAvailable(),
}
}

View File

@@ -0,0 +1,51 @@
package verification
import (
"context"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
payloadattestation "github.com/OffchainLabs/prysm/v7/consensus-types/epbs/payload-attestation"
)
type MockPayloadAttestation struct {
ErrIncorrectPayloadAttSlot error
ErrIncorrectPayloadAttStatus 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) VerifyPayloadStatus() error {
return m.ErrIncorrectPayloadAttStatus
}
func (m *MockPayloadAttestation) VerifyValidatorInPTC(ctx context.Context, st state.BeaconState) 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.BeaconState) (err error) {
return m.ErrInvalidMessageSignature
}
func (m *MockPayloadAttestation) VerifiedPayloadAttestation() (payloadattestation.VerifiedROMessage, error) {
return payloadattestation.VerifiedROMessage{}, nil
}
func (m *MockPayloadAttestation) SatisfyRequirement(req Requirement) {}

View File

@@ -1,3 +0,0 @@
### Ignored
- improving maintainability and deduplication on get and post block parsing.

View File

@@ -0,0 +1,15 @@
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/epbs/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",
],
)

View File

@@ -0,0 +1,87 @@
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}
}

View File

@@ -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",

View File

@@ -66,13 +66,12 @@ func (c *beaconApiValidatorClient) duties(ctx context.Context, in *ethpb.DutiesR
}()
nextEpochDuties := &ethpb.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 &ethpb.ValidatorDutiesContainer{

View File

@@ -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 := &ethpb.BeaconBlockContentsFulu{}
if err := block.UnmarshalSSZ(data); err != nil {
return nil, err
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Fulu{Fulu: block}}, nil
},
unmarshalBlinded: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
blindedBlock := &ethpb.BlindedBeaconBlockFulu{}
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
return nil, err
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_BlindedFulu{BlindedFulu: blindedBlock}, IsBlinded: true}, nil
},
},
},
{
minVersion: version.Electra,
codec: sszBlockCodec{
unmarshalBlock: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
block := &ethpb.BeaconBlockContentsElectra{}
if err := block.UnmarshalSSZ(data); err != nil {
return nil, err
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Electra{Electra: block}}, nil
},
unmarshalBlinded: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
blindedBlock := &ethpb.BlindedBeaconBlockElectra{}
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
return nil, err
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_BlindedElectra{BlindedElectra: blindedBlock}, IsBlinded: true}, nil
},
},
},
{
minVersion: version.Deneb,
codec: sszBlockCodec{
unmarshalBlock: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
block := &ethpb.BeaconBlockContentsDeneb{}
if err := block.UnmarshalSSZ(data); err != nil {
return nil, err
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Deneb{Deneb: block}}, nil
},
unmarshalBlinded: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
blindedBlock := &ethpb.BlindedBeaconBlockDeneb{}
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
return nil, err
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_BlindedDeneb{BlindedDeneb: blindedBlock}, IsBlinded: true}, nil
},
},
},
{
minVersion: version.Capella,
codec: sszBlockCodec{
unmarshalBlock: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
block := &ethpb.BeaconBlockCapella{}
if err := block.UnmarshalSSZ(data); err != nil {
return nil, err
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Capella{Capella: block}}, nil
},
unmarshalBlinded: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
blindedBlock := &ethpb.BlindedBeaconBlockCapella{}
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
return nil, err
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_BlindedCapella{BlindedCapella: blindedBlock}, IsBlinded: true}, nil
},
},
},
{
minVersion: version.Bellatrix,
codec: sszBlockCodec{
unmarshalBlock: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
block := &ethpb.BeaconBlockBellatrix{}
if err := block.UnmarshalSSZ(data); err != nil {
return nil, err
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Bellatrix{Bellatrix: block}}, nil
},
unmarshalBlinded: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
blindedBlock := &ethpb.BlindedBeaconBlockBellatrix{}
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
return nil, err
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_BlindedBellatrix{BlindedBellatrix: blindedBlock}, IsBlinded: true}, nil
},
},
},
{
minVersion: version.Altair,
codec: sszBlockCodec{
unmarshalBlock: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
block := &ethpb.BeaconBlockAltair{}
if err := block.UnmarshalSSZ(data); err != nil {
return nil, err
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Altair{Altair: block}}, nil
},
},
},
{
minVersion: version.Phase0,
codec: sszBlockCodec{
unmarshalBlock: func(data []byte) (*ethpb.GenericBeaconBlock, error) {
block := &ethpb.BeaconBlock{}
if err := block.UnmarshalSSZ(data); err != nil {
return nil, err
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.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 := &ethpb.BeaconBlockAltair{}
if err := block.UnmarshalSSZ(data); err != nil {
return nil, err
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Altair{Altair: block}}, nil
}
if ver >= version.Phase0 {
block := &ethpb.BeaconBlock{}
if err := block.UnmarshalSSZ(data); err != nil {
return nil, err
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.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 := &ethpb.BlindedBeaconBlockFulu{}
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
return nil, err
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_BlindedFulu{BlindedFulu: blindedBlock}, IsBlinded: true}, nil
}
block := &ethpb.BeaconBlockContentsFulu{}
if err := block.UnmarshalSSZ(data); err != nil {
return nil, err
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Fulu{Fulu: block}}, nil
}
func processBlockSSZResponseElectra(data []byte, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
if isBlinded {
blindedBlock := &ethpb.BlindedBeaconBlockElectra{}
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
return nil, err
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_BlindedElectra{BlindedElectra: blindedBlock}, IsBlinded: true}, nil
}
block := &ethpb.BeaconBlockContentsElectra{}
if err := block.UnmarshalSSZ(data); err != nil {
return nil, err
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Electra{Electra: block}}, nil
}
func processBlockSSZResponseDeneb(data []byte, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
if isBlinded {
blindedBlock := &ethpb.BlindedBeaconBlockDeneb{}
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
return nil, err
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_BlindedDeneb{BlindedDeneb: blindedBlock}, IsBlinded: true}, nil
}
block := &ethpb.BeaconBlockContentsDeneb{}
if err := block.UnmarshalSSZ(data); err != nil {
return nil, err
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Deneb{Deneb: block}}, nil
}
func processBlockSSZResponseCapella(data []byte, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
if isBlinded {
blindedBlock := &ethpb.BlindedBeaconBlockCapella{}
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
return nil, err
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_BlindedCapella{BlindedCapella: blindedBlock}, IsBlinded: true}, nil
}
block := &ethpb.BeaconBlockCapella{}
if err := block.UnmarshalSSZ(data); err != nil {
return nil, err
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Capella{Capella: block}}, nil
}
func processBlockSSZResponseBellatrix(data []byte, isBlinded bool) (*ethpb.GenericBeaconBlock, error) {
if isBlinded {
blindedBlock := &ethpb.BlindedBeaconBlockBellatrix{}
if err := blindedBlock.UnmarshalSSZ(data); err != nil {
return nil, err
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_BlindedBellatrix{BlindedBellatrix: blindedBlock}, IsBlinded: true}, nil
}
block := &ethpb.BeaconBlockBellatrix{}
if err := block.UnmarshalSSZ(data); err != nil {
return nil, err
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.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)
}

View File

@@ -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 := &ethpb.GenericBeaconBlock{
Block: &ethpb.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 := &ethpb.GenericBeaconBlock{
Block: &ethpb.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 := &ethpb.GenericBeaconBlock{
Block: &ethpb.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 := &ethpb.GenericBeaconBlock{
Block: &ethpb.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)),

View File

@@ -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 &ethpb.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
}

View File

@@ -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, &ethpb.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)
}

View File

@@ -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",
],

View File

@@ -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 &ethpb.BeaconBlockContentsFulu{
Block: electra.Block,
KzgProofs: electra.KzgProofs,
Blobs: electra.Blobs,
}
}
func GenerateProtoBlindedFuluBeaconBlock() *ethpb.BlindedBeaconBlockFulu {
electra := GenerateProtoBlindedElectraBeaconBlock()
return &ethpb.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,
}
}