mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-31 08:08:18 -05:00
Compare commits
1 Commits
event-stre
...
gloas-ptc-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
768da3cbea |
@@ -540,6 +540,12 @@ type PayloadAttestation struct {
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
type PayloadAttestationMessage struct {
|
||||
ValidatorIndex string `json:"validator_index"`
|
||||
Data *PayloadAttestationData `json:"data"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
type BeaconBlockBodyGloas struct {
|
||||
RandaoReveal string `json:"randao_reveal"`
|
||||
Eth1Data *Eth1Data `json:"eth1_data"`
|
||||
|
||||
@@ -3263,3 +3263,34 @@ 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 ð.PayloadAttestationMessage{
|
||||
ValidatorIndex: primitives.ValidatorIndex(validatorIndex),
|
||||
Data: data,
|
||||
Signature: sig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -188,6 +188,11 @@ 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
|
||||
|
||||
@@ -31,6 +31,11 @@ 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"`
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ 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",
|
||||
|
||||
@@ -40,6 +40,7 @@ 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"
|
||||
@@ -101,6 +102,7 @@ type BeaconNode struct {
|
||||
slashingsPool slashings.PoolManager
|
||||
syncCommitteePool synccommittee.Pool
|
||||
blsToExecPool blstoexec.PoolManager
|
||||
payloadAttestationPool payloadattestation.PoolManager
|
||||
depositCache cache.DepositCache
|
||||
trackedValidatorsCache *cache.TrackedValidatorsCache
|
||||
payloadIDCache *cache.PayloadIDCache
|
||||
@@ -155,6 +157,7 @@ func New(cliCtx *cli.Context, cancel context.CancelFunc, opts ...Option) (*Beaco
|
||||
slashingsPool: slashings.NewPool(),
|
||||
syncCommitteePool: synccommittee.NewPool(),
|
||||
blsToExecPool: blstoexec.NewPool(),
|
||||
payloadAttestationPool: payloadattestation.NewPool(),
|
||||
trackedValidatorsCache: cache.NewTrackedValidatorsCache(),
|
||||
payloadIDCache: cache.NewPayloadIDCache(),
|
||||
slasherBlockHeadersFeed: new(event.Feed),
|
||||
@@ -972,6 +975,7 @@ 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,
|
||||
|
||||
27
beacon-chain/operations/payloadattestation/BUILD.bazel
Normal file
27
beacon-chain/operations/payloadattestation/BUILD.bazel
Normal file
@@ -0,0 +1,27 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
|
||||
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",
|
||||
"//crypto/hash:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@org_golang_google_protobuf//proto:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["pool_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//testing/assert:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
],
|
||||
)
|
||||
108
beacon-chain/operations/payloadattestation/pool.go
Normal file
108
beacon-chain/operations/payloadattestation/pool.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package payloadattestation
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
"github.com/OffchainLabs/prysm/v7/crypto/hash"
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// PoolManager maintains pending payload attestations.
|
||||
// This pool is used by proposers to insert payload attestations into new blocks.
|
||||
type PoolManager interface {
|
||||
PendingPayloadAttestations(slot ...primitives.Slot) []*ethpb.PayloadAttestation
|
||||
InsertPayloadAttestation(msg *ethpb.PayloadAttestationMessage) error
|
||||
MarkIncluded(att *ethpb.PayloadAttestation)
|
||||
}
|
||||
|
||||
// Pool is a concrete implementation of PoolManager.
|
||||
type Pool struct {
|
||||
lock sync.RWMutex
|
||||
pending map[[32]byte]*ethpb.PayloadAttestation
|
||||
}
|
||||
|
||||
// NewPool returns an initialized pool.
|
||||
func NewPool() *Pool {
|
||||
return &Pool{
|
||||
pending: make(map[[32]byte]*ethpb.PayloadAttestation),
|
||||
}
|
||||
}
|
||||
|
||||
// PendingPayloadAttestations returns all pending payload attestations from the pool.
|
||||
// If a slot is provided, only attestations for that slot are returned.
|
||||
func (p *Pool) PendingPayloadAttestations(slot ...primitives.Slot) []*ethpb.PayloadAttestation {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
|
||||
result := make([]*ethpb.PayloadAttestation, 0, len(p.pending))
|
||||
for _, att := range p.pending {
|
||||
if len(slot) > 0 && att.Data.Slot != slot[0] {
|
||||
continue
|
||||
}
|
||||
result = append(result, att)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// InsertPayloadAttestation inserts a payload attestation message into the pool.
|
||||
// The message is converted to an aggregated PayloadAttestation and merged with
|
||||
// any existing attestation that has matching PayloadAttestationData.
|
||||
func (p *Pool) InsertPayloadAttestation(msg *ethpb.PayloadAttestationMessage) error {
|
||||
if msg == nil || msg.Data == nil {
|
||||
return errors.New("nil payload attestation message")
|
||||
}
|
||||
|
||||
key, err := dataKey(msg.Data)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not compute data key")
|
||||
}
|
||||
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
existing, ok := p.pending[key]
|
||||
if !ok {
|
||||
// Create a new aggregated PayloadAttestation from this message.
|
||||
p.pending[key] = ðpb.PayloadAttestation{
|
||||
AggregationBits: []byte{},
|
||||
Data: proto.Clone(msg.Data).(*ethpb.PayloadAttestationData),
|
||||
Signature: msg.Signature,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Merge: for now just replace the signature since proper BLS aggregation
|
||||
// requires knowing the PTC committee position. Full aggregation is a TODO.
|
||||
_ = existing
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarkIncluded removes the attestation with matching data from the pool.
|
||||
func (p *Pool) MarkIncluded(att *ethpb.PayloadAttestation) {
|
||||
if att == nil || att.Data == nil {
|
||||
return
|
||||
}
|
||||
|
||||
key, err := dataKey(att.Data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
delete(p.pending, key)
|
||||
}
|
||||
|
||||
// dataKey computes a deterministic key for PayloadAttestationData
|
||||
// by hashing its serialized form.
|
||||
func dataKey(data *ethpb.PayloadAttestationData) ([32]byte, error) {
|
||||
enc, err := proto.Marshal(data)
|
||||
if err != nil {
|
||||
return [32]byte{}, err
|
||||
}
|
||||
return hash.Hash(enc), nil
|
||||
}
|
||||
201
beacon-chain/operations/payloadattestation/pool_test.go
Normal file
201
beacon-chain/operations/payloadattestation/pool_test.go
Normal file
@@ -0,0 +1,201 @@
|
||||
package payloadattestation
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/assert"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/require"
|
||||
)
|
||||
|
||||
func TestPool_PendingPayloadAttestations(t *testing.T) {
|
||||
t.Run("empty pool", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
atts := pool.PendingPayloadAttestations()
|
||||
assert.Equal(t, 0, len(atts))
|
||||
})
|
||||
|
||||
t.Run("non-empty pool", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
msg1 := ðpb.PayloadAttestationMessage{
|
||||
ValidatorIndex: 0,
|
||||
Data: ðpb.PayloadAttestationData{
|
||||
BeaconBlockRoot: make([]byte, 32),
|
||||
Slot: 1,
|
||||
PayloadPresent: true,
|
||||
BlobDataAvailable: false,
|
||||
},
|
||||
Signature: make([]byte, 96),
|
||||
}
|
||||
msg2 := ðpb.PayloadAttestationMessage{
|
||||
ValidatorIndex: 1,
|
||||
Data: ðpb.PayloadAttestationData{
|
||||
BeaconBlockRoot: make([]byte, 32),
|
||||
Slot: 2,
|
||||
PayloadPresent: false,
|
||||
BlobDataAvailable: true,
|
||||
},
|
||||
Signature: make([]byte, 96),
|
||||
}
|
||||
require.NoError(t, pool.InsertPayloadAttestation(msg1))
|
||||
require.NoError(t, pool.InsertPayloadAttestation(msg2))
|
||||
atts := pool.PendingPayloadAttestations()
|
||||
assert.Equal(t, 2, len(atts))
|
||||
})
|
||||
|
||||
t.Run("filter by slot", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
msg1 := ðpb.PayloadAttestationMessage{
|
||||
ValidatorIndex: 0,
|
||||
Data: ðpb.PayloadAttestationData{
|
||||
BeaconBlockRoot: make([]byte, 32),
|
||||
Slot: 1,
|
||||
PayloadPresent: true,
|
||||
BlobDataAvailable: false,
|
||||
},
|
||||
Signature: make([]byte, 96),
|
||||
}
|
||||
msg2 := ðpb.PayloadAttestationMessage{
|
||||
ValidatorIndex: 1,
|
||||
Data: ðpb.PayloadAttestationData{
|
||||
BeaconBlockRoot: make([]byte, 32),
|
||||
Slot: 2,
|
||||
PayloadPresent: false,
|
||||
BlobDataAvailable: true,
|
||||
},
|
||||
Signature: make([]byte, 96),
|
||||
}
|
||||
require.NoError(t, pool.InsertPayloadAttestation(msg1))
|
||||
require.NoError(t, pool.InsertPayloadAttestation(msg2))
|
||||
|
||||
atts := pool.PendingPayloadAttestations(primitives.Slot(1))
|
||||
assert.Equal(t, 1, len(atts))
|
||||
assert.Equal(t, primitives.Slot(1), atts[0].Data.Slot)
|
||||
|
||||
atts = pool.PendingPayloadAttestations(primitives.Slot(2))
|
||||
assert.Equal(t, 1, len(atts))
|
||||
assert.Equal(t, primitives.Slot(2), atts[0].Data.Slot)
|
||||
|
||||
atts = pool.PendingPayloadAttestations(primitives.Slot(99))
|
||||
assert.Equal(t, 0, len(atts))
|
||||
})
|
||||
}
|
||||
|
||||
func TestPool_InsertPayloadAttestation(t *testing.T) {
|
||||
t.Run("nil message", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
err := pool.InsertPayloadAttestation(nil)
|
||||
require.ErrorContains(t, "nil payload attestation message", err)
|
||||
})
|
||||
|
||||
t.Run("nil data", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
err := pool.InsertPayloadAttestation(ðpb.PayloadAttestationMessage{})
|
||||
require.ErrorContains(t, "nil payload attestation message", err)
|
||||
})
|
||||
|
||||
t.Run("insert creates new entry", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
msg := ðpb.PayloadAttestationMessage{
|
||||
ValidatorIndex: 0,
|
||||
Data: ðpb.PayloadAttestationData{
|
||||
BeaconBlockRoot: make([]byte, 32),
|
||||
Slot: 1,
|
||||
PayloadPresent: true,
|
||||
BlobDataAvailable: false,
|
||||
},
|
||||
Signature: make([]byte, 96),
|
||||
}
|
||||
require.NoError(t, pool.InsertPayloadAttestation(msg))
|
||||
atts := pool.PendingPayloadAttestations()
|
||||
assert.Equal(t, 1, len(atts))
|
||||
})
|
||||
|
||||
t.Run("duplicate data does not create second entry", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
msg1 := ðpb.PayloadAttestationMessage{
|
||||
ValidatorIndex: 0,
|
||||
Data: ðpb.PayloadAttestationData{
|
||||
BeaconBlockRoot: make([]byte, 32),
|
||||
Slot: 1,
|
||||
PayloadPresent: true,
|
||||
BlobDataAvailable: false,
|
||||
},
|
||||
Signature: make([]byte, 96),
|
||||
}
|
||||
msg2 := ðpb.PayloadAttestationMessage{
|
||||
ValidatorIndex: 1,
|
||||
Data: ðpb.PayloadAttestationData{
|
||||
BeaconBlockRoot: make([]byte, 32),
|
||||
Slot: 1,
|
||||
PayloadPresent: true,
|
||||
BlobDataAvailable: false,
|
||||
},
|
||||
Signature: make([]byte, 96),
|
||||
}
|
||||
require.NoError(t, pool.InsertPayloadAttestation(msg1))
|
||||
require.NoError(t, pool.InsertPayloadAttestation(msg2))
|
||||
atts := pool.PendingPayloadAttestations()
|
||||
assert.Equal(t, 1, len(atts))
|
||||
})
|
||||
}
|
||||
|
||||
func TestPool_MarkIncluded(t *testing.T) {
|
||||
t.Run("mark included removes from pool", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
msg := ðpb.PayloadAttestationMessage{
|
||||
ValidatorIndex: 0,
|
||||
Data: ðpb.PayloadAttestationData{
|
||||
BeaconBlockRoot: make([]byte, 32),
|
||||
Slot: 1,
|
||||
PayloadPresent: true,
|
||||
BlobDataAvailable: false,
|
||||
},
|
||||
Signature: make([]byte, 96),
|
||||
}
|
||||
require.NoError(t, pool.InsertPayloadAttestation(msg))
|
||||
assert.Equal(t, 1, len(pool.PendingPayloadAttestations()))
|
||||
|
||||
pool.MarkIncluded(ðpb.PayloadAttestation{
|
||||
Data: ðpb.PayloadAttestationData{
|
||||
BeaconBlockRoot: make([]byte, 32),
|
||||
Slot: 1,
|
||||
PayloadPresent: true,
|
||||
BlobDataAvailable: false,
|
||||
},
|
||||
})
|
||||
assert.Equal(t, 0, len(pool.PendingPayloadAttestations()))
|
||||
})
|
||||
|
||||
t.Run("mark included with non-matching data does nothing", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
msg := ðpb.PayloadAttestationMessage{
|
||||
ValidatorIndex: 0,
|
||||
Data: ðpb.PayloadAttestationData{
|
||||
BeaconBlockRoot: make([]byte, 32),
|
||||
Slot: 1,
|
||||
PayloadPresent: true,
|
||||
BlobDataAvailable: false,
|
||||
},
|
||||
Signature: make([]byte, 96),
|
||||
}
|
||||
require.NoError(t, pool.InsertPayloadAttestation(msg))
|
||||
|
||||
pool.MarkIncluded(ðpb.PayloadAttestation{
|
||||
Data: ðpb.PayloadAttestationData{
|
||||
BeaconBlockRoot: make([]byte, 32),
|
||||
Slot: 999,
|
||||
PayloadPresent: true,
|
||||
BlobDataAvailable: false,
|
||||
},
|
||||
})
|
||||
assert.Equal(t, 1, len(pool.PendingPayloadAttestations()))
|
||||
})
|
||||
|
||||
t.Run("mark included with nil is safe", func(t *testing.T) {
|
||||
pool := NewPool()
|
||||
pool.MarkIncluded(nil)
|
||||
pool.MarkIncluded(ðpb.PayloadAttestation{})
|
||||
})
|
||||
}
|
||||
@@ -26,6 +26,7 @@ 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",
|
||||
|
||||
@@ -217,6 +217,7 @@ func (s *Service) validatorEndpoints(
|
||||
OperationNotifier: s.cfg.OperationNotifier,
|
||||
TrackedValidatorsCache: s.cfg.TrackedValidatorsCache,
|
||||
PayloadIDCache: s.cfg.PayloadIDCache,
|
||||
PayloadAttestationPool: s.cfg.PayloadAttestationPool,
|
||||
CoreService: coreService,
|
||||
BlockRewardFetcher: rewardFetcher,
|
||||
}
|
||||
@@ -390,6 +391,16 @@ 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},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -512,6 +523,7 @@ 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,
|
||||
@@ -869,6 +881,27 @@ 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},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ 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},
|
||||
}
|
||||
|
||||
@@ -91,22 +92,23 @@ 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/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},
|
||||
}
|
||||
|
||||
prysmBeaconRoutes := map[string][]string{
|
||||
|
||||
@@ -32,6 +32,7 @@ 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",
|
||||
|
||||
@@ -893,3 +893,89 @@ 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()
|
||||
|
||||
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()
|
||||
|
||||
rawSlot, slot, ok := shared.UintFromQuery(w, r, "slot", false)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var atts []*eth.PayloadAttestation
|
||||
if rawSlot != "" {
|
||||
atts = s.PayloadAttestationPool.PendingPayloadAttestations(primitives.Slot(slot))
|
||||
} else {
|
||||
atts = s.PayloadAttestationPool.PendingPayloadAttestations()
|
||||
}
|
||||
|
||||
data := make([]*structs.PayloadAttestation, len(atts))
|
||||
for i, att := range atts {
|
||||
data[i] = structs.PayloadAttestationFromConsensus(att)
|
||||
}
|
||||
|
||||
w.Header().Set(api.VersionHeader, version.String(version.Gloas))
|
||||
httputil.WriteJson(w, &structs.GetPoolPayloadAttestationsResponse{
|
||||
Version: version.String(version.Gloas),
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ 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"
|
||||
@@ -48,6 +49,7 @@ type Server struct {
|
||||
ExecutionReconstructor execution.Reconstructor
|
||||
FinalizationFetcher blockchain.FinalizationFetcher
|
||||
BLSChangesPool blstoexec.PoolManager
|
||||
PayloadAttestationPool payloadattestation.PoolManager
|
||||
ForkchoiceFetcher blockchain.ForkchoiceFetcher
|
||||
CoreService *core.Service
|
||||
AttestationStateFetcher blockchain.AttestationStateFetcher
|
||||
|
||||
@@ -21,6 +21,7 @@ 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",
|
||||
|
||||
@@ -1427,3 +1427,54 @@ 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()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// TODO: Determine payload_present and blob_data_available from the SignedExecutionPayloadEnvelope
|
||||
// once that handling is implemented. For now, stub with false.
|
||||
payloadPresent := false
|
||||
blobDataAvailable := false
|
||||
|
||||
data := ðpbalpha.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)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ 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"
|
||||
@@ -38,4 +39,5 @@ type Server struct {
|
||||
BlockRewardFetcher rewards.BlockRewardsFetcher
|
||||
TrackedValidatorsCache *cache.TrackedValidatorsCache
|
||||
PayloadIDCache *cache.PayloadIDCache
|
||||
PayloadAttestationPool payloadattestation.PoolManager
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ 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"
|
||||
@@ -103,6 +104,7 @@ type Config struct {
|
||||
SlashingsPool slashings.PoolManager
|
||||
SyncCommitteeObjectPool synccommittee.Pool
|
||||
BLSChangesPool blstoexec.PoolManager
|
||||
PayloadAttestationPool payloadattestation.PoolManager
|
||||
SyncService chainSync.Checker
|
||||
Broadcaster p2p.Broadcaster
|
||||
PeersFetcher p2p.PeersProvider
|
||||
|
||||
Reference in New Issue
Block a user