validator startup deadline bug (#12049)

* trying fix for validator startup deadline

* updating deadline duration to be set by params

* adding a runner test

* trying nishant's suggestion

* editing based on review feedback

* reverting a change

* fixing epoch deadline

* reverting aliasing
This commit is contained in:
james-prysm
2023-03-13 23:52:56 -05:00
committed by GitHub
parent f6eb42b761
commit 7d5d30ac94
7 changed files with 95 additions and 33 deletions

View File

@@ -186,7 +186,7 @@ func (MockValidator) HasProposerSettings() bool {
}
// PushProposerSettings for mocking
func (_ MockValidator) PushProposerSettings(_ context.Context, _ keymanager.IKeymanager) error {
func (_ MockValidator) PushProposerSettings(_ context.Context, _ keymanager.IKeymanager, _ time.Time) error {
panic("implement me")
}

View File

@@ -61,7 +61,7 @@ type Validator interface {
ReceiveBlocks(ctx context.Context, connectionErrorChannel chan<- error)
HandleKeyReload(ctx context.Context, currentKeys [][fieldparams.BLSPubkeyLength]byte) (bool, error)
CheckDoppelGanger(ctx context.Context) error
PushProposerSettings(ctx context.Context, km keymanager.IKeymanager) error
PushProposerSettings(ctx context.Context, km keymanager.IKeymanager, deadline time.Time) error
SignValidatorRegistrationRequest(ctx context.Context, signer SigningFunc, newValidatorRegistration *ethpb.ValidatorRegistrationV1) (*ethpb.SignedValidatorRegistrationV1, error)
ProposerSettings() *validatorserviceconfig.ProposerSettings
SetProposerSettings(*validatorserviceconfig.ProposerSettings)

View File

@@ -10,7 +10,7 @@ import (
"github.com/prysmaticlabs/prysm/v3/cmd/validator/flags"
fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
"github.com/prysmaticlabs/prysm/v3/config/params"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v3/time/slots"
"github.com/prysmaticlabs/prysm/v3/validator/client/iface"
@@ -57,7 +57,8 @@ func run(ctx context.Context, v iface.Validator) {
if v.ProposerSettings() != nil {
log.Infof("Validator client started with provided proposer settings that sets options such as fee recipient"+
" and will periodically update the beacon node and custom builder (if --%s)", flags.EnableBuilderFlag.Name)
if err := v.PushProposerSettings(ctx, km); err != nil {
deadline := time.Now().Add(time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second)
if err := v.PushProposerSettings(ctx, km, deadline); err != nil {
if errors.Is(err, ErrBuilderValidatorRegistration) {
log.WithError(err).Warn("Push proposer settings error")
} else {
@@ -126,8 +127,9 @@ func run(ctx context.Context, v iface.Validator) {
if slots.IsEpochStart(slot) && v.ProposerSettings() != nil {
go func() {
//deadline set for next epoch rounded up
if err := v.PushProposerSettings(ctx, km); err != nil {
//deadline set for end of epoch
epochDeadline := v.SlotDeadline(slot + params.BeaconConfig().SlotsPerEpoch - 1)
if err := v.PushProposerSettings(ctx, km, epochDeadline); err != nil {
log.WithError(err).Warn("Failed to update proposer settings")
}
}()
@@ -152,11 +154,11 @@ func run(ctx context.Context, v iface.Validator) {
}
}
func initializeValidatorAndGetHeadSlot(ctx context.Context, v iface.Validator) (types.Slot, error) {
func initializeValidatorAndGetHeadSlot(ctx context.Context, v iface.Validator) (primitives.Slot, error) {
ticker := time.NewTicker(backOffPeriod)
defer ticker.Stop()
var headSlot types.Slot
var headSlot primitives.Slot
firstTime := true
for {
if !firstTime {
@@ -218,7 +220,7 @@ func initializeValidatorAndGetHeadSlot(ctx context.Context, v iface.Validator) (
return headSlot, nil
}
func performRoles(slotCtx context.Context, allRoles map[[48]byte][]iface.ValidatorRole, v iface.Validator, slot types.Slot, wg *sync.WaitGroup, span *trace.Span) {
func performRoles(slotCtx context.Context, allRoles map[[48]byte][]iface.ValidatorRole, v iface.Validator, slot primitives.Slot, wg *sync.WaitGroup, span *trace.Span) {
for pubKey, roles := range allRoles {
wg.Add(len(roles))
for _, role := range roles {
@@ -268,7 +270,7 @@ func isConnectionError(err error) bool {
return err != nil && errors.Is(err, iface.ErrConnectionIssue)
}
func handleAssignmentError(err error, slot types.Slot) {
func handleAssignmentError(err error, slot primitives.Slot) {
if errCode, ok := status.FromError(err); ok && errCode.Code() == codes.NotFound {
log.WithField(
"epoch", slot/params.BeaconConfig().SlotsPerEpoch,

View File

@@ -11,7 +11,7 @@ import (
fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
"github.com/prysmaticlabs/prysm/v3/config/params"
validatorserviceconfig "github.com/prysmaticlabs/prysm/v3/config/validator/service"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v3/testing/assert"
"github.com/prysmaticlabs/prysm/v3/testing/require"
"github.com/prysmaticlabs/prysm/v3/validator/client/iface"
@@ -68,8 +68,8 @@ func TestUpdateDuties_NextSlot(t *testing.T) {
v := &testutil.FakeValidator{Km: &mockKeymanager{accountsChangedFeed: &event.Feed{}}}
ctx, cancel := context.WithCancel(context.Background())
slot := types.Slot(55)
ticker := make(chan types.Slot)
slot := primitives.Slot(55)
ticker := make(chan primitives.Slot)
v.NextSlotRet = ticker
go func() {
ticker <- slot
@@ -88,8 +88,8 @@ func TestUpdateDuties_HandlesError(t *testing.T) {
v := &testutil.FakeValidator{Km: &mockKeymanager{accountsChangedFeed: &event.Feed{}}}
ctx, cancel := context.WithCancel(context.Background())
slot := types.Slot(55)
ticker := make(chan types.Slot)
slot := primitives.Slot(55)
ticker := make(chan primitives.Slot)
v.NextSlotRet = ticker
go func() {
ticker <- slot
@@ -107,8 +107,8 @@ func TestRoleAt_NextSlot(t *testing.T) {
v := &testutil.FakeValidator{Km: &mockKeymanager{accountsChangedFeed: &event.Feed{}}}
ctx, cancel := context.WithCancel(context.Background())
slot := types.Slot(55)
ticker := make(chan types.Slot)
slot := primitives.Slot(55)
ticker := make(chan primitives.Slot)
v.NextSlotRet = ticker
go func() {
ticker <- slot
@@ -126,8 +126,8 @@ func TestAttests_NextSlot(t *testing.T) {
v := &testutil.FakeValidator{Km: &mockKeymanager{accountsChangedFeed: &event.Feed{}}}
ctx, cancel := context.WithCancel(context.Background())
slot := types.Slot(55)
ticker := make(chan types.Slot)
slot := primitives.Slot(55)
ticker := make(chan primitives.Slot)
v.NextSlotRet = ticker
v.RolesAtRet = []iface.ValidatorRole{iface.RoleAttester}
go func() {
@@ -146,8 +146,8 @@ func TestProposes_NextSlot(t *testing.T) {
v := &testutil.FakeValidator{Km: &mockKeymanager{accountsChangedFeed: &event.Feed{}}}
ctx, cancel := context.WithCancel(context.Background())
slot := types.Slot(55)
ticker := make(chan types.Slot)
slot := primitives.Slot(55)
ticker := make(chan primitives.Slot)
v.NextSlotRet = ticker
v.RolesAtRet = []iface.ValidatorRole{iface.RoleProposer}
go func() {
@@ -166,8 +166,8 @@ func TestBothProposesAndAttests_NextSlot(t *testing.T) {
v := &testutil.FakeValidator{Km: &mockKeymanager{accountsChangedFeed: &event.Feed{}}}
ctx, cancel := context.WithCancel(context.Background())
slot := types.Slot(55)
ticker := make(chan types.Slot)
slot := primitives.Slot(55)
ticker := make(chan primitives.Slot)
v.NextSlotRet = ticker
v.RolesAtRet = []iface.ValidatorRole{iface.RoleAttester, iface.RoleProposer}
go func() {
@@ -189,8 +189,8 @@ func TestAllValidatorsAreExited_NextSlot(t *testing.T) {
ctx, cancel := context.WithCancel(context.WithValue(context.Background(), testutil.AllValidatorsAreExitedCtxKey, true))
hook := logTest.NewGlobal()
slot := types.Slot(55)
ticker := make(chan types.Slot)
slot := primitives.Slot(55)
ticker := make(chan primitives.Slot)
v.NextSlotRet = ticker
go func() {
ticker <- slot
@@ -243,7 +243,7 @@ func TestUpdateProposerSettingsAt_EpochStart(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
hook := logTest.NewGlobal()
slot := params.BeaconConfig().SlotsPerEpoch
ticker := make(chan types.Slot)
ticker := make(chan primitives.Slot)
v.NextSlotRet = ticker
go func() {
ticker <- slot
@@ -255,6 +255,54 @@ func TestUpdateProposerSettingsAt_EpochStart(t *testing.T) {
assert.LogsContain(t, hook, "updated proposer settings")
}
func TestUpdateProposerSettingsAt_EpochEndExceeded(t *testing.T) {
v := &testutil.FakeValidator{Km: &mockKeymanager{accountsChangedFeed: &event.Feed{}}, ProposerSettingWait: time.Duration(params.BeaconConfig().SecondsPerSlot+1) * time.Second}
v.SetProposerSettings(&validatorserviceconfig.ProposerSettings{
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"),
},
},
})
ctx, cancel := context.WithCancel(context.Background())
hook := logTest.NewGlobal()
slot := params.BeaconConfig().SlotsPerEpoch - 1 //have it set close to the end of epoch
ticker := make(chan primitives.Slot)
v.NextSlotRet = ticker
go func() {
ticker <- slot
cancel()
}()
run(ctx, v)
// can't test "Failed to update proposer settings" because of log.fatal
assert.LogsContain(t, hook, "deadline exceeded")
}
func TestUpdateProposerSettingsAt_EpochEndOk(t *testing.T) {
v := &testutil.FakeValidator{Km: &mockKeymanager{accountsChangedFeed: &event.Feed{}}, ProposerSettingWait: time.Duration(params.BeaconConfig().SecondsPerSlot-1) * time.Second}
v.SetProposerSettings(&validatorserviceconfig.ProposerSettings{
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipientConfig: &validatorserviceconfig.FeeRecipientConfig{
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455012BFEBf6177F1D2e9738D9"),
},
},
})
ctx, cancel := context.WithCancel(context.Background())
hook := logTest.NewGlobal()
slot := params.BeaconConfig().SlotsPerEpoch - 1 //have it set close to the end of epoch
ticker := make(chan primitives.Slot)
v.NextSlotRet = ticker
go func() {
ticker <- slot
cancel()
}()
run(ctx, v)
// can't test "Failed to update proposer settings" because of log.fatal
assert.LogsContain(t, hook, "Mock updated proposer settings")
}
func TestUpdateProposerSettings_ContinuesAfterValidatorRegistrationFails(t *testing.T) {
errSomeotherError := errors.New("some internal error")
v := &testutil.FakeValidator{
@@ -271,7 +319,7 @@ func TestUpdateProposerSettings_ContinuesAfterValidatorRegistrationFails(t *test
ctx, cancel := context.WithCancel(context.Background())
hook := logTest.NewGlobal()
slot := params.BeaconConfig().SlotsPerEpoch
ticker := make(chan types.Slot)
ticker := make(chan primitives.Slot)
v.NextSlotRet = ticker
go func() {
ticker <- slot

View File

@@ -53,6 +53,7 @@ type FakeValidator struct {
PubkeyToIndexMap map[[fieldparams.BLSPubkeyLength]byte]uint64
PubkeysToStatusesMap map[[fieldparams.BLSPubkeyLength]byte]ethpb.ValidatorStatus
proposerSettings *validatorserviceconfig.ProposerSettings
ProposerSettingWait time.Duration
Km keymanager.IKeymanager
}
@@ -258,10 +259,21 @@ func (*FakeValidator) HasProposerSettings() bool {
}
// PushProposerSettings for mocking
func (fv *FakeValidator) PushProposerSettings(_ context.Context, _ keymanager.IKeymanager) error {
func (fv *FakeValidator) PushProposerSettings(ctx context.Context, _ keymanager.IKeymanager, deadline time.Time) error {
nctx, cancel := context.WithDeadline(ctx, deadline)
ctx = nctx
defer cancel()
time.Sleep(fv.ProposerSettingWait)
if ctx.Err() == context.DeadlineExceeded {
log.Error("deadline exceeded")
// can't return error or it will trigger a log.fatal
return nil
}
if fv.ProposerSettingsErr != nil {
return fv.ProposerSettingsErr
}
log.Infoln("Mock updated proposer settings")
return nil
}

View File

@@ -985,13 +985,12 @@ func (v *validator) SetProposerSettings(settings *validatorserviceconfig.Propose
}
// 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) error {
func (v *validator) PushProposerSettings(ctx context.Context, km keymanager.IKeymanager, deadline time.Time) error {
if km == nil {
return errors.New("keymanager is nil when calling PrepareBeaconProposer")
}
deadline := v.SlotDeadline(slots.RoundUpToNearestEpoch(slots.CurrentSlot(v.genesisTime)))
ctx, cancel := context.WithDeadline(ctx, deadline)
nctx, cancel := context.WithDeadline(ctx, deadline)
ctx = nctx
defer cancel()
pubkeys, err := km.FetchValidatingPublicKeys(ctx)

View File

@@ -1958,7 +1958,8 @@ func TestValidator_PushProposerSettings(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); tt.err != "" {
deadline := time.Now().Add(time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second)
if err := v.PushProposerSettings(ctx, km, deadline); tt.err != "" {
assert.ErrorContains(t, tt.err, err)
}
if len(tt.logMessages) > 0 {