github.com/prysmaticlabs/prysm@v1.4.4/validator/client/validator.go (about)

     1  // Package client represents a gRPC polling-based implementation
     2  // of an Ethereum validator client.
     3  package client
     4  
     5  import (
     6  	"bytes"
     7  	"context"
     8  	"encoding/binary"
     9  	"encoding/hex"
    10  	"fmt"
    11  	"io"
    12  	"strconv"
    13  	"strings"
    14  	"sync"
    15  	"time"
    16  
    17  	"github.com/dgraph-io/ristretto"
    18  	lru "github.com/hashicorp/golang-lru"
    19  	"github.com/pkg/errors"
    20  	types "github.com/prysmaticlabs/eth2-types"
    21  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
    22  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    23  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    24  	"github.com/prysmaticlabs/prysm/shared/event"
    25  	"github.com/prysmaticlabs/prysm/shared/featureconfig"
    26  	"github.com/prysmaticlabs/prysm/shared/hashutil"
    27  	"github.com/prysmaticlabs/prysm/shared/params"
    28  	"github.com/prysmaticlabs/prysm/shared/slotutil"
    29  	accountsiface "github.com/prysmaticlabs/prysm/validator/accounts/iface"
    30  	"github.com/prysmaticlabs/prysm/validator/accounts/wallet"
    31  	"github.com/prysmaticlabs/prysm/validator/client/iface"
    32  	vdb "github.com/prysmaticlabs/prysm/validator/db"
    33  	"github.com/prysmaticlabs/prysm/validator/db/kv"
    34  	"github.com/prysmaticlabs/prysm/validator/graffiti"
    35  	"github.com/prysmaticlabs/prysm/validator/keymanager"
    36  	slashingiface "github.com/prysmaticlabs/prysm/validator/slashing-protection/iface"
    37  	"github.com/sirupsen/logrus"
    38  	"go.opencensus.io/trace"
    39  	"google.golang.org/protobuf/proto"
    40  	"google.golang.org/protobuf/types/known/emptypb"
    41  )
    42  
    43  // reconnectPeriod is the frequency that we try to restart our
    44  // slasher connection when the slasher client connection is not ready.
    45  var reconnectPeriod = 5 * time.Second
    46  
    47  // keyFetchPeriod is the frequency that we try to refetch validating keys
    48  // in case no keys were fetched previously.
    49  var keyRefetchPeriod = 30 * time.Second
    50  
    51  var (
    52  	msgCouldNotFetchKeys = "could not fetch validating keys"
    53  	msgNoKeysFetched     = "No validating keys fetched. Trying again"
    54  )
    55  
    56  type validator struct {
    57  	logValidatorBalances               bool
    58  	useWeb                             bool
    59  	emitAccountMetrics                 bool
    60  	logDutyCountDown                   bool
    61  	domainDataLock                     sync.Mutex
    62  	attLogsLock                        sync.Mutex
    63  	aggregatedSlotCommitteeIDCacheLock sync.Mutex
    64  	prevBalanceLock                    sync.RWMutex
    65  	slashableKeysLock                  sync.RWMutex
    66  	walletInitializedFeed              *event.Feed
    67  	blockFeed                          *event.Feed
    68  	genesisTime                        uint64
    69  	highestValidSlot                   types.Slot
    70  	domainDataCache                    *ristretto.Cache
    71  	aggregatedSlotCommitteeIDCache     *lru.Cache
    72  	ticker                             slotutil.Ticker
    73  	prevBalance                        map[[48]byte]uint64
    74  	duties                             *ethpb.DutiesResponse
    75  	startBalances                      map[[48]byte]uint64
    76  	attLogs                            map[[32]byte]*attSubmitted
    77  	node                               ethpb.NodeClient
    78  	keyManager                         keymanager.IKeymanager
    79  	beaconClient                       ethpb.BeaconChainClient
    80  	validatorClient                    ethpb.BeaconNodeValidatorClient
    81  	protector                          slashingiface.Protector
    82  	db                                 vdb.Database
    83  	graffiti                           []byte
    84  	voteStats                          voteStats
    85  	graffitiStruct                     *graffiti.Graffiti
    86  	graffitiOrderedIndex               uint64
    87  	eipImportBlacklistedPublicKeys     map[[48]byte]bool
    88  }
    89  
    90  type validatorStatus struct {
    91  	publicKey []byte
    92  	status    *ethpb.ValidatorStatusResponse
    93  	index     types.ValidatorIndex
    94  }
    95  
    96  // Done cleans up the validator.
    97  func (v *validator) Done() {
    98  	v.ticker.Done()
    99  }
   100  
   101  // WaitForWalletInitialization checks if the validator needs to wait for
   102  func (v *validator) WaitForWalletInitialization(ctx context.Context) error {
   103  	// This function should only run if we are using managing the
   104  	// validator client using the Prysm web UI.
   105  	if !v.useWeb {
   106  		return nil
   107  	}
   108  	if v.keyManager != nil {
   109  		return nil
   110  	}
   111  	walletChan := make(chan *wallet.Wallet)
   112  	sub := v.walletInitializedFeed.Subscribe(walletChan)
   113  	defer sub.Unsubscribe()
   114  	for {
   115  		select {
   116  		case w := <-walletChan:
   117  			keyManager, err := w.InitializeKeymanager(ctx, accountsiface.InitKeymanagerConfig{ListenForChanges: true})
   118  			if err != nil {
   119  				return errors.Wrap(err, "could not read keymanager")
   120  			}
   121  			v.keyManager = keyManager
   122  			return nil
   123  		case <-ctx.Done():
   124  			return errors.New("context canceled")
   125  		case <-sub.Err():
   126  			log.Error("Subscriber closed, exiting goroutine")
   127  			return nil
   128  		}
   129  	}
   130  }
   131  
   132  // WaitForChainStart checks whether the beacon node has started its runtime. That is,
   133  // it calls to the beacon node which then verifies the ETH1.0 deposit contract logs to check
   134  // for the ChainStart log to have been emitted. If so, it starts a ticker based on the ChainStart
   135  // unix timestamp which will be used to keep track of time within the validator client.
   136  func (v *validator) WaitForChainStart(ctx context.Context) error {
   137  	ctx, span := trace.StartSpan(ctx, "validator.WaitForChainStart")
   138  	defer span.End()
   139  	// First, check if the beacon chain has started.
   140  	stream, err := v.validatorClient.WaitForChainStart(ctx, &emptypb.Empty{})
   141  	if err != nil {
   142  		return errors.Wrap(
   143  			iface.ErrConnectionIssue,
   144  			errors.Wrap(err, "could not setup beacon chain ChainStart streaming client").Error(),
   145  		)
   146  	}
   147  
   148  	log.Info("Waiting for beacon chain start log from the ETH 1.0 deposit contract")
   149  	chainStartRes, err := stream.Recv()
   150  	if err != io.EOF {
   151  		if ctx.Err() == context.Canceled {
   152  			return errors.Wrap(ctx.Err(), "context has been canceled so shutting down the loop")
   153  		}
   154  		if err != nil {
   155  			return errors.Wrap(
   156  				iface.ErrConnectionIssue,
   157  				errors.Wrap(err, "could not receive ChainStart from stream").Error(),
   158  			)
   159  		}
   160  		v.genesisTime = chainStartRes.GenesisTime
   161  		curGenValRoot, err := v.db.GenesisValidatorsRoot(ctx)
   162  		if err != nil {
   163  			return errors.Wrap(err, "could not get current genesis validators root")
   164  		}
   165  		if len(curGenValRoot) == 0 {
   166  			if err := v.db.SaveGenesisValidatorsRoot(ctx, chainStartRes.GenesisValidatorsRoot); err != nil {
   167  				return errors.Wrap(err, "could not save genesis validator root")
   168  			}
   169  		} else {
   170  			if !bytes.Equal(curGenValRoot, chainStartRes.GenesisValidatorsRoot) {
   171  				log.Errorf("The genesis validators root received from the beacon node does not match what is in " +
   172  					"your validator database. This could indicate that this is a database meant for another network. If " +
   173  					"you were previously running this validator database on another network, please run --clear-db to " +
   174  					"clear the database. If not, please file an issue at https://github.com/prysmaticlabs/prysm/issues")
   175  				return fmt.Errorf(
   176  					"genesis validators root from beacon node (%#x) does not match root saved in validator db (%#x)",
   177  					chainStartRes.GenesisValidatorsRoot,
   178  					curGenValRoot,
   179  				)
   180  			}
   181  		}
   182  	} else {
   183  		return iface.ErrConnectionIssue
   184  	}
   185  
   186  	// Once the ChainStart log is received, we update the genesis time of the validator client
   187  	// and begin a slot ticker used to track the current slot the beacon node is in.
   188  	v.ticker = slotutil.NewSlotTicker(time.Unix(int64(v.genesisTime), 0), params.BeaconConfig().SecondsPerSlot)
   189  	log.WithField("genesisTime", time.Unix(int64(v.genesisTime), 0)).Info("Beacon chain started")
   190  	return nil
   191  }
   192  
   193  // WaitForSync checks whether the beacon node has sync to the latest head.
   194  func (v *validator) WaitForSync(ctx context.Context) error {
   195  	ctx, span := trace.StartSpan(ctx, "validator.WaitForSync")
   196  	defer span.End()
   197  
   198  	s, err := v.node.GetSyncStatus(ctx, &emptypb.Empty{})
   199  	if err != nil {
   200  		return errors.Wrap(iface.ErrConnectionIssue, errors.Wrap(err, "could not get sync status").Error())
   201  	}
   202  	if !s.Syncing {
   203  		return nil
   204  	}
   205  
   206  	for {
   207  		select {
   208  		// Poll every half slot.
   209  		case <-time.After(slotutil.DivideSlotBy(2 /* twice per slot */)):
   210  			s, err := v.node.GetSyncStatus(ctx, &emptypb.Empty{})
   211  			if err != nil {
   212  				return errors.Wrap(iface.ErrConnectionIssue, errors.Wrap(err, "could not get sync status").Error())
   213  			}
   214  			if !s.Syncing {
   215  				return nil
   216  			}
   217  			log.Info("Waiting for beacon node to sync to latest chain head")
   218  		case <-ctx.Done():
   219  			return errors.New("context has been canceled, exiting goroutine")
   220  		}
   221  	}
   222  }
   223  
   224  // SlasherReady checks if slasher that was configured as external protection
   225  // is reachable.
   226  func (v *validator) SlasherReady(ctx context.Context) error {
   227  	ctx, span := trace.StartSpan(ctx, "validator.SlasherReady")
   228  	defer span.End()
   229  	if featureconfig.Get().SlasherProtection {
   230  		err := v.protector.Status()
   231  		if err == nil {
   232  			return nil
   233  		}
   234  		ticker := time.NewTicker(reconnectPeriod)
   235  		defer ticker.Stop()
   236  		for {
   237  			select {
   238  			case <-ticker.C:
   239  				log.WithError(err).Info("Slasher connection wasn't ready. Trying again")
   240  				err = v.protector.Status()
   241  				if err != nil {
   242  					continue
   243  				}
   244  				log.Info("Slasher connection is ready")
   245  				return nil
   246  			case <-ctx.Done():
   247  				log.Debug("Context closed, exiting reconnect external protection")
   248  				return ctx.Err()
   249  			}
   250  		}
   251  	}
   252  	return nil
   253  }
   254  
   255  // ReceiveBlocks starts a gRPC client stream listener to obtain
   256  // blocks from the beacon node. Upon receiving a block, the service
   257  // broadcasts it to a feed for other usages to subscribe to.
   258  func (v *validator) ReceiveBlocks(ctx context.Context, connectionErrorChannel chan<- error) {
   259  	stream, err := v.beaconClient.StreamBlocks(ctx, &ethpb.StreamBlocksRequest{VerifiedOnly: true})
   260  	if err != nil {
   261  		log.WithError(err).Error("Failed to retrieve blocks stream, " + iface.ErrConnectionIssue.Error())
   262  		connectionErrorChannel <- errors.Wrap(iface.ErrConnectionIssue, err.Error())
   263  		return
   264  	}
   265  
   266  	for {
   267  		if ctx.Err() == context.Canceled {
   268  			log.WithError(ctx.Err()).Error("Context canceled - shutting down blocks receiver")
   269  			return
   270  		}
   271  		res, err := stream.Recv()
   272  		if err != nil {
   273  			log.WithError(err).Error("Could not receive blocks from beacon node, " + iface.ErrConnectionIssue.Error())
   274  			connectionErrorChannel <- errors.Wrap(iface.ErrConnectionIssue, err.Error())
   275  			return
   276  		}
   277  		if res == nil || res.Block == nil {
   278  			continue
   279  		}
   280  		if res.Block.Slot > v.highestValidSlot {
   281  			v.highestValidSlot = res.Block.Slot
   282  		}
   283  
   284  		v.blockFeed.Send(res)
   285  	}
   286  }
   287  
   288  func (v *validator) checkAndLogValidatorStatus(statuses []*validatorStatus) bool {
   289  	nonexistentIndex := types.ValidatorIndex(^uint64(0))
   290  	var validatorActivated bool
   291  	for _, status := range statuses {
   292  		fields := logrus.Fields{
   293  			"pubKey": fmt.Sprintf("%#x", bytesutil.Trunc(status.publicKey)),
   294  			"status": status.status.Status.String(),
   295  		}
   296  		if status.index != nonexistentIndex {
   297  			fields["index"] = status.index
   298  		}
   299  		log := log.WithFields(fields)
   300  		if v.emitAccountMetrics {
   301  			fmtKey := fmt.Sprintf("%#x", status.publicKey)
   302  			ValidatorStatusesGaugeVec.WithLabelValues(fmtKey).Set(float64(status.status.Status))
   303  		}
   304  		switch status.status.Status {
   305  		case ethpb.ValidatorStatus_UNKNOWN_STATUS:
   306  			log.Info("Waiting for deposit to be observed by beacon node")
   307  		case ethpb.ValidatorStatus_DEPOSITED:
   308  			if status.status.PositionInActivationQueue != 0 {
   309  				log.WithField(
   310  					"positionInActivationQueue", status.status.PositionInActivationQueue,
   311  				).Info("Deposit processed, entering activation queue after finalization")
   312  			}
   313  		case ethpb.ValidatorStatus_PENDING:
   314  			if status.status.ActivationEpoch == params.BeaconConfig().FarFutureEpoch {
   315  				log.WithFields(logrus.Fields{
   316  					"positionInActivationQueue": status.status.PositionInActivationQueue,
   317  				}).Info("Waiting to be assigned activation epoch")
   318  			} else {
   319  				log.WithFields(logrus.Fields{
   320  					"activationEpoch": status.status.ActivationEpoch,
   321  				}).Info("Waiting for activation")
   322  			}
   323  		case ethpb.ValidatorStatus_ACTIVE, ethpb.ValidatorStatus_EXITING:
   324  			validatorActivated = true
   325  		case ethpb.ValidatorStatus_EXITED:
   326  			log.Info("Validator exited")
   327  		case ethpb.ValidatorStatus_INVALID:
   328  			log.Warn("Invalid Eth1 deposit")
   329  		default:
   330  			log.WithFields(logrus.Fields{
   331  				"activationEpoch": status.status.ActivationEpoch,
   332  			}).Info("Validator status")
   333  		}
   334  	}
   335  	return validatorActivated
   336  }
   337  
   338  func logActiveValidatorStatus(statuses []*validatorStatus) {
   339  	for _, s := range statuses {
   340  		if s.status.Status != ethpb.ValidatorStatus_ACTIVE {
   341  			continue
   342  		}
   343  		log.WithFields(logrus.Fields{
   344  			"publicKey": fmt.Sprintf("%#x", bytesutil.Trunc(s.publicKey)),
   345  			"index":     s.index,
   346  		}).Info("Validator activated")
   347  	}
   348  }
   349  
   350  // CanonicalHeadSlot returns the slot of canonical block currently found in the
   351  // beacon chain via RPC.
   352  func (v *validator) CanonicalHeadSlot(ctx context.Context) (types.Slot, error) {
   353  	ctx, span := trace.StartSpan(ctx, "validator.CanonicalHeadSlot")
   354  	defer span.End()
   355  	head, err := v.beaconClient.GetChainHead(ctx, &emptypb.Empty{})
   356  	if err != nil {
   357  		return 0, errors.Wrap(iface.ErrConnectionIssue, err.Error())
   358  	}
   359  	return head.HeadSlot, nil
   360  }
   361  
   362  // NextSlot emits the next slot number at the start time of that slot.
   363  func (v *validator) NextSlot() <-chan types.Slot {
   364  	return v.ticker.C()
   365  }
   366  
   367  // SlotDeadline is the start time of the next slot.
   368  func (v *validator) SlotDeadline(slot types.Slot) time.Time {
   369  	secs := time.Duration((slot + 1).Mul(params.BeaconConfig().SecondsPerSlot))
   370  	return time.Unix(int64(v.genesisTime), 0 /*ns*/).Add(secs * time.Second)
   371  }
   372  
   373  // CheckDoppelGanger checks if the current actively provided keys have
   374  // any duplicates active in the network.
   375  func (v *validator) CheckDoppelGanger(ctx context.Context) error {
   376  	if !featureconfig.Get().EnableDoppelGanger {
   377  		return nil
   378  	}
   379  	pubkeys, err := v.keyManager.FetchValidatingPublicKeys(ctx)
   380  	if err != nil {
   381  		return err
   382  	}
   383  	log.WithField("keys", len(pubkeys)).Info("Running doppelganger check")
   384  	// Exit early if no validating pub keys are found.
   385  	if len(pubkeys) == 0 {
   386  		return nil
   387  	}
   388  	req := &ethpb.DoppelGangerRequest{ValidatorRequests: []*ethpb.DoppelGangerRequest_ValidatorRequest{}}
   389  	for _, pkey := range pubkeys {
   390  		attRec, err := v.db.AttestationHistoryForPubKey(ctx, pkey)
   391  		if err != nil {
   392  			return err
   393  		}
   394  		if len(attRec) == 0 {
   395  			// If no history exists we simply send in a zero
   396  			// value for the request epoch and root.
   397  			req.ValidatorRequests = append(req.ValidatorRequests,
   398  				&ethpb.DoppelGangerRequest_ValidatorRequest{
   399  					PublicKey:  pkey[:],
   400  					Epoch:      0,
   401  					SignedRoot: make([]byte, 32),
   402  				})
   403  			continue
   404  		}
   405  		r := retrieveLatestRecord(attRec)
   406  		if pkey != r.PubKey {
   407  			return errors.New("attestation record mismatched public key")
   408  		}
   409  		req.ValidatorRequests = append(req.ValidatorRequests,
   410  			&ethpb.DoppelGangerRequest_ValidatorRequest{
   411  				PublicKey:  r.PubKey[:],
   412  				Epoch:      r.Target,
   413  				SignedRoot: r.SigningRoot[:],
   414  			})
   415  	}
   416  	resp, err := v.validatorClient.CheckDoppelGanger(ctx, req)
   417  	if err != nil {
   418  		return err
   419  	}
   420  	// If nothing is returned by the beacon node, we return an
   421  	// error as it is unsafe for us to proceed.
   422  	if resp == nil || resp.Responses == nil || len(resp.Responses) == 0 {
   423  		return errors.New("beacon node returned 0 responses for doppelganger check")
   424  	}
   425  	return buildDuplicateError(resp.Responses)
   426  }
   427  
   428  func buildDuplicateError(respones []*ethpb.DoppelGangerResponse_ValidatorResponse) error {
   429  	duplicates := make([][]byte, 0)
   430  	for _, valRes := range respones {
   431  		if valRes.DuplicateExists {
   432  			duplicates = append(duplicates, valRes.PublicKey)
   433  		}
   434  	}
   435  	if len(duplicates) == 0 {
   436  		return nil
   437  	}
   438  	return errors.Errorf("Duplicate instances exists in the network for validator keys: %#x", duplicates)
   439  }
   440  
   441  // Ensures that the latest attestion history is retrieved.
   442  func retrieveLatestRecord(recs []*kv.AttestationRecord) *kv.AttestationRecord {
   443  	if len(recs) == 0 {
   444  		return nil
   445  	}
   446  	lastSource := recs[len(recs)-1].Source
   447  	chosenRec := recs[len(recs)-1]
   448  	for i := len(recs) - 1; i >= 0; i-- {
   449  		// Exit if we are now on a different source
   450  		// as it is assumed that all source records are
   451  		// byte sorted.
   452  		if recs[i].Source != lastSource {
   453  			break
   454  		}
   455  		// If we have a smaller target, we do
   456  		// change our chosen record.
   457  		if chosenRec.Target < recs[i].Target {
   458  			chosenRec = recs[i]
   459  		}
   460  	}
   461  	return chosenRec
   462  }
   463  
   464  // UpdateDuties checks the slot number to determine if the validator's
   465  // list of upcoming assignments needs to be updated. For example, at the
   466  // beginning of a new epoch.
   467  func (v *validator) UpdateDuties(ctx context.Context, slot types.Slot) error {
   468  	if slot%params.BeaconConfig().SlotsPerEpoch != 0 && v.duties != nil {
   469  		// Do nothing if not epoch start AND assignments already exist.
   470  		return nil
   471  	}
   472  	// Set deadline to end of epoch.
   473  	ss, err := helpers.StartSlot(helpers.SlotToEpoch(slot) + 1)
   474  	if err != nil {
   475  		return err
   476  	}
   477  	ctx, cancel := context.WithDeadline(ctx, v.SlotDeadline(ss))
   478  	defer cancel()
   479  	ctx, span := trace.StartSpan(ctx, "validator.UpdateAssignments")
   480  	defer span.End()
   481  
   482  	validatingKeys, err := v.keyManager.FetchValidatingPublicKeys(ctx)
   483  	if err != nil {
   484  		return err
   485  	}
   486  
   487  	// Filter out the slashable public keys from the duties request.
   488  	filteredKeys := make([][48]byte, 0, len(validatingKeys))
   489  	v.slashableKeysLock.RLock()
   490  	for _, pubKey := range validatingKeys {
   491  		if ok := v.eipImportBlacklistedPublicKeys[pubKey]; !ok {
   492  			filteredKeys = append(filteredKeys, pubKey)
   493  		} else {
   494  			log.WithField(
   495  				"publicKey", fmt.Sprintf("%#x", bytesutil.Trunc(pubKey[:])),
   496  			).Warn("Not including slashable public key from slashing protection import " +
   497  				"in request to update validator duties")
   498  		}
   499  	}
   500  	v.slashableKeysLock.RUnlock()
   501  
   502  	req := &ethpb.DutiesRequest{
   503  		Epoch:      types.Epoch(slot / params.BeaconConfig().SlotsPerEpoch),
   504  		PublicKeys: bytesutil.FromBytes48Array(filteredKeys),
   505  	}
   506  
   507  	// If duties is nil it means we have had no prior duties and just started up.
   508  	resp, err := v.validatorClient.GetDuties(ctx, req)
   509  	if err != nil {
   510  		v.duties = nil // Clear assignments so we know to retry the request.
   511  		log.Error(err)
   512  		return err
   513  	}
   514  
   515  	v.duties = resp
   516  	v.logDuties(slot, v.duties.CurrentEpochDuties)
   517  
   518  	// Non-blocking call for beacon node to start subscriptions for aggregators.
   519  	go func() {
   520  		if err := v.subscribeToSubnets(context.Background(), resp); err != nil {
   521  			log.WithError(err).Error("Failed to subscribe to subnets")
   522  		}
   523  	}()
   524  
   525  	return nil
   526  }
   527  
   528  // subscribeToSubnets iterates through each validator duty, signs each slot, and asks beacon node
   529  // to eagerly subscribe to subnets so that the aggregator has attestations to aggregate.
   530  func (v *validator) subscribeToSubnets(ctx context.Context, res *ethpb.DutiesResponse) error {
   531  	subscribeSlots := make([]types.Slot, 0, len(res.CurrentEpochDuties)+len(res.NextEpochDuties))
   532  	subscribeCommitteeIndices := make([]types.CommitteeIndex, 0, len(res.CurrentEpochDuties)+len(res.NextEpochDuties))
   533  	subscribeIsAggregator := make([]bool, 0, len(res.CurrentEpochDuties)+len(res.NextEpochDuties))
   534  	alreadySubscribed := make(map[[64]byte]bool)
   535  
   536  	for _, duty := range res.CurrentEpochDuties {
   537  		pk := bytesutil.ToBytes48(duty.PublicKey)
   538  		if duty.Status == ethpb.ValidatorStatus_ACTIVE || duty.Status == ethpb.ValidatorStatus_EXITING {
   539  			attesterSlot := duty.AttesterSlot
   540  			committeeIndex := duty.CommitteeIndex
   541  
   542  			alreadySubscribedKey := validatorSubscribeKey(attesterSlot, committeeIndex)
   543  			if _, ok := alreadySubscribed[alreadySubscribedKey]; ok {
   544  				continue
   545  			}
   546  
   547  			aggregator, err := v.isAggregator(ctx, duty.Committee, attesterSlot, pk)
   548  			if err != nil {
   549  				return errors.Wrap(err, "could not check if a validator is an aggregator")
   550  			}
   551  			if aggregator {
   552  				alreadySubscribed[alreadySubscribedKey] = true
   553  			}
   554  
   555  			subscribeSlots = append(subscribeSlots, attesterSlot)
   556  			subscribeCommitteeIndices = append(subscribeCommitteeIndices, committeeIndex)
   557  			subscribeIsAggregator = append(subscribeIsAggregator, aggregator)
   558  		}
   559  	}
   560  
   561  	for _, duty := range res.NextEpochDuties {
   562  		if duty.Status == ethpb.ValidatorStatus_ACTIVE || duty.Status == ethpb.ValidatorStatus_EXITING {
   563  			attesterSlot := duty.AttesterSlot
   564  			committeeIndex := duty.CommitteeIndex
   565  
   566  			alreadySubscribedKey := validatorSubscribeKey(attesterSlot, committeeIndex)
   567  			if _, ok := alreadySubscribed[alreadySubscribedKey]; ok {
   568  				continue
   569  			}
   570  
   571  			aggregator, err := v.isAggregator(ctx, duty.Committee, attesterSlot, bytesutil.ToBytes48(duty.PublicKey))
   572  			if err != nil {
   573  				return errors.Wrap(err, "could not check if a validator is an aggregator")
   574  			}
   575  			if aggregator {
   576  				alreadySubscribed[alreadySubscribedKey] = true
   577  			}
   578  
   579  			subscribeSlots = append(subscribeSlots, attesterSlot)
   580  			subscribeCommitteeIndices = append(subscribeCommitteeIndices, committeeIndex)
   581  			subscribeIsAggregator = append(subscribeIsAggregator, aggregator)
   582  		}
   583  	}
   584  
   585  	_, err := v.validatorClient.SubscribeCommitteeSubnets(ctx, &ethpb.CommitteeSubnetsSubscribeRequest{
   586  		Slots:        subscribeSlots,
   587  		CommitteeIds: subscribeCommitteeIndices,
   588  		IsAggregator: subscribeIsAggregator,
   589  	})
   590  
   591  	return err
   592  }
   593  
   594  // RolesAt slot returns the validator roles at the given slot. Returns nil if the
   595  // validator is known to not have a roles at the slot. Returns UNKNOWN if the
   596  // validator assignments are unknown. Otherwise returns a valid ValidatorRole map.
   597  func (v *validator) RolesAt(ctx context.Context, slot types.Slot) (map[[48]byte][]iface.ValidatorRole, error) {
   598  	rolesAt := make(map[[48]byte][]iface.ValidatorRole)
   599  	for _, duty := range v.duties.Duties {
   600  		var roles []iface.ValidatorRole
   601  
   602  		if duty == nil {
   603  			continue
   604  		}
   605  		if len(duty.ProposerSlots) > 0 {
   606  			for _, proposerSlot := range duty.ProposerSlots {
   607  				if proposerSlot != 0 && proposerSlot == slot {
   608  					roles = append(roles, iface.RoleProposer)
   609  					break
   610  				}
   611  			}
   612  		}
   613  		if duty.AttesterSlot == slot {
   614  			roles = append(roles, iface.RoleAttester)
   615  
   616  			aggregator, err := v.isAggregator(ctx, duty.Committee, slot, bytesutil.ToBytes48(duty.PublicKey))
   617  			if err != nil {
   618  				return nil, errors.Wrap(err, "could not check if a validator is an aggregator")
   619  			}
   620  			if aggregator {
   621  				roles = append(roles, iface.RoleAggregator)
   622  			}
   623  
   624  		}
   625  		if len(roles) == 0 {
   626  			roles = append(roles, iface.RoleUnknown)
   627  		}
   628  
   629  		var pubKey [48]byte
   630  		copy(pubKey[:], duty.PublicKey)
   631  		rolesAt[pubKey] = roles
   632  	}
   633  	return rolesAt, nil
   634  }
   635  
   636  // GetKeymanager returns the underlying validator's keymanager.
   637  func (v *validator) GetKeymanager() keymanager.IKeymanager {
   638  	return v.keyManager
   639  }
   640  
   641  // isAggregator checks if a validator is an aggregator of a given slot and committee,
   642  // it uses a modulo calculated by validator count in committee and samples randomness around it.
   643  func (v *validator) isAggregator(ctx context.Context, committee []types.ValidatorIndex, slot types.Slot, pubKey [48]byte) (bool, error) {
   644  	modulo := uint64(1)
   645  	if len(committee)/int(params.BeaconConfig().TargetAggregatorsPerCommittee) > 1 {
   646  		modulo = uint64(len(committee)) / params.BeaconConfig().TargetAggregatorsPerCommittee
   647  	}
   648  
   649  	slotSig, err := v.signSlotWithSelectionProof(ctx, pubKey, slot)
   650  	if err != nil {
   651  		return false, err
   652  	}
   653  
   654  	b := hashutil.Hash(slotSig)
   655  
   656  	return binary.LittleEndian.Uint64(b[:8])%modulo == 0, nil
   657  }
   658  
   659  // UpdateDomainDataCaches by making calls for all of the possible domain data. These can change when
   660  // the fork version changes which can happen once per epoch. Although changing for the fork version
   661  // is very rare, a validator should check these data every epoch to be sure the validator is
   662  // participating on the correct fork version.
   663  func (v *validator) UpdateDomainDataCaches(ctx context.Context, slot types.Slot) {
   664  	for _, d := range [][]byte{
   665  		params.BeaconConfig().DomainRandao[:],
   666  		params.BeaconConfig().DomainBeaconAttester[:],
   667  		params.BeaconConfig().DomainBeaconProposer[:],
   668  		params.BeaconConfig().DomainSelectionProof[:],
   669  		params.BeaconConfig().DomainAggregateAndProof[:],
   670  	} {
   671  		_, err := v.domainData(ctx, helpers.SlotToEpoch(slot), d)
   672  		if err != nil {
   673  			log.WithError(err).Errorf("Failed to update domain data for domain %v", d)
   674  		}
   675  	}
   676  }
   677  
   678  // AllValidatorsAreExited informs whether all validators have already exited.
   679  func (v *validator) AllValidatorsAreExited(ctx context.Context) (bool, error) {
   680  	validatingKeys, err := v.keyManager.FetchValidatingPublicKeys(ctx)
   681  	if err != nil {
   682  		return false, errors.Wrap(err, "could not fetch validating keys")
   683  	}
   684  	if len(validatingKeys) == 0 {
   685  		return false, nil
   686  	}
   687  	var publicKeys [][]byte
   688  	for _, key := range validatingKeys {
   689  		copyKey := key
   690  		publicKeys = append(publicKeys, copyKey[:])
   691  	}
   692  	request := &ethpb.MultipleValidatorStatusRequest{
   693  		PublicKeys: publicKeys,
   694  	}
   695  	response, err := v.validatorClient.MultipleValidatorStatus(ctx, request)
   696  	if err != nil {
   697  		return false, err
   698  	}
   699  	if len(response.Statuses) != len(request.PublicKeys) {
   700  		return false, errors.New("number of status responses did not match number of requested keys")
   701  	}
   702  	for _, status := range response.Statuses {
   703  		if status.Status != ethpb.ValidatorStatus_EXITED {
   704  			return false, nil
   705  		}
   706  	}
   707  	return true, nil
   708  }
   709  
   710  func (v *validator) domainData(ctx context.Context, epoch types.Epoch, domain []byte) (*ethpb.DomainResponse, error) {
   711  	v.domainDataLock.Lock()
   712  	defer v.domainDataLock.Unlock()
   713  
   714  	req := &ethpb.DomainRequest{
   715  		Epoch:  epoch,
   716  		Domain: domain,
   717  	}
   718  
   719  	key := strings.Join([]string{strconv.FormatUint(uint64(req.Epoch), 10), hex.EncodeToString(req.Domain)}, ",")
   720  
   721  	if val, ok := v.domainDataCache.Get(key); ok {
   722  		return proto.Clone(val.(proto.Message)).(*ethpb.DomainResponse), nil
   723  	}
   724  
   725  	res, err := v.validatorClient.DomainData(ctx, req)
   726  	if err != nil {
   727  		return nil, err
   728  	}
   729  
   730  	v.domainDataCache.Set(key, proto.Clone(res), 1)
   731  
   732  	return res, nil
   733  }
   734  
   735  func (v *validator) logDuties(slot types.Slot, duties []*ethpb.DutiesResponse_Duty) {
   736  	attesterKeys := make([][]string, params.BeaconConfig().SlotsPerEpoch)
   737  	for i := range attesterKeys {
   738  		attesterKeys[i] = make([]string, 0)
   739  	}
   740  	proposerKeys := make([]string, params.BeaconConfig().SlotsPerEpoch)
   741  	slotOffset := slot - (slot % params.BeaconConfig().SlotsPerEpoch)
   742  	var totalAttestingKeys uint64
   743  	for _, duty := range duties {
   744  		validatorNotTruncatedKey := fmt.Sprintf("%#x", duty.PublicKey)
   745  		if v.emitAccountMetrics {
   746  			ValidatorStatusesGaugeVec.WithLabelValues(validatorNotTruncatedKey).Set(float64(duty.Status))
   747  		}
   748  
   749  		// Only interested in validators who are attesting/proposing.
   750  		// Note that SLASHING validators will have duties but their results are ignored by the network so we don't bother with them.
   751  		if duty.Status != ethpb.ValidatorStatus_ACTIVE && duty.Status != ethpb.ValidatorStatus_EXITING {
   752  			continue
   753  		}
   754  
   755  		validatorKey := fmt.Sprintf("%#x", bytesutil.Trunc(duty.PublicKey))
   756  		attesterIndex := duty.AttesterSlot - slotOffset
   757  		if attesterIndex >= params.BeaconConfig().SlotsPerEpoch {
   758  			log.WithField("duty", duty).Warn("Invalid attester slot")
   759  		} else {
   760  			attesterKeys[duty.AttesterSlot-slotOffset] = append(attesterKeys[duty.AttesterSlot-slotOffset], validatorKey)
   761  			totalAttestingKeys++
   762  			if v.emitAccountMetrics {
   763  				ValidatorNextAttestationSlotGaugeVec.WithLabelValues(validatorNotTruncatedKey).Set(float64(duty.AttesterSlot))
   764  			}
   765  		}
   766  
   767  		for _, proposerSlot := range duty.ProposerSlots {
   768  			proposerIndex := proposerSlot - slotOffset
   769  			if proposerIndex >= params.BeaconConfig().SlotsPerEpoch {
   770  				log.WithField("duty", duty).Warn("Invalid proposer slot")
   771  			} else {
   772  				proposerKeys[proposerIndex] = validatorKey
   773  			}
   774  			if v.emitAccountMetrics {
   775  				ValidatorNextProposalSlotGaugeVec.WithLabelValues(validatorNotTruncatedKey).Set(float64(proposerSlot))
   776  			}
   777  		}
   778  	}
   779  	for i := types.Slot(0); i < params.BeaconConfig().SlotsPerEpoch; i++ {
   780  		if len(attesterKeys[i]) > 0 {
   781  			log.WithFields(logrus.Fields{
   782  				"slot":                  slotOffset + i,
   783  				"slotInEpoch":           (slotOffset + i) % params.BeaconConfig().SlotsPerEpoch,
   784  				"attesterDutiesAtSlot":  len(attesterKeys[i]),
   785  				"totalAttestersInEpoch": totalAttestingKeys,
   786  				"pubKeys":               attesterKeys[i],
   787  			}).Info("Attestation schedule")
   788  		}
   789  		if proposerKeys[i] != "" {
   790  			log.WithField("slot", slotOffset+i).WithField("pubKey", proposerKeys[i]).Info("Proposal schedule")
   791  		}
   792  	}
   793  }
   794  
   795  // This constructs a validator subscribed key, it's used to track
   796  // which subnet has already been pending requested.
   797  func validatorSubscribeKey(slot types.Slot, committeeID types.CommitteeIndex) [64]byte {
   798  	return bytesutil.ToBytes64(append(bytesutil.Bytes32(uint64(slot)), bytesutil.Bytes32(uint64(committeeID))...))
   799  }
   800  
   801  // This tracks all validators' voting status.
   802  type voteStats struct {
   803  	startEpoch            types.Epoch
   804  	includedAttestedCount uint64
   805  	totalAttestedCount    uint64
   806  	totalDistance         types.Slot
   807  	correctSources        uint64
   808  	totalSources          uint64
   809  	correctTargets        uint64
   810  	totalTargets          uint64
   811  	correctHeads          uint64
   812  	totalHeads            uint64
   813  }