Compare commits

..

1 Commits

Author SHA1 Message Date
james-prysm
b3454853b5 initial events 2026-02-03 11:33:36 -06:00
27 changed files with 152 additions and 601 deletions

View File

@@ -2971,6 +2971,14 @@ func PayloadAttestationDataFromConsensus(d *eth.PayloadAttestationData) *Payload
}
}
func PayloadAttestationMessageFromConsensus(m *eth.PayloadAttestationMessage) *PayloadAttestationMessage {
return &PayloadAttestationMessage{
ValidatorIndex: fmt.Sprintf("%d", m.ValidatorIndex),
Data: PayloadAttestationDataFromConsensus(m.Data),
Signature: hexutil.Encode(m.Signature),
}
}
func (b *SignedBeaconBlockGloas) ToConsensus() (*eth.SignedBeaconBlockGloas, error) {
if b == nil {
return nil, errNilValue
@@ -3263,34 +3271,3 @@ func (d *PayloadAttestationData) ToConsensus() (*eth.PayloadAttestationData, err
BlobDataAvailable: d.BlobDataAvailable,
}, nil
}
func PayloadAttestationMessageFromConsensus(msg *eth.PayloadAttestationMessage) *PayloadAttestationMessage {
return &PayloadAttestationMessage{
ValidatorIndex: fmt.Sprintf("%d", msg.ValidatorIndex),
Data: PayloadAttestationDataFromConsensus(msg.Data),
Signature: hexutil.Encode(msg.Signature),
}
}
func (p *PayloadAttestationMessage) ToConsensus() (*eth.PayloadAttestationMessage, error) {
if p == nil {
return nil, errNilValue
}
validatorIndex, err := strconv.ParseUint(p.ValidatorIndex, 10, 64)
if err != nil {
return nil, server.NewDecodeError(err, "ValidatorIndex")
}
data, err := p.Data.ToConsensus()
if err != nil {
return nil, server.NewDecodeError(err, "Data")
}
sig, err := bytesutil.DecodeHexWithLength(p.Signature, fieldparams.BLSSignatureLength)
if err != nil {
return nil, server.NewDecodeError(err, "Signature")
}
return &eth.PayloadAttestationMessage{
ValidatorIndex: primitives.ValidatorIndex(validatorIndex),
Data: data,
Signature: sig,
}, nil
}

View File

@@ -188,11 +188,6 @@ type BLSToExecutionChangesPoolResponse struct {
Data []*SignedBLSToExecutionChange `json:"data"`
}
type GetPoolPayloadAttestationsResponse struct {
Version string `json:"version"`
Data []*PayloadAttestation `json:"data"`
}
type GetAttesterSlashingsResponse struct {
Version string `json:"version,omitempty"`
Data json.RawMessage `json:"data"` // Accepts both `[]*AttesterSlashing` and `[]*AttesterSlashingElectra` types

View File

@@ -112,3 +112,8 @@ type LightClientOptimisticUpdateEvent struct {
Version string `json:"version"`
Data *LightClientOptimisticUpdate `json:"data"`
}
type ExecutionPayloadAvailableEvent struct {
Slot string `json:"slot"`
BlockRoot string `json:"block_root"`
}

View File

@@ -31,11 +31,6 @@ type GetAttestationDataResponse struct {
Data *AttestationData `json:"data"`
}
type GetPayloadAttestationDataResponse struct {
Version string `json:"version"`
Data *PayloadAttestationData `json:"data"`
}
type ProduceSyncCommitteeContributionResponse struct {
Data *SyncCommitteeContribution `json:"data"`
}

View File

@@ -46,6 +46,14 @@ const (
// DataColumnReceived is sent after a data column has been seen after gossip validation rules.
DataColumnReceived = 12
// ExecutionPayloadBidReceived is sent after a signed execution payload bid is received from gossip or API
// that passes gossip validation on the execution_payload_bid topic.
ExecutionPayloadBidReceived = 13
// PayloadAttestationMessageReceived is sent after a payload attestation message is received
// that passes validation rules of the payload_attestation_message topic.
PayloadAttestationMessageReceived = 14
)
// UnAggregatedAttReceivedData is the data sent with UnaggregatedAttReceived events.
@@ -114,3 +122,13 @@ type DataColumnReceivedData struct {
BlockRoot [32]byte
KzgCommitments [][]byte
}
// ExecutionPayloadBidReceivedData is the data sent with ExecutionPayloadBidReceived events.
type ExecutionPayloadBidReceivedData struct {
SignedBid *ethpb.SignedExecutionPayloadBid
}
// PayloadAttestationMessageReceivedData is the data sent with PayloadAttestationMessageReceived events.
type PayloadAttestationMessageReceivedData struct {
PayloadAttestationMessage *ethpb.PayloadAttestationMessage
}

View File

@@ -33,6 +33,9 @@ const (
LightClientOptimisticUpdate
// PayloadAttributes events are fired upon a missed slot or new head.
PayloadAttributes
// ExecutionPayloadAvailable is sent when the node has verified that the execution payload
// and blobs for a block are available and ready for payload attestation.
ExecutionPayloadAvailable
)
// BlockProcessedData is the data sent with BlockProcessed events.
@@ -72,3 +75,11 @@ type InitializedData struct {
// GenesisValidatorsRoot represents state.validators.HashTreeRoot().
GenesisValidatorsRoot []byte
}
// ExecutionPayloadAvailableData is the data sent with ExecutionPayloadAvailable events.
type ExecutionPayloadAvailableData struct {
// Slot is the slot of the block whose execution payload became available.
Slot primitives.Slot
// BlockRoot is the root of the block whose execution payload became available.
BlockRoot [32]byte
}

View File

@@ -37,7 +37,6 @@ go_library(
"//beacon-chain/node/registration:go_default_library",
"//beacon-chain/operations/attestations:go_default_library",
"//beacon-chain/operations/blstoexec:go_default_library",
"//beacon-chain/operations/payloadattestation:go_default_library",
"//beacon-chain/operations/slashings:go_default_library",
"//beacon-chain/operations/synccommittee:go_default_library",
"//beacon-chain/operations/voluntaryexits:go_default_library",

View File

@@ -40,7 +40,6 @@ import (
"github.com/OffchainLabs/prysm/v7/beacon-chain/node/registration"
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/attestations"
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/blstoexec"
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/payloadattestation"
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/slashings"
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/synccommittee"
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/voluntaryexits"
@@ -102,7 +101,6 @@ type BeaconNode struct {
slashingsPool slashings.PoolManager
syncCommitteePool synccommittee.Pool
blsToExecPool blstoexec.PoolManager
payloadAttestationPool payloadattestation.PoolManager
depositCache cache.DepositCache
trackedValidatorsCache *cache.TrackedValidatorsCache
payloadIDCache *cache.PayloadIDCache
@@ -143,21 +141,20 @@ func New(cliCtx *cli.Context, cancel context.CancelFunc, opts ...Option) (*Beaco
ctx := cliCtx.Context
beacon := &BeaconNode{
cliCtx: cliCtx,
ctx: ctx,
cancel: cancel,
services: runtime.NewServiceRegistry(),
stop: make(chan struct{}),
stateFeed: new(event.Feed),
blockFeed: new(event.Feed),
opFeed: new(event.Feed),
attestationCache: cache.NewAttestationCache(),
attestationPool: attestations.NewPool(),
exitPool: voluntaryexits.NewPool(),
slashingsPool: slashings.NewPool(),
syncCommitteePool: synccommittee.NewPool(),
blsToExecPool: blstoexec.NewPool(),
// TODO payloadAttestationPool: set once a PoolManager implementation exists.
cliCtx: cliCtx,
ctx: ctx,
cancel: cancel,
services: runtime.NewServiceRegistry(),
stop: make(chan struct{}),
stateFeed: new(event.Feed),
blockFeed: new(event.Feed),
opFeed: new(event.Feed),
attestationCache: cache.NewAttestationCache(),
attestationPool: attestations.NewPool(),
exitPool: voluntaryexits.NewPool(),
slashingsPool: slashings.NewPool(),
syncCommitteePool: synccommittee.NewPool(),
blsToExecPool: blstoexec.NewPool(),
trackedValidatorsCache: cache.NewTrackedValidatorsCache(),
payloadIDCache: cache.NewPayloadIDCache(),
slasherBlockHeadersFeed: new(event.Feed),
@@ -975,7 +972,6 @@ func (b *BeaconNode) registerRPCService(router *http.ServeMux) error {
SlashingsPool: b.slashingsPool,
BLSChangesPool: b.blsToExecPool,
SyncCommitteeObjectPool: b.syncCommitteePool,
PayloadAttestationPool: b.payloadAttestationPool,
ExecutionChainService: web3Service,
ExecutionChainInfoFetcher: web3Service,
ChainStartFetcher: chainStartFetcher,

View File

@@ -1,12 +0,0 @@
load("@prysm//tools/go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["pool.go"],
importpath = "github.com/OffchainLabs/prysm/v7/beacon-chain/operations/payloadattestation",
visibility = ["//visibility:public"],
deps = [
"//consensus-types/primitives:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
],
)

View File

@@ -1,9 +0,0 @@
load("@prysm//tools/go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["mock.go"],
importpath = "github.com/OffchainLabs/prysm/v7/beacon-chain/operations/payloadattestation/mock",
visibility = ["//visibility:public"],
deps = ["//proto/prysm/v1alpha1:go_default_library"],
)

View File

@@ -1,28 +0,0 @@
package mock
import (
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
)
// PoolMock is a fake implementation of PoolManager.
type PoolMock struct {
Attestations []*ethpb.PayloadAttestation
}
// PendingPayloadAttestations --
func (m *PoolMock) PendingPayloadAttestations() []*ethpb.PayloadAttestation {
return m.Attestations
}
// InsertPayloadAttestation --
func (m *PoolMock) InsertPayloadAttestation(msg *ethpb.PayloadAttestationMessage) error {
m.Attestations = append(m.Attestations, &ethpb.PayloadAttestation{
Data: msg.Data,
Signature: msg.Signature,
})
return nil
}
// MarkIncluded --
func (*PoolMock) MarkIncluded(_ *ethpb.PayloadAttestation) {
}

View File

@@ -1,20 +0,0 @@
package payloadattestation
import (
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
)
// PoolManager maintains pending payload attestations.
// This pool is used by proposers to insert payload attestations into new blocks.
type PoolManager interface {
PendingPayloadAttestations() []*ethpb.PayloadAttestation
InsertPayloadAttestation(msg *ethpb.PayloadAttestationMessage) error
MarkIncluded(att *ethpb.PayloadAttestation)
}
// PayloadStatusFetcher determines the payload presence and blob data availability
// for a given slot. This is used by PTC validators to produce PayloadAttestationData.
type PayloadStatusFetcher interface {
PayloadStatus(slot primitives.Slot) (payloadPresent bool, blobDataAvailable bool, err error)
}

View File

@@ -26,7 +26,6 @@ go_library(
"//beacon-chain/light-client:go_default_library",
"//beacon-chain/operations/attestations:go_default_library",
"//beacon-chain/operations/blstoexec:go_default_library",
"//beacon-chain/operations/payloadattestation:go_default_library",
"//beacon-chain/operations/slashings:go_default_library",
"//beacon-chain/operations/synccommittee:go_default_library",
"//beacon-chain/operations/voluntaryexits:go_default_library",

View File

@@ -217,7 +217,6 @@ func (s *Service) validatorEndpoints(
OperationNotifier: s.cfg.OperationNotifier,
TrackedValidatorsCache: s.cfg.TrackedValidatorsCache,
PayloadIDCache: s.cfg.PayloadIDCache,
PayloadAttestationPool: s.cfg.PayloadAttestationPool,
CoreService: coreService,
BlockRewardFetcher: rewardFetcher,
}
@@ -391,16 +390,6 @@ func (s *Service) validatorEndpoints(
handler: server.SyncCommitteeSelections,
methods: []string{http.MethodPost},
},
{
template: "/eth/v1/validator/payload_attestation_data/{slot}",
name: namespace + ".GetPayloadAttestationData",
middleware: []middleware.Middleware{
middleware.AcceptHeaderHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}),
middleware.AcceptEncodingHeaderHandler(),
},
handler: server.GetPayloadAttestationData,
methods: []string{http.MethodGet},
},
}
}
@@ -523,7 +512,6 @@ func (s *Service) beaconEndpoints(
SyncChecker: s.cfg.SyncService,
ExecutionReconstructor: s.cfg.ExecutionReconstructor,
BLSChangesPool: s.cfg.BLSChangesPool,
PayloadAttestationPool: s.cfg.PayloadAttestationPool,
FinalizationFetcher: s.cfg.FinalizationFetcher,
ForkchoiceFetcher: s.cfg.ForkchoiceFetcher,
CoreService: coreService,
@@ -881,27 +869,6 @@ func (s *Service) beaconEndpoints(
handler: server.GetProposerLookahead,
methods: []string{http.MethodGet},
},
{
template: "/eth/v1/beacon/pool/payload_attestations",
name: namespace + ".ListPayloadAttestations",
middleware: []middleware.Middleware{
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
middleware.AcceptEncodingHeaderHandler(),
},
handler: server.ListPayloadAttestations,
methods: []string{http.MethodGet},
},
{
template: "/eth/v1/beacon/pool/payload_attestations",
name: namespace + ".SubmitPayloadAttestations",
middleware: []middleware.Middleware{
middleware.ContentTypeHandler([]string{api.JsonMediaType}),
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
middleware.AcceptEncodingHeaderHandler(),
},
handler: server.SubmitPayloadAttestations,
methods: []string{http.MethodPost},
},
}
}

View File

@@ -48,7 +48,6 @@ func Test_endpoints(t *testing.T) {
"/eth/v1/beacon/pool/sync_committees": {http.MethodPost},
"/eth/v1/beacon/pool/voluntary_exits": {http.MethodGet, http.MethodPost},
"/eth/v1/beacon/pool/bls_to_execution_changes": {http.MethodGet, http.MethodPost},
"/eth/v1/beacon/pool/payload_attestations": {http.MethodGet, http.MethodPost},
"/prysm/v1/beacon/individual_votes": {http.MethodPost},
}
@@ -92,23 +91,22 @@ func Test_endpoints(t *testing.T) {
}
validatorRoutes := map[string][]string{
"/eth/v1/validator/duties/attester/{epoch}": {http.MethodPost},
"/eth/v1/validator/duties/proposer/{epoch}": {http.MethodGet},
"/eth/v1/validator/duties/sync/{epoch}": {http.MethodPost},
"/eth/v3/validator/blocks/{slot}": {http.MethodGet},
"/eth/v1/validator/attestation_data": {http.MethodGet},
"/eth/v2/validator/aggregate_attestation": {http.MethodGet},
"/eth/v2/validator/aggregate_and_proofs": {http.MethodPost},
"/eth/v1/validator/beacon_committee_subscriptions": {http.MethodPost},
"/eth/v1/validator/sync_committee_subscriptions": {http.MethodPost},
"/eth/v1/validator/beacon_committee_selections": {http.MethodPost},
"/eth/v1/validator/sync_committee_selections": {http.MethodPost},
"/eth/v1/validator/sync_committee_contribution": {http.MethodGet},
"/eth/v1/validator/contribution_and_proofs": {http.MethodPost},
"/eth/v1/validator/prepare_beacon_proposer": {http.MethodPost},
"/eth/v1/validator/register_validator": {http.MethodPost},
"/eth/v1/validator/liveness/{epoch}": {http.MethodPost},
"/eth/v1/validator/payload_attestation_data/{slot}": {http.MethodGet},
"/eth/v1/validator/duties/attester/{epoch}": {http.MethodPost},
"/eth/v1/validator/duties/proposer/{epoch}": {http.MethodGet},
"/eth/v1/validator/duties/sync/{epoch}": {http.MethodPost},
"/eth/v3/validator/blocks/{slot}": {http.MethodGet},
"/eth/v1/validator/attestation_data": {http.MethodGet},
"/eth/v2/validator/aggregate_attestation": {http.MethodGet},
"/eth/v2/validator/aggregate_and_proofs": {http.MethodPost},
"/eth/v1/validator/beacon_committee_subscriptions": {http.MethodPost},
"/eth/v1/validator/sync_committee_subscriptions": {http.MethodPost},
"/eth/v1/validator/beacon_committee_selections": {http.MethodPost},
"/eth/v1/validator/sync_committee_selections": {http.MethodPost},
"/eth/v1/validator/sync_committee_contribution": {http.MethodGet},
"/eth/v1/validator/contribution_and_proofs": {http.MethodPost},
"/eth/v1/validator/prepare_beacon_proposer": {http.MethodPost},
"/eth/v1/validator/register_validator": {http.MethodPost},
"/eth/v1/validator/liveness/{epoch}": {http.MethodPost},
}
prysmBeaconRoutes := map[string][]string{

View File

@@ -32,7 +32,6 @@ go_library(
"//beacon-chain/execution:go_default_library",
"//beacon-chain/operations/attestations:go_default_library",
"//beacon-chain/operations/blstoexec:go_default_library",
"//beacon-chain/operations/payloadattestation:go_default_library",
"//beacon-chain/operations/slashings:go_default_library",
"//beacon-chain/operations/voluntaryexits:go_default_library",
"//beacon-chain/p2p:go_default_library",
@@ -95,7 +94,6 @@ go_test(
"//beacon-chain/operations/attestations:go_default_library",
"//beacon-chain/operations/blstoexec:go_default_library",
"//beacon-chain/operations/blstoexec/mock:go_default_library",
"//beacon-chain/operations/payloadattestation/mock:go_default_library",
"//beacon-chain/operations/slashings/mock:go_default_library",
"//beacon-chain/operations/synccommittee:go_default_library",
"//beacon-chain/operations/voluntaryexits/mock:go_default_library",

View File

@@ -893,99 +893,3 @@ func (s *Server) SubmitProposerSlashing(w http.ResponseWriter, r *http.Request)
}
}
}
// SubmitPayloadAttestations submits payload attestation messages to the node's pool.
func (s *Server) SubmitPayloadAttestations(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.SubmitPayloadAttestations")
defer span.End()
currentEpoch := slots.ToEpoch(s.TimeFetcher.CurrentSlot())
if currentEpoch < params.BeaconConfig().GloasForkEpoch {
httputil.HandleError(w, fmt.Sprintf("payload attestations require the Gloas fork, current epoch %d, Gloas epoch %d", currentEpoch, params.BeaconConfig().GloasForkEpoch), http.StatusBadRequest)
return
}
if shared.IsSyncing(ctx, w, s.SyncChecker, s.HeadFetcher, s.TimeFetcher, s.OptimisticModeFetcher) {
return
}
versionHeader := r.Header.Get(api.VersionHeader)
if versionHeader == "" {
httputil.HandleError(w, api.VersionHeader+" header is required", http.StatusBadRequest)
return
}
var msgs []*structs.PayloadAttestationMessage
if err := json.NewDecoder(r.Body).Decode(&msgs); err != nil {
httputil.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest)
return
}
var failures []*server.IndexedError
for i, msg := range msgs {
consensusMsg, err := msg.ToConsensus()
if err != nil {
failures = append(failures, &server.IndexedError{
Index: i,
Message: "Could not convert message: " + err.Error(),
})
continue
}
// TODO: Add full gossip validation (BLS signatures, PTC membership).
if err := s.PayloadAttestationPool.InsertPayloadAttestation(consensusMsg); err != nil {
failures = append(failures, &server.IndexedError{
Index: i,
Message: "Could not insert payload attestation: " + err.Error(),
})
continue
}
if err := s.Broadcaster.Broadcast(ctx, consensusMsg); err != nil {
log.WithError(err).Error("Could not broadcast payload attestation message")
}
}
if len(failures) > 0 {
failuresErr := &server.IndexedErrorContainer{
Code: http.StatusBadRequest,
Message: server.ErrIndexedValidationFail,
Failures: failures,
}
httputil.WriteError(w, failuresErr)
return
}
}
// ListPayloadAttestations retrieves payload attestations from the pool.
func (s *Server) ListPayloadAttestations(w http.ResponseWriter, r *http.Request) {
_, span := trace.StartSpan(r.Context(), "beacon.ListPayloadAttestations")
defer span.End()
currentEpoch := slots.ToEpoch(s.TimeFetcher.CurrentSlot())
if currentEpoch < params.BeaconConfig().GloasForkEpoch {
httputil.HandleError(w, fmt.Sprintf("payload attestations require the Gloas fork, current epoch %d, Gloas epoch %d", currentEpoch, params.BeaconConfig().GloasForkEpoch), http.StatusBadRequest)
return
}
rawSlot, slot, ok := shared.UintFromQuery(w, r, "slot", false)
if !ok {
return
}
allAtts := s.PayloadAttestationPool.PendingPayloadAttestations()
var data []*structs.PayloadAttestation
for _, att := range allAtts {
if rawSlot != "" && att.Data.Slot != primitives.Slot(slot) {
continue
}
data = append(data, structs.PayloadAttestationFromConsensus(att))
}
w.Header().Set(api.VersionHeader, version.String(version.Gloas))
httputil.WriteJson(w, &structs.GetPoolPayloadAttestationsResponse{
Version: version.String(version.Gloas),
Data: data,
})
}

View File

@@ -21,7 +21,6 @@ import (
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/attestations"
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/blstoexec"
blstoexecmock "github.com/OffchainLabs/prysm/v7/beacon-chain/operations/blstoexec/mock"
payloadattestationmock "github.com/OffchainLabs/prysm/v7/beacon-chain/operations/payloadattestation/mock"
slashingsmock "github.com/OffchainLabs/prysm/v7/beacon-chain/operations/slashings/mock"
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/synccommittee"
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/voluntaryexits/mock"
@@ -2548,253 +2547,3 @@ var (
}
}`
)
func TestSubmitPayloadAttestations(t *testing.T) {
t.Run("pre-gloas fork returns error", func(t *testing.T) {
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig().Copy()
cfg.GloasForkEpoch = 100
params.OverrideBeaconConfig(cfg)
slot := primitives.Slot(0)
chainService := &blockchainmock.ChainService{Slot: &slot}
s := &Server{
TimeFetcher: chainService,
}
request := httptest.NewRequest(http.MethodPost, "http://example.com", nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.SubmitPayloadAttestations(writer, request)
assert.Equal(t, http.StatusBadRequest, writer.Code)
assert.StringContains(t, "Gloas fork", writer.Body.String())
})
t.Run("no version header", func(t *testing.T) {
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig().Copy()
cfg.GloasForkEpoch = 0
params.OverrideBeaconConfig(cfg)
slot := primitives.Slot(0)
chainService := &blockchainmock.ChainService{Slot: &slot}
s := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: false},
HeadFetcher: chainService,
TimeFetcher: chainService,
OptimisticModeFetcher: chainService,
}
request := httptest.NewRequest(http.MethodPost, "http://example.com", nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.SubmitPayloadAttestations(writer, request)
assert.Equal(t, http.StatusBadRequest, writer.Code)
assert.StringContains(t, "Eth-Consensus-Version", writer.Body.String())
})
t.Run("ok", func(t *testing.T) {
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig().Copy()
cfg.GloasForkEpoch = 0
params.OverrideBeaconConfig(cfg)
slot := primitives.Slot(1)
chainService := &blockchainmock.ChainService{Slot: &slot}
broadcaster := &p2pMock.MockBroadcaster{}
pool := &payloadattestationmock.PoolMock{}
s := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: false},
HeadFetcher: chainService,
TimeFetcher: chainService,
OptimisticModeFetcher: chainService,
Broadcaster: broadcaster,
PayloadAttestationPool: pool,
}
body := `[{
"validator_index": "1",
"data": {
"beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"slot": "1",
"payload_present": true,
"blob_data_available": true
},
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
}]`
request := httptest.NewRequest(http.MethodPost, "http://example.com", strings.NewReader(body))
request.Header.Set(api.VersionHeader, "gloas")
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.SubmitPayloadAttestations(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
assert.Equal(t, 1, len(pool.Attestations))
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
})
t.Run("invalid body", func(t *testing.T) {
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig().Copy()
cfg.GloasForkEpoch = 0
params.OverrideBeaconConfig(cfg)
slot := primitives.Slot(1)
chainService := &blockchainmock.ChainService{Slot: &slot}
s := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: false},
HeadFetcher: chainService,
TimeFetcher: chainService,
OptimisticModeFetcher: chainService,
}
request := httptest.NewRequest(http.MethodPost, "http://example.com", strings.NewReader("invalid"))
request.Header.Set(api.VersionHeader, "gloas")
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.SubmitPayloadAttestations(writer, request)
assert.Equal(t, http.StatusBadRequest, writer.Code)
})
}
func TestListPayloadAttestations(t *testing.T) {
t.Run("pre-gloas fork returns error", func(t *testing.T) {
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig().Copy()
cfg.GloasForkEpoch = 100
params.OverrideBeaconConfig(cfg)
slot := primitives.Slot(0)
chainService := &blockchainmock.ChainService{Slot: &slot}
s := &Server{
TimeFetcher: chainService,
}
request := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.ListPayloadAttestations(writer, request)
assert.Equal(t, http.StatusBadRequest, writer.Code)
assert.StringContains(t, "Gloas fork", writer.Body.String())
})
t.Run("empty pool", func(t *testing.T) {
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig().Copy()
cfg.GloasForkEpoch = 0
params.OverrideBeaconConfig(cfg)
slot := primitives.Slot(0)
chainService := &blockchainmock.ChainService{Slot: &slot}
pool := &payloadattestationmock.PoolMock{}
s := &Server{
TimeFetcher: chainService,
PayloadAttestationPool: pool,
}
request := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.ListPayloadAttestations(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
resp := &structs.GetPoolPayloadAttestationsResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, 0, len(resp.Data))
})
t.Run("returns attestations", func(t *testing.T) {
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig().Copy()
cfg.GloasForkEpoch = 0
params.OverrideBeaconConfig(cfg)
slot := primitives.Slot(0)
chainService := &blockchainmock.ChainService{Slot: &slot}
pool := &payloadattestationmock.PoolMock{
Attestations: []*ethpbv1alpha1.PayloadAttestation{
{
Data: &ethpbv1alpha1.PayloadAttestationData{
BeaconBlockRoot: bytesutil.PadTo([]byte("root1"), 32),
Slot: 1,
PayloadPresent: true,
BlobDataAvailable: true,
},
Signature: bytesutil.PadTo([]byte("sig1"), 96),
},
{
Data: &ethpbv1alpha1.PayloadAttestationData{
BeaconBlockRoot: bytesutil.PadTo([]byte("root2"), 32),
Slot: 2,
PayloadPresent: false,
BlobDataAvailable: false,
},
Signature: bytesutil.PadTo([]byte("sig2"), 96),
},
},
}
s := &Server{
TimeFetcher: chainService,
PayloadAttestationPool: pool,
}
request := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.ListPayloadAttestations(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
resp := &structs.GetPoolPayloadAttestationsResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, 2, len(resp.Data))
assert.Equal(t, "gloas", resp.Version)
})
t.Run("filter by slot", func(t *testing.T) {
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig().Copy()
cfg.GloasForkEpoch = 0
params.OverrideBeaconConfig(cfg)
slot := primitives.Slot(0)
chainService := &blockchainmock.ChainService{Slot: &slot}
pool := &payloadattestationmock.PoolMock{
Attestations: []*ethpbv1alpha1.PayloadAttestation{
{
Data: &ethpbv1alpha1.PayloadAttestationData{
BeaconBlockRoot: bytesutil.PadTo([]byte("root1"), 32),
Slot: 1,
PayloadPresent: true,
BlobDataAvailable: true,
},
Signature: bytesutil.PadTo([]byte("sig1"), 96),
},
{
Data: &ethpbv1alpha1.PayloadAttestationData{
BeaconBlockRoot: bytesutil.PadTo([]byte("root2"), 32),
Slot: 2,
PayloadPresent: false,
BlobDataAvailable: false,
},
Signature: bytesutil.PadTo([]byte("sig2"), 96),
},
},
}
s := &Server{
TimeFetcher: chainService,
PayloadAttestationPool: pool,
}
request := httptest.NewRequest(http.MethodGet, "http://example.com?slot=1", nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.ListPayloadAttestations(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
resp := &structs.GetPoolPayloadAttestationsResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, 1, len(resp.Data))
assert.Equal(t, "1", resp.Data[0].Data.Slot)
})
}

View File

@@ -12,7 +12,6 @@ import (
"github.com/OffchainLabs/prysm/v7/beacon-chain/execution"
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/attestations"
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/blstoexec"
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/payloadattestation"
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/slashings"
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/voluntaryexits"
"github.com/OffchainLabs/prysm/v7/beacon-chain/p2p"
@@ -49,7 +48,6 @@ type Server struct {
ExecutionReconstructor execution.Reconstructor
FinalizationFetcher blockchain.FinalizationFetcher
BLSChangesPool blstoexec.PoolManager
PayloadAttestationPool payloadattestation.PoolManager
ForkchoiceFetcher blockchain.ForkchoiceFetcher
CoreService *core.Service
AttestationStateFetcher blockchain.AttestationStateFetcher

View File

@@ -74,6 +74,12 @@ const (
LightClientOptimisticUpdateTopic = "light_client_optimistic_update"
// DataColumnTopic represents a data column sidecar event topic
DataColumnTopic = "data_column_sidecar"
// ExecutionPayloadAvailableTopic represents an event indicating execution payload and blobs are available.
ExecutionPayloadAvailableTopic = "execution_payload_available"
// ExecutionPayloadBidTopic represents an event for a signed execution payload bid passing gossip validation.
ExecutionPayloadBidTopic = "execution_payload_bid"
// PayloadAttestationMessageTopic represents an event for a payload attestation message passing validation.
PayloadAttestationMessageTopic = "payload_attestation_message"
)
var (
@@ -108,6 +114,8 @@ var opsFeedEventTopics = map[feed.EventType]string{
operation.ProposerSlashingReceived: ProposerSlashingTopic,
operation.BlockGossipReceived: BlockGossipTopic,
operation.DataColumnReceived: DataColumnTopic,
operation.ExecutionPayloadBidReceived: ExecutionPayloadBidTopic,
operation.PayloadAttestationMessageReceived: PayloadAttestationMessageTopic,
}
var stateFeedEventTopics = map[feed.EventType]string{
@@ -118,6 +126,7 @@ var stateFeedEventTopics = map[feed.EventType]string{
statefeed.Reorg: ChainReorgTopic,
statefeed.BlockProcessed: BlockTopic,
statefeed.PayloadAttributes: PayloadAttributesTopic,
statefeed.ExecutionPayloadAvailable: ExecutionPayloadAvailableTopic,
}
var topicsForStateFeed = topicsForFeed(stateFeedEventTopics)
@@ -466,6 +475,12 @@ func topicForEvent(event *feed.Event) string {
return PayloadAttributesTopic
case *operation.DataColumnReceivedData:
return DataColumnTopic
case *operation.ExecutionPayloadBidReceivedData:
return ExecutionPayloadBidTopic
case *operation.PayloadAttestationMessageReceivedData:
return PayloadAttestationMessageTopic
case *statefeed.ExecutionPayloadAvailableData:
return ExecutionPayloadAvailableTopic
default:
return InvalidTopic
}
@@ -638,6 +653,21 @@ func (s *Server) lazyReaderForEvent(ctx context.Context, event *feed.Event, topi
}
return jsonMarshalReader(eventName, blk)
}, nil
case *statefeed.ExecutionPayloadAvailableData:
return func() io.Reader {
return jsonMarshalReader(eventName, &structs.ExecutionPayloadAvailableEvent{
Slot: fmt.Sprintf("%d", v.Slot),
BlockRoot: hexutil.Encode(v.BlockRoot[:]),
})
}, nil
case *operation.ExecutionPayloadBidReceivedData:
return func() io.Reader {
return jsonMarshalReader(eventName, structs.SignedExecutionPayloadBidFromConsensus(v.SignedBid))
}, nil
case *operation.PayloadAttestationMessageReceivedData:
return func() io.Reader {
return jsonMarshalReader(eventName, structs.PayloadAttestationMessageFromConsensus(v.PayloadAttestationMessage))
}, nil
default:
return nil, errors.Wrapf(errUnhandledEventData, "event data type %T unsupported", v)
}

View File

@@ -123,6 +123,8 @@ func operationEventsFixtures(t *testing.T) (*topicRequest, []*feed.Event) {
ProposerSlashingTopic,
BlockGossipTopic,
DataColumnTopic,
ExecutionPayloadBidTopic,
PayloadAttestationMessageTopic,
})
require.NoError(t, err)
ro, err := blocks.NewROBlob(util.HydrateBlobSidecar(&eth.BlobSidecar{}))
@@ -312,6 +314,42 @@ func operationEventsFixtures(t *testing.T) (*topicRequest, []*feed.Event) {
KzgCommitments: [][]byte{{'a'}, {'b'}, {'c'}},
},
},
{
Type: operation.ExecutionPayloadBidReceived,
Data: &operation.ExecutionPayloadBidReceivedData{
SignedBid: &eth.SignedExecutionPayloadBid{
Message: &eth.ExecutionPayloadBid{
ParentBlockHash: make([]byte, 32),
ParentBlockRoot: make([]byte, 32),
BlockHash: make([]byte, 32),
PrevRandao: make([]byte, 32),
FeeRecipient: make([]byte, 20),
GasLimit: 30000000,
BuilderIndex: 42,
Slot: 10,
Value: 1000000000,
ExecutionPayment: 0,
BlobKzgCommitmentsRoot: make([]byte, 32),
},
Signature: make([]byte, 96),
},
},
},
{
Type: operation.PayloadAttestationMessageReceived,
Data: &operation.PayloadAttestationMessageReceivedData{
PayloadAttestationMessage: &eth.PayloadAttestationMessage{
ValidatorIndex: 123,
Data: &eth.PayloadAttestationData{
BeaconBlockRoot: make([]byte, 32),
Slot: 10,
PayloadPresent: true,
BlobDataAvailable: true,
},
Signature: make([]byte, 96),
},
},
},
}
}
@@ -393,6 +431,7 @@ func TestStreamEvents_OperationsEvents(t *testing.T) {
FinalizedCheckpointTopic,
ChainReorgTopic,
BlockTopic,
ExecutionPayloadAvailableTopic,
})
require.NoError(t, err)
request := topics.testHttpRequest(testSync.ctx, t)
@@ -445,6 +484,13 @@ func TestStreamEvents_OperationsEvents(t *testing.T) {
ExecutionOptimistic: false,
},
},
{
Type: statefeed.ExecutionPayloadAvailable,
Data: &statefeed.ExecutionPayloadAvailableData{
Slot: 10,
BlockRoot: [32]byte{0x9a},
},
},
}
go func() {
@@ -721,7 +767,7 @@ func TestStuckReaderScenarios(t *testing.T) {
func wedgedWriterTestCase(t *testing.T, queueDepth func([]*feed.Event) int) {
topics, events := operationEventsFixtures(t)
require.Equal(t, 12, len(events))
require.Equal(t, 14, len(events))
// set eventFeedDepth to a number lower than the events we intend to send to force the server to drop the reader.
stn := mockChain.NewEventFeedWrapper()

View File

@@ -21,7 +21,6 @@ go_library(
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/db:go_default_library",
"//beacon-chain/operations/attestations:go_default_library",
"//beacon-chain/operations/payloadattestation:go_default_library",
"//beacon-chain/operations/synccommittee:go_default_library",
"//beacon-chain/p2p:go_default_library",
"//beacon-chain/rpc/core:go_default_library",

View File

@@ -1427,61 +1427,3 @@ func sortProposerDuties(duties []*structs.ProposerDuty) error {
})
return err
}
// GetPayloadAttestationData produces payload attestation data for the requested slot.
func (s *Server) GetPayloadAttestationData(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "validator.GetPayloadAttestationData")
defer span.End()
currentEpoch := slots.ToEpoch(s.TimeFetcher.CurrentSlot())
if currentEpoch < params.BeaconConfig().GloasForkEpoch {
httputil.HandleError(w, fmt.Sprintf("payload attestation data requires the Gloas fork, current epoch %d, Gloas epoch %d", currentEpoch, params.BeaconConfig().GloasForkEpoch), http.StatusBadRequest)
return
}
if shared.IsSyncing(ctx, w, s.SyncChecker, s.HeadFetcher, s.TimeFetcher, s.OptimisticModeFetcher) {
return
}
_, slot, ok := shared.UintFromRoute(w, r, "slot")
if !ok {
return
}
headRoot, err := s.HeadFetcher.HeadRoot(ctx)
if err != nil {
httputil.HandleError(w, "Could not get head root: "+err.Error(), http.StatusInternalServerError)
return
}
payloadPresent, blobDataAvailable, err := s.PayloadStatusFetcher.PayloadStatus(primitives.Slot(slot))
if err != nil {
httputil.HandleError(w, "Could not get payload status: "+err.Error(), http.StatusInternalServerError)
return
}
data := &ethpbalpha.PayloadAttestationData{
BeaconBlockRoot: headRoot,
Slot: primitives.Slot(slot),
PayloadPresent: payloadPresent,
BlobDataAvailable: blobDataAvailable,
}
if httputil.RespondWithSsz(r) {
sszData, err := data.MarshalSSZ()
if err != nil {
httputil.HandleError(w, "Could not marshal payload attestation data: "+err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set(api.VersionHeader, version.String(version.Gloas))
httputil.WriteSsz(w, sszData)
return
}
response := &structs.GetPayloadAttestationDataResponse{
Version: version.String(version.Gloas),
Data: structs.PayloadAttestationDataFromConsensus(data),
}
w.Header().Set(api.VersionHeader, version.String(version.Gloas))
httputil.WriteJson(w, response)
}

View File

@@ -7,7 +7,6 @@ import (
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/feed/operation"
"github.com/OffchainLabs/prysm/v7/beacon-chain/db"
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/attestations"
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/payloadattestation"
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/synccommittee"
"github.com/OffchainLabs/prysm/v7/beacon-chain/p2p"
"github.com/OffchainLabs/prysm/v7/beacon-chain/rpc/core"
@@ -39,6 +38,4 @@ type Server struct {
BlockRewardFetcher rewards.BlockRewardsFetcher
TrackedValidatorsCache *cache.TrackedValidatorsCache
PayloadIDCache *cache.PayloadIDCache
PayloadAttestationPool payloadattestation.PoolManager
PayloadStatusFetcher payloadattestation.PayloadStatusFetcher
}

View File

@@ -22,7 +22,6 @@ import (
lightClient "github.com/OffchainLabs/prysm/v7/beacon-chain/light-client"
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/attestations"
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/blstoexec"
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/payloadattestation"
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/slashings"
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/synccommittee"
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/voluntaryexits"
@@ -104,7 +103,6 @@ type Config struct {
SlashingsPool slashings.PoolManager
SyncCommitteeObjectPool synccommittee.Pool
BLSChangesPool blstoexec.PoolManager
PayloadAttestationPool payloadattestation.PoolManager
SyncService chainSync.Checker
Broadcaster p2p.Broadcaster
PeersFetcher p2p.PeersProvider

View File

@@ -0,0 +1,3 @@
### Added
- the following events available at gloas `execution_payload_available`, `execution_payload_bid`,and `payload_attestation_message`

View File

@@ -1,4 +0,0 @@
### Added
- GET /eth/v1/validator/payload_attestation_data/{slot}
- POST & GET /eth/v1/beacon/pool/payload_attestations