Unregister validator - fix behind feature flag (#12316)

* adding changes to blocks

* trying out expiration

* adding implementation, have WIP for tests

* adding unit tests for cache

* fixing bazel complaints

* fix linting

* adding safe check for unint type

* changing approach to safety check

* adding cache to bazel to test fixing build

* reverting bazel change and adding flag to usage

* implementing interface on mock to fix build error

* fixing unit tests

* fixing unit test

* fixing unit tests

* fixing linting

* fixing more unit tests

* fixing produce blinded block tests

* Update beacon-chain/cache/registration.go

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>

* resolving review comments

* fixing cache

* Update beacon-chain/cache/registration.go

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>

* Update beacon-chain/cache/registration.go

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>

* fixing time logic

* adding context to trace

* fix bazel lint

* fixing context dependency

* fix linting

* Update cmd/beacon-chain/flags/base.go

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>

* addressing review comments

* fixing deepsource issues

* improving the default settings

* fixing bazel

* removing irrelevant unit test

* updating name

---------

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>
This commit is contained in:
james-prysm 2023-04-28 16:27:47 -05:00 committed by GitHub
parent d382abe198
commit 83416f31a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 294 additions and 43 deletions

View File

@ -12,6 +12,7 @@ go_library(
deps = [
"//api/client/builder:go_default_library",
"//beacon-chain/blockchain:go_default_library",
"//beacon-chain/cache:go_default_library",
"//beacon-chain/db:go_default_library",
"//cmd/beacon-chain/flags:go_default_library",
"//consensus-types/interfaces:go_default_library",

View File

@ -3,6 +3,7 @@ package builder
import (
"github.com/prysmaticlabs/prysm/v4/api/client/builder"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db"
"github.com/prysmaticlabs/prysm/v4/cmd/beacon-chain/flags"
"github.com/urfave/cli/v2"
@ -50,3 +51,11 @@ func WithDatabase(beaconDB db.HeadAccessDatabase) Option {
return nil
}
}
// WithRegistrationCache uses a cache for the validator registrations instead of a persistent db.
func WithRegistrationCache() Option {
return func(s *Service) error {
s.registrationCache = cache.NewRegistrationCache()
return nil
}
}

View File

@ -8,6 +8,7 @@ import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/api/client/builder"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
@ -26,6 +27,7 @@ type BlockBuilder interface {
SubmitBlindedBlock(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock) (interfaces.ExecutionData, error)
GetHeader(ctx context.Context, slot primitives.Slot, parentHash [32]byte, pubKey [48]byte) (builder.SignedBid, error)
RegisterValidator(ctx context.Context, reg []*ethpb.SignedValidatorRegistrationV1) error
RegistrationByValidatorID(ctx context.Context, id primitives.ValidatorIndex) (*ethpb.ValidatorRegistrationV1, error)
Configured() bool
}
@ -38,10 +40,11 @@ type config struct {
// Service defines a service that provides a client for interacting with the beacon chain and MEV relay network.
type Service struct {
cfg *config
c builder.BuilderClient
ctx context.Context
cancel context.CancelFunc
cfg *config
c builder.BuilderClient
ctx context.Context
cancel context.CancelFunc
registrationCache *cache.RegistrationCache
}
// NewService instantiates a new service.
@ -139,8 +142,12 @@ func (s *Service) RegisterValidator(ctx context.Context, reg []*ethpb.SignedVali
return ErrNoBuilder
}
// should be removed if db is removed
idxs := make([]primitives.ValidatorIndex, 0)
msgs := make([]*ethpb.ValidatorRegistrationV1, 0)
indexToRegistration := make(map[primitives.ValidatorIndex]*ethpb.ValidatorRegistrationV1)
valid := make([]*ethpb.SignedValidatorRegistrationV1, 0)
for i := 0; i < len(reg); i++ {
r := reg[i]
@ -154,12 +161,33 @@ func (s *Service) RegisterValidator(ctx context.Context, reg []*ethpb.SignedVali
idxs = append(idxs, nx)
msgs = append(msgs, r.Message)
valid = append(valid, r)
indexToRegistration[nx] = r.Message
}
if err := s.c.RegisterValidator(ctx, valid); err != nil {
return errors.Wrap(err, "could not register validator(s)")
}
return s.cfg.beaconDB.SaveRegistrationsByValidatorIDs(ctx, idxs, msgs)
if len(indexToRegistration) != len(msgs) {
return errors.New("ids and registrations must be the same length")
}
if s.registrationCache != nil {
s.registrationCache.UpdateIndexToRegisteredMap(ctx, indexToRegistration)
return nil
} else {
return s.cfg.beaconDB.SaveRegistrationsByValidatorIDs(ctx, idxs, msgs)
}
}
// RegistrationByValidatorID returns either the values from the cache or db.
func (s *Service) RegistrationByValidatorID(ctx context.Context, id primitives.ValidatorIndex) (*ethpb.ValidatorRegistrationV1, error) {
if s.registrationCache != nil {
return s.registrationCache.RegistrationByIndex(id)
} else {
if s.cfg == nil || s.cfg.beaconDB == nil {
return nil, errors.New("nil beacon db")
}
return s.cfg.beaconDB.RegistrationByValidatorID(ctx, id)
}
}
// Configured returns true if the user has configured a builder client.

View File

@ -8,6 +8,8 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//api/client/builder:go_default_library",
"//beacon-chain/cache:go_default_library",
"//beacon-chain/db:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/interfaces:go_default_library",

View File

@ -5,6 +5,8 @@ import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/api/client/builder"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
@ -14,6 +16,11 @@ import (
"github.com/prysmaticlabs/prysm/v4/time/slots"
)
// Config defines a config struct for dependencies into the service.
type Config struct {
BeaconDB db.HeadAccessDatabase
}
// MockBuilderService to mock builder.
type MockBuilderService struct {
HasConfigured bool
@ -22,8 +29,10 @@ type MockBuilderService struct {
ErrSubmitBlindedBlock error
Bid *ethpb.SignedBuilderBid
BidCapella *ethpb.SignedBuilderBidCapella
RegistrationCache *cache.RegistrationCache
ErrGetHeader error
ErrRegisterValidator error
Cfg *Config
}
// Configured for mocking.
@ -48,7 +57,7 @@ func (s *MockBuilderService) SubmitBlindedBlock(_ context.Context, _ interfaces.
}
// GetHeader for mocking.
func (s *MockBuilderService) GetHeader(ctx context.Context, slot primitives.Slot, hr [32]byte, pb [48]byte) (builder.SignedBid, error) {
func (s *MockBuilderService) GetHeader(_ context.Context, slot primitives.Slot, _ [32]byte, _ [48]byte) (builder.SignedBid, error) {
if slots.ToEpoch(slot) >= params.BeaconConfig().CapellaForkEpoch {
return builder.WrappedSignedBuilderBidCapella(s.BidCapella)
}
@ -59,6 +68,17 @@ func (s *MockBuilderService) GetHeader(ctx context.Context, slot primitives.Slot
return w, s.ErrGetHeader
}
// RegistrationByValidatorID returns either the values from the cache or db.
func (s *MockBuilderService) RegistrationByValidatorID(ctx context.Context, id primitives.ValidatorIndex) (*ethpb.ValidatorRegistrationV1, error) {
if s.RegistrationCache != nil {
return s.RegistrationCache.RegistrationByIndex(id)
}
if s.Cfg.BeaconDB != nil {
return s.Cfg.BeaconDB.RegistrationByValidatorID(ctx, id)
}
return nil, cache.ErrNotFoundRegistration
}
// RegisterValidator for mocking.
func (s *MockBuilderService) RegisterValidator(context.Context, []*ethpb.SignedValidatorRegistrationV1) error {
return s.ErrRegisterValidator

View File

@ -17,6 +17,7 @@ go_library(
"proposer_indices.go",
"proposer_indices_disabled.go", # keep
"proposer_indices_type.go",
"registration.go",
"skip_slot_cache.go",
"subnet_ids.go",
"sync_committee.go",
@ -65,6 +66,7 @@ go_test(
"committee_test.go",
"payload_id_test.go",
"proposer_indices_test.go",
"registration_test.go",
"skip_slot_cache_test.go",
"subnet_ids_test.go",
"sync_committee_head_state_test.go",
@ -83,7 +85,9 @@ go_test(
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
"//testing/util:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_google_gofuzz//:go_default_library",
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
],
)

View File

@ -1,6 +1,6 @@
package cache
import "errors"
import "github.com/pkg/errors"
var (
// ErrNilValueProvided for when we try to put a nil value in a cache.
@ -12,4 +12,6 @@ var (
// ErrNonExistingSyncCommitteeKey when sync committee key (root) does not exist in cache.
ErrNonExistingSyncCommitteeKey = errors.New("does not exist sync committee key")
errNotSyncCommitteeIndexPosition = errors.New("not syncCommitteeIndexPosition struct")
// ErrNotFoundRegistration when validator registration does not exist in cache.
ErrNotFoundRegistration = errors.Wrap(ErrNotFound, "no validator registered")
)

82
beacon-chain/cache/registration.go vendored Normal file
View File

@ -0,0 +1,82 @@
package cache
import (
"context"
"sync"
"time"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v4/math"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
log "github.com/sirupsen/logrus"
"go.opencensus.io/trace"
)
// RegistrationCache is used to store the cached results of an Validator Registration request.
// beacon api /eth/v1/validator/register_validator
type RegistrationCache struct {
indexToRegistration map[primitives.ValidatorIndex]*ethpb.ValidatorRegistrationV1
lock sync.RWMutex
}
// NewRegistrationCache initializes the map and underlying cache.
func NewRegistrationCache() *RegistrationCache {
return &RegistrationCache{
indexToRegistration: make(map[primitives.ValidatorIndex]*ethpb.ValidatorRegistrationV1),
lock: sync.RWMutex{},
}
}
// RegistrationByIndex returns the registration by index in the cache and also removes items in the cache if expired.
func (regCache *RegistrationCache) RegistrationByIndex(id primitives.ValidatorIndex) (*ethpb.ValidatorRegistrationV1, error) {
regCache.lock.RLock()
v, ok := regCache.indexToRegistration[id]
if !ok {
regCache.lock.RUnlock()
return nil, errors.Wrapf(ErrNotFoundRegistration, "validator id %d", id)
}
isExpired, err := RegistrationTimeStampExpired(v.Timestamp)
if err != nil {
return nil, errors.Wrapf(err, "failed to check registration expiration")
}
if isExpired {
regCache.lock.RUnlock()
regCache.lock.Lock()
defer regCache.lock.Unlock()
delete(regCache.indexToRegistration, id)
log.Warnf("registration for validator index %d expired at unix time %d", id, v.Timestamp)
return nil, errors.Wrapf(ErrNotFoundRegistration, "validator id %d", id)
}
regCache.lock.RUnlock()
return v, nil
}
func RegistrationTimeStampExpired(ts uint64) (bool, error) {
// safely convert unint64 to int64
i, err := math.Int(ts)
if err != nil {
return false, err
}
expiryDuration := params.BeaconConfig().RegistrationDuration
// registered time + expiration duration < current time = expired
return time.Unix(int64(i), 0).Add(expiryDuration).Before(time.Now()), nil
}
// UpdateIndexToRegisteredMap adds or updates values in the cache based on the argument.
func (regCache *RegistrationCache) UpdateIndexToRegisteredMap(ctx context.Context, m map[primitives.ValidatorIndex]*ethpb.ValidatorRegistrationV1) {
_, span := trace.StartSpan(ctx, "RegistrationCache.UpdateIndexToRegisteredMap")
defer span.End()
regCache.lock.Lock()
defer regCache.lock.Unlock()
for key, value := range m {
regCache.indexToRegistration[key] = &ethpb.ValidatorRegistrationV1{
Pubkey: bytesutil.SafeCopyBytes(value.Pubkey),
FeeRecipient: bytesutil.SafeCopyBytes(value.FeeRecipient),
GasLimit: value.GasLimit,
Timestamp: value.Timestamp,
}
}
}

82
beacon-chain/cache/registration_test.go vendored Normal file
View File

@ -0,0 +1,82 @@
package cache
import (
"context"
"testing"
"time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/require"
logTest "github.com/sirupsen/logrus/hooks/test"
)
func TestRegistrationCache(t *testing.T) {
hook := logTest.NewGlobal()
pubkey, err := hexutil.Decode("0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a")
require.NoError(t, err)
validatorIndex := primitives.ValidatorIndex(1)
cache := NewRegistrationCache()
m := make(map[primitives.ValidatorIndex]*ethpb.ValidatorRegistrationV1)
m[validatorIndex] = &ethpb.ValidatorRegistrationV1{
FeeRecipient: []byte{},
GasLimit: 100,
Timestamp: uint64(time.Now().Unix()),
Pubkey: pubkey,
}
cache.UpdateIndexToRegisteredMap(context.Background(), m)
reg, err := cache.RegistrationByIndex(validatorIndex)
require.NoError(t, err)
require.Equal(t, string(reg.Pubkey), string(pubkey))
t.Run("Registration expired", func(t *testing.T) {
validatorIndex2 := primitives.ValidatorIndex(2)
overExpirationPadTime := time.Second * time.Duration(params.BeaconConfig().SecondsPerSlot*uint64(params.BeaconConfig().SlotsPerEpoch)*4) // 4 epochs
m[validatorIndex2] = &ethpb.ValidatorRegistrationV1{
FeeRecipient: []byte{},
GasLimit: 100,
Timestamp: uint64(time.Now().Add(-1 * overExpirationPadTime).Unix()),
Pubkey: pubkey,
}
cache.UpdateIndexToRegisteredMap(context.Background(), m)
_, err := cache.RegistrationByIndex(validatorIndex2)
require.ErrorContains(t, "no validator registered", err)
require.LogsContain(t, hook, "expired")
})
t.Run("Registration close to expiration still passes", func(t *testing.T) {
pubkey, err := hexutil.Decode("0x88247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a")
require.NoError(t, err)
validatorIndex2 := primitives.ValidatorIndex(2)
overExpirationPadTime := time.Second * time.Duration((params.BeaconConfig().SecondsPerSlot*uint64(params.BeaconConfig().SlotsPerEpoch)*3)-5) // 3 epochs - 5 seconds
m[validatorIndex2] = &ethpb.ValidatorRegistrationV1{
FeeRecipient: []byte{},
GasLimit: 100,
Timestamp: uint64(time.Now().Add(-1 * overExpirationPadTime).Unix()),
Pubkey: pubkey,
}
cache.UpdateIndexToRegisteredMap(context.Background(), m)
reg, err := cache.RegistrationByIndex(validatorIndex2)
require.NoError(t, err)
require.Equal(t, string(reg.Pubkey), string(pubkey))
})
}
func Test_RegistrationTimeStampExpired(t *testing.T) {
// expiration set at 3 epochs
t.Run("expired registration", func(t *testing.T) {
overExpirationPadTime := time.Second * time.Duration(params.BeaconConfig().SecondsPerSlot*uint64(params.BeaconConfig().SlotsPerEpoch)*4) // 4 epochs
ts := uint64(time.Now().Add(-1 * overExpirationPadTime).Unix())
isExpired, err := RegistrationTimeStampExpired(ts)
require.NoError(t, err)
require.Equal(t, true, isExpired)
})
t.Run("is not expired registration", func(t *testing.T) {
overExpirationPadTime := time.Second * time.Duration((params.BeaconConfig().SecondsPerSlot*uint64(params.BeaconConfig().SlotsPerEpoch)*3)-5) // 3 epochs -5 seconds
ts := uint64(time.Now().Add(-1 * overExpirationPadTime).Unix())
isExpired, err := RegistrationTimeStampExpired(ts)
require.NoError(t, err)
require.Equal(t, false, isExpired)
})
}

View File

@ -3,6 +3,7 @@ package kv
import (
"context"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/pkg/errors"
@ -910,25 +911,25 @@ func TestStore_RegistrationsByValidatorID(t *testing.T) {
ids := []primitives.ValidatorIndex{0, 0, 0}
regs := []*ethpb.ValidatorRegistrationV1{{}, {}, {}, {}}
require.ErrorContains(t, "ids and registrations must be the same length", db.SaveRegistrationsByValidatorIDs(ctx, ids, regs))
timestamp := time.Now().Unix()
ids = []primitives.ValidatorIndex{0, 1, 2}
regs = []*ethpb.ValidatorRegistrationV1{
{
FeeRecipient: bytesutil.PadTo([]byte("a"), 20),
GasLimit: 1,
Timestamp: 2,
Timestamp: uint64(timestamp),
Pubkey: bytesutil.PadTo([]byte("b"), 48),
},
{
FeeRecipient: bytesutil.PadTo([]byte("c"), 20),
GasLimit: 3,
Timestamp: 4,
Timestamp: uint64(timestamp),
Pubkey: bytesutil.PadTo([]byte("d"), 48),
},
{
FeeRecipient: bytesutil.PadTo([]byte("e"), 20),
GasLimit: 5,
Timestamp: 6,
Timestamp: uint64(timestamp),
Pubkey: bytesutil.PadTo([]byte("f"), 48),
},
}
@ -938,7 +939,7 @@ func TestStore_RegistrationsByValidatorID(t *testing.T) {
require.DeepEqual(t, &ethpb.ValidatorRegistrationV1{
FeeRecipient: bytesutil.PadTo([]byte("a"), 20),
GasLimit: 1,
Timestamp: 2,
Timestamp: uint64(timestamp),
Pubkey: bytesutil.PadTo([]byte("b"), 48),
}, f)
f, err = db.RegistrationByValidatorID(ctx, 1)
@ -946,7 +947,7 @@ func TestStore_RegistrationsByValidatorID(t *testing.T) {
require.DeepEqual(t, &ethpb.ValidatorRegistrationV1{
FeeRecipient: bytesutil.PadTo([]byte("c"), 20),
GasLimit: 3,
Timestamp: 4,
Timestamp: uint64(timestamp),
Pubkey: bytesutil.PadTo([]byte("d"), 48),
}, f)
f, err = db.RegistrationByValidatorID(ctx, 2)
@ -954,7 +955,7 @@ func TestStore_RegistrationsByValidatorID(t *testing.T) {
require.DeepEqual(t, &ethpb.ValidatorRegistrationV1{
FeeRecipient: bytesutil.PadTo([]byte("e"), 20),
GasLimit: 5,
Timestamp: 6,
Timestamp: uint64(timestamp),
Pubkey: bytesutil.PadTo([]byte("f"), 48),
}, f)
_, err = db.RegistrationByValidatorID(ctx, 3)

View File

@ -249,7 +249,7 @@ func New(cliCtx *cli.Context, opts ...Option) (*BeaconNode, error) {
}
log.Debugln("Registering builder service")
if err := beacon.registerBuilderService(); err != nil {
if err := beacon.registerBuilderService(cliCtx); err != nil {
return nil, err
}
@ -961,7 +961,7 @@ func (b *BeaconNode) registerValidatorMonitorService() error {
return b.services.RegisterService(svc)
}
func (b *BeaconNode) registerBuilderService() error {
func (b *BeaconNode) registerBuilderService(cliCtx *cli.Context) error {
var chainService *blockchain.Service
if err := b.services.FetchService(&chainService); err != nil {
return err
@ -970,6 +970,9 @@ func (b *BeaconNode) registerBuilderService() error {
opts := append(b.serviceFlagOpts.builderOpts,
builder.WithHeadFetcher(chainService),
builder.WithDatabase(b.db))
if cliCtx.Bool(flags.EnableRegistrationCache.Name) {
opts = append(opts, builder.WithRegistrationCache())
}
svc, err := builder.NewService(b.ctx, opts...)
if err != nil {
return err

View File

@ -744,6 +744,7 @@ func (vs *Server) PrepareBeaconProposer(
if err := vs.BeaconDB.SaveFeeRecipientsByValidatorIDs(ctx, validatorIndices, feeRecipients); err != nil {
return nil, status.Errorf(codes.Internal, "Could not save fee recipients: %v", err)
}
log.WithFields(log.Fields{
"validatorIndices": validatorIndices,
}).Info("Updated fee recipient addresses for validator indices")

View File

@ -776,7 +776,6 @@ func TestProduceBlockV2(t *testing.T) {
resp, err := server.ProduceBlockV2(ctx, &ethpbv1.ProduceBlockRequest{})
require.NoError(t, err)
assert.Equal(t, ethpbv2.Version_CAPELLA, resp.Version)
containerBlock, ok := resp.Data.Block.(*ethpbv2.BeaconBlockContainerV2_CapellaBlock)
require.Equal(t, true, ok)

View File

@ -69,7 +69,7 @@ func TestServer_setExecutionData(t *testing.T) {
FinalizationFetcher: &blockchainTest.ChainService{},
BeaconDB: beaconDB,
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true},
BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true, Cfg: &builderTest.Config{BeaconDB: beaconDB}},
}
t.Run("No builder configured. Use local block", func(t *testing.T) {
@ -84,7 +84,7 @@ func TestServer_setExecutionData(t *testing.T) {
blk, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella())
require.NoError(t, err)
require.NoError(t, vs.BeaconDB.SaveRegistrationsByValidatorIDs(ctx, []primitives.ValidatorIndex{blk.Block().ProposerIndex()},
[]*ethpb.ValidatorRegistrationV1{{FeeRecipient: make([]byte, fieldparams.FeeRecipientLength), Pubkey: make([]byte, fieldparams.BLSPubkeyLength)}}))
[]*ethpb.ValidatorRegistrationV1{{FeeRecipient: make([]byte, fieldparams.FeeRecipientLength), Timestamp: uint64(time.Now().Unix()), Pubkey: make([]byte, fieldparams.BLSPubkeyLength)}}))
ti, err := slots.ToTime(uint64(time.Now().Unix()), 0)
require.NoError(t, err)
sk, err := bls.RandKey()
@ -119,6 +119,7 @@ func TestServer_setExecutionData(t *testing.T) {
vs.BlockBuilder = &builderTest.MockBuilderService{
BidCapella: sBid,
HasConfigured: true,
Cfg: &builderTest.Config{BeaconDB: beaconDB},
}
wb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockBellatrix())
require.NoError(t, err)
@ -136,7 +137,7 @@ func TestServer_setExecutionData(t *testing.T) {
blk, err := blocks.NewSignedBeaconBlock(util.NewBlindedBeaconBlockCapella())
require.NoError(t, err)
require.NoError(t, vs.BeaconDB.SaveRegistrationsByValidatorIDs(ctx, []primitives.ValidatorIndex{blk.Block().ProposerIndex()},
[]*ethpb.ValidatorRegistrationV1{{FeeRecipient: make([]byte, fieldparams.FeeRecipientLength), Pubkey: make([]byte, fieldparams.BLSPubkeyLength)}}))
[]*ethpb.ValidatorRegistrationV1{{FeeRecipient: make([]byte, fieldparams.FeeRecipientLength), Timestamp: uint64(time.Now().Unix()), Pubkey: make([]byte, fieldparams.BLSPubkeyLength)}}))
ti, err := slots.ToTime(uint64(time.Now().Unix()), 0)
require.NoError(t, err)
sk, err := bls.RandKey()
@ -174,6 +175,7 @@ func TestServer_setExecutionData(t *testing.T) {
vs.BlockBuilder = &builderTest.MockBuilderService{
BidCapella: sBid,
HasConfigured: true,
Cfg: &builderTest.Config{BeaconDB: beaconDB},
}
wb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella())
require.NoError(t, err)
@ -219,6 +221,7 @@ func TestServer_setExecutionData(t *testing.T) {
vs.BlockBuilder = &builderTest.MockBuilderService{
ErrGetHeader: errors.New("fault"),
HasConfigured: true,
Cfg: &builderTest.Config{BeaconDB: beaconDB},
}
vs.ExecutionEngineCaller = &powtesting.EngineClient{PayloadIDBytes: id, ExecutionPayloadCapella: &v1.ExecutionPayloadCapella{BlockNumber: 4}, BlockValue: 0}
require.NoError(t, vs.setExecutionData(context.Background(), blk, capellaTransitionState))
@ -227,7 +230,6 @@ func TestServer_setExecutionData(t *testing.T) {
require.Equal(t, uint64(4), e.BlockNumber()) // Local block
})
}
func TestServer_getPayloadHeader(t *testing.T) {
params.SetupTestConfigCleanup(t)
bc := params.BeaconConfig()

View File

@ -4,6 +4,7 @@ import (
"context"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db/kv"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
@ -36,12 +37,12 @@ func (vs *Server) canUseBuilder(ctx context.Context, slot primitives.Slot, idx p
// validatorRegistered returns true if validator with index `id` was previously registered in the database.
func (vs *Server) validatorRegistered(ctx context.Context, id primitives.ValidatorIndex) (bool, error) {
if vs.BeaconDB == nil {
return false, errors.New("nil beacon db")
if vs.BlockBuilder == nil {
return false, nil
}
_, err := vs.BeaconDB.RegistrationByValidatorID(ctx, id)
_, err := vs.BlockBuilder.RegistrationByValidatorID(ctx, id)
switch {
case errors.Is(err, kv.ErrNotFoundFeeRecipient):
case errors.Is(err, kv.ErrNotFoundFeeRecipient), errors.Is(err, cache.ErrNotFoundRegistration):
return false, nil
case err != nil:
return false, err

View File

@ -6,6 +6,7 @@ import (
"time"
blockchainTest "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/builder"
testing2 "github.com/prysmaticlabs/prysm/v4/beacon-chain/builder/testing"
dbTest "github.com/prysmaticlabs/prysm/v4/beacon-chain/db/testing"
doublylinkedtree "github.com/prysmaticlabs/prysm/v4/beacon-chain/forkchoice/doubly-linked-tree"
@ -71,22 +72,28 @@ func TestServer_circuitBreakBuilder(t *testing.T) {
}
func TestServer_validatorRegistered(t *testing.T) {
proposerServer := &Server{}
b, err := builder.NewService(context.Background())
require.NoError(t, err)
proposerServer := &Server{
BlockBuilder: b,
}
ctx := context.Background()
reg, err := proposerServer.validatorRegistered(ctx, 0)
require.ErrorContains(t, "nil beacon db", err)
require.Equal(t, false, reg)
proposerServer.BeaconDB = dbTest.SetupDB(t)
db := dbTest.SetupDB(t)
realBuilder, err := builder.NewService(context.Background(), builder.WithDatabase(db))
require.NoError(t, err)
proposerServer.BlockBuilder = realBuilder
reg, err = proposerServer.validatorRegistered(ctx, 0)
require.NoError(t, err)
require.Equal(t, false, reg)
f := bytesutil.PadTo([]byte{}, fieldparams.FeeRecipientLength)
p := bytesutil.PadTo([]byte{}, fieldparams.BLSPubkeyLength)
require.NoError(t, proposerServer.BeaconDB.SaveRegistrationsByValidatorIDs(ctx, []primitives.ValidatorIndex{0, 1},
[]*ethpb.ValidatorRegistrationV1{{FeeRecipient: f, Pubkey: p}, {FeeRecipient: f, Pubkey: p}}))
require.NoError(t, db.SaveRegistrationsByValidatorIDs(ctx, []primitives.ValidatorIndex{0, 1},
[]*ethpb.ValidatorRegistrationV1{{FeeRecipient: f, Timestamp: uint64(time.Now().Unix()), Pubkey: p}, {FeeRecipient: f, Timestamp: uint64(time.Now().Unix()), Pubkey: p}}))
reg, err = proposerServer.validatorRegistered(ctx, 0)
require.NoError(t, err)
@ -94,6 +101,7 @@ func TestServer_validatorRegistered(t *testing.T) {
reg, err = proposerServer.validatorRegistered(ctx, 1)
require.NoError(t, err)
require.Equal(t, true, reg)
}
func TestServer_canUseBuilder(t *testing.T) {
@ -105,9 +113,6 @@ func TestServer_canUseBuilder(t *testing.T) {
reg, err := proposerServer.canUseBuilder(context.Background(), 0, 0)
require.NoError(t, err)
require.Equal(t, false, reg)
proposerServer.BlockBuilder = &testing2.MockBuilderService{
HasConfigured: true,
}
ctx := context.Background()
@ -116,20 +121,21 @@ func TestServer_canUseBuilder(t *testing.T) {
reg, err = proposerServer.canUseBuilder(ctx, params.BeaconConfig().MaxBuilderConsecutiveMissedSlots+1, 0)
require.NoError(t, err)
require.Equal(t, false, reg)
db := dbTest.SetupDB(t)
reg, err = proposerServer.validatorRegistered(ctx, 0)
require.ErrorContains(t, "nil beacon db", err)
require.Equal(t, false, reg)
proposerServer.BlockBuilder = &testing2.MockBuilderService{
HasConfigured: true,
Cfg: &testing2.Config{BeaconDB: db},
}
proposerServer.BeaconDB = dbTest.SetupDB(t)
reg, err = proposerServer.canUseBuilder(ctx, 1, 0)
require.NoError(t, err)
require.Equal(t, false, reg)
f := bytesutil.PadTo([]byte{}, fieldparams.FeeRecipientLength)
p := bytesutil.PadTo([]byte{}, fieldparams.BLSPubkeyLength)
require.NoError(t, proposerServer.BeaconDB.SaveRegistrationsByValidatorIDs(ctx, []primitives.ValidatorIndex{0},
[]*ethpb.ValidatorRegistrationV1{{FeeRecipient: f, Pubkey: p}}))
require.NoError(t, db.SaveRegistrationsByValidatorIDs(ctx, []primitives.ValidatorIndex{0},
[]*ethpb.ValidatorRegistrationV1{{FeeRecipient: f, Timestamp: uint64(time.Now().Unix()), Pubkey: p}}))
reg, err = proposerServer.canUseBuilder(ctx, params.BeaconConfig().MaxBuilderConsecutiveMissedSlots-1, 0)
require.NoError(t, err)

View File

@ -3,8 +3,6 @@
package flags
import (
"strings"
"github.com/prysmaticlabs/prysm/v4/cmd"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/urfave/cli/v2"
@ -94,7 +92,7 @@ var (
HTTPModules = &cli.StringFlag{
Name: "http-modules",
Usage: "Comma-separated list of API module names. Possible values: `" + PrysmAPIModule + `,` + EthAPIModule + "`.",
Value: strings.Join([]string{PrysmAPIModule, EthAPIModule}, ","),
Value: PrysmAPIModule + `,` + EthAPIModule,
}
// DisableGRPCGateway for JSON-HTTP requests to the beacon node.
DisableGRPCGateway = &cli.BoolFlag{
@ -204,6 +202,11 @@ var (
Usage: "Sets the maximum number of headers that a deposit log query can fetch.",
Value: uint64(1000),
}
// EnableRegistrationCache a temporary flag for enabling the validator registration cache instead of db.
EnableRegistrationCache = &cli.BoolFlag{
Name: "enable-registration-cache",
Usage: "A temporary flag for enabling the validator registration cache instead of persisting in db. The cache will clear on restart.",
}
// WeakSubjectivityCheckpoint defines the weak subjectivity checkpoint the node must sync through to defend against long range attacks.
WeakSubjectivityCheckpoint = &cli.StringFlag{
Name: "weak-subjectivity-checkpoint",

View File

@ -58,6 +58,7 @@ var appFlags = []cli.Flag{
flags.InteropGenesisTimeFlag,
flags.SlotsPerArchivedPoint,
flags.EnableDebugRPCEndpoints,
flags.EnableRegistrationCache,
flags.SubscribeToAllSubnets,
flags.HistoricalSlasherNode,
flags.ChainID,

View File

@ -113,6 +113,7 @@ var appHelpFlagGroups = []flagGroup{
flags.BlockBatchLimit,
flags.BlockBatchLimitBurstFactor,
flags.EnableDebugRPCEndpoints,
flags.EnableRegistrationCache,
flags.SubscribeToAllSubnets,
flags.HistoricalSlasherNode,
flags.ChainID,

View File

@ -209,6 +209,8 @@ type BeaconChainConfig struct {
MaxBuilderEpochMissedSlots primitives.Slot // MaxBuilderEpochMissedSlots is defines the number of total skip slot (per epoch rolling windows) to fallback from using relay/builder to local execution engine for block construction.
LocalBlockValueBoost uint64 // LocalBlockValueBoost is the value boost for local block construction. This is used to prioritize local block construction over relay/builder block construction.
// Mev validator registration
RegistrationDuration time.Duration // RegistrationDuration is the duration a validator registration is valid for.
// Execution engine timeout value
ExecutionEngineTimeoutValue uint64 // ExecutionEngineTimeoutValue defines the seconds to wait before timing out engine endpoints with execution payload execution semantics (newPayload, forkchoiceUpdated).
}

View File

@ -257,7 +257,8 @@ var mainnetBeaconConfig = &BeaconChainConfig{
// Mevboost circuit breaker
MaxBuilderConsecutiveMissedSlots: 3,
MaxBuilderEpochMissedSlots: 5,
// Mev validator registration duration before expiration
RegistrationDuration: 12 * 32 * 3 * time.Second, // defaults to 3 Epochs
// Execution engine timeout value
ExecutionEngineTimeoutValue: 8, // 8 seconds default based on: https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md#core
}