mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -05:00
looking at ways to reduce validator registration calls (#14371)
* looking at ways to reduce validator registration calls * small mistake, should be epoch start * adding more optimizations for reducing registration calls while covering more edgecases * linting * adding change log and force full push override * fixing bug and adding tests * changing if statement just to be safe * potuz feedback for easier readability * more review feedback for simplicity * more review suggestions from potuz * fix unit test * reduce redundancy * Update CHANGELOG.md Co-authored-by: Radosław Kapka <rkapka@wp.pl> * small nitpick * fixing typo * updating logs --------- Co-authored-by: Radosław Kapka <rkapka@wp.pl>
This commit is contained in:
@@ -45,6 +45,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve
|
||||
- Electra: build blocks with blobs.
|
||||
- E2E: fixed gas limit at genesis
|
||||
- Light client support: use LightClientHeader instead of BeaconBlockHeader.
|
||||
- validator registration log changed to debug, and the frequency of validator registration calls are reduced
|
||||
- Core: Fix process effective balance update to safe copy validator for Electra.
|
||||
- `== nil` checks before calling `IsNil()` on interfaces to prevent panics.
|
||||
|
||||
|
||||
@@ -278,7 +278,11 @@ func (c *Client) RegisterValidator(ctx context.Context, svr []*ethpb.SignedValid
|
||||
}
|
||||
|
||||
_, err = c.do(ctx, http.MethodPost, postRegisterValidatorPath, bytes.NewBuffer(body))
|
||||
return err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.WithField("num_registrations", len(svr)).Info("successfully registered validator(s) on builder")
|
||||
return nil
|
||||
}
|
||||
|
||||
var errResponseVersionMismatch = errors.New("builder API response uses a different version than requested in " + api.VersionHeader + " header")
|
||||
|
||||
@@ -429,7 +429,7 @@ func (vs *Server) PrepareBeaconProposer(
|
||||
if len(validatorIndices) != 0 {
|
||||
log.WithFields(logrus.Fields{
|
||||
"validatorCount": len(validatorIndices),
|
||||
}).Info("Updated fee recipient addresses for validator indices")
|
||||
}).Debug("Updated fee recipient addresses for validator indices")
|
||||
}
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ func (*Validator) HasProposerSettings() bool {
|
||||
}
|
||||
|
||||
// PushProposerSettings for mocking
|
||||
func (_ *Validator) PushProposerSettings(_ context.Context, _ keymanager.IKeymanager, _ primitives.Slot) error {
|
||||
func (_ *Validator) PushProposerSettings(_ context.Context, _ keymanager.IKeymanager, _ primitives.Slot, _ bool) error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ func (_ *Validator) SetPubKeyToValidatorIndexMap(_ context.Context, _ keymanager
|
||||
}
|
||||
|
||||
// SignValidatorRegistrationRequest for mocking
|
||||
func (_ *Validator) SignValidatorRegistrationRequest(_ context.Context, _ iface2.SigningFunc, _ *ethpb.ValidatorRegistrationV1) (*ethpb.SignedValidatorRegistrationV1, error) {
|
||||
func (_ *Validator) SignValidatorRegistrationRequest(_ context.Context, _ iface2.SigningFunc, _ *ethpb.ValidatorRegistrationV1) (*ethpb.SignedValidatorRegistrationV1, bool, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
|
||||
@@ -175,9 +175,7 @@ go_test(
|
||||
"@com_github_wealdtech_go_eth2_util//:go_default_library",
|
||||
"@in_gopkg_d4l3k_messagediff_v1//:go_default_library",
|
||||
"@io_bazel_rules_go//go/tools/bazel:go_default_library",
|
||||
"@org_golang_google_grpc//codes:go_default_library",
|
||||
"@org_golang_google_grpc//metadata:go_default_library",
|
||||
"@org_golang_google_grpc//status:go_default_library",
|
||||
"@org_golang_google_protobuf//types/known/emptypb:go_default_library",
|
||||
"@org_uber_go_mock//gomock:go_default_library",
|
||||
],
|
||||
|
||||
@@ -57,8 +57,8 @@ type Validator interface {
|
||||
Keymanager() (keymanager.IKeymanager, error)
|
||||
HandleKeyReload(ctx context.Context, currentKeys [][fieldparams.BLSPubkeyLength]byte) (bool, error)
|
||||
CheckDoppelGanger(ctx context.Context) error
|
||||
PushProposerSettings(ctx context.Context, km keymanager.IKeymanager, slot primitives.Slot) error
|
||||
SignValidatorRegistrationRequest(ctx context.Context, signer SigningFunc, newValidatorRegistration *ethpb.ValidatorRegistrationV1) (*ethpb.SignedValidatorRegistrationV1, error)
|
||||
PushProposerSettings(ctx context.Context, km keymanager.IKeymanager, slot primitives.Slot, forceFullPush bool) error
|
||||
SignValidatorRegistrationRequest(ctx context.Context, signer SigningFunc, newValidatorRegistration *ethpb.ValidatorRegistrationV1) (*ethpb.SignedValidatorRegistrationV1, bool /* isCached */, error)
|
||||
StartEventStream(ctx context.Context, topics []string, eventsChan chan<- *event.Event)
|
||||
EventStreamIsRunning() bool
|
||||
ProcessEvent(event *event.Event)
|
||||
|
||||
@@ -7,34 +7,18 @@ import (
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||
validator2 "github.com/prysmaticlabs/prysm/v5/consensus-types/validator"
|
||||
"github.com/prysmaticlabs/prysm/v5/monitoring/tracing/trace"
|
||||
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/client/iface"
|
||||
)
|
||||
|
||||
// HandleKeyReload makes sure the validator keeps operating correctly after a change to the underlying keys.
|
||||
// It is also responsible for logging out information about the new state of keys.
|
||||
func (v *validator) HandleKeyReload(ctx context.Context, currentKeys [][fieldparams.BLSPubkeyLength]byte) (anyActive bool, err error) {
|
||||
func (v *validator) HandleKeyReload(ctx context.Context, currentKeys [][fieldparams.BLSPubkeyLength]byte) (bool, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "validator.HandleKeyReload")
|
||||
defer span.End()
|
||||
|
||||
statusRequestKeys := make([][]byte, len(currentKeys))
|
||||
for i := range currentKeys {
|
||||
statusRequestKeys[i] = currentKeys[i][:]
|
||||
}
|
||||
resp, err := v.validatorClient.MultipleValidatorStatus(ctx, ð.MultipleValidatorStatusRequest{
|
||||
PublicKeys: statusRequestKeys,
|
||||
})
|
||||
if err != nil {
|
||||
if err := v.updateValidatorStatusCache(ctx, currentKeys); err != nil {
|
||||
return false, err
|
||||
}
|
||||
statuses := make([]*validatorStatus, len(resp.Statuses))
|
||||
for i, s := range resp.Statuses {
|
||||
statuses[i] = &validatorStatus{
|
||||
publicKey: resp.PublicKeys[i],
|
||||
status: s,
|
||||
index: resp.Indices[i],
|
||||
}
|
||||
}
|
||||
|
||||
// "-1" indicates that validator count endpoint is not supported by the beacon node.
|
||||
var valCount int64 = -1
|
||||
@@ -47,5 +31,5 @@ func (v *validator) HandleKeyReload(ctx context.Context, currentKeys [][fieldpar
|
||||
valCount = int64(valCounts[0].Count)
|
||||
}
|
||||
|
||||
return v.checkAndLogValidatorStatus(statuses, valCount), nil
|
||||
return v.checkAndLogValidatorStatus(valCount), nil
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ func TestValidator_HandleKeyReload(t *testing.T) {
|
||||
genesisTime: 1,
|
||||
chainClient: chainClient,
|
||||
prysmChainClient: prysmChainClient,
|
||||
pubkeyToStatus: make(map[[fieldparams.BLSPubkeyLength]byte]*validatorStatus),
|
||||
}
|
||||
|
||||
resp := testutil.GenerateMultipleValidatorStatusResponse([][]byte{inactive.pub[:], active.pub[:]})
|
||||
@@ -73,6 +74,7 @@ func TestValidator_HandleKeyReload(t *testing.T) {
|
||||
genesisTime: 1,
|
||||
chainClient: chainClient,
|
||||
prysmChainClient: prysmChainClient,
|
||||
pubkeyToStatus: make(map[[fieldparams.BLSPubkeyLength]byte]*validatorStatus),
|
||||
}
|
||||
|
||||
resp := testutil.GenerateMultipleValidatorStatusResponse([][]byte{kp.pub[:]})
|
||||
@@ -103,6 +105,7 @@ func TestValidator_HandleKeyReload(t *testing.T) {
|
||||
validatorClient: client,
|
||||
km: newMockKeymanager(t, kp),
|
||||
genesisTime: 1,
|
||||
pubkeyToStatus: make(map[[fieldparams.BLSPubkeyLength]byte]*validatorStatus),
|
||||
}
|
||||
|
||||
client.EXPECT().MultipleValidatorStatus(
|
||||
|
||||
@@ -93,24 +93,24 @@ func signValidatorRegistration(ctx context.Context, signer iface.SigningFunc, re
|
||||
}
|
||||
|
||||
// SignValidatorRegistrationRequest compares and returns either the cached validator registration request or signs a new one.
|
||||
func (v *validator) SignValidatorRegistrationRequest(ctx context.Context, signer iface.SigningFunc, newValidatorRegistration *ethpb.ValidatorRegistrationV1) (*ethpb.SignedValidatorRegistrationV1, error) {
|
||||
func (v *validator) SignValidatorRegistrationRequest(ctx context.Context, signer iface.SigningFunc, newValidatorRegistration *ethpb.ValidatorRegistrationV1) (*ethpb.SignedValidatorRegistrationV1, bool /* isCached */, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "validator.SignValidatorRegistrationRequest")
|
||||
defer span.End()
|
||||
|
||||
signedReg, ok := v.signedValidatorRegistrations[bytesutil.ToBytes48(newValidatorRegistration.Pubkey)]
|
||||
if ok && isValidatorRegistrationSame(signedReg.Message, newValidatorRegistration) {
|
||||
return signedReg, nil
|
||||
return signedReg, true, nil
|
||||
} else {
|
||||
sig, err := signValidatorRegistration(ctx, signer, newValidatorRegistration)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, false, err
|
||||
}
|
||||
newRequest := ðpb.SignedValidatorRegistrationV1{
|
||||
Message: newValidatorRegistration,
|
||||
Signature: sig,
|
||||
}
|
||||
v.signedValidatorRegistrations[bytesutil.ToBytes48(newValidatorRegistration.Pubkey)] = newRequest
|
||||
return newRequest, nil
|
||||
return newRequest, false, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
||||
@@ -172,7 +171,7 @@ func TestValidator_SignValidatorRegistrationRequest(t *testing.T) {
|
||||
},
|
||||
validatorSetter: func(t *testing.T) *validator {
|
||||
v := validator{
|
||||
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex),
|
||||
pubkeyToStatus: make(map[[fieldparams.BLSPubkeyLength]byte]*validatorStatus),
|
||||
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
|
||||
useWeb: false,
|
||||
genesisTime: 0,
|
||||
@@ -200,7 +199,7 @@ func TestValidator_SignValidatorRegistrationRequest(t *testing.T) {
|
||||
},
|
||||
validatorSetter: func(t *testing.T) *validator {
|
||||
v := validator{
|
||||
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex),
|
||||
pubkeyToStatus: make(map[[fieldparams.BLSPubkeyLength]byte]*validatorStatus),
|
||||
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
|
||||
useWeb: false,
|
||||
genesisTime: 0,
|
||||
@@ -228,7 +227,7 @@ func TestValidator_SignValidatorRegistrationRequest(t *testing.T) {
|
||||
},
|
||||
validatorSetter: func(t *testing.T) *validator {
|
||||
v := validator{
|
||||
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex),
|
||||
pubkeyToStatus: make(map[[fieldparams.BLSPubkeyLength]byte]*validatorStatus),
|
||||
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
|
||||
useWeb: false,
|
||||
genesisTime: 0,
|
||||
@@ -256,7 +255,7 @@ func TestValidator_SignValidatorRegistrationRequest(t *testing.T) {
|
||||
},
|
||||
validatorSetter: func(t *testing.T) *validator {
|
||||
v := validator{
|
||||
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex),
|
||||
pubkeyToStatus: make(map[[fieldparams.BLSPubkeyLength]byte]*validatorStatus),
|
||||
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
|
||||
useWeb: false,
|
||||
genesisTime: 0,
|
||||
@@ -272,7 +271,7 @@ func TestValidator_SignValidatorRegistrationRequest(t *testing.T) {
|
||||
|
||||
startingReq, ok := v.signedValidatorRegistrations[bytesutil.ToBytes48(tt.arg.Pubkey)]
|
||||
|
||||
got, err := v.SignValidatorRegistrationRequest(ctx, m.signfunc, tt.arg)
|
||||
got, _, err := v.SignValidatorRegistrationRequest(ctx, m.signfunc, tt.arg)
|
||||
require.NoError(t, err)
|
||||
if tt.isCached {
|
||||
require.DeepEqual(t, got, v.signedValidatorRegistrations[bytesutil.ToBytes48(tt.arg.Pubkey)])
|
||||
|
||||
@@ -62,7 +62,7 @@ func run(ctx context.Context, v iface.Validator) {
|
||||
log.Warn("Validator client started without proposer settings such as fee recipient" +
|
||||
" and will continue to use settings provided in the beacon node.")
|
||||
}
|
||||
if err := v.PushProposerSettings(ctx, km, headSlot); err != nil {
|
||||
if err := v.PushProposerSettings(ctx, km, headSlot, true); err != nil {
|
||||
log.WithError(err).Fatal("Failed to update proposer settings")
|
||||
}
|
||||
for {
|
||||
@@ -97,7 +97,7 @@ func run(ctx context.Context, v iface.Validator) {
|
||||
// call push proposer settings often to account for the following edge cases:
|
||||
// proposer is activated at the start of epoch and tries to propose immediately
|
||||
// account has changed in the middle of an epoch
|
||||
if err := v.PushProposerSettings(ctx, km, slot); err != nil {
|
||||
if err := v.PushProposerSettings(ctx, km, slot, false); err != nil {
|
||||
log.WithError(err).Warn("Failed to update proposer settings")
|
||||
}
|
||||
|
||||
@@ -316,7 +316,7 @@ func runHealthCheckRoutine(ctx context.Context, v iface.Validator, eventsChan ch
|
||||
log.WithError(err).Error("Could not get canonical head slot")
|
||||
return
|
||||
}
|
||||
if err := v.PushProposerSettings(ctx, km, slot); err != nil {
|
||||
if err := v.PushProposerSettings(ctx, km, slot, true); err != nil {
|
||||
log.WithError(err).Warn("Failed to update proposer settings")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ func (v *ValidatorService) Start() {
|
||||
startBalances: make(map[[fieldparams.BLSPubkeyLength]byte]uint64),
|
||||
prevEpochBalances: make(map[[fieldparams.BLSPubkeyLength]byte]uint64),
|
||||
blacklistedPubkeys: slashablePublicKeys,
|
||||
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex),
|
||||
pubkeyToStatus: make(map[[fieldparams.BLSPubkeyLength]byte]*validatorStatus),
|
||||
wallet: v.wallet,
|
||||
walletInitializedChan: make(chan *wallet.Wallet, 1),
|
||||
walletInitializedFeed: v.walletInitializedFeed,
|
||||
|
||||
@@ -254,7 +254,7 @@ func (*FakeValidator) HasProposerSettings() bool {
|
||||
}
|
||||
|
||||
// PushProposerSettings for mocking
|
||||
func (fv *FakeValidator) PushProposerSettings(ctx context.Context, _ keymanager.IKeymanager, _ primitives.Slot) error {
|
||||
func (fv *FakeValidator) PushProposerSettings(ctx context.Context, _ keymanager.IKeymanager, _ primitives.Slot, _ bool) error {
|
||||
time.Sleep(fv.ProposerSettingWait)
|
||||
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
|
||||
log.Error("deadline exceeded")
|
||||
@@ -276,8 +276,8 @@ func (*FakeValidator) SetPubKeyToValidatorIndexMap(_ context.Context, _ keymanag
|
||||
}
|
||||
|
||||
// SignValidatorRegistrationRequest for mocking
|
||||
func (*FakeValidator) SignValidatorRegistrationRequest(_ context.Context, _ iface.SigningFunc, _ *ethpb.ValidatorRegistrationV1) (*ethpb.SignedValidatorRegistrationV1, error) {
|
||||
return nil, nil
|
||||
func (*FakeValidator) SignValidatorRegistrationRequest(_ context.Context, _ iface.SigningFunc, _ *ethpb.ValidatorRegistrationV1) (*ethpb.SignedValidatorRegistrationV1, bool, error) {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
// ProposerSettings for mocking
|
||||
|
||||
@@ -40,7 +40,6 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v5/time/slots"
|
||||
accountsiface "github.com/prysmaticlabs/prysm/v5/validator/accounts/iface"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/accounts/wallet"
|
||||
beaconapi "github.com/prysmaticlabs/prysm/v5/validator/client/beacon-api"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/client/iface"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/db"
|
||||
dbCommon "github.com/prysmaticlabs/prysm/v5/validator/db/common"
|
||||
@@ -49,9 +48,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/keymanager/local"
|
||||
remoteweb3signer "github.com/prysmaticlabs/prysm/v5/validator/keymanager/remote-web3signer"
|
||||
"github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
@@ -77,7 +74,7 @@ type validator struct {
|
||||
startBalances map[[fieldparams.BLSPubkeyLength]byte]uint64
|
||||
prevEpochBalances map[[fieldparams.BLSPubkeyLength]byte]uint64
|
||||
blacklistedPubkeys map[[fieldparams.BLSPubkeyLength]byte]bool
|
||||
pubkeyToValidatorIndex map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex
|
||||
pubkeyToStatus map[[fieldparams.BLSPubkeyLength]byte]*validatorStatus
|
||||
wallet *wallet.Wallet
|
||||
walletInitializedChan chan *wallet.Wallet
|
||||
walletInitializedFeed *event.Feed
|
||||
@@ -352,10 +349,10 @@ func (v *validator) WaitForSync(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (v *validator) checkAndLogValidatorStatus(statuses []*validatorStatus, activeValCount int64) bool {
|
||||
func (v *validator) checkAndLogValidatorStatus(activeValCount int64) bool {
|
||||
nonexistentIndex := primitives.ValidatorIndex(^uint64(0))
|
||||
var validatorActivated bool
|
||||
for _, s := range statuses {
|
||||
for _, s := range v.pubkeyToStatus {
|
||||
fields := logrus.Fields{
|
||||
"pubkey": fmt.Sprintf("%#x", bytesutil.Trunc(s.publicKey)),
|
||||
"status": s.status.Status.String(),
|
||||
@@ -1102,7 +1099,7 @@ func (v *validator) SetProposerSettings(ctx context.Context, settings *proposer.
|
||||
}
|
||||
|
||||
// PushProposerSettings calls the prepareBeaconProposer RPC to set the fee recipient and also the register validator API if using a custom builder.
|
||||
func (v *validator) PushProposerSettings(ctx context.Context, km keymanager.IKeymanager, slot primitives.Slot) error {
|
||||
func (v *validator) PushProposerSettings(ctx context.Context, km keymanager.IKeymanager, slot primitives.Slot, forceFullPush bool) error {
|
||||
ctx, span := trace.StartSpan(ctx, "validator.PushProposerSettings")
|
||||
defer span.End()
|
||||
|
||||
@@ -1143,7 +1140,7 @@ func (v *validator) PushProposerSettings(ctx context.Context, km keymanager.IKey
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
signedRegReqs := v.buildSignedRegReqs(ctx, filteredKeys, km.Sign)
|
||||
signedRegReqs := v.buildSignedRegReqs(ctx, filteredKeys, km.Sign, slot, forceFullPush)
|
||||
if len(signedRegReqs) > 0 {
|
||||
go func() {
|
||||
if err := SubmitValidatorRegistrations(ctx, v.validatorClient, signedRegReqs, v.validatorsRegBatchSize); err != nil {
|
||||
@@ -1212,44 +1209,31 @@ func (v *validator) ChangeHost() {
|
||||
func (v *validator) filterAndCacheActiveKeys(ctx context.Context, pubkeys [][fieldparams.BLSPubkeyLength]byte, slot primitives.Slot) ([][fieldparams.BLSPubkeyLength]byte, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "validator.filterAndCacheActiveKeys")
|
||||
defer span.End()
|
||||
|
||||
isEpochStart := slots.IsEpochStart(slot)
|
||||
filteredKeys := make([][fieldparams.BLSPubkeyLength]byte, 0)
|
||||
statusRequestKeys := make([][]byte, 0)
|
||||
for _, k := range pubkeys {
|
||||
_, ok := v.pubkeyToValidatorIndex[k]
|
||||
// Get validator index from RPC server if not found.
|
||||
if !ok {
|
||||
i, ok, err := v.validatorIndex(ctx, k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok { // Nothing we can do if RPC server doesn't have validator index.
|
||||
continue
|
||||
}
|
||||
v.pubkeyToValidatorIndex[k] = i
|
||||
if len(pubkeys) == 0 {
|
||||
return filteredKeys, nil
|
||||
}
|
||||
var err error
|
||||
// repopulate the statuses if epoch start or if a new key is added missing the cache
|
||||
if isEpochStart || len(v.pubkeyToStatus) != len(pubkeys) /* cache not populated or updated correctly */ {
|
||||
if err = v.updateValidatorStatusCache(ctx, pubkeys); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to update validator status cache")
|
||||
}
|
||||
copiedk := k
|
||||
statusRequestKeys = append(statusRequestKeys, copiedk[:])
|
||||
}
|
||||
resp, err := v.validatorClient.MultipleValidatorStatus(ctx, ðpb.MultipleValidatorStatusRequest{
|
||||
PublicKeys: statusRequestKeys,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i, s := range resp.Statuses {
|
||||
for k, s := range v.pubkeyToStatus {
|
||||
currEpoch := primitives.Epoch(slot / params.BeaconConfig().SlotsPerEpoch)
|
||||
currActivating := s.Status == ethpb.ValidatorStatus_PENDING && currEpoch >= s.ActivationEpoch
|
||||
currActivating := s.status.Status == ethpb.ValidatorStatus_PENDING && currEpoch >= s.status.ActivationEpoch
|
||||
|
||||
active := s.Status == ethpb.ValidatorStatus_ACTIVE
|
||||
exiting := s.Status == ethpb.ValidatorStatus_EXITING
|
||||
active := s.status.Status == ethpb.ValidatorStatus_ACTIVE
|
||||
exiting := s.status.Status == ethpb.ValidatorStatus_EXITING
|
||||
|
||||
if currActivating || active || exiting {
|
||||
filteredKeys = append(filteredKeys, bytesutil.ToBytes48(resp.PublicKeys[i]))
|
||||
filteredKeys = append(filteredKeys, k)
|
||||
} else {
|
||||
log.WithFields(logrus.Fields{
|
||||
"pubkey": hexutil.Encode(resp.PublicKeys[i]),
|
||||
"status": s.Status.String(),
|
||||
"pubkey": hexutil.Encode(s.publicKey),
|
||||
"status": s.status.Status.String(),
|
||||
}).Debugf("Skipping non-active status key.")
|
||||
}
|
||||
}
|
||||
@@ -1257,11 +1241,47 @@ func (v *validator) filterAndCacheActiveKeys(ctx context.Context, pubkeys [][fie
|
||||
return filteredKeys, nil
|
||||
}
|
||||
|
||||
// updateValidatorStatusCache updates the validator statuses cache, a map of keys currently used by the validator client
|
||||
func (v *validator) updateValidatorStatusCache(ctx context.Context, pubkeys [][fieldparams.BLSPubkeyLength]byte) error {
|
||||
statusRequestKeys := make([][]byte, 0)
|
||||
for _, k := range pubkeys {
|
||||
statusRequestKeys = append(statusRequestKeys, k[:])
|
||||
}
|
||||
resp, err := v.validatorClient.MultipleValidatorStatus(ctx, ðpb.MultipleValidatorStatusRequest{
|
||||
PublicKeys: statusRequestKeys,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp == nil {
|
||||
return errors.New("response is nil")
|
||||
}
|
||||
if len(resp.Statuses) != len(resp.PublicKeys) {
|
||||
return fmt.Errorf("expected %d pubkeys in status, received %d", len(resp.Statuses), len(resp.PublicKeys))
|
||||
}
|
||||
if len(resp.Statuses) != len(resp.Indices) {
|
||||
return fmt.Errorf("expected %d indices in status, received %d", len(resp.Statuses), len(resp.Indices))
|
||||
}
|
||||
for i, s := range resp.Statuses {
|
||||
v.pubkeyToStatus[bytesutil.ToBytes48(resp.PublicKeys[i])] = &validatorStatus{
|
||||
publicKey: resp.PublicKeys[i],
|
||||
status: s,
|
||||
index: resp.Indices[i],
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *validator) buildPrepProposerReqs(activePubkeys [][fieldparams.BLSPubkeyLength]byte) ([]*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer, error) {
|
||||
var prepareProposerReqs []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer
|
||||
for _, k := range activePubkeys {
|
||||
s, ok := v.pubkeyToStatus[k]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Default case: Define fee recipient to burn address
|
||||
var feeRecipient common.Address
|
||||
feeRecipient := common.HexToAddress(params.BeaconConfig().EthBurnAddressHex)
|
||||
|
||||
// If fee recipient is defined in default configuration, use it
|
||||
if v.ProposerSettings() != nil && v.ProposerSettings().DefaultConfig != nil && v.ProposerSettings().DefaultConfig.FeeRecipientConfig != nil {
|
||||
@@ -1277,13 +1297,8 @@ func (v *validator) buildPrepProposerReqs(activePubkeys [][fieldparams.BLSPubkey
|
||||
}
|
||||
}
|
||||
|
||||
validatorIndex, ok := v.pubkeyToValidatorIndex[k]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
prepareProposerReqs = append(prepareProposerReqs, ðpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
|
||||
ValidatorIndex: validatorIndex,
|
||||
ValidatorIndex: s.index,
|
||||
FeeRecipient: feeRecipient[:],
|
||||
})
|
||||
}
|
||||
@@ -1294,19 +1309,27 @@ func (v *validator) buildSignedRegReqs(
|
||||
ctx context.Context,
|
||||
activePubkeys [][fieldparams.BLSPubkeyLength]byte,
|
||||
signer iface.SigningFunc,
|
||||
slot primitives.Slot,
|
||||
forceFullPush bool,
|
||||
) []*ethpb.SignedValidatorRegistrationV1 {
|
||||
ctx, span := trace.StartSpan(ctx, "validator.buildSignedRegReqs")
|
||||
defer span.End()
|
||||
|
||||
var signedValRegRegs []*ethpb.SignedValidatorRegistrationV1
|
||||
var signedValRegRequests []*ethpb.SignedValidatorRegistrationV1
|
||||
if v.ProposerSettings() == nil {
|
||||
return signedValRegRegs
|
||||
return signedValRegRequests
|
||||
}
|
||||
// if the timestamp is pre-genesis, don't create registrations
|
||||
if v.genesisTime > uint64(time.Now().UTC().Unix()) {
|
||||
return signedValRegRegs
|
||||
return signedValRegRequests
|
||||
}
|
||||
for i, k := range activePubkeys {
|
||||
// map is populated before this function in buildPrepProposerReq
|
||||
_, ok := v.pubkeyToStatus[k]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
feeRecipient := common.HexToAddress(params.BeaconConfig().EthBurnAddressHex)
|
||||
gasLimit := params.BeaconConfig().DefaultBuilderGasLimit
|
||||
enabled := false
|
||||
@@ -1346,12 +1369,6 @@ func (v *validator) buildSignedRegReqs(
|
||||
continue
|
||||
}
|
||||
|
||||
// map is populated before this function in buildPrepProposerReq
|
||||
_, ok := v.pubkeyToValidatorIndex[k]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
req := ðpb.ValidatorRegistrationV1{
|
||||
FeeRecipient: feeRecipient[:],
|
||||
GasLimit: gasLimit,
|
||||
@@ -1359,7 +1376,7 @@ func (v *validator) buildSignedRegReqs(
|
||||
Pubkey: activePubkeys[i][:],
|
||||
}
|
||||
|
||||
signedReq, err := v.SignValidatorRegistrationRequest(ctx, signer, req)
|
||||
signedRequest, isCached, err := v.SignValidatorRegistrationRequest(ctx, signer, req)
|
||||
if err != nil {
|
||||
log.WithFields(logrus.Fields{
|
||||
"pubkey": fmt.Sprintf("%#x", req.Pubkey),
|
||||
@@ -1368,38 +1385,20 @@ func (v *validator) buildSignedRegReqs(
|
||||
continue
|
||||
}
|
||||
|
||||
signedValRegRegs = append(signedValRegRegs, signedReq)
|
||||
|
||||
if hexutil.Encode(feeRecipient.Bytes()) == params.BeaconConfig().EthBurnAddressHex {
|
||||
log.WithFields(logrus.Fields{
|
||||
"pubkey": fmt.Sprintf("%#x", req.Pubkey),
|
||||
"feeRecipient": feeRecipient,
|
||||
}).Warn("Fee recipient is burn address")
|
||||
}
|
||||
}
|
||||
return signedValRegRegs
|
||||
}
|
||||
|
||||
func (v *validator) validatorIndex(ctx context.Context, pubkey [fieldparams.BLSPubkeyLength]byte) (primitives.ValidatorIndex, bool, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "validator.validatorIndex")
|
||||
defer span.End()
|
||||
|
||||
resp, err := v.validatorClient.ValidatorIndex(ctx, ðpb.ValidatorIndexRequest{PublicKey: pubkey[:]})
|
||||
switch {
|
||||
case status.Code(err) == codes.NotFound:
|
||||
log.Debugf("Could not find validator index for public key %#x. "+
|
||||
"Perhaps the validator is not yet active.", pubkey)
|
||||
return 0, false, nil
|
||||
case err != nil:
|
||||
notFoundErr := &beaconapi.IndexNotFoundError{}
|
||||
if errors.As(err, ¬FoundErr) {
|
||||
log.Debugf("Could not find validator index for public key %#x. "+
|
||||
"Perhaps the validator is not yet active.", pubkey)
|
||||
return 0, false, nil
|
||||
if slots.IsEpochStart(slot) || forceFullPush || !isCached {
|
||||
// if epoch start (or forced to) send all validator registrations
|
||||
// otherwise if slot is not epoch start then only send new non cached values
|
||||
signedValRegRequests = append(signedValRegRequests, signedRequest)
|
||||
}
|
||||
return 0, false, err
|
||||
}
|
||||
return resp.Index, true, nil
|
||||
return signedValRegRequests
|
||||
}
|
||||
|
||||
func (v *validator) aggregatedSelectionProofs(ctx context.Context, duties *ethpb.DutiesResponse) error {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
@@ -45,8 +46,6 @@ import (
|
||||
logTest "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/urfave/cli/v2"
|
||||
"go.uber.org/mock/gomock"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
@@ -59,6 +58,8 @@ var _ iface.Validator = (*validator)(nil)
|
||||
|
||||
const cancelledCtx = "context has been canceled"
|
||||
|
||||
var unknownIndex = primitives.ValidatorIndex(^uint64(0))
|
||||
|
||||
func genMockKeymanager(t *testing.T, numKeys int) *mockKeymanager {
|
||||
pairs := make([]keypair, numKeys)
|
||||
for i := 0; i < numKeys; i++ {
|
||||
@@ -354,6 +355,7 @@ func TestWaitMultipleActivation_LogsActivationEpochOK(t *testing.T) {
|
||||
km: newMockKeymanager(t, kp),
|
||||
chainClient: chainClient,
|
||||
prysmChainClient: prysmChainClient,
|
||||
pubkeyToStatus: make(map[[48]byte]*validatorStatus),
|
||||
}
|
||||
|
||||
resp := generateMockStatusResponse([][]byte{kp.pub[:]})
|
||||
@@ -937,9 +939,10 @@ func TestCheckAndLogValidatorStatus_OK(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
pubkeyToStatus: make(map[[48]byte]*validatorStatus),
|
||||
}
|
||||
|
||||
active := v.checkAndLogValidatorStatus([]*validatorStatus{test.status}, 100)
|
||||
v.pubkeyToStatus[bytesutil.ToBytes48(test.status.publicKey)] = test.status
|
||||
active := v.checkAndLogValidatorStatus(100)
|
||||
require.Equal(t, test.active, active)
|
||||
if test.log != "" {
|
||||
require.LogsContain(t, hook, test.log)
|
||||
@@ -1489,7 +1492,7 @@ func TestValidator_PushSettings(t *testing.T) {
|
||||
validatorClient: client,
|
||||
nodeClient: nodeClient,
|
||||
db: db,
|
||||
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex),
|
||||
pubkeyToStatus: make(map[[fieldparams.BLSPubkeyLength]byte]*validatorStatus),
|
||||
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
|
||||
useWeb: false,
|
||||
interopKeysConfig: &local.InteropKeymanagerConfig{
|
||||
@@ -1504,14 +1507,23 @@ func TestValidator_PushSettings(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
keys, err := km.FetchValidatingPublicKeys(ctx)
|
||||
require.NoError(t, err)
|
||||
v.pubkeyToValidatorIndex[keys[0]] = primitives.ValidatorIndex(1)
|
||||
v.pubkeyToValidatorIndex[keys[1]] = primitives.ValidatorIndex(2)
|
||||
v.pubkeyToStatus[keys[0]] = &validatorStatus{
|
||||
publicKey: keys[0][:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: primitives.ValidatorIndex(1),
|
||||
}
|
||||
v.pubkeyToStatus[keys[1]] = &validatorStatus{
|
||||
publicKey: keys[1][:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: primitives.ValidatorIndex(2),
|
||||
}
|
||||
client.EXPECT().MultipleValidatorStatus(
|
||||
gomock.Any(),
|
||||
gomock.Any()).Return(
|
||||
ðpb.MultipleValidatorStatusResponse{
|
||||
Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}, {Status: ethpb.ValidatorStatus_ACTIVE}},
|
||||
PublicKeys: [][]byte{keys[0][:], keys[1][:]},
|
||||
Indices: []primitives.ValidatorIndex{1, 2},
|
||||
}, nil)
|
||||
client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.PrepareBeaconProposerRequest{
|
||||
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
|
||||
@@ -1571,7 +1583,7 @@ func TestValidator_PushSettings(t *testing.T) {
|
||||
validatorClient: client,
|
||||
nodeClient: nodeClient,
|
||||
db: db,
|
||||
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex),
|
||||
pubkeyToStatus: make(map[[fieldparams.BLSPubkeyLength]byte]*validatorStatus),
|
||||
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
|
||||
useWeb: false,
|
||||
interopKeysConfig: &local.InteropKeymanagerConfig{
|
||||
@@ -1586,14 +1598,23 @@ func TestValidator_PushSettings(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
keys, err := km.FetchValidatingPublicKeys(ctx)
|
||||
require.NoError(t, err)
|
||||
v.pubkeyToValidatorIndex[keys[0]] = primitives.ValidatorIndex(1)
|
||||
v.pubkeyToValidatorIndex[keys[1]] = primitives.ValidatorIndex(2)
|
||||
v.pubkeyToStatus[keys[0]] = &validatorStatus{
|
||||
publicKey: keys[0][:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: primitives.ValidatorIndex(1),
|
||||
}
|
||||
v.pubkeyToStatus[keys[1]] = &validatorStatus{
|
||||
publicKey: keys[1][:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: primitives.ValidatorIndex(2),
|
||||
}
|
||||
client.EXPECT().MultipleValidatorStatus(
|
||||
gomock.Any(),
|
||||
gomock.Any()).Return(
|
||||
ðpb.MultipleValidatorStatusResponse{
|
||||
Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}, {Status: ethpb.ValidatorStatus_ACTIVE}},
|
||||
PublicKeys: [][]byte{keys[0][:], keys[1][:]},
|
||||
Indices: []primitives.ValidatorIndex{1, 2},
|
||||
}, nil)
|
||||
client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.PrepareBeaconProposerRequest{
|
||||
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
|
||||
@@ -1644,12 +1665,11 @@ func TestValidator_PushSettings(t *testing.T) {
|
||||
{
|
||||
name: " Happy Path default doesn't send any validator registrations",
|
||||
validatorSetter: func(t *testing.T) *validator {
|
||||
|
||||
v := validator{
|
||||
validatorClient: client,
|
||||
nodeClient: nodeClient,
|
||||
db: db,
|
||||
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex),
|
||||
pubkeyToStatus: make(map[[fieldparams.BLSPubkeyLength]byte]*validatorStatus),
|
||||
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
|
||||
useWeb: false,
|
||||
interopKeysConfig: &local.InteropKeymanagerConfig{
|
||||
@@ -1664,14 +1684,23 @@ func TestValidator_PushSettings(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
keys, err := km.FetchValidatingPublicKeys(ctx)
|
||||
require.NoError(t, err)
|
||||
v.pubkeyToValidatorIndex[keys[0]] = primitives.ValidatorIndex(1)
|
||||
v.pubkeyToValidatorIndex[keys[1]] = primitives.ValidatorIndex(2)
|
||||
v.pubkeyToStatus[keys[0]] = &validatorStatus{
|
||||
publicKey: keys[0][:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: primitives.ValidatorIndex(1),
|
||||
}
|
||||
v.pubkeyToStatus[keys[1]] = &validatorStatus{
|
||||
publicKey: keys[1][:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: primitives.ValidatorIndex(2),
|
||||
}
|
||||
client.EXPECT().MultipleValidatorStatus(
|
||||
gomock.Any(),
|
||||
gomock.Any()).Return(
|
||||
ðpb.MultipleValidatorStatusResponse{
|
||||
Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}, {Status: ethpb.ValidatorStatus_ACTIVE}},
|
||||
PublicKeys: [][]byte{keys[0][:], keys[1][:]},
|
||||
Indices: []primitives.ValidatorIndex{1, 2},
|
||||
}, nil)
|
||||
client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.PrepareBeaconProposerRequest{
|
||||
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
|
||||
@@ -1710,7 +1739,7 @@ func TestValidator_PushSettings(t *testing.T) {
|
||||
validatorClient: client,
|
||||
nodeClient: nodeClient,
|
||||
db: db,
|
||||
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex),
|
||||
pubkeyToStatus: make(map[[fieldparams.BLSPubkeyLength]byte]*validatorStatus),
|
||||
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
|
||||
useWeb: false,
|
||||
interopKeysConfig: &local.InteropKeymanagerConfig{
|
||||
@@ -1740,13 +1769,18 @@ func TestValidator_PushSettings(t *testing.T) {
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
v.pubkeyToValidatorIndex[keys[0]] = primitives.ValidatorIndex(1)
|
||||
v.pubkeyToStatus[keys[0]] = &validatorStatus{
|
||||
publicKey: keys[0][:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: primitives.ValidatorIndex(1),
|
||||
}
|
||||
client.EXPECT().MultipleValidatorStatus(
|
||||
gomock.Any(),
|
||||
gomock.Any()).Return(
|
||||
ðpb.MultipleValidatorStatusResponse{
|
||||
Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}},
|
||||
PublicKeys: [][]byte{keys[0][:]},
|
||||
Indices: []primitives.ValidatorIndex{1},
|
||||
}, nil)
|
||||
|
||||
client.EXPECT().SubmitValidatorRegistrations(
|
||||
@@ -1778,7 +1812,7 @@ func TestValidator_PushSettings(t *testing.T) {
|
||||
validatorClient: client,
|
||||
nodeClient: nodeClient,
|
||||
db: db,
|
||||
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex),
|
||||
pubkeyToStatus: make(map[[fieldparams.BLSPubkeyLength]byte]*validatorStatus),
|
||||
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
|
||||
useWeb: false,
|
||||
interopKeysConfig: &local.InteropKeymanagerConfig{
|
||||
@@ -1805,13 +1839,18 @@ func TestValidator_PushSettings(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
keys, err := km.FetchValidatingPublicKeys(ctx)
|
||||
require.NoError(t, err)
|
||||
v.pubkeyToValidatorIndex[keys[0]] = primitives.ValidatorIndex(1)
|
||||
v.pubkeyToStatus[keys[0]] = &validatorStatus{
|
||||
publicKey: keys[0][:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: primitives.ValidatorIndex(1),
|
||||
}
|
||||
client.EXPECT().MultipleValidatorStatus(
|
||||
gomock.Any(),
|
||||
gomock.Any()).Return(
|
||||
ðpb.MultipleValidatorStatusResponse{
|
||||
Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}},
|
||||
PublicKeys: [][]byte{keys[0][:]},
|
||||
Indices: []primitives.ValidatorIndex{1},
|
||||
}, nil)
|
||||
client.EXPECT().SubmitValidatorRegistrations(
|
||||
gomock.Any(),
|
||||
@@ -1842,7 +1881,7 @@ func TestValidator_PushSettings(t *testing.T) {
|
||||
validatorClient: client,
|
||||
nodeClient: nodeClient,
|
||||
db: db,
|
||||
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex),
|
||||
pubkeyToStatus: make(map[[fieldparams.BLSPubkeyLength]byte]*validatorStatus),
|
||||
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
|
||||
useWeb: false,
|
||||
interopKeysConfig: &local.InteropKeymanagerConfig{
|
||||
@@ -1857,13 +1896,18 @@ func TestValidator_PushSettings(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
keys, err := km.FetchValidatingPublicKeys(ctx)
|
||||
require.NoError(t, err)
|
||||
v.pubkeyToValidatorIndex[keys[0]] = primitives.ValidatorIndex(1)
|
||||
v.pubkeyToStatus[keys[0]] = &validatorStatus{
|
||||
publicKey: keys[0][:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: primitives.ValidatorIndex(1),
|
||||
}
|
||||
client.EXPECT().MultipleValidatorStatus(
|
||||
gomock.Any(),
|
||||
gomock.Any()).Return(
|
||||
ðpb.MultipleValidatorStatusResponse{
|
||||
Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}},
|
||||
PublicKeys: [][]byte{keys[0][:]},
|
||||
Indices: []primitives.ValidatorIndex{1},
|
||||
}, nil)
|
||||
client.EXPECT().PrepareBeaconProposer(gomock.Any(), ðpb.PrepareBeaconProposerRequest{
|
||||
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
|
||||
@@ -1894,7 +1938,7 @@ func TestValidator_PushSettings(t *testing.T) {
|
||||
v := validator{
|
||||
validatorClient: client,
|
||||
db: db,
|
||||
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex),
|
||||
pubkeyToStatus: make(map[[fieldparams.BLSPubkeyLength]byte]*validatorStatus),
|
||||
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
|
||||
useWeb: false,
|
||||
interopKeysConfig: &local.InteropKeymanagerConfig{
|
||||
@@ -1909,15 +1953,19 @@ func TestValidator_PushSettings(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
keys, err := km.FetchValidatingPublicKeys(ctx)
|
||||
require.NoError(t, err)
|
||||
client.EXPECT().ValidatorIndex(
|
||||
gomock.Any(), // ctx
|
||||
ðpb.ValidatorIndexRequest{PublicKey: keys[0][:]},
|
||||
).Return(nil, errors.New("could not find validator index for public key"))
|
||||
config[keys[0]] = &proposer.Option{
|
||||
FeeRecipientConfig: &proposer.FeeRecipientConfig{
|
||||
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"),
|
||||
},
|
||||
}
|
||||
client.EXPECT().MultipleValidatorStatus(
|
||||
gomock.Any(),
|
||||
gomock.Any()).Return(
|
||||
ðpb.MultipleValidatorStatusResponse{
|
||||
Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_UNKNOWN_STATUS}},
|
||||
PublicKeys: [][]byte{keys[0][:]},
|
||||
Indices: []primitives.ValidatorIndex{unknownIndex},
|
||||
}, nil)
|
||||
err = v.SetProposerSettings(context.Background(), &proposer.Settings{
|
||||
ProposeConfig: config,
|
||||
DefaultConfig: &proposer.Option{
|
||||
@@ -1937,7 +1985,7 @@ func TestValidator_PushSettings(t *testing.T) {
|
||||
validatorClient: client,
|
||||
nodeClient: nodeClient,
|
||||
db: db,
|
||||
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex),
|
||||
pubkeyToStatus: make(map[[fieldparams.BLSPubkeyLength]byte]*validatorStatus),
|
||||
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
|
||||
useWeb: false,
|
||||
interopKeysConfig: &local.InteropKeymanagerConfig{
|
||||
@@ -1952,13 +2000,18 @@ func TestValidator_PushSettings(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
keys, err := km.FetchValidatingPublicKeys(ctx)
|
||||
require.NoError(t, err)
|
||||
v.pubkeyToValidatorIndex[keys[0]] = primitives.ValidatorIndex(1)
|
||||
v.pubkeyToStatus[keys[0]] = &validatorStatus{
|
||||
publicKey: keys[0][:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: primitives.ValidatorIndex(1),
|
||||
}
|
||||
client.EXPECT().MultipleValidatorStatus(
|
||||
gomock.Any(),
|
||||
gomock.Any()).Return(
|
||||
ðpb.MultipleValidatorStatusResponse{
|
||||
Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}},
|
||||
PublicKeys: [][]byte{keys[0][:]},
|
||||
Indices: []primitives.ValidatorIndex{1},
|
||||
}, nil)
|
||||
|
||||
config[keys[0]] = &proposer.Option{
|
||||
@@ -2009,7 +2062,7 @@ func TestValidator_PushSettings(t *testing.T) {
|
||||
if tt.feeRecipientMap != nil {
|
||||
feeRecipients, err := v.buildPrepProposerReqs(pubkeys)
|
||||
require.NoError(t, err)
|
||||
signedRegisterValidatorRequests := v.buildSignedRegReqs(ctx, pubkeys, km.Sign)
|
||||
signedRegisterValidatorRequests := v.buildSignedRegReqs(ctx, pubkeys, km.Sign, 0, false)
|
||||
for _, recipient := range feeRecipients {
|
||||
require.Equal(t, strings.ToLower(tt.feeRecipientMap[recipient.ValidatorIndex]), strings.ToLower(hexutil.Encode(recipient.FeeRecipient)))
|
||||
}
|
||||
@@ -2027,7 +2080,7 @@ func TestValidator_PushSettings(t *testing.T) {
|
||||
require.Equal(t, len(tt.mockExpectedRequests), len(signedRegisterValidatorRequests))
|
||||
require.Equal(t, len(signedRegisterValidatorRequests), len(v.signedValidatorRegistrations))
|
||||
}
|
||||
if err := v.PushProposerSettings(ctx, km, 0); tt.err != "" {
|
||||
if err := v.PushProposerSettings(ctx, km, 0, false); tt.err != "" {
|
||||
assert.ErrorContains(t, tt.err, err)
|
||||
}
|
||||
if len(tt.logMessages) > 0 {
|
||||
@@ -2091,28 +2144,14 @@ func TestValidator_buildPrepProposerReqs_WithoutDefaultConfig(t *testing.T) {
|
||||
|
||||
ctx := context.Background()
|
||||
client := validatormock.NewMockValidatorClient(ctrl)
|
||||
client.EXPECT().ValidatorIndex(
|
||||
gomock.Any(),
|
||||
ðpb.ValidatorIndexRequest{
|
||||
PublicKey: pubkey2[:],
|
||||
},
|
||||
).Return(ðpb.ValidatorIndexResponse{
|
||||
Index: 2,
|
||||
}, nil)
|
||||
|
||||
client.EXPECT().ValidatorIndex(
|
||||
gomock.Any(),
|
||||
ðpb.ValidatorIndexRequest{
|
||||
PublicKey: pubkey3[:],
|
||||
},
|
||||
).Return(nil, status.Error(codes.NotFound, "NOT_FOUND"))
|
||||
|
||||
client.EXPECT().MultipleValidatorStatus(
|
||||
gomock.Any(),
|
||||
gomock.Any()).Return(
|
||||
ðpb.MultipleValidatorStatusResponse{
|
||||
Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}, {Status: ethpb.ValidatorStatus_ACTIVE}, {Status: ethpb.ValidatorStatus_ACTIVE}},
|
||||
PublicKeys: [][]byte{pubkey1[:], pubkey2[:], pubkey4[:]},
|
||||
Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}, {Status: ethpb.ValidatorStatus_ACTIVE}, {Status: ethpb.ValidatorStatus_UNKNOWN_STATUS}, {Status: ethpb.ValidatorStatus_ACTIVE}},
|
||||
PublicKeys: [][]byte{pubkey1[:], pubkey2[:], pubkey3[:], pubkey4[:]},
|
||||
Indices: []primitives.ValidatorIndex{1, 2, unknownIndex, 4},
|
||||
}, nil)
|
||||
v := validator{
|
||||
validatorClient: client,
|
||||
@@ -2141,9 +2180,17 @@ func TestValidator_buildPrepProposerReqs_WithoutDefaultConfig(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
pubkeyToValidatorIndex: map[[48]byte]primitives.ValidatorIndex{
|
||||
pubkey1: 1,
|
||||
pubkey4: 4,
|
||||
pubkeyToStatus: map[[48]byte]*validatorStatus{
|
||||
pubkey1: {
|
||||
publicKey: pubkey1[:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: 1,
|
||||
},
|
||||
pubkey4: {
|
||||
publicKey: pubkey4[:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: 4,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2167,18 +2214,121 @@ func TestValidator_buildPrepProposerReqs_WithoutDefaultConfig(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
actual, err := v.buildPrepProposerReqs(filteredKeys)
|
||||
require.NoError(t, err)
|
||||
sort.Slice(actual, func(i, j int) bool {
|
||||
return actual[i].ValidatorIndex < actual[j].ValidatorIndex
|
||||
})
|
||||
assert.DeepEqual(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestValidator_filterAndCacheActiveKeys(t *testing.T) {
|
||||
// Public keys
|
||||
pubkey1 := pubkeyFromString(t, "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111")
|
||||
pubkey2 := pubkeyFromString(t, "0x222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222")
|
||||
pubkey3 := pubkeyFromString(t, "0x333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333")
|
||||
pubkey4 := pubkeyFromString(t, "0x444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444")
|
||||
|
||||
t.Run("refetch all keys at start of epoch", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
ctx := context.Background()
|
||||
client := validatormock.NewMockValidatorClient(ctrl)
|
||||
|
||||
client.EXPECT().MultipleValidatorStatus(
|
||||
gomock.Any(),
|
||||
gomock.Any()).Return(
|
||||
ðpb.MultipleValidatorStatusResponse{
|
||||
Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}, {Status: ethpb.ValidatorStatus_ACTIVE}, {Status: ethpb.ValidatorStatus_UNKNOWN_STATUS}, {Status: ethpb.ValidatorStatus_ACTIVE}},
|
||||
PublicKeys: [][]byte{pubkey1[:], pubkey2[:], pubkey3[:], pubkey4[:]},
|
||||
Indices: []primitives.ValidatorIndex{1, 2, unknownIndex, 4},
|
||||
}, nil)
|
||||
v := validator{
|
||||
validatorClient: client,
|
||||
pubkeyToStatus: make(map[[48]byte]*validatorStatus),
|
||||
}
|
||||
keys, err := v.filterAndCacheActiveKeys(ctx, [][48]byte{pubkey1, pubkey2, pubkey3, pubkey4}, 0)
|
||||
require.NoError(t, err)
|
||||
// one key is unknown status
|
||||
require.Equal(t, 3, len(keys))
|
||||
})
|
||||
t.Run("refetch all keys at start of epoch, even with cache", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
ctx := context.Background()
|
||||
client := validatormock.NewMockValidatorClient(ctrl)
|
||||
|
||||
client.EXPECT().MultipleValidatorStatus(
|
||||
gomock.Any(),
|
||||
gomock.Any()).Return(
|
||||
ðpb.MultipleValidatorStatusResponse{
|
||||
Statuses: []*ethpb.ValidatorStatusResponse{{Status: ethpb.ValidatorStatus_ACTIVE}, {Status: ethpb.ValidatorStatus_ACTIVE}, {Status: ethpb.ValidatorStatus_UNKNOWN_STATUS}, {Status: ethpb.ValidatorStatus_ACTIVE}},
|
||||
PublicKeys: [][]byte{pubkey1[:], pubkey2[:], pubkey3[:], pubkey4[:]},
|
||||
Indices: []primitives.ValidatorIndex{1, 2, unknownIndex, 4},
|
||||
}, nil)
|
||||
v := validator{
|
||||
validatorClient: client,
|
||||
pubkeyToStatus: map[[48]byte]*validatorStatus{
|
||||
pubkey1: {
|
||||
publicKey: pubkey1[:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: 1,
|
||||
},
|
||||
pubkey2: {
|
||||
publicKey: pubkey2[:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: 2,
|
||||
},
|
||||
pubkey3: {
|
||||
publicKey: pubkey3[:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE}, // gets overridden
|
||||
index: 3,
|
||||
},
|
||||
pubkey4: {
|
||||
publicKey: pubkey4[:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: 4,
|
||||
},
|
||||
},
|
||||
}
|
||||
keys, err := v.filterAndCacheActiveKeys(ctx, [][48]byte{pubkey1, pubkey2, pubkey3, pubkey4}, 0)
|
||||
require.NoError(t, err)
|
||||
// one key is unknown status
|
||||
require.Equal(t, 3, len(keys))
|
||||
})
|
||||
t.Run("cache used mid epoch, no new keys added", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
v := validator{
|
||||
pubkeyToStatus: map[[48]byte]*validatorStatus{
|
||||
pubkey1: {
|
||||
publicKey: pubkey1[:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: 1,
|
||||
},
|
||||
pubkey4: {
|
||||
publicKey: pubkey4[:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: 4,
|
||||
},
|
||||
},
|
||||
}
|
||||
keys, err := v.filterAndCacheActiveKeys(ctx, [][48]byte{pubkey1, pubkey4}, 5)
|
||||
require.NoError(t, err)
|
||||
// one key is unknown status
|
||||
require.Equal(t, 2, len(keys))
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestValidator_buildPrepProposerReqs_WithDefaultConfig(t *testing.T) {
|
||||
// pubkey1 => feeRecipient1 - Status: active (already in `v.validatorIndex`)
|
||||
// pubkey2 => feeRecipient2 - Status: active (NOT in `v.validatorIndex`, index found by beacon node)
|
||||
// pubkey3 => feeRecipient3 - Status: active (NOT in `v.validatorIndex`, index NOT found by beacon node)
|
||||
// pubkey4 => Nothing - Status: active (already in `v.validatorIndex`)
|
||||
// pubkey5 => Nothing - Status: unknown (already in `v.validatorIndex`)
|
||||
// pubkey6 => Nothing - Status: pending (already in `v.validatorIndex`) - ActivationEpoch: 35 (current slot: 641 - current epoch: 20)
|
||||
// pubkey7 => Nothing - Status: pending (already in `v.validatorIndex`) - ActivationEpoch: 20 (current slot: 641 - current epoch: 20)
|
||||
// pubkey8 => feeRecipient8 - Status: exiting (already in `v.validatorIndex`)
|
||||
// pubkey1 => feeRecipient1 - Status: active
|
||||
// pubkey2 => feeRecipient2 - Status: active
|
||||
// pubkey3 => feeRecipient3 - Status: unknown
|
||||
// pubkey4 => Nothing - Status: active
|
||||
// pubkey5 => Nothing - Status: exited
|
||||
// pubkey6 => Nothing - Status: pending - ActivationEpoch: 35 (current slot: 641 - current epoch: 20)
|
||||
// pubkey7 => Nothing - Status: pending - ActivationEpoch: 20 (current slot: 641 - current epoch: 20)
|
||||
// pubkey8 => feeRecipient8 - Status: exiting
|
||||
|
||||
// Public keys
|
||||
pubkey1 := pubkeyFromString(t, "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111")
|
||||
@@ -2201,9 +2351,9 @@ func TestValidator_buildPrepProposerReqs_WithDefaultConfig(t *testing.T) {
|
||||
pubkeyToStatus := map[[fieldparams.BLSPubkeyLength]byte]ethpb.ValidatorStatus{
|
||||
pubkey1: ethpb.ValidatorStatus_ACTIVE,
|
||||
pubkey2: ethpb.ValidatorStatus_ACTIVE,
|
||||
pubkey3: ethpb.ValidatorStatus_ACTIVE,
|
||||
pubkey3: ethpb.ValidatorStatus_UNKNOWN_STATUS,
|
||||
pubkey4: ethpb.ValidatorStatus_ACTIVE,
|
||||
pubkey5: ethpb.ValidatorStatus_UNKNOWN_STATUS,
|
||||
pubkey5: ethpb.ValidatorStatus_EXITED,
|
||||
pubkey6: ethpb.ValidatorStatus_PENDING,
|
||||
pubkey7: ethpb.ValidatorStatus_PENDING,
|
||||
pubkey8: ethpb.ValidatorStatus_EXITING,
|
||||
@@ -2220,28 +2370,23 @@ func TestValidator_buildPrepProposerReqs_WithDefaultConfig(t *testing.T) {
|
||||
pubkey8: 0,
|
||||
}
|
||||
|
||||
pubkeyToIndex := map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex{
|
||||
pubkey1: 1,
|
||||
pubkey2: 2,
|
||||
pubkey3: unknownIndex,
|
||||
pubkey4: 4,
|
||||
pubkey5: 5,
|
||||
pubkey6: 6,
|
||||
pubkey7: 7,
|
||||
pubkey8: 8,
|
||||
}
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
ctx := context.Background()
|
||||
client := validatormock.NewMockValidatorClient(ctrl)
|
||||
|
||||
client.EXPECT().ValidatorIndex(
|
||||
gomock.Any(),
|
||||
ðpb.ValidatorIndexRequest{
|
||||
PublicKey: pubkey2[:],
|
||||
},
|
||||
).Return(ðpb.ValidatorIndexResponse{
|
||||
Index: 2,
|
||||
}, nil)
|
||||
|
||||
client.EXPECT().ValidatorIndex(
|
||||
gomock.Any(),
|
||||
ðpb.ValidatorIndexRequest{
|
||||
PublicKey: pubkey3[:],
|
||||
},
|
||||
).Return(nil, status.Error(codes.NotFound, "NOT_FOUND"))
|
||||
|
||||
client.EXPECT().MultipleValidatorStatus(
|
||||
gomock.Any(),
|
||||
gomock.Any()).DoAndReturn(func(ctx context.Context, val *ethpb.MultipleValidatorStatusRequest) (*ethpb.MultipleValidatorStatusResponse, error) {
|
||||
@@ -2253,6 +2398,8 @@ func TestValidator_buildPrepProposerReqs_WithDefaultConfig(t *testing.T) {
|
||||
Status: pubkeyToStatus[bytesutil.ToBytes48(k)],
|
||||
ActivationEpoch: pubkeyToActivationEpoch[bytesutil.ToBytes48(k)],
|
||||
})
|
||||
index := pubkeyToIndex[bytesutil.ToBytes48(k)]
|
||||
resp.Indices = append(resp.Indices, index)
|
||||
}
|
||||
return resp, nil
|
||||
})
|
||||
@@ -2288,13 +2435,47 @@ func TestValidator_buildPrepProposerReqs_WithDefaultConfig(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
pubkeyToValidatorIndex: map[[fieldparams.BLSPubkeyLength]byte]primitives.ValidatorIndex{
|
||||
pubkey1: 1,
|
||||
pubkey4: 4,
|
||||
pubkey5: 5,
|
||||
pubkey6: 6,
|
||||
pubkey7: 7,
|
||||
pubkey8: 8,
|
||||
pubkeyToStatus: map[[fieldparams.BLSPubkeyLength]byte]*validatorStatus{
|
||||
pubkey1: {
|
||||
publicKey: pubkey1[:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: 1,
|
||||
},
|
||||
pubkey2: {
|
||||
publicKey: pubkey2[:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: 2,
|
||||
},
|
||||
pubkey3: {
|
||||
publicKey: pubkey3[:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_UNKNOWN_STATUS},
|
||||
index: unknownIndex,
|
||||
},
|
||||
pubkey4: {
|
||||
publicKey: pubkey4[:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: 4,
|
||||
},
|
||||
pubkey5: {
|
||||
publicKey: pubkey5[:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: 5,
|
||||
},
|
||||
pubkey6: {
|
||||
publicKey: pubkey6[:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: 6,
|
||||
},
|
||||
pubkey7: {
|
||||
publicKey: pubkey7[:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: 7,
|
||||
},
|
||||
pubkey8: {
|
||||
publicKey: pubkey8[:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: 8,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2331,10 +2512,13 @@ func TestValidator_buildPrepProposerReqs_WithDefaultConfig(t *testing.T) {
|
||||
FeeRecipient: feeRecipient8[:],
|
||||
},
|
||||
}
|
||||
filteredKeys, err := v.filterAndCacheActiveKeys(ctx, pubkeys, 641)
|
||||
filteredKeys, err := v.filterAndCacheActiveKeys(ctx, pubkeys, 640)
|
||||
require.NoError(t, err)
|
||||
actual, err := v.buildPrepProposerReqs(filteredKeys)
|
||||
require.NoError(t, err)
|
||||
sort.Slice(actual, func(i, j int) bool {
|
||||
return actual[i].ValidatorIndex < actual[j].ValidatorIndex
|
||||
})
|
||||
assert.DeepEqual(t, expected, actual)
|
||||
}
|
||||
|
||||
@@ -2404,7 +2588,7 @@ func TestValidator_buildSignedRegReqs_DefaultConfigDisabled(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
pubkeyToValidatorIndex: make(map[[48]byte]primitives.ValidatorIndex),
|
||||
pubkeyToStatus: make(map[[48]byte]*validatorStatus),
|
||||
}
|
||||
|
||||
pubkeys := [][fieldparams.BLSPubkeyLength]byte{pubkey1, pubkey2, pubkey3}
|
||||
@@ -2412,26 +2596,41 @@ func TestValidator_buildSignedRegReqs_DefaultConfigDisabled(t *testing.T) {
|
||||
var signer = func(_ context.Context, _ *validatorpb.SignRequest) (bls.Signature, error) {
|
||||
return signature, nil
|
||||
}
|
||||
v.pubkeyToValidatorIndex[pubkey1] = primitives.ValidatorIndex(1)
|
||||
v.pubkeyToValidatorIndex[pubkey2] = primitives.ValidatorIndex(2)
|
||||
v.pubkeyToValidatorIndex[pubkey3] = primitives.ValidatorIndex(3)
|
||||
actual := v.buildSignedRegReqs(ctx, pubkeys, signer)
|
||||
v.pubkeyToStatus[pubkey1] = &validatorStatus{
|
||||
publicKey: pubkey1[:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: 1,
|
||||
}
|
||||
v.pubkeyToStatus[pubkey2] = &validatorStatus{
|
||||
publicKey: pubkey2[:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: 2,
|
||||
}
|
||||
v.pubkeyToStatus[pubkey3] = &validatorStatus{
|
||||
publicKey: pubkey3[:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: 3,
|
||||
}
|
||||
actual := v.buildSignedRegReqs(ctx, pubkeys, signer, 0, false)
|
||||
|
||||
assert.Equal(t, 1, len(actual))
|
||||
assert.DeepEqual(t, feeRecipient1[:], actual[0].Message.FeeRecipient)
|
||||
assert.Equal(t, uint64(1111), actual[0].Message.GasLimit)
|
||||
assert.DeepEqual(t, pubkey1[:], actual[0].Message.Pubkey)
|
||||
|
||||
}
|
||||
|
||||
func TestValidator_buildSignedRegReqs_DefaultConfigEnabled(t *testing.T) {
|
||||
// pubkey1 => feeRecipient1, builder enabled
|
||||
// pubkey2 => feeRecipient2, builder disabled
|
||||
// pubkey3 => Nothing, builder enabled
|
||||
// pubkey4 => added after builder requests built once, used in mid epoch test
|
||||
|
||||
// Public keys
|
||||
pubkey1 := pubkeyFromString(t, "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111")
|
||||
pubkey2 := pubkeyFromString(t, "0x222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222")
|
||||
pubkey3 := pubkeyFromString(t, "0x333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333")
|
||||
pubkey4 := pubkeyFromString(t, "0x444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444")
|
||||
|
||||
// Fee recipients
|
||||
feeRecipient1 := feeRecipientFromString(t, "0x0000000000000000000000000000000000000000")
|
||||
@@ -2446,8 +2645,7 @@ func TestValidator_buildSignedRegReqs_DefaultConfigEnabled(t *testing.T) {
|
||||
client := validatormock.NewMockValidatorClient(ctrl)
|
||||
|
||||
signature := blsmock.NewMockSignature(ctrl)
|
||||
signature.EXPECT().Marshal().Return([]byte{}).Times(2)
|
||||
|
||||
signature.EXPECT().Marshal().Return([]byte{}).AnyTimes()
|
||||
v := validator{
|
||||
signedValidatorRegistrations: map[[48]byte]*ethpb.SignedValidatorRegistrationV1{},
|
||||
validatorClient: client,
|
||||
@@ -2489,7 +2687,7 @@ func TestValidator_buildSignedRegReqs_DefaultConfigEnabled(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
pubkeyToValidatorIndex: make(map[[48]byte]primitives.ValidatorIndex),
|
||||
pubkeyToStatus: make(map[[48]byte]*validatorStatus),
|
||||
}
|
||||
|
||||
pubkeys := [][fieldparams.BLSPubkeyLength]byte{pubkey1, pubkey2, pubkey3}
|
||||
@@ -2497,10 +2695,22 @@ func TestValidator_buildSignedRegReqs_DefaultConfigEnabled(t *testing.T) {
|
||||
var signer = func(_ context.Context, _ *validatorpb.SignRequest) (bls.Signature, error) {
|
||||
return signature, nil
|
||||
}
|
||||
v.pubkeyToValidatorIndex[pubkey1] = primitives.ValidatorIndex(1)
|
||||
v.pubkeyToValidatorIndex[pubkey2] = primitives.ValidatorIndex(2)
|
||||
v.pubkeyToValidatorIndex[pubkey3] = primitives.ValidatorIndex(3)
|
||||
actual := v.buildSignedRegReqs(ctx, pubkeys, signer)
|
||||
v.pubkeyToStatus[pubkey1] = &validatorStatus{
|
||||
publicKey: pubkey1[:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: 1,
|
||||
}
|
||||
v.pubkeyToStatus[pubkey2] = &validatorStatus{
|
||||
publicKey: pubkey2[:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: 2,
|
||||
}
|
||||
v.pubkeyToStatus[pubkey3] = &validatorStatus{
|
||||
publicKey: pubkey3[:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: 3,
|
||||
}
|
||||
actual := v.buildSignedRegReqs(ctx, pubkeys, signer, 0, false)
|
||||
|
||||
assert.Equal(t, 2, len(actual))
|
||||
|
||||
@@ -2511,6 +2721,26 @@ func TestValidator_buildSignedRegReqs_DefaultConfigEnabled(t *testing.T) {
|
||||
assert.DeepEqual(t, defaultFeeRecipient[:], actual[1].Message.FeeRecipient)
|
||||
assert.Equal(t, uint64(9999), actual[1].Message.GasLimit)
|
||||
assert.DeepEqual(t, pubkey3[:], actual[1].Message.Pubkey)
|
||||
|
||||
t.Run("mid epoch only pushes newly added key", func(t *testing.T) {
|
||||
v.pubkeyToStatus[pubkey4] = &validatorStatus{
|
||||
publicKey: pubkey4[:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: 4,
|
||||
}
|
||||
pubkeys = append(pubkeys, pubkey4)
|
||||
actual = v.buildSignedRegReqs(ctx, pubkeys, signer, 5, false)
|
||||
assert.Equal(t, 1, len(actual))
|
||||
|
||||
assert.DeepEqual(t, defaultFeeRecipient[:], actual[0].Message.FeeRecipient)
|
||||
assert.Equal(t, uint64(9999), actual[0].Message.GasLimit)
|
||||
assert.DeepEqual(t, pubkey4[:], actual[0].Message.Pubkey)
|
||||
})
|
||||
|
||||
t.Run("force push all keys mid epoch", func(t *testing.T) {
|
||||
actual = v.buildSignedRegReqs(ctx, pubkeys, signer, 5, true)
|
||||
assert.Equal(t, 3, len(actual))
|
||||
})
|
||||
}
|
||||
|
||||
func TestValidator_buildSignedRegReqs_SignerOnError(t *testing.T) {
|
||||
@@ -2548,7 +2778,7 @@ func TestValidator_buildSignedRegReqs_SignerOnError(t *testing.T) {
|
||||
return nil, errors.New("custom error")
|
||||
}
|
||||
|
||||
actual := v.buildSignedRegReqs(ctx, pubkeys, signer)
|
||||
actual := v.buildSignedRegReqs(ctx, pubkeys, signer, 0, false)
|
||||
assert.Equal(t, 0, len(actual))
|
||||
}
|
||||
|
||||
@@ -2595,7 +2825,7 @@ func TestValidator_buildSignedRegReqs_TimestampBeforeGenesis(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
pubkeyToValidatorIndex: make(map[[48]byte]primitives.ValidatorIndex),
|
||||
pubkeyToStatus: make(map[[48]byte]*validatorStatus),
|
||||
}
|
||||
|
||||
pubkeys := [][fieldparams.BLSPubkeyLength]byte{pubkey1}
|
||||
@@ -2603,8 +2833,12 @@ func TestValidator_buildSignedRegReqs_TimestampBeforeGenesis(t *testing.T) {
|
||||
var signer = func(_ context.Context, _ *validatorpb.SignRequest) (bls.Signature, error) {
|
||||
return signature, nil
|
||||
}
|
||||
v.pubkeyToValidatorIndex[pubkey1] = primitives.ValidatorIndex(1)
|
||||
actual := v.buildSignedRegReqs(ctx, pubkeys, signer)
|
||||
v.pubkeyToStatus[pubkey1] = &validatorStatus{
|
||||
publicKey: pubkey1[:],
|
||||
status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE},
|
||||
index: 1,
|
||||
}
|
||||
actual := v.buildSignedRegReqs(ctx, pubkeys, signer, 0, false)
|
||||
assert.Equal(t, 0, len(actual))
|
||||
}
|
||||
|
||||
|
||||
@@ -109,9 +109,8 @@ func (v *validator) internalWaitForActivation(ctx context.Context, accountsChang
|
||||
return v.internalWaitForActivation(incrementRetries(ctx), accountsChangedChan)
|
||||
}
|
||||
|
||||
statuses := make([]*validatorStatus, len(res.Statuses))
|
||||
for i, s := range res.Statuses {
|
||||
statuses[i] = &validatorStatus{
|
||||
for _, s := range res.Statuses {
|
||||
v.pubkeyToStatus[bytesutil.ToBytes48(s.PublicKey)] = &validatorStatus{
|
||||
publicKey: s.PublicKey,
|
||||
status: s.Status,
|
||||
index: s.Index,
|
||||
@@ -129,7 +128,7 @@ func (v *validator) internalWaitForActivation(ctx context.Context, accountsChang
|
||||
valCount = int64(valCounts[0].Count)
|
||||
}
|
||||
|
||||
someAreActive = v.checkAndLogValidatorStatus(statuses, valCount)
|
||||
someAreActive = v.checkAndLogValidatorStatus(valCount)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ func TestWaitActivation_ContextCanceled(t *testing.T) {
|
||||
validatorClient: validatorClient,
|
||||
km: newMockKeymanager(t, kp),
|
||||
chainClient: chainClient,
|
||||
pubkeyToStatus: make(map[[48]byte]*validatorStatus),
|
||||
}
|
||||
clientStream := mock.NewMockBeaconNodeValidator_WaitForActivationClient(ctrl)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
@@ -65,6 +66,7 @@ func TestWaitActivation_StreamSetupFails_AttemptsToReconnect(t *testing.T) {
|
||||
km: newMockKeymanager(t, kp),
|
||||
chainClient: chainClient,
|
||||
prysmChainClient: prysmChainClient,
|
||||
pubkeyToStatus: make(map[[48]byte]*validatorStatus),
|
||||
}
|
||||
clientStream := mock.NewMockBeaconNodeValidator_WaitForActivationClient(ctrl)
|
||||
validatorClient.EXPECT().WaitForActivation(
|
||||
@@ -96,6 +98,7 @@ func TestWaitForActivation_ReceiveErrorFromStream_AttemptsReconnection(t *testin
|
||||
km: newMockKeymanager(t, kp),
|
||||
chainClient: chainClient,
|
||||
prysmChainClient: prysmChainClient,
|
||||
pubkeyToStatus: make(map[[48]byte]*validatorStatus),
|
||||
}
|
||||
clientStream := mock.NewMockBeaconNodeValidator_WaitForActivationClient(ctrl)
|
||||
validatorClient.EXPECT().WaitForActivation(
|
||||
@@ -133,6 +136,7 @@ func TestWaitActivation_LogsActivationEpochOK(t *testing.T) {
|
||||
genesisTime: 1,
|
||||
chainClient: chainClient,
|
||||
prysmChainClient: prysmChainClient,
|
||||
pubkeyToStatus: make(map[[48]byte]*validatorStatus),
|
||||
}
|
||||
resp := generateMockStatusResponse([][]byte{kp.pub[:]})
|
||||
resp.Statuses[0].Status.Status = ethpb.ValidatorStatus_ACTIVE
|
||||
@@ -168,6 +172,7 @@ func TestWaitForActivation_Exiting(t *testing.T) {
|
||||
km: newMockKeymanager(t, kp),
|
||||
chainClient: chainClient,
|
||||
prysmChainClient: prysmChainClient,
|
||||
pubkeyToStatus: make(map[[48]byte]*validatorStatus),
|
||||
}
|
||||
resp := generateMockStatusResponse([][]byte{kp.pub[:]})
|
||||
resp.Statuses[0].Status.Status = ethpb.ValidatorStatus_EXITING
|
||||
@@ -211,6 +216,7 @@ func TestWaitForActivation_RefetchKeys(t *testing.T) {
|
||||
km: km,
|
||||
chainClient: chainClient,
|
||||
prysmChainClient: prysmChainClient,
|
||||
pubkeyToStatus: make(map[[48]byte]*validatorStatus),
|
||||
}
|
||||
resp := generateMockStatusResponse([][]byte{kp.pub[:]})
|
||||
resp.Statuses[0].Status.Status = ethpb.ValidatorStatus_ACTIVE
|
||||
@@ -264,6 +270,7 @@ func TestWaitForActivation_AccountsChanged(t *testing.T) {
|
||||
km: km,
|
||||
chainClient: chainClient,
|
||||
prysmChainClient: prysmChainClient,
|
||||
pubkeyToStatus: make(map[[48]byte]*validatorStatus),
|
||||
}
|
||||
inactiveResp := generateMockStatusResponse([][]byte{inactive.pub[:]})
|
||||
inactiveResp.Statuses[0].Status.Status = ethpb.ValidatorStatus_UNKNOWN_STATUS
|
||||
@@ -355,6 +362,7 @@ func TestWaitForActivation_AccountsChanged(t *testing.T) {
|
||||
genesisTime: 1,
|
||||
chainClient: chainClient,
|
||||
prysmChainClient: prysmChainClient,
|
||||
pubkeyToStatus: make(map[[48]byte]*validatorStatus),
|
||||
}
|
||||
|
||||
inactiveResp := generateMockStatusResponse([][]byte{inactivePubKey[:]})
|
||||
@@ -423,6 +431,7 @@ func TestWaitActivation_NotAllValidatorsActivatedOK(t *testing.T) {
|
||||
km: newMockKeymanager(t, kp),
|
||||
chainClient: chainClient,
|
||||
prysmChainClient: prysmChainClient,
|
||||
pubkeyToStatus: make(map[[48]byte]*validatorStatus),
|
||||
}
|
||||
resp := generateMockStatusResponse([][]byte{kp.pub[:]})
|
||||
resp.Statuses[0].Status.Status = ethpb.ValidatorStatus_ACTIVE
|
||||
|
||||
Reference in New Issue
Block a user