2020-12-11 01:26:31 +00:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"io"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
2024-02-15 05:46:47 +00:00
|
|
|
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
|
|
|
validator2 "github.com/prysmaticlabs/prysm/v5/consensus-types/validator"
|
|
|
|
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
|
|
|
"github.com/prysmaticlabs/prysm/v5/math"
|
|
|
|
"github.com/prysmaticlabs/prysm/v5/monitoring/tracing"
|
|
|
|
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
|
|
|
"github.com/prysmaticlabs/prysm/v5/validator/client/iface"
|
2020-12-11 01:26:31 +00:00
|
|
|
"go.opencensus.io/trace"
|
|
|
|
)
|
|
|
|
|
|
|
|
// WaitForActivation checks whether the validator pubkey is in the active
|
|
|
|
// validator set. If not, this operation will block until an activation message is
|
2021-03-05 18:33:39 +00:00
|
|
|
// received. This method also monitors the keymanager for updates while waiting for an activation
|
|
|
|
// from the gRPC server.
|
2021-03-12 17:23:56 +00:00
|
|
|
//
|
|
|
|
// If the channel parameter is nil, WaitForActivation creates and manages its own channel.
|
2022-01-06 17:33:08 +00:00
|
|
|
func (v *validator) WaitForActivation(ctx context.Context, accountsChangedChan chan [][fieldparams.BLSPubkeyLength]byte) error {
|
2021-03-05 18:33:39 +00:00
|
|
|
// Monitor the key manager for updates.
|
2021-03-12 17:23:56 +00:00
|
|
|
if accountsChangedChan == nil {
|
2022-01-06 17:33:08 +00:00
|
|
|
accountsChangedChan = make(chan [][fieldparams.BLSPubkeyLength]byte, 1)
|
2022-01-31 16:44:17 +00:00
|
|
|
km, err := v.Keymanager()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-01-16 17:04:54 +00:00
|
|
|
// subscribe to the channel if it's the first time
|
2022-01-31 16:44:17 +00:00
|
|
|
sub := km.SubscribeAccountChanges(accountsChangedChan)
|
2021-03-12 17:23:56 +00:00
|
|
|
defer func() {
|
|
|
|
sub.Unsubscribe()
|
|
|
|
close(accountsChangedChan)
|
|
|
|
}()
|
|
|
|
}
|
2023-03-09 03:21:12 +00:00
|
|
|
return v.internalWaitForActivation(ctx, accountsChangedChan)
|
2021-03-05 18:33:39 +00:00
|
|
|
}
|
|
|
|
|
2023-03-09 03:21:12 +00:00
|
|
|
// internalWaitForActivation performs the following:
|
2024-01-16 17:04:54 +00:00
|
|
|
// 1) While the key manager is empty, subscribe to keymanager changes until some validator keys exist.
|
2021-03-05 18:33:39 +00:00
|
|
|
// 2) Open a server side stream for activation events against the given keys.
|
|
|
|
// 3) In another go routine, the key manager is monitored for updates and emits an update event on
|
2023-03-09 03:21:12 +00:00
|
|
|
// the accountsChangedChan. When an event signal is received, restart the internalWaitForActivation routine.
|
2021-03-05 18:33:39 +00:00
|
|
|
// 4) If the stream is reset in error, restart the routine.
|
|
|
|
// 5) If the stream returns a response indicating one or more validators are active, exit the routine.
|
2023-03-09 03:21:12 +00:00
|
|
|
func (v *validator) internalWaitForActivation(ctx context.Context, accountsChangedChan <-chan [][fieldparams.BLSPubkeyLength]byte) error {
|
2020-12-11 01:26:31 +00:00
|
|
|
ctx, span := trace.StartSpan(ctx, "validator.WaitForActivation")
|
|
|
|
defer span.End()
|
|
|
|
validatingKeys, err := v.keyManager.FetchValidatingPublicKeys(ctx)
|
|
|
|
if err != nil {
|
2024-01-16 17:04:54 +00:00
|
|
|
return errors.Wrap(err, msgCouldNotFetchKeys)
|
2020-12-11 01:26:31 +00:00
|
|
|
}
|
2024-01-16 17:04:54 +00:00
|
|
|
// if there are no validating keys, wait for some
|
2020-12-11 19:55:52 +00:00
|
|
|
if len(validatingKeys) == 0 {
|
|
|
|
log.Warn(msgNoKeysFetched)
|
2024-01-16 17:04:54 +00:00
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
log.Debug("Context closed, exiting fetching validating keys")
|
|
|
|
return ctx.Err()
|
|
|
|
case <-accountsChangedChan:
|
|
|
|
// if the accounts changed try it again
|
|
|
|
return v.internalWaitForActivation(ctx, accountsChangedChan)
|
2020-12-11 19:55:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-16 17:04:54 +00:00
|
|
|
stream, err := v.validatorClient.WaitForActivation(ctx, ðpb.ValidatorActivationRequest{
|
2020-12-11 01:26:31 +00:00
|
|
|
PublicKeys: bytesutil.FromBytes48Array(validatingKeys),
|
2024-01-16 17:04:54 +00:00
|
|
|
})
|
2020-12-11 01:26:31 +00:00
|
|
|
if err != nil {
|
2021-09-14 20:59:51 +00:00
|
|
|
tracing.AnnotateError(span, err)
|
2021-01-25 21:27:30 +00:00
|
|
|
attempts := streamAttempts(ctx)
|
2020-12-11 01:26:31 +00:00
|
|
|
log.WithError(err).WithField("attempts", attempts).
|
|
|
|
Error("Stream broken while waiting for activation. Reconnecting...")
|
|
|
|
// Reconnection attempt backoff, up to 60s.
|
2021-09-17 21:55:24 +00:00
|
|
|
time.Sleep(time.Second * time.Duration(math.Min(uint64(attempts), 60)))
|
2023-03-09 03:21:12 +00:00
|
|
|
return v.internalWaitForActivation(incrementRetries(ctx), accountsChangedChan)
|
2020-12-11 01:26:31 +00:00
|
|
|
}
|
2021-03-16 15:00:05 +00:00
|
|
|
|
2024-01-16 17:04:54 +00:00
|
|
|
someAreActive := false
|
|
|
|
for !someAreActive {
|
2022-06-18 10:14:43 +00:00
|
|
|
select {
|
2024-01-16 17:04:54 +00:00
|
|
|
case <-ctx.Done():
|
|
|
|
log.Debug("Context closed, exiting fetching validating keys")
|
|
|
|
return ctx.Err()
|
2022-06-18 10:14:43 +00:00
|
|
|
case <-accountsChangedChan:
|
|
|
|
// Accounts (keys) changed, restart the process.
|
2023-03-09 03:21:12 +00:00
|
|
|
return v.internalWaitForActivation(ctx, accountsChangedChan)
|
2022-06-18 10:14:43 +00:00
|
|
|
default:
|
2024-01-16 17:04:54 +00:00
|
|
|
res, err := (stream).Recv() // retrieve from stream one loop at a time
|
2022-06-18 10:14:43 +00:00
|
|
|
// If the stream is closed, we stop the loop.
|
|
|
|
if errors.Is(err, io.EOF) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
// If context is canceled we return from the function.
|
|
|
|
if ctx.Err() == context.Canceled {
|
|
|
|
return errors.Wrap(ctx.Err(), "context has been canceled so shutting down the loop")
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
tracing.AnnotateError(span, err)
|
|
|
|
attempts := streamAttempts(ctx)
|
|
|
|
log.WithError(err).WithField("attempts", attempts).
|
|
|
|
Error("Stream broken while waiting for activation. Reconnecting...")
|
|
|
|
// Reconnection attempt backoff, up to 60s.
|
|
|
|
time.Sleep(time.Second * time.Duration(math.Min(uint64(attempts), 60)))
|
2023-03-09 03:21:12 +00:00
|
|
|
return v.internalWaitForActivation(incrementRetries(ctx), accountsChangedChan)
|
2022-06-18 10:14:43 +00:00
|
|
|
}
|
2021-03-16 15:00:05 +00:00
|
|
|
|
2022-06-18 10:14:43 +00:00
|
|
|
statuses := make([]*validatorStatus, len(res.Statuses))
|
|
|
|
for i, s := range res.Statuses {
|
|
|
|
statuses[i] = &validatorStatus{
|
|
|
|
publicKey: s.PublicKey,
|
|
|
|
status: s.Status,
|
|
|
|
index: s.Index,
|
2021-03-16 15:00:05 +00:00
|
|
|
}
|
2022-06-18 10:14:43 +00:00
|
|
|
}
|
2021-03-16 15:00:05 +00:00
|
|
|
|
2023-10-11 13:23:02 +00:00
|
|
|
// "-1" indicates that validator count endpoint is not supported by the beacon node.
|
|
|
|
var valCount int64 = -1
|
2023-10-20 16:45:33 +00:00
|
|
|
valCounts, err := v.prysmBeaconClient.GetValidatorCount(ctx, "head", []validator2.Status{validator2.Active})
|
2023-10-11 13:23:02 +00:00
|
|
|
if err != nil && !errors.Is(err, iface.ErrNotSupported) {
|
2022-08-16 18:05:56 +00:00
|
|
|
return errors.Wrap(err, "could not get active validator count")
|
|
|
|
}
|
|
|
|
|
2023-10-11 13:23:02 +00:00
|
|
|
if len(valCounts) > 0 {
|
|
|
|
valCount = int64(valCounts[0].Count)
|
|
|
|
}
|
|
|
|
|
2024-01-16 17:04:54 +00:00
|
|
|
someAreActive = v.checkAndLogValidatorStatus(statuses, valCount)
|
2020-12-11 01:26:31 +00:00
|
|
|
}
|
|
|
|
}
|
2024-01-16 17:04:54 +00:00
|
|
|
|
2020-12-11 01:26:31 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Preferred way to use context keys is with a non built-in type. See: RVV-B0003
|
|
|
|
type waitForActivationContextKey string
|
|
|
|
|
|
|
|
const waitForActivationAttemptsContextKey = waitForActivationContextKey("WaitForActivation-attempts")
|
|
|
|
|
2021-01-25 21:27:30 +00:00
|
|
|
func streamAttempts(ctx context.Context) int {
|
2020-12-11 01:26:31 +00:00
|
|
|
attempts, ok := ctx.Value(waitForActivationAttemptsContextKey).(int)
|
|
|
|
if !ok {
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
return attempts
|
|
|
|
}
|
|
|
|
|
|
|
|
func incrementRetries(ctx context.Context) context.Context {
|
2021-01-25 21:27:30 +00:00
|
|
|
attempts := streamAttempts(ctx)
|
2020-12-11 01:26:31 +00:00
|
|
|
return context.WithValue(ctx, waitForActivationAttemptsContextKey, attempts+1)
|
|
|
|
}
|