mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-04-19 03:01:06 -04:00
Compare commits
7 Commits
defer-api-
...
proposer-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9085a0c4c4 | ||
|
|
1d03c8146b | ||
|
|
07bacc58d6 | ||
|
|
9a7bdef5c9 | ||
|
|
d73fb859db | ||
|
|
138948c65d | ||
|
|
9e980f3ace |
@@ -54,11 +54,14 @@ go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"conversions_block_execution_test.go",
|
||||
"conversions_gloas_test.go",
|
||||
"conversions_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//consensus-types/blocks:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//encoding/bytesutil:go_default_library",
|
||||
"//proto/engine/v1:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//testing/assert:go_default_library",
|
||||
|
||||
@@ -546,6 +546,18 @@ type PayloadAttestationMessage struct {
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
type ProposerPreferences struct {
|
||||
ProposalSlot string `json:"proposal_slot"`
|
||||
ValidatorIndex string `json:"validator_index"`
|
||||
FeeRecipient string `json:"fee_recipient"`
|
||||
GasLimit string `json:"gas_limit"`
|
||||
}
|
||||
|
||||
type SignedProposerPreferences struct {
|
||||
Message *ProposerPreferences `json:"message"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
type BeaconBlockBodyGloas struct {
|
||||
RandaoReveal string `json:"randao_reveal"`
|
||||
Eth1Data *Eth1Data `json:"eth1_data"`
|
||||
|
||||
@@ -2,8 +2,13 @@ package structs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/api/server"
|
||||
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
|
||||
"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/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
@@ -38,6 +43,61 @@ func ROExecutionPayloadBidFromConsensus(b interfaces.ROExecutionPayloadBid) *Exe
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SignedProposerPreferences) ToConsensus() (*ethpb.SignedProposerPreferences, error) {
|
||||
if s.Message == nil {
|
||||
return nil, server.NewDecodeError(errNilValue, "Message")
|
||||
}
|
||||
msg, err := s.Message.ToConsensus()
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "Message")
|
||||
}
|
||||
sig, err := bytesutil.DecodeHexWithLength(s.Signature, fieldparams.BLSSignatureLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "Signature")
|
||||
}
|
||||
return ðpb.SignedProposerPreferences{
|
||||
Message: msg,
|
||||
Signature: sig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *ProposerPreferences) ToConsensus() (*ethpb.ProposerPreferences, error) {
|
||||
slot, err := strconv.ParseUint(p.ProposalSlot, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ProposalSlot")
|
||||
}
|
||||
valIdx, err := strconv.ParseUint(p.ValidatorIndex, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ValidatorIndex")
|
||||
}
|
||||
feeRecipient, err := bytesutil.DecodeHexWithLength(p.FeeRecipient, fieldparams.FeeRecipientLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "FeeRecipient")
|
||||
}
|
||||
gasLimit, err := strconv.ParseUint(p.GasLimit, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "GasLimit")
|
||||
}
|
||||
return ðpb.ProposerPreferences{
|
||||
ProposalSlot: primitives.Slot(slot),
|
||||
ValidatorIndex: primitives.ValidatorIndex(valIdx),
|
||||
FeeRecipient: feeRecipient,
|
||||
GasLimit: gasLimit,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func SignedProposerPreferencesFromConsensus(sp *ethpb.SignedProposerPreferences) *SignedProposerPreferences {
|
||||
return &SignedProposerPreferences{
|
||||
Message: &ProposerPreferences{
|
||||
ProposalSlot: fmt.Sprintf("%d", sp.Message.ProposalSlot),
|
||||
ValidatorIndex: fmt.Sprintf("%d", sp.Message.ValidatorIndex),
|
||||
FeeRecipient: hexutil.Encode(sp.Message.FeeRecipient),
|
||||
GasLimit: fmt.Sprintf("%d", sp.Message.GasLimit),
|
||||
},
|
||||
Signature: hexutil.Encode(sp.Signature),
|
||||
}
|
||||
}
|
||||
|
||||
func BuildersFromConsensus(builders []*ethpb.Builder) []*Builder {
|
||||
newBuilders := make([]*Builder, len(builders))
|
||||
for i, b := range builders {
|
||||
|
||||
110
api/server/structs/conversions_gloas_test.go
Normal file
110
api/server/structs/conversions_gloas_test.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package structs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
|
||||
eth "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/assert"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/require"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
func TestSignedProposerPreferences_ToConsensus_NilMessage(t *testing.T) {
|
||||
s := &SignedProposerPreferences{Message: nil, Signature: ""}
|
||||
_, err := s.ToConsensus()
|
||||
require.ErrorContains(t, errNilValue.Error(), err)
|
||||
}
|
||||
|
||||
func TestSignedProposerPreferences_ToConsensus(t *testing.T) {
|
||||
feeRecipient := bytesutil.PadTo([]byte{0xaa, 0xbb}, 20)
|
||||
sig := bytesutil.PadTo([]byte{0xcc}, 96)
|
||||
|
||||
s := &SignedProposerPreferences{
|
||||
Message: &ProposerPreferences{
|
||||
ProposalSlot: "32",
|
||||
ValidatorIndex: "5",
|
||||
FeeRecipient: hexutil.Encode(feeRecipient),
|
||||
GasLimit: "30000000",
|
||||
},
|
||||
Signature: hexutil.Encode(sig),
|
||||
}
|
||||
|
||||
result, err := s.ToConsensus()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, primitives.Slot(32), result.Message.ProposalSlot)
|
||||
assert.Equal(t, primitives.ValidatorIndex(5), result.Message.ValidatorIndex)
|
||||
assert.DeepEqual(t, feeRecipient, result.Message.FeeRecipient)
|
||||
assert.Equal(t, uint64(30000000), result.Message.GasLimit)
|
||||
assert.DeepEqual(t, sig, result.Signature)
|
||||
}
|
||||
|
||||
func TestProposerPreferences_ToConsensus_InvalidSlot(t *testing.T) {
|
||||
p := &ProposerPreferences{
|
||||
ProposalSlot: "not_a_number",
|
||||
ValidatorIndex: "5",
|
||||
FeeRecipient: hexutil.Encode(make([]byte, 20)),
|
||||
GasLimit: "30000000",
|
||||
}
|
||||
_, err := p.ToConsensus()
|
||||
require.ErrorContains(t, "ProposalSlot", err)
|
||||
}
|
||||
|
||||
func TestProposerPreferences_ToConsensus_InvalidFeeRecipient(t *testing.T) {
|
||||
p := &ProposerPreferences{
|
||||
ProposalSlot: "32",
|
||||
ValidatorIndex: "5",
|
||||
FeeRecipient: "0xinvalid",
|
||||
GasLimit: "30000000",
|
||||
}
|
||||
_, err := p.ToConsensus()
|
||||
require.ErrorContains(t, "FeeRecipient", err)
|
||||
}
|
||||
|
||||
func TestSignedProposerPreferencesFromConsensus(t *testing.T) {
|
||||
feeRecipient := bytesutil.PadTo([]byte{0xaa, 0xbb}, 20)
|
||||
sig := bytesutil.PadTo([]byte{0xcc}, 96)
|
||||
|
||||
sp := ð.SignedProposerPreferences{
|
||||
Message: ð.ProposerPreferences{
|
||||
ProposalSlot: 32,
|
||||
ValidatorIndex: 5,
|
||||
FeeRecipient: feeRecipient,
|
||||
GasLimit: 30000000,
|
||||
},
|
||||
Signature: sig,
|
||||
}
|
||||
|
||||
result := SignedProposerPreferencesFromConsensus(sp)
|
||||
assert.Equal(t, "32", result.Message.ProposalSlot)
|
||||
assert.Equal(t, "5", result.Message.ValidatorIndex)
|
||||
assert.Equal(t, hexutil.Encode(feeRecipient), result.Message.FeeRecipient)
|
||||
assert.Equal(t, "30000000", result.Message.GasLimit)
|
||||
assert.Equal(t, hexutil.Encode(sig), result.Signature)
|
||||
}
|
||||
|
||||
func TestSignedProposerPreferences_RoundTrip(t *testing.T) {
|
||||
feeRecipient := bytesutil.PadTo([]byte{0xaa, 0xbb}, 20)
|
||||
sig := bytesutil.PadTo([]byte{0xcc}, 96)
|
||||
|
||||
original := ð.SignedProposerPreferences{
|
||||
Message: ð.ProposerPreferences{
|
||||
ProposalSlot: 32,
|
||||
ValidatorIndex: 5,
|
||||
FeeRecipient: feeRecipient,
|
||||
GasLimit: 30000000,
|
||||
},
|
||||
Signature: sig,
|
||||
}
|
||||
|
||||
jsonStruct := SignedProposerPreferencesFromConsensus(original)
|
||||
roundTripped, err := jsonStruct.ToConsensus()
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, original.Message.ProposalSlot, roundTripped.Message.ProposalSlot)
|
||||
assert.Equal(t, original.Message.ValidatorIndex, roundTripped.Message.ValidatorIndex)
|
||||
assert.DeepEqual(t, original.Message.FeeRecipient, roundTripped.Message.FeeRecipient)
|
||||
assert.Equal(t, original.Message.GasLimit, roundTripped.Message.GasLimit)
|
||||
assert.DeepEqual(t, original.Signature, roundTripped.Signature)
|
||||
}
|
||||
@@ -188,6 +188,11 @@ type BLSToExecutionChangesPoolResponse struct {
|
||||
Data []*SignedBLSToExecutionChange `json:"data"`
|
||||
}
|
||||
|
||||
type ProposerPreferencesPoolResponse struct {
|
||||
Version string `json:"version"`
|
||||
Data []*SignedProposerPreferences `json:"data"`
|
||||
}
|
||||
|
||||
type GetAttesterSlashingsResponse struct {
|
||||
Version string `json:"version,omitempty"`
|
||||
Data json.RawMessage `json:"data"` // Accepts both `[]*AttesterSlashing` and `[]*AttesterSlashingElectra` types
|
||||
|
||||
60
beacon-chain/cache/proposer_preferences.go
vendored
60
beacon-chain/cache/proposer_preferences.go
vendored
@@ -4,57 +4,42 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
// ProposerPreference stores the proposer fee recipient and gas limit for a slot.
|
||||
type ProposerPreference struct {
|
||||
FeeRecipient []byte
|
||||
GasLimit uint64
|
||||
}
|
||||
|
||||
// ProposerPreferencesCache stores proposer preferences by slot.
|
||||
// ProposerPreferencesCache stores signed proposer preferences by slot.
|
||||
type ProposerPreferencesCache struct {
|
||||
slotToPreferences map[primitives.Slot]ProposerPreference
|
||||
slotToPreferences map[primitives.Slot]*ethpb.SignedProposerPreferences
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// NewProposerPreferencesCache initializes a proposer preferences cache.
|
||||
func NewProposerPreferencesCache() *ProposerPreferencesCache {
|
||||
return &ProposerPreferencesCache{
|
||||
slotToPreferences: make(map[primitives.Slot]ProposerPreference),
|
||||
slotToPreferences: make(map[primitives.Slot]*ethpb.SignedProposerPreferences),
|
||||
}
|
||||
}
|
||||
|
||||
// Add stores proposer preferences for a slot. If the slot already exists, the
|
||||
// existing value is kept and false is returned.
|
||||
func (c *ProposerPreferencesCache) Add(slot primitives.Slot, feeRecipient []byte, gasLimit uint64) bool {
|
||||
// Add stores signed proposer preferences for a slot. If the slot already
|
||||
// exists, the existing value is kept and false is returned.
|
||||
func (c *ProposerPreferencesCache) Add(slot primitives.Slot, signed *ethpb.SignedProposerPreferences) bool {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
if _, ok := c.slotToPreferences[slot]; ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// FeeRecipient comes from validated SSZ-decoded proposer preferences, so
|
||||
// retaining the slice reference here is intentional.
|
||||
c.slotToPreferences[slot] = ProposerPreference{
|
||||
FeeRecipient: feeRecipient,
|
||||
GasLimit: gasLimit,
|
||||
}
|
||||
c.slotToPreferences[slot] = signed
|
||||
return true
|
||||
}
|
||||
|
||||
// Get returns proposer preferences for a slot.
|
||||
func (c *ProposerPreferencesCache) Get(slot primitives.Slot) (ProposerPreference, bool) {
|
||||
// Get returns the signed proposer preferences for a slot.
|
||||
func (c *ProposerPreferencesCache) Get(slot primitives.Slot) (*ethpb.SignedProposerPreferences, bool) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
|
||||
pref, ok := c.slotToPreferences[slot]
|
||||
if !ok {
|
||||
return ProposerPreference{}, false
|
||||
}
|
||||
|
||||
return pref, true
|
||||
sp, ok := c.slotToPreferences[slot]
|
||||
return sp, ok
|
||||
}
|
||||
|
||||
// Has returns true if proposer preferences for the slot already exist.
|
||||
@@ -66,6 +51,25 @@ func (c *ProposerPreferencesCache) Has(slot primitives.Slot) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
// Pending returns cached signed proposer preferences not yet included in a
|
||||
// block. If slot is non-zero, only the entry for that slot is returned.
|
||||
func (c *ProposerPreferencesCache) Pending(slot primitives.Slot) []*ethpb.SignedProposerPreferences {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
|
||||
if slot != 0 {
|
||||
if sp, ok := c.slotToPreferences[slot]; ok {
|
||||
return []*ethpb.SignedProposerPreferences{sp}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
result := make([]*ethpb.SignedProposerPreferences, 0, len(c.slotToPreferences))
|
||||
for _, sp := range c.slotToPreferences {
|
||||
result = append(result, sp)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// PruneBefore removes all proposer preferences for slots before the provided slot.
|
||||
func (c *ProposerPreferencesCache) PruneBefore(slot primitives.Slot) {
|
||||
c.lock.Lock()
|
||||
@@ -83,5 +87,5 @@ func (c *ProposerPreferencesCache) Clear() {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
c.slotToPreferences = make(map[primitives.Slot]ProposerPreference)
|
||||
c.slotToPreferences = make(map[primitives.Slot]*ethpb.SignedProposerPreferences)
|
||||
}
|
||||
|
||||
55
beacon-chain/cache/proposer_preferences_test.go
vendored
55
beacon-chain/cache/proposer_preferences_test.go
vendored
@@ -4,43 +4,54 @@ 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/require"
|
||||
)
|
||||
|
||||
func testSigned(slot primitives.Slot, feeRecipient []byte, gasLimit uint64) *ethpb.SignedProposerPreferences {
|
||||
return ðpb.SignedProposerPreferences{
|
||||
Message: ðpb.ProposerPreferences{
|
||||
ProposalSlot: slot,
|
||||
FeeRecipient: feeRecipient,
|
||||
GasLimit: gasLimit,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestProposerPreferencesCache_AddGetHas(t *testing.T) {
|
||||
c := NewProposerPreferencesCache()
|
||||
slot := primitives.Slot(123)
|
||||
feeRecipient := []byte{1, 2, 3, 4}
|
||||
|
||||
require.Equal(t, false, c.Has(slot))
|
||||
added := c.Add(slot, feeRecipient, 42)
|
||||
added := c.Add(slot, testSigned(slot, feeRecipient, 42))
|
||||
require.Equal(t, true, added)
|
||||
require.Equal(t, true, c.Has(slot))
|
||||
|
||||
pref, ok := c.Get(slot)
|
||||
sp, ok := c.Get(slot)
|
||||
require.Equal(t, true, ok)
|
||||
require.DeepEqual(t, feeRecipient, pref.FeeRecipient)
|
||||
require.Equal(t, uint64(42), pref.GasLimit)
|
||||
require.DeepEqual(t, feeRecipient, sp.Message.FeeRecipient)
|
||||
require.Equal(t, uint64(42), sp.Message.GasLimit)
|
||||
}
|
||||
|
||||
func TestProposerPreferencesCache_AddDuplicateSlot(t *testing.T) {
|
||||
c := NewProposerPreferencesCache()
|
||||
slot := primitives.Slot(456)
|
||||
|
||||
require.Equal(t, true, c.Add(slot, []byte{1}, 10))
|
||||
require.Equal(t, false, c.Add(slot, []byte{2}, 20))
|
||||
require.Equal(t, true, c.Add(slot, testSigned(slot, []byte{1}, 10)))
|
||||
require.Equal(t, false, c.Add(slot, testSigned(slot, []byte{2}, 20)))
|
||||
|
||||
pref, ok := c.Get(slot)
|
||||
sp, ok := c.Get(slot)
|
||||
require.Equal(t, true, ok)
|
||||
require.DeepEqual(t, []byte{1}, pref.FeeRecipient)
|
||||
require.Equal(t, uint64(10), pref.GasLimit)
|
||||
require.DeepEqual(t, []byte{1}, sp.Message.FeeRecipient)
|
||||
require.Equal(t, uint64(10), sp.Message.GasLimit)
|
||||
}
|
||||
|
||||
func TestProposerPreferencesCache_Clear(t *testing.T) {
|
||||
c := NewProposerPreferencesCache()
|
||||
slot := primitives.Slot(789)
|
||||
|
||||
require.Equal(t, true, c.Add(slot, []byte{1}, 10))
|
||||
require.Equal(t, true, c.Add(slot, testSigned(slot, []byte{1}, 10)))
|
||||
c.Clear()
|
||||
|
||||
require.Equal(t, false, c.Has(slot))
|
||||
@@ -51,9 +62,9 @@ func TestProposerPreferencesCache_Clear(t *testing.T) {
|
||||
func TestProposerPreferencesCache_PruneBefore(t *testing.T) {
|
||||
c := NewProposerPreferencesCache()
|
||||
|
||||
require.Equal(t, true, c.Add(10, []byte{1}, 10))
|
||||
require.Equal(t, true, c.Add(11, []byte{2}, 11))
|
||||
require.Equal(t, true, c.Add(12, []byte{3}, 12))
|
||||
require.Equal(t, true, c.Add(10, testSigned(10, []byte{1}, 10)))
|
||||
require.Equal(t, true, c.Add(11, testSigned(11, []byte{2}, 11)))
|
||||
require.Equal(t, true, c.Add(12, testSigned(12, []byte{3}, 12)))
|
||||
|
||||
c.PruneBefore(11)
|
||||
|
||||
@@ -61,3 +72,21 @@ func TestProposerPreferencesCache_PruneBefore(t *testing.T) {
|
||||
require.Equal(t, true, c.Has(11))
|
||||
require.Equal(t, true, c.Has(12))
|
||||
}
|
||||
|
||||
func TestProposerPreferencesCache_Pending(t *testing.T) {
|
||||
c := NewProposerPreferencesCache()
|
||||
|
||||
c.Add(10, testSigned(10, []byte{1}, 10))
|
||||
c.Add(11, testSigned(11, []byte{2}, 11))
|
||||
c.Add(12, testSigned(12, []byte{3}, 12))
|
||||
|
||||
all := c.Pending(0)
|
||||
require.Equal(t, 3, len(all))
|
||||
|
||||
bySlot := c.Pending(11)
|
||||
require.Equal(t, 1, len(bySlot))
|
||||
require.Equal(t, primitives.Slot(11), bySlot[0].Message.ProposalSlot)
|
||||
|
||||
empty := c.Pending(999)
|
||||
require.Equal(t, 0, len(empty))
|
||||
}
|
||||
|
||||
@@ -49,6 +49,9 @@ const (
|
||||
|
||||
// PayloadAttestationMessageReceived is sent after a payload attestation message is received from gossip or rpc.
|
||||
PayloadAttestationMessageReceived = 13
|
||||
|
||||
// ProposerPreferencesReceived is sent after a signed proposer preferences message is received from gossip or rpc.
|
||||
ProposerPreferencesReceived = 14
|
||||
)
|
||||
|
||||
// UnAggregatedAttReceivedData is the data sent with UnaggregatedAttReceived events.
|
||||
@@ -122,3 +125,8 @@ type DataColumnReceivedData struct {
|
||||
type PayloadAttestationMessageReceivedData struct {
|
||||
Message *ethpb.PayloadAttestationMessage
|
||||
}
|
||||
|
||||
// ProposerPreferencesReceivedData is the data sent with ProposerPreferencesReceived events.
|
||||
type ProposerPreferencesReceivedData struct {
|
||||
Preferences *ethpb.SignedProposerPreferences
|
||||
}
|
||||
|
||||
@@ -200,25 +200,26 @@ func (s *Service) validatorEndpoints(
|
||||
rewardFetcher rewards.BlockRewardsFetcher,
|
||||
) []endpoint {
|
||||
server := &validator.Server{
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
TimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
SyncChecker: s.cfg.SyncService,
|
||||
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
|
||||
AttestationCache: s.cfg.AttestationCache,
|
||||
AttestationsPool: s.cfg.AttestationsPool,
|
||||
PeerManager: s.cfg.PeerManager,
|
||||
Broadcaster: s.cfg.Broadcaster,
|
||||
V1Alpha1Server: validatorServer,
|
||||
Stater: stater,
|
||||
SyncCommitteePool: s.cfg.SyncCommitteeObjectPool,
|
||||
ChainInfoFetcher: s.cfg.ChainInfoFetcher,
|
||||
BeaconDB: s.cfg.BeaconDB,
|
||||
BlockBuilder: s.cfg.BlockBuilder,
|
||||
OperationNotifier: s.cfg.OperationNotifier,
|
||||
TrackedValidatorsCache: s.cfg.TrackedValidatorsCache,
|
||||
PayloadIDCache: s.cfg.PayloadIDCache,
|
||||
CoreService: coreService,
|
||||
BlockRewardFetcher: rewardFetcher,
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
TimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
SyncChecker: s.cfg.SyncService,
|
||||
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
|
||||
AttestationCache: s.cfg.AttestationCache,
|
||||
AttestationsPool: s.cfg.AttestationsPool,
|
||||
PeerManager: s.cfg.PeerManager,
|
||||
Broadcaster: s.cfg.Broadcaster,
|
||||
V1Alpha1Server: validatorServer,
|
||||
Stater: stater,
|
||||
SyncCommitteePool: s.cfg.SyncCommitteeObjectPool,
|
||||
ChainInfoFetcher: s.cfg.ChainInfoFetcher,
|
||||
BeaconDB: s.cfg.BeaconDB,
|
||||
BlockBuilder: s.cfg.BlockBuilder,
|
||||
OperationNotifier: s.cfg.OperationNotifier,
|
||||
TrackedValidatorsCache: s.cfg.TrackedValidatorsCache,
|
||||
PayloadIDCache: s.cfg.PayloadIDCache,
|
||||
CoreService: coreService,
|
||||
BlockRewardFetcher: rewardFetcher,
|
||||
ProposerPreferencesCache: s.cfg.ProposerPreferencesCache,
|
||||
}
|
||||
|
||||
const namespace = "validator"
|
||||
@@ -411,6 +412,17 @@ func (s *Service) validatorEndpoints(
|
||||
handler: server.SyncCommitteeSelections,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/validator/proposer_preferences",
|
||||
name: namespace + ".SubmitProposerPreferences",
|
||||
middleware: []middleware.Middleware{
|
||||
middleware.ContentTypeHandler([]string{api.JsonMediaType}),
|
||||
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
|
||||
middleware.AcceptEncodingHeaderHandler(),
|
||||
},
|
||||
handler: server.SubmitProposerPreferences,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -522,32 +534,33 @@ func (s *Service) beaconEndpoints(
|
||||
coreService *core.Service,
|
||||
) []endpoint {
|
||||
server := &beacon.Server{
|
||||
CanonicalHistory: ch,
|
||||
BeaconDB: s.cfg.BeaconDB,
|
||||
AttestationCache: s.cfg.AttestationCache,
|
||||
AttestationsPool: s.cfg.AttestationsPool,
|
||||
SlashingsPool: s.cfg.SlashingsPool,
|
||||
ChainInfoFetcher: s.cfg.ChainInfoFetcher,
|
||||
GenesisTimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
BlockNotifier: s.cfg.BlockNotifier,
|
||||
OperationNotifier: s.cfg.OperationNotifier,
|
||||
Broadcaster: s.cfg.Broadcaster,
|
||||
BlockReceiver: s.cfg.BlockReceiver,
|
||||
StateGenService: s.cfg.StateGen,
|
||||
Stater: stater,
|
||||
Blocker: blocker,
|
||||
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
TimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
VoluntaryExitsPool: s.cfg.ExitPool,
|
||||
V1Alpha1ValidatorServer: validatorServer,
|
||||
SyncChecker: s.cfg.SyncService,
|
||||
ExecutionReconstructor: s.cfg.ExecutionReconstructor,
|
||||
BLSChangesPool: s.cfg.BLSChangesPool,
|
||||
FinalizationFetcher: s.cfg.FinalizationFetcher,
|
||||
ForkchoiceFetcher: s.cfg.ForkchoiceFetcher,
|
||||
CoreService: coreService,
|
||||
AttestationStateFetcher: s.cfg.AttestationReceiver,
|
||||
CanonicalHistory: ch,
|
||||
BeaconDB: s.cfg.BeaconDB,
|
||||
AttestationCache: s.cfg.AttestationCache,
|
||||
AttestationsPool: s.cfg.AttestationsPool,
|
||||
SlashingsPool: s.cfg.SlashingsPool,
|
||||
ChainInfoFetcher: s.cfg.ChainInfoFetcher,
|
||||
GenesisTimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
BlockNotifier: s.cfg.BlockNotifier,
|
||||
OperationNotifier: s.cfg.OperationNotifier,
|
||||
Broadcaster: s.cfg.Broadcaster,
|
||||
BlockReceiver: s.cfg.BlockReceiver,
|
||||
StateGenService: s.cfg.StateGen,
|
||||
Stater: stater,
|
||||
Blocker: blocker,
|
||||
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
|
||||
HeadFetcher: s.cfg.HeadFetcher,
|
||||
TimeFetcher: s.cfg.GenesisTimeFetcher,
|
||||
VoluntaryExitsPool: s.cfg.ExitPool,
|
||||
V1Alpha1ValidatorServer: validatorServer,
|
||||
SyncChecker: s.cfg.SyncService,
|
||||
ExecutionReconstructor: s.cfg.ExecutionReconstructor,
|
||||
BLSChangesPool: s.cfg.BLSChangesPool,
|
||||
FinalizationFetcher: s.cfg.FinalizationFetcher,
|
||||
ForkchoiceFetcher: s.cfg.ForkchoiceFetcher,
|
||||
CoreService: coreService,
|
||||
AttestationStateFetcher: s.cfg.AttestationReceiver,
|
||||
ProposerPreferencesCache: s.cfg.ProposerPreferencesCache,
|
||||
}
|
||||
|
||||
const namespace = "beacon"
|
||||
@@ -759,6 +772,16 @@ func (s *Service) beaconEndpoints(
|
||||
handler: server.SubmitAttesterSlashingsV2,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/pool/proposer_preferences",
|
||||
name: namespace + ".ListProposerPreferences",
|
||||
middleware: []middleware.Middleware{
|
||||
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
|
||||
middleware.AcceptEncodingHeaderHandler(),
|
||||
},
|
||||
handler: server.ListProposerPreferences,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/pool/proposer_slashings",
|
||||
name: namespace + ".GetProposerSlashings",
|
||||
|
||||
@@ -47,6 +47,7 @@ func Test_endpoints(t *testing.T) {
|
||||
"/eth/v2/beacon/pool/attestations": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v2/beacon/pool/attester_slashings": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/pool/proposer_slashings": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/pool/proposer_preferences": {http.MethodGet},
|
||||
"/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},
|
||||
@@ -112,6 +113,7 @@ func Test_endpoints(t *testing.T) {
|
||||
"/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/proposer_preferences": {http.MethodPost},
|
||||
}
|
||||
|
||||
prysmBeaconRoutes := map[string][]string{
|
||||
|
||||
@@ -88,6 +88,7 @@ go_test(
|
||||
"//api/server/structs:go_default_library",
|
||||
"//beacon-chain/blockchain/kzg:go_default_library",
|
||||
"//beacon-chain/blockchain/testing:go_default_library",
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/core/signing:go_default_library",
|
||||
"//beacon-chain/core/time:go_default_library",
|
||||
"//beacon-chain/core/transition:go_default_library",
|
||||
|
||||
@@ -941,3 +941,35 @@ func (s *Server) SubmitProposerSlashing(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ListProposerPreferences retrieves proposer preferences known by the node but not necessarily incorporated into any block.
|
||||
func (s *Server) ListProposerPreferences(w http.ResponseWriter, r *http.Request) {
|
||||
_, span := trace.StartSpan(r.Context(), "beacon.ListProposerPreferences")
|
||||
defer span.End()
|
||||
|
||||
if slots.ToEpoch(s.TimeFetcher.CurrentSlot()) < params.BeaconConfig().GloasForkEpoch {
|
||||
httputil.HandleError(w, "Proposer preferences are not supported before the gloas fork", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
_, slot, ok := shared.UintFromQuery(w, r, "slot", false)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if slot != 0 && slots.ToEpoch(primitives.Slot(slot)) < params.BeaconConfig().GloasForkEpoch {
|
||||
httputil.HandleError(w, "Requested slot is before the gloas fork", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
pending := s.ProposerPreferencesCache.Pending(primitives.Slot(slot))
|
||||
data := make([]*structs.SignedProposerPreferences, 0, len(pending))
|
||||
for _, sp := range pending {
|
||||
data = append(data, structs.SignedProposerPreferencesFromConsensus(sp))
|
||||
}
|
||||
|
||||
w.Header().Set(api.VersionHeader, version.String(version.Gloas))
|
||||
httputil.WriteJson(w, &structs.ProposerPreferencesPoolResponse{
|
||||
Version: version.String(version.Gloas),
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/OffchainLabs/prysm/v7/api/server"
|
||||
"github.com/OffchainLabs/prysm/v7/api/server/structs"
|
||||
blockchainmock "github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/testing"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/cache"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/signing"
|
||||
prysmtime "github.com/OffchainLabs/prysm/v7/beacon-chain/core/time"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/transition"
|
||||
@@ -2296,6 +2297,118 @@ func TestSubmitProposerSlashing_InvalidSlashing(t *testing.T) {
|
||||
assert.StringContains(t, "Invalid proposer slashing", e.Message)
|
||||
}
|
||||
|
||||
func TestListProposerPreferences(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.GloasForkEpoch = 0
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
c := cache.NewProposerPreferencesCache()
|
||||
sp1 := ðpbv1alpha1.SignedProposerPreferences{
|
||||
Message: ðpbv1alpha1.ProposerPreferences{
|
||||
ProposalSlot: 32,
|
||||
ValidatorIndex: 1,
|
||||
FeeRecipient: bytesutil.PadTo([]byte("fee1"), 20),
|
||||
GasLimit: 30000000,
|
||||
},
|
||||
Signature: bytesutil.PadTo([]byte("sig1"), 96),
|
||||
}
|
||||
sp2 := ðpbv1alpha1.SignedProposerPreferences{
|
||||
Message: ðpbv1alpha1.ProposerPreferences{
|
||||
ProposalSlot: 33,
|
||||
ValidatorIndex: 2,
|
||||
FeeRecipient: bytesutil.PadTo([]byte("fee2"), 20),
|
||||
GasLimit: 30000001,
|
||||
},
|
||||
Signature: bytesutil.PadTo([]byte("sig2"), 96),
|
||||
}
|
||||
c.Add(32, sp1)
|
||||
c.Add(33, sp2)
|
||||
|
||||
s := &Server{
|
||||
ProposerPreferencesCache: c,
|
||||
TimeFetcher: &blockchainmock.ChainService{},
|
||||
}
|
||||
|
||||
t.Run("all", func(t *testing.T) {
|
||||
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/pool/proposer_preferences", nil)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.ListProposerPreferences(writer, request)
|
||||
assert.Equal(t, http.StatusOK, writer.Code)
|
||||
assert.Equal(t, "gloas", writer.Header().Get(api.VersionHeader))
|
||||
|
||||
resp := &structs.ProposerPreferencesPoolResponse{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
||||
require.Equal(t, 2, len(resp.Data))
|
||||
assert.Equal(t, "gloas", resp.Version)
|
||||
})
|
||||
t.Run("filter by slot", func(t *testing.T) {
|
||||
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/pool/proposer_preferences?slot=32", nil)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.ListProposerPreferences(writer, request)
|
||||
assert.Equal(t, http.StatusOK, writer.Code)
|
||||
|
||||
resp := &structs.ProposerPreferencesPoolResponse{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
||||
require.Equal(t, 1, len(resp.Data))
|
||||
assert.Equal(t, "32", resp.Data[0].Message.ProposalSlot)
|
||||
})
|
||||
t.Run("empty result", func(t *testing.T) {
|
||||
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/pool/proposer_preferences?slot=999", nil)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.ListProposerPreferences(writer, request)
|
||||
assert.Equal(t, http.StatusOK, writer.Code)
|
||||
|
||||
resp := &structs.ProposerPreferencesPoolResponse{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
||||
require.Equal(t, 0, len(resp.Data))
|
||||
})
|
||||
}
|
||||
|
||||
func TestListProposerPreferences_PreFork(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.GloasForkEpoch = 100
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
currentSlot := primitives.Slot(0)
|
||||
s := &Server{
|
||||
ProposerPreferencesCache: cache.NewProposerPreferencesCache(),
|
||||
TimeFetcher: &blockchainmock.ChainService{Slot: ¤tSlot},
|
||||
}
|
||||
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/pool/proposer_preferences", nil)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.ListProposerPreferences(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
}
|
||||
|
||||
func TestListProposerPreferences_PreForkSlot(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.GloasForkEpoch = 1
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
currentSlot := primitives.Slot(32)
|
||||
s := &Server{
|
||||
ProposerPreferencesCache: cache.NewProposerPreferencesCache(),
|
||||
TimeFetcher: &blockchainmock.ChainService{Slot: ¤tSlot},
|
||||
}
|
||||
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/pool/proposer_preferences?slot=5", nil)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.ListProposerPreferences(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
}
|
||||
|
||||
var (
|
||||
singleAtt = `[
|
||||
{
|
||||
|
||||
@@ -25,30 +25,31 @@ import (
|
||||
// Server defines a server implementation of the gRPC Beacon Chain service,
|
||||
// providing RPC endpoints to access data relevant to the Ethereum Beacon Chain.
|
||||
type Server struct {
|
||||
BeaconDB db.ReadOnlyDatabase
|
||||
ChainInfoFetcher blockchain.ChainInfoFetcher
|
||||
GenesisTimeFetcher blockchain.TimeFetcher
|
||||
BlockReceiver blockchain.BlockReceiver
|
||||
BlockNotifier blockfeed.Notifier
|
||||
OperationNotifier operation.Notifier
|
||||
Broadcaster p2p.Broadcaster
|
||||
AttestationCache *cache.AttestationCache
|
||||
AttestationsPool attestations.Pool
|
||||
SlashingsPool slashings.PoolManager
|
||||
VoluntaryExitsPool voluntaryexits.PoolManager
|
||||
StateGenService stategen.StateManager
|
||||
Stater lookup.Stater
|
||||
Blocker lookup.Blocker
|
||||
HeadFetcher blockchain.HeadFetcher
|
||||
TimeFetcher blockchain.TimeFetcher
|
||||
OptimisticModeFetcher blockchain.OptimisticModeFetcher
|
||||
V1Alpha1ValidatorServer eth.BeaconNodeValidatorServer
|
||||
SyncChecker sync.Checker
|
||||
CanonicalHistory *stategen.CanonicalHistory
|
||||
ExecutionReconstructor execution.Reconstructor
|
||||
FinalizationFetcher blockchain.FinalizationFetcher
|
||||
BLSChangesPool blstoexec.PoolManager
|
||||
ForkchoiceFetcher blockchain.ForkchoiceFetcher
|
||||
CoreService *core.Service
|
||||
AttestationStateFetcher blockchain.AttestationStateFetcher
|
||||
BeaconDB db.ReadOnlyDatabase
|
||||
ChainInfoFetcher blockchain.ChainInfoFetcher
|
||||
GenesisTimeFetcher blockchain.TimeFetcher
|
||||
BlockReceiver blockchain.BlockReceiver
|
||||
BlockNotifier blockfeed.Notifier
|
||||
OperationNotifier operation.Notifier
|
||||
Broadcaster p2p.Broadcaster
|
||||
AttestationCache *cache.AttestationCache
|
||||
AttestationsPool attestations.Pool
|
||||
SlashingsPool slashings.PoolManager
|
||||
VoluntaryExitsPool voluntaryexits.PoolManager
|
||||
StateGenService stategen.StateManager
|
||||
Stater lookup.Stater
|
||||
Blocker lookup.Blocker
|
||||
HeadFetcher blockchain.HeadFetcher
|
||||
TimeFetcher blockchain.TimeFetcher
|
||||
OptimisticModeFetcher blockchain.OptimisticModeFetcher
|
||||
V1Alpha1ValidatorServer eth.BeaconNodeValidatorServer
|
||||
SyncChecker sync.Checker
|
||||
CanonicalHistory *stategen.CanonicalHistory
|
||||
ExecutionReconstructor execution.Reconstructor
|
||||
FinalizationFetcher blockchain.FinalizationFetcher
|
||||
BLSChangesPool blstoexec.PoolManager
|
||||
ForkchoiceFetcher blockchain.ForkchoiceFetcher
|
||||
CoreService *core.Service
|
||||
AttestationStateFetcher blockchain.AttestationStateFetcher
|
||||
ProposerPreferencesCache *cache.ProposerPreferencesCache
|
||||
}
|
||||
|
||||
@@ -81,6 +81,8 @@ const (
|
||||
ExecutionPayloadBidTopic = "execution_payload_bid"
|
||||
// PayloadAttestationMessageTopic represents a new payload attestation message event topic.
|
||||
PayloadAttestationMessageTopic = "payload_attestation_message"
|
||||
// ProposerPreferencesTopic represents a new proposer preferences event topic.
|
||||
ProposerPreferencesTopic = "proposer_preferences"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -116,6 +118,7 @@ var opsFeedEventTopics = map[feed.EventType]string{
|
||||
operation.BlockGossipReceived: BlockGossipTopic,
|
||||
operation.DataColumnReceived: DataColumnTopic,
|
||||
operation.PayloadAttestationMessageReceived: PayloadAttestationMessageTopic,
|
||||
operation.ProposerPreferencesReceived: ProposerPreferencesTopic,
|
||||
}
|
||||
|
||||
var stateFeedEventTopics = map[feed.EventType]string{
|
||||
@@ -481,6 +484,8 @@ func topicForEvent(event *feed.Event) string {
|
||||
return DataColumnTopic
|
||||
case *operation.PayloadAttestationMessageReceivedData:
|
||||
return PayloadAttestationMessageTopic
|
||||
case *operation.ProposerPreferencesReceivedData:
|
||||
return ProposerPreferencesTopic
|
||||
case *statefeed.PayloadProcessedData:
|
||||
return ExecutionPayloadTopic
|
||||
default:
|
||||
@@ -659,6 +664,10 @@ func (s *Server) lazyReaderForEvent(ctx context.Context, event *feed.Event, topi
|
||||
return func() io.Reader {
|
||||
return jsonMarshalReader(eventName, structs.PayloadAttestationMessageFromConsensus(v.Message))
|
||||
}, nil
|
||||
case *operation.ProposerPreferencesReceivedData:
|
||||
return func() io.Reader {
|
||||
return jsonMarshalReader(eventName, structs.SignedProposerPreferencesFromConsensus(v.Preferences))
|
||||
}, nil
|
||||
case *statefeed.PayloadProcessedData:
|
||||
return func() io.Reader {
|
||||
return jsonMarshalReader(eventName, &structs.PayloadEvent{
|
||||
|
||||
@@ -124,6 +124,7 @@ func operationEventsFixtures(t *testing.T) (*topicRequest, []*feed.Event) {
|
||||
BlockGossipTopic,
|
||||
DataColumnTopic,
|
||||
PayloadAttestationMessageTopic,
|
||||
ProposerPreferencesTopic,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
ro, err := blocks.NewROBlob(util.HydrateBlobSidecar(ð.BlobSidecar{}))
|
||||
@@ -328,6 +329,20 @@ func operationEventsFixtures(t *testing.T) (*topicRequest, []*feed.Event) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: operation.ProposerPreferencesReceived,
|
||||
Data: &operation.ProposerPreferencesReceivedData{
|
||||
Preferences: ð.SignedProposerPreferences{
|
||||
Message: ð.ProposerPreferences{
|
||||
ProposalSlot: 32,
|
||||
ValidatorIndex: 1,
|
||||
FeeRecipient: make([]byte, 20),
|
||||
GasLimit: 30000000,
|
||||
},
|
||||
Signature: make([]byte, fieldparams.BLSSignatureLength),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ go_library(
|
||||
"//beacon-chain/blockchain:go_default_library",
|
||||
"//beacon-chain/builder:go_default_library",
|
||||
"//beacon-chain/cache:go_default_library",
|
||||
"//beacon-chain/core/feed:go_default_library",
|
||||
"//beacon-chain/core/feed/operation:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
@@ -57,11 +58,13 @@ go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"handlers_block_test.go",
|
||||
"handlers_gloas_test.go",
|
||||
"handlers_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//api:go_default_library",
|
||||
"//api/server:go_default_library",
|
||||
"//api/server/structs:go_default_library",
|
||||
"//beacon-chain/blockchain/testing:go_default_library",
|
||||
"//beacon-chain/builder/testing:go_default_library",
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/api"
|
||||
"github.com/OffchainLabs/prysm/v7/api/server"
|
||||
"github.com/OffchainLabs/prysm/v7/api/server/structs"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/feed"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/feed/operation"
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
"github.com/OffchainLabs/prysm/v7/monitoring/tracing/trace"
|
||||
"github.com/OffchainLabs/prysm/v7/network/httputil"
|
||||
"github.com/OffchainLabs/prysm/v7/time/slots"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// ProduceBlockV4 requests a beacon node to produce a valid Gloas block.
|
||||
@@ -29,3 +41,86 @@ func (s *Server) ExecutionPayloadEnvelope(w http.ResponseWriter, r *http.Request
|
||||
func (s *Server) PublishExecutionPayloadEnvelope(w http.ResponseWriter, r *http.Request) {
|
||||
httputil.HandleError(w, "PublishExecutionPayloadEnvelope not yet implemented", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
// SubmitProposerPreferences submits signed proposer preferences to the node's pool.
|
||||
func (s *Server) SubmitProposerPreferences(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := trace.StartSpan(r.Context(), "validator.SubmitProposerPreferences")
|
||||
defer span.End()
|
||||
|
||||
if slots.ToEpoch(s.TimeFetcher.CurrentSlot()) < params.BeaconConfig().GloasForkEpoch {
|
||||
httputil.HandleError(w, "Proposer preferences are not supported before the gloas fork", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
versionHeader := r.Header.Get(api.VersionHeader)
|
||||
if versionHeader == "" {
|
||||
httputil.HandleError(w, api.VersionHeader+" header is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var req []*structs.SignedProposerPreferences
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
switch {
|
||||
case errors.Is(err, io.EOF):
|
||||
httputil.HandleError(w, "No data submitted", http.StatusBadRequest)
|
||||
return
|
||||
case err != nil:
|
||||
httputil.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if len(req) == 0 {
|
||||
httputil.HandleError(w, "No data submitted", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
currentEpoch := slots.ToEpoch(s.TimeFetcher.CurrentSlot())
|
||||
var failures []*server.IndexedError
|
||||
|
||||
for i, sp := range req {
|
||||
consensus, err := sp.ToConsensus()
|
||||
if err != nil {
|
||||
failures = append(failures, &server.IndexedError{
|
||||
Index: i,
|
||||
Message: "Unable to decode SignedProposerPreferences: " + err.Error(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
if consensus.Message == nil {
|
||||
failures = append(failures, &server.IndexedError{
|
||||
Index: i,
|
||||
Message: "Message is nil",
|
||||
})
|
||||
continue
|
||||
}
|
||||
proposalSlot := consensus.Message.ProposalSlot
|
||||
if slots.ToEpoch(proposalSlot) != currentEpoch+1 {
|
||||
failures = append(failures, &server.IndexedError{
|
||||
Index: i,
|
||||
Message: fmt.Sprintf("proposal_slot must be in the next epoch: slot %d currentEpoch %d", proposalSlot, currentEpoch),
|
||||
})
|
||||
continue
|
||||
}
|
||||
if s.ProposerPreferencesCache.Has(proposalSlot) {
|
||||
continue
|
||||
}
|
||||
s.OperationNotifier.OperationFeed().Send(&feed.Event{
|
||||
Type: operation.ProposerPreferencesReceived,
|
||||
Data: &operation.ProposerPreferencesReceivedData{
|
||||
Preferences: consensus,
|
||||
},
|
||||
})
|
||||
s.ProposerPreferencesCache.Add(proposalSlot, consensus)
|
||||
if err := s.Broadcaster.Broadcast(ctx, consensus); err != nil {
|
||||
log.WithError(err).Error("Could not broadcast signed proposer preferences")
|
||||
}
|
||||
}
|
||||
|
||||
if len(failures) > 0 {
|
||||
failuresErr := &server.IndexedErrorContainer{
|
||||
Code: http.StatusBadRequest,
|
||||
Message: server.ErrIndexedValidationFail,
|
||||
Failures: failures,
|
||||
}
|
||||
httputil.WriteError(w, failuresErr)
|
||||
}
|
||||
}
|
||||
|
||||
208
beacon-chain/rpc/eth/validator/handlers_gloas_test.go
Normal file
208
beacon-chain/rpc/eth/validator/handlers_gloas_test.go
Normal file
@@ -0,0 +1,208 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/api"
|
||||
"github.com/OffchainLabs/prysm/v7/api/server"
|
||||
blockchainmock "github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/testing"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/cache"
|
||||
p2pMock "github.com/OffchainLabs/prysm/v7/beacon-chain/p2p/testing"
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
"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/OffchainLabs/prysm/v7/testing/assert"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/require"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
func TestSubmitProposerPreferences_PreFork(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.GloasForkEpoch = 100
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
s := &Server{TimeFetcher: &blockchainmock.ChainService{}}
|
||||
request := httptest.NewRequest(http.MethodPost, "http://example.com/eth/v1/validator/proposer_preferences", strings.NewReader("[]"))
|
||||
request.Header.Set(api.VersionHeader, "gloas")
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.SubmitProposerPreferences(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
}
|
||||
|
||||
func TestSubmitProposerPreferences_OK(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.GloasForkEpoch = 0
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
c := cache.NewProposerPreferencesCache()
|
||||
currentSlot := primitives.Slot(31)
|
||||
broadcaster := &p2pMock.MockBroadcaster{}
|
||||
|
||||
s := &Server{
|
||||
ProposerPreferencesCache: c,
|
||||
TimeFetcher: &blockchainmock.ChainService{Slot: ¤tSlot},
|
||||
OperationNotifier: &blockchainmock.MockOperationNotifier{},
|
||||
Broadcaster: broadcaster,
|
||||
}
|
||||
|
||||
feeRecipient := bytesutil.PadTo([]byte{0xaa}, 20)
|
||||
sig := bytesutil.PadTo([]byte{0xcc}, 96)
|
||||
body := fmt.Sprintf(`[{
|
||||
"message": {
|
||||
"proposal_slot": "32",
|
||||
"validator_index": "5",
|
||||
"fee_recipient": "%s",
|
||||
"gas_limit": "30000000"
|
||||
},
|
||||
"signature": "%s"
|
||||
}]`, hexutil.Encode(feeRecipient), hexutil.Encode(sig))
|
||||
|
||||
request := httptest.NewRequest(http.MethodPost, "http://example.com/eth/v1/validator/proposer_preferences", strings.NewReader(body))
|
||||
request.Header.Set(api.VersionHeader, "gloas")
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.SubmitProposerPreferences(writer, request)
|
||||
assert.Equal(t, http.StatusOK, writer.Code)
|
||||
|
||||
require.Equal(t, true, c.Has(32))
|
||||
pref, ok := c.Get(32)
|
||||
require.Equal(t, true, ok)
|
||||
assert.DeepEqual(t, feeRecipient, pref.Message.FeeRecipient)
|
||||
assert.Equal(t, uint64(30000000), pref.Message.GasLimit)
|
||||
}
|
||||
|
||||
func TestSubmitProposerPreferences_MissingVersionHeader(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.GloasForkEpoch = 0
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
s := &Server{TimeFetcher: &blockchainmock.ChainService{}}
|
||||
request := httptest.NewRequest(http.MethodPost, "http://example.com/eth/v1/validator/proposer_preferences", strings.NewReader("[]"))
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.SubmitProposerPreferences(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
}
|
||||
|
||||
func TestSubmitProposerPreferences_EmptyBody(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.GloasForkEpoch = 0
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
s := &Server{TimeFetcher: &blockchainmock.ChainService{}}
|
||||
request := httptest.NewRequest(http.MethodPost, "http://example.com/eth/v1/validator/proposer_preferences", nil)
|
||||
request.Header.Set(api.VersionHeader, "gloas")
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.SubmitProposerPreferences(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
}
|
||||
|
||||
func TestSubmitProposerPreferences_WrongEpoch(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.GloasForkEpoch = 0
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
c := cache.NewProposerPreferencesCache()
|
||||
currentSlot := primitives.Slot(31)
|
||||
|
||||
s := &Server{
|
||||
ProposerPreferencesCache: c,
|
||||
TimeFetcher: &blockchainmock.ChainService{Slot: ¤tSlot},
|
||||
}
|
||||
|
||||
feeRecipient := bytesutil.PadTo([]byte{0xaa}, 20)
|
||||
sig := bytesutil.PadTo([]byte{0xcc}, 96)
|
||||
body := fmt.Sprintf(`[{
|
||||
"message": {
|
||||
"proposal_slot": "0",
|
||||
"validator_index": "5",
|
||||
"fee_recipient": "%s",
|
||||
"gas_limit": "30000000"
|
||||
},
|
||||
"signature": "%s"
|
||||
}]`, hexutil.Encode(feeRecipient), hexutil.Encode(sig))
|
||||
|
||||
request := httptest.NewRequest(http.MethodPost, "http://example.com/eth/v1/validator/proposer_preferences", strings.NewReader(body))
|
||||
request.Header.Set(api.VersionHeader, "gloas")
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.SubmitProposerPreferences(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
|
||||
e := &server.IndexedErrorContainer{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
||||
require.Equal(t, 1, len(e.Failures))
|
||||
assert.StringContains(t, "next epoch", e.Failures[0].Message)
|
||||
}
|
||||
|
||||
func TestSubmitProposerPreferences_Duplicate(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.GloasForkEpoch = 0
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
c := cache.NewProposerPreferencesCache()
|
||||
currentSlot := primitives.Slot(31)
|
||||
broadcaster := &p2pMock.MockBroadcaster{}
|
||||
|
||||
sp := ðpb.SignedProposerPreferences{
|
||||
Message: ðpb.ProposerPreferences{
|
||||
ProposalSlot: 32,
|
||||
ValidatorIndex: 5,
|
||||
FeeRecipient: bytesutil.PadTo([]byte{0xaa}, 20),
|
||||
GasLimit: 30000000,
|
||||
},
|
||||
Signature: bytesutil.PadTo([]byte{0xcc}, 96),
|
||||
}
|
||||
c.Add(32, sp)
|
||||
|
||||
s := &Server{
|
||||
ProposerPreferencesCache: c,
|
||||
TimeFetcher: &blockchainmock.ChainService{Slot: ¤tSlot},
|
||||
OperationNotifier: &blockchainmock.MockOperationNotifier{},
|
||||
Broadcaster: broadcaster,
|
||||
}
|
||||
|
||||
feeRecipient := bytesutil.PadTo([]byte{0xbb}, 20)
|
||||
sig := bytesutil.PadTo([]byte{0xdd}, 96)
|
||||
body := fmt.Sprintf(`[{
|
||||
"message": {
|
||||
"proposal_slot": "32",
|
||||
"validator_index": "6",
|
||||
"fee_recipient": "%s",
|
||||
"gas_limit": "30000001"
|
||||
},
|
||||
"signature": "%s"
|
||||
}]`, hexutil.Encode(feeRecipient), hexutil.Encode(sig))
|
||||
|
||||
request := httptest.NewRequest(http.MethodPost, "http://example.com/eth/v1/validator/proposer_preferences", strings.NewReader(body))
|
||||
request.Header.Set(api.VersionHeader, "gloas")
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.SubmitProposerPreferences(writer, request)
|
||||
assert.Equal(t, http.StatusOK, writer.Code)
|
||||
|
||||
pref, ok := c.Get(32)
|
||||
require.Equal(t, true, ok)
|
||||
assert.DeepEqual(t, bytesutil.PadTo([]byte{0xaa}, 20), pref.Message.FeeRecipient)
|
||||
}
|
||||
@@ -19,23 +19,24 @@ import (
|
||||
// Server defines a server implementation of the gRPC Validator service,
|
||||
// providing RPC endpoints intended for validator clients.
|
||||
type Server struct {
|
||||
HeadFetcher blockchain.HeadFetcher
|
||||
TimeFetcher blockchain.TimeFetcher
|
||||
SyncChecker sync.Checker
|
||||
AttestationCache *cache.AttestationCache
|
||||
AttestationsPool attestations.Pool
|
||||
PeerManager p2p.PeerManager
|
||||
Broadcaster p2p.Broadcaster
|
||||
Stater lookup.Stater
|
||||
OptimisticModeFetcher blockchain.OptimisticModeFetcher
|
||||
SyncCommitteePool synccommittee.Pool
|
||||
V1Alpha1Server eth.BeaconNodeValidatorServer
|
||||
ChainInfoFetcher blockchain.ChainInfoFetcher
|
||||
BeaconDB db.HeadAccessDatabase
|
||||
BlockBuilder builder.BlockBuilder
|
||||
OperationNotifier operation.Notifier
|
||||
CoreService *core.Service
|
||||
BlockRewardFetcher rewards.BlockRewardsFetcher
|
||||
TrackedValidatorsCache *cache.TrackedValidatorsCache
|
||||
PayloadIDCache *cache.PayloadIDCache
|
||||
HeadFetcher blockchain.HeadFetcher
|
||||
TimeFetcher blockchain.TimeFetcher
|
||||
SyncChecker sync.Checker
|
||||
AttestationCache *cache.AttestationCache
|
||||
AttestationsPool attestations.Pool
|
||||
PeerManager p2p.PeerManager
|
||||
Broadcaster p2p.Broadcaster
|
||||
Stater lookup.Stater
|
||||
OptimisticModeFetcher blockchain.OptimisticModeFetcher
|
||||
SyncCommitteePool synccommittee.Pool
|
||||
V1Alpha1Server eth.BeaconNodeValidatorServer
|
||||
ChainInfoFetcher blockchain.ChainInfoFetcher
|
||||
BeaconDB db.HeadAccessDatabase
|
||||
BlockBuilder builder.BlockBuilder
|
||||
OperationNotifier operation.Notifier
|
||||
CoreService *core.Service
|
||||
BlockRewardFetcher rewards.BlockRewardsFetcher
|
||||
TrackedValidatorsCache *cache.TrackedValidatorsCache
|
||||
PayloadIDCache *cache.PayloadIDCache
|
||||
ProposerPreferencesCache *cache.ProposerPreferencesCache
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package validator
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/feed"
|
||||
opfeed "github.com/OffchainLabs/prysm/v7/beacon-chain/core/feed/operation"
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
"github.com/OffchainLabs/prysm/v7/monitoring/tracing/trace"
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
@@ -69,7 +71,13 @@ func (vs *Server) SubmitSignedProposerPreferences(
|
||||
broadcast, len(req.SignedProposerPreferences), err)
|
||||
}
|
||||
|
||||
vs.ProposerPreferencesCache.Add(proposalSlot, msg.Message.FeeRecipient, msg.Message.GasLimit)
|
||||
vs.ProposerPreferencesCache.Add(proposalSlot, msg)
|
||||
vs.OperationNotifier.OperationFeed().Send(&feed.Event{
|
||||
Type: opfeed.ProposerPreferencesReceived,
|
||||
Data: &opfeed.ProposerPreferencesReceivedData{
|
||||
Preferences: msg,
|
||||
},
|
||||
})
|
||||
broadcast++
|
||||
}
|
||||
|
||||
|
||||
@@ -53,8 +53,8 @@ func TestSubmitSignedProposerPreferences_OK(t *testing.T) {
|
||||
assert.Equal(t, true, p2p.BroadcastCalled.Load())
|
||||
pref, ok := cache.Get(proposalSlot)
|
||||
require.Equal(t, true, ok)
|
||||
require.DeepEqual(t, req.SignedProposerPreferences[0].Message.FeeRecipient, pref.FeeRecipient)
|
||||
require.Equal(t, req.SignedProposerPreferences[0].Message.GasLimit, pref.GasLimit)
|
||||
require.DeepEqual(t, req.SignedProposerPreferences[0].Message.FeeRecipient, pref.Message.FeeRecipient)
|
||||
require.Equal(t, req.SignedProposerPreferences[0].Message.GasLimit, pref.Message.GasLimit)
|
||||
}
|
||||
|
||||
func TestSubmitSignedProposerPreferences_Multiple(t *testing.T) {
|
||||
@@ -105,7 +105,7 @@ func TestSubmitSignedProposerPreferences_Multiple(t *testing.T) {
|
||||
require.Equal(t, true, ok)
|
||||
pref2, ok := c.Get(currentSlot + 2)
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, uint64(25_000_000), pref2.GasLimit)
|
||||
require.Equal(t, uint64(25_000_000), pref2.Message.GasLimit)
|
||||
}
|
||||
|
||||
func TestSubmitSignedProposerPreferences_DuplicateSlot(t *testing.T) {
|
||||
@@ -119,7 +119,9 @@ func TestSubmitSignedProposerPreferences_DuplicateSlot(t *testing.T) {
|
||||
chain := &chainMock.ChainService{Slot: ¤tSlot}
|
||||
p2p := &p2pmock.MockBroadcaster{}
|
||||
c := cache.NewProposerPreferencesCache()
|
||||
c.Add(proposalSlot, make([]byte, 20), 30_000_000)
|
||||
c.Add(proposalSlot, ðpb.SignedProposerPreferences{
|
||||
Message: ðpb.ProposerPreferences{ProposalSlot: proposalSlot, FeeRecipient: make([]byte, 20), GasLimit: 30_000_000},
|
||||
})
|
||||
vs := &Server{
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
TimeFetcher: chain,
|
||||
|
||||
@@ -75,11 +75,11 @@ func (s *Service) validateExecutionPayloadBidGossip(ctx context.Context, pid pee
|
||||
return pubsub.ValidationReject, err
|
||||
}
|
||||
// [REJECT] bid.fee_recipient matches the fee_recipient from the proposer's SignedProposerPreferences associated with bid.slot.
|
||||
if err := v.VerifyFeeRecipientMatches(pref.FeeRecipient); err != nil {
|
||||
if err := v.VerifyFeeRecipientMatches(pref.Message.FeeRecipient); err != nil {
|
||||
return pubsub.ValidationReject, err
|
||||
}
|
||||
// [REJECT] bid.gas_limit matches the gas_limit from the proposer's SignedProposerPreferences associated with bid.slot.
|
||||
if err := v.VerifyGasLimitMatches(pref.GasLimit); err != nil {
|
||||
if err := v.VerifyGasLimitMatches(pref.Message.GasLimit); err != nil {
|
||||
return pubsub.ValidationReject, err
|
||||
}
|
||||
// The spec lists signature validation later, but the "first signed bid seen
|
||||
|
||||
@@ -363,7 +363,13 @@ func setupExecutionPayloadBidService(t *testing.T) (*Service, *pubsub.Message, *
|
||||
}
|
||||
signedBid := util.GenerateTestSignedExecutionPayloadBid(1)
|
||||
signedBid.Message.BuilderIndex = 1
|
||||
require.Equal(t, true, s.proposerPreferencesCache.Add(signedBid.Message.Slot, signedBid.Message.FeeRecipient, signedBid.Message.GasLimit))
|
||||
require.Equal(t, true, s.proposerPreferencesCache.Add(signedBid.Message.Slot, ðpb.SignedProposerPreferences{
|
||||
Message: ðpb.ProposerPreferences{
|
||||
ProposalSlot: signedBid.Message.Slot,
|
||||
FeeRecipient: signedBid.Message.FeeRecipient,
|
||||
GasLimit: signedBid.Message.GasLimit,
|
||||
},
|
||||
}))
|
||||
msg := executionPayloadBidToPubsub(t, s, p, signedBid)
|
||||
return s, msg, signedBid
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package sync
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/feed"
|
||||
opfeed "github.com/OffchainLabs/prysm/v7/beacon-chain/core/feed/operation"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/p2p"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/verification"
|
||||
"github.com/OffchainLabs/prysm/v7/monitoring/tracing/trace"
|
||||
@@ -68,15 +70,21 @@ func (s *Service) validateSignedProposerPreferencesGossip(ctx context.Context, p
|
||||
return pubsub.ValidationReject, err
|
||||
}
|
||||
|
||||
s.proposerPreferencesCache.Add(slot, signedPreferences.Message.FeeRecipient, signedPreferences.Message.GasLimit)
|
||||
s.proposerPreferencesCache.Add(slot, signedPreferences)
|
||||
msg.ValidatorData = signedPreferences
|
||||
return pubsub.ValidationAccept, nil
|
||||
}
|
||||
|
||||
func (s *Service) signedProposerPreferencesSubscriber(_ context.Context, msg proto.Message) error {
|
||||
_, ok := msg.(*ethpb.SignedProposerPreferences)
|
||||
sp, ok := msg.(*ethpb.SignedProposerPreferences)
|
||||
if !ok {
|
||||
return errWrongMessage
|
||||
}
|
||||
s.cfg.operationNotifier.OperationFeed().Send(&feed.Event{
|
||||
Type: opfeed.ProposerPreferencesReceived,
|
||||
Data: &opfeed.ProposerPreferencesReceivedData{
|
||||
Preferences: sp,
|
||||
},
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -96,7 +96,9 @@ func TestValidateSignedProposerPreferencesGossip_AlreadySeenSlot(t *testing.T) {
|
||||
s, msg, signedPreferences := setupSignedProposerPreferencesService(t)
|
||||
s.newSignedProposerPreferencesVerifier = testNewSignedProposerPreferencesVerifier(mockSignedProposerPreferencesVerifier{})
|
||||
|
||||
require.Equal(t, true, s.proposerPreferencesCache.Add(signedPreferences.Message.ProposalSlot, []byte{0x01}, 10))
|
||||
require.Equal(t, true, s.proposerPreferencesCache.Add(signedPreferences.Message.ProposalSlot, ðpb.SignedProposerPreferences{
|
||||
Message: ðpb.ProposerPreferences{ProposalSlot: signedPreferences.Message.ProposalSlot, FeeRecipient: []byte{0x01}, GasLimit: 10},
|
||||
}))
|
||||
result, err := s.validateSignedProposerPreferencesGossip(ctx, "", msg)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, pubsub.ValidationIgnore, result)
|
||||
@@ -114,21 +116,25 @@ func TestValidateSignedProposerPreferencesGossip_HappyPath(t *testing.T) {
|
||||
|
||||
got, ok := s.proposerPreferencesCache.Get(signedPreferences.Message.ProposalSlot)
|
||||
require.Equal(t, true, ok)
|
||||
require.DeepEqual(t, signedPreferences.Message.FeeRecipient, got.FeeRecipient)
|
||||
require.Equal(t, signedPreferences.Message.GasLimit, got.GasLimit)
|
||||
require.DeepEqual(t, signedPreferences.Message.FeeRecipient, got.Message.FeeRecipient)
|
||||
require.Equal(t, signedPreferences.Message.GasLimit, got.Message.GasLimit)
|
||||
validatorData, ok := msg.ValidatorData.(*ethpb.SignedProposerPreferences)
|
||||
require.Equal(t, true, ok)
|
||||
require.DeepEqual(t, signedPreferences, validatorData)
|
||||
}
|
||||
|
||||
func TestSignedProposerPreferencesSubscriber_WrongMessage(t *testing.T) {
|
||||
s := &Service{}
|
||||
s := &Service{
|
||||
cfg: &config{operationNotifier: &mock.MockOperationNotifier{}},
|
||||
}
|
||||
err := s.signedProposerPreferencesSubscriber(context.Background(), ðpb.BeaconBlock{})
|
||||
require.ErrorIs(t, errWrongMessage, err)
|
||||
}
|
||||
|
||||
func TestSignedProposerPreferencesSubscriber_HappyPath(t *testing.T) {
|
||||
s := &Service{}
|
||||
s := &Service{
|
||||
cfg: &config{operationNotifier: &mock.MockOperationNotifier{}},
|
||||
}
|
||||
err := s.signedProposerPreferencesSubscriber(context.Background(), ðpb.SignedProposerPreferences{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user