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

     1  package client
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"sync"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/golang/mock/gomock"
    13  	types "github.com/prysmaticlabs/eth2-types"
    14  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    15  	validatorpb "github.com/prysmaticlabs/prysm/proto/validator/accounts/v2"
    16  	"github.com/prysmaticlabs/prysm/shared/bls"
    17  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    18  	"github.com/prysmaticlabs/prysm/shared/event"
    19  	"github.com/prysmaticlabs/prysm/shared/featureconfig"
    20  	"github.com/prysmaticlabs/prysm/shared/mock"
    21  	"github.com/prysmaticlabs/prysm/shared/params"
    22  	"github.com/prysmaticlabs/prysm/shared/testutil"
    23  	"github.com/prysmaticlabs/prysm/shared/testutil/assert"
    24  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    25  	"github.com/prysmaticlabs/prysm/validator/client/iface"
    26  	dbTest "github.com/prysmaticlabs/prysm/validator/db/testing"
    27  	"github.com/sirupsen/logrus"
    28  	logTest "github.com/sirupsen/logrus/hooks/test"
    29  	grpc "google.golang.org/grpc"
    30  	"google.golang.org/protobuf/types/known/emptypb"
    31  )
    32  
    33  func init() {
    34  	logrus.SetLevel(logrus.DebugLevel)
    35  	logrus.SetOutput(ioutil.Discard)
    36  }
    37  
    38  var _ iface.Validator = (*validator)(nil)
    39  
    40  const cancelledCtx = "context has been canceled"
    41  
    42  func genMockKeymanager(numKeys int) *mockKeymanager {
    43  	km := make(map[[48]byte]bls.SecretKey, numKeys)
    44  	for i := 0; i < numKeys; i++ {
    45  		k, err := bls.RandKey()
    46  		if err != nil {
    47  			panic(err)
    48  		}
    49  		km[bytesutil.ToBytes48(k.PublicKey().Marshal())] = k
    50  	}
    51  
    52  	return &mockKeymanager{keysMap: km}
    53  }
    54  
    55  type mockKeymanager struct {
    56  	lock                sync.RWMutex
    57  	keysMap             map[[48]byte]bls.SecretKey
    58  	fetchNoKeys         bool
    59  	accountsChangedFeed *event.Feed
    60  }
    61  
    62  func (m *mockKeymanager) FetchValidatingPublicKeys(ctx context.Context) ([][48]byte, error) {
    63  	m.lock.RLock()
    64  	defer m.lock.RUnlock()
    65  	keys := make([][48]byte, 0)
    66  	if m.fetchNoKeys {
    67  		m.fetchNoKeys = false
    68  		return keys, nil
    69  	}
    70  	for pubKey := range m.keysMap {
    71  		keys = append(keys, pubKey)
    72  	}
    73  	return keys, nil
    74  }
    75  
    76  func (m *mockKeymanager) Sign(ctx context.Context, req *validatorpb.SignRequest) (bls.Signature, error) {
    77  	pubKey := [48]byte{}
    78  	copy(pubKey[:], req.PublicKey)
    79  	privKey, ok := m.keysMap[pubKey]
    80  	if !ok {
    81  		return nil, errors.New("not found")
    82  	}
    83  	sig := privKey.Sign(req.SigningRoot)
    84  	return sig, nil
    85  }
    86  
    87  func (m *mockKeymanager) SubscribeAccountChanges(pubKeysChan chan [][48]byte) event.Subscription {
    88  	if m.accountsChangedFeed == nil {
    89  		m.accountsChangedFeed = &event.Feed{}
    90  	}
    91  	return m.accountsChangedFeed.Subscribe(pubKeysChan)
    92  }
    93  
    94  func (m *mockKeymanager) SimulateAccountChanges(newKeys [][48]byte) {
    95  	m.accountsChangedFeed.Send(newKeys)
    96  }
    97  
    98  func generateMockStatusResponse(pubkeys [][]byte) *ethpb.ValidatorActivationResponse {
    99  	multipleStatus := make([]*ethpb.ValidatorActivationResponse_Status, len(pubkeys))
   100  	for i, key := range pubkeys {
   101  		multipleStatus[i] = &ethpb.ValidatorActivationResponse_Status{
   102  			PublicKey: key,
   103  			Status: &ethpb.ValidatorStatusResponse{
   104  				Status: ethpb.ValidatorStatus_UNKNOWN_STATUS,
   105  			},
   106  		}
   107  	}
   108  	return &ethpb.ValidatorActivationResponse{Statuses: multipleStatus}
   109  }
   110  
   111  func TestWaitForChainStart_SetsGenesisInfo(t *testing.T) {
   112  	ctrl := gomock.NewController(t)
   113  	defer ctrl.Finish()
   114  	client := mock.NewMockBeaconNodeValidatorClient(ctrl)
   115  
   116  	db := dbTest.SetupDB(t, [][48]byte{})
   117  	v := validator{
   118  		validatorClient: client,
   119  		db:              db,
   120  	}
   121  
   122  	// Make sure its clean at the start.
   123  	savedGenValRoot, err := db.GenesisValidatorsRoot(context.Background())
   124  	require.NoError(t, err)
   125  	assert.DeepEqual(t, []byte(nil), savedGenValRoot, "Unexpected saved genesis validator root")
   126  
   127  	genesis := uint64(time.Unix(1, 0).Unix())
   128  	genesisValidatorsRoot := bytesutil.ToBytes32([]byte("validators"))
   129  	clientStream := mock.NewMockBeaconNodeValidator_WaitForChainStartClient(ctrl)
   130  	client.EXPECT().WaitForChainStart(
   131  		gomock.Any(),
   132  		&emptypb.Empty{},
   133  	).Return(clientStream, nil)
   134  	clientStream.EXPECT().Recv().Return(
   135  		&ethpb.ChainStartResponse{
   136  			Started:               true,
   137  			GenesisTime:           genesis,
   138  			GenesisValidatorsRoot: genesisValidatorsRoot[:],
   139  		},
   140  		nil,
   141  	)
   142  	require.NoError(t, v.WaitForChainStart(context.Background()))
   143  	savedGenValRoot, err = db.GenesisValidatorsRoot(context.Background())
   144  	require.NoError(t, err)
   145  
   146  	assert.DeepEqual(t, genesisValidatorsRoot[:], savedGenValRoot, "Unexpected saved genesis validator root")
   147  	assert.Equal(t, genesis, v.genesisTime, "Unexpected chain start time")
   148  	assert.NotNil(t, v.ticker, "Expected ticker to be set, received nil")
   149  
   150  	// Make sure theres no errors running if its the same data.
   151  	client.EXPECT().WaitForChainStart(
   152  		gomock.Any(),
   153  		&emptypb.Empty{},
   154  	).Return(clientStream, nil)
   155  	clientStream.EXPECT().Recv().Return(
   156  		&ethpb.ChainStartResponse{
   157  			Started:               true,
   158  			GenesisTime:           genesis,
   159  			GenesisValidatorsRoot: genesisValidatorsRoot[:],
   160  		},
   161  		nil,
   162  	)
   163  	require.NoError(t, v.WaitForChainStart(context.Background()))
   164  }
   165  
   166  func TestWaitForChainStart_SetsGenesisInfo_IncorrectSecondTry(t *testing.T) {
   167  	ctrl := gomock.NewController(t)
   168  	defer ctrl.Finish()
   169  	client := mock.NewMockBeaconNodeValidatorClient(ctrl)
   170  
   171  	db := dbTest.SetupDB(t, [][48]byte{})
   172  	v := validator{
   173  		validatorClient: client,
   174  		db:              db,
   175  	}
   176  	genesis := uint64(time.Unix(1, 0).Unix())
   177  	genesisValidatorsRoot := bytesutil.ToBytes32([]byte("validators"))
   178  	clientStream := mock.NewMockBeaconNodeValidator_WaitForChainStartClient(ctrl)
   179  	client.EXPECT().WaitForChainStart(
   180  		gomock.Any(),
   181  		&emptypb.Empty{},
   182  	).Return(clientStream, nil)
   183  	clientStream.EXPECT().Recv().Return(
   184  		&ethpb.ChainStartResponse{
   185  			Started:               true,
   186  			GenesisTime:           genesis,
   187  			GenesisValidatorsRoot: genesisValidatorsRoot[:],
   188  		},
   189  		nil,
   190  	)
   191  	require.NoError(t, v.WaitForChainStart(context.Background()))
   192  	savedGenValRoot, err := db.GenesisValidatorsRoot(context.Background())
   193  	require.NoError(t, err)
   194  
   195  	assert.DeepEqual(t, genesisValidatorsRoot[:], savedGenValRoot, "Unexpected saved genesis validator root")
   196  	assert.Equal(t, genesis, v.genesisTime, "Unexpected chain start time")
   197  	assert.NotNil(t, v.ticker, "Expected ticker to be set, received nil")
   198  
   199  	genesisValidatorsRoot = bytesutil.ToBytes32([]byte("badvalidators"))
   200  
   201  	// Make sure theres no errors running if its the same data.
   202  	client.EXPECT().WaitForChainStart(
   203  		gomock.Any(),
   204  		&emptypb.Empty{},
   205  	).Return(clientStream, nil)
   206  	clientStream.EXPECT().Recv().Return(
   207  		&ethpb.ChainStartResponse{
   208  			Started:               true,
   209  			GenesisTime:           genesis,
   210  			GenesisValidatorsRoot: genesisValidatorsRoot[:],
   211  		},
   212  		nil,
   213  	)
   214  	err = v.WaitForChainStart(context.Background())
   215  	require.ErrorContains(t, "does not match root saved", err)
   216  }
   217  
   218  func TestWaitForChainStart_ContextCanceled(t *testing.T) {
   219  	ctrl := gomock.NewController(t)
   220  	defer ctrl.Finish()
   221  	client := mock.NewMockBeaconNodeValidatorClient(ctrl)
   222  
   223  	v := validator{
   224  		//keyManager:      testKeyManager,
   225  		validatorClient: client,
   226  	}
   227  	genesis := uint64(time.Unix(0, 0).Unix())
   228  	genesisValidatorsRoot := bytesutil.PadTo([]byte("validators"), 32)
   229  	clientStream := mock.NewMockBeaconNodeValidator_WaitForChainStartClient(ctrl)
   230  	client.EXPECT().WaitForChainStart(
   231  		gomock.Any(),
   232  		&emptypb.Empty{},
   233  	).Return(clientStream, nil)
   234  	clientStream.EXPECT().Recv().Return(
   235  		&ethpb.ChainStartResponse{
   236  			Started:               true,
   237  			GenesisTime:           genesis,
   238  			GenesisValidatorsRoot: genesisValidatorsRoot,
   239  		},
   240  		nil,
   241  	)
   242  	ctx, cancel := context.WithCancel(context.Background())
   243  	cancel()
   244  	assert.ErrorContains(t, cancelledCtx, v.WaitForChainStart(ctx))
   245  }
   246  
   247  func TestWaitForChainStart_StreamSetupFails(t *testing.T) {
   248  	ctrl := gomock.NewController(t)
   249  	defer ctrl.Finish()
   250  	client := mock.NewMockBeaconNodeValidatorClient(ctrl)
   251  
   252  	privKey, err := bls.RandKey()
   253  	require.NoError(t, err)
   254  	pubKey := [48]byte{}
   255  	copy(pubKey[:], privKey.PublicKey().Marshal())
   256  	km := &mockKeymanager{
   257  		keysMap: make(map[[48]byte]bls.SecretKey),
   258  	}
   259  	v := validator{
   260  		validatorClient: client,
   261  		keyManager:      km,
   262  	}
   263  	clientStream := mock.NewMockBeaconNodeValidator_WaitForChainStartClient(ctrl)
   264  	client.EXPECT().WaitForChainStart(
   265  		gomock.Any(),
   266  		&emptypb.Empty{},
   267  	).Return(clientStream, errors.New("failed stream"))
   268  	err = v.WaitForChainStart(context.Background())
   269  	want := "could not setup beacon chain ChainStart streaming client"
   270  	assert.ErrorContains(t, want, err)
   271  }
   272  
   273  func TestWaitForChainStart_ReceiveErrorFromStream(t *testing.T) {
   274  	ctrl := gomock.NewController(t)
   275  	defer ctrl.Finish()
   276  	client := mock.NewMockBeaconNodeValidatorClient(ctrl)
   277  
   278  	v := validator{
   279  		validatorClient: client,
   280  	}
   281  	clientStream := mock.NewMockBeaconNodeValidator_WaitForChainStartClient(ctrl)
   282  	client.EXPECT().WaitForChainStart(
   283  		gomock.Any(),
   284  		&emptypb.Empty{},
   285  	).Return(clientStream, nil)
   286  	clientStream.EXPECT().Recv().Return(
   287  		nil,
   288  		errors.New("fails"),
   289  	)
   290  	err := v.WaitForChainStart(context.Background())
   291  	want := "could not receive ChainStart from stream"
   292  	assert.ErrorContains(t, want, err)
   293  }
   294  
   295  func TestCanonicalHeadSlot_FailedRPC(t *testing.T) {
   296  	ctrl := gomock.NewController(t)
   297  	defer ctrl.Finish()
   298  	client := mock.NewMockBeaconChainClient(ctrl)
   299  	v := validator{
   300  		beaconClient: client,
   301  		genesisTime:  1,
   302  	}
   303  	client.EXPECT().GetChainHead(
   304  		gomock.Any(),
   305  		gomock.Any(),
   306  	).Return(nil, errors.New("failed"))
   307  	_, err := v.CanonicalHeadSlot(context.Background())
   308  	assert.ErrorContains(t, "failed", err)
   309  }
   310  
   311  func TestCanonicalHeadSlot_OK(t *testing.T) {
   312  	ctrl := gomock.NewController(t)
   313  	defer ctrl.Finish()
   314  	client := mock.NewMockBeaconChainClient(ctrl)
   315  	v := validator{
   316  		beaconClient: client,
   317  	}
   318  	client.EXPECT().GetChainHead(
   319  		gomock.Any(),
   320  		gomock.Any(),
   321  	).Return(&ethpb.ChainHead{HeadSlot: 0}, nil)
   322  	headSlot, err := v.CanonicalHeadSlot(context.Background())
   323  	require.NoError(t, err)
   324  	assert.Equal(t, types.Slot(0), headSlot, "Mismatch slots")
   325  }
   326  
   327  func TestWaitMultipleActivation_LogsActivationEpochOK(t *testing.T) {
   328  	hook := logTest.NewGlobal()
   329  	ctrl := gomock.NewController(t)
   330  	defer ctrl.Finish()
   331  	client := mock.NewMockBeaconNodeValidatorClient(ctrl)
   332  	privKey, err := bls.RandKey()
   333  	require.NoError(t, err)
   334  	pubKey := [48]byte{}
   335  	copy(pubKey[:], privKey.PublicKey().Marshal())
   336  	km := &mockKeymanager{
   337  		keysMap: map[[48]byte]bls.SecretKey{
   338  			pubKey: privKey,
   339  		},
   340  	}
   341  	v := validator{
   342  		validatorClient: client,
   343  		keyManager:      km,
   344  		genesisTime:     1,
   345  	}
   346  
   347  	resp := generateMockStatusResponse([][]byte{pubKey[:]})
   348  	resp.Statuses[0].Status.Status = ethpb.ValidatorStatus_ACTIVE
   349  	clientStream := mock.NewMockBeaconNodeValidator_WaitForActivationClient(ctrl)
   350  	client.EXPECT().WaitForActivation(
   351  		gomock.Any(),
   352  		&ethpb.ValidatorActivationRequest{
   353  			PublicKeys: [][]byte{pubKey[:]},
   354  		},
   355  	).Return(clientStream, nil)
   356  	clientStream.EXPECT().Recv().Return(
   357  		resp,
   358  		nil,
   359  	)
   360  	require.NoError(t, v.WaitForActivation(context.Background(), nil), "Could not wait for activation")
   361  	require.LogsContain(t, hook, "Validator activated")
   362  }
   363  
   364  func TestWaitActivation_NotAllValidatorsActivatedOK(t *testing.T) {
   365  	ctrl := gomock.NewController(t)
   366  	defer ctrl.Finish()
   367  	client := mock.NewMockBeaconNodeValidatorClient(ctrl)
   368  
   369  	privKey, err := bls.RandKey()
   370  	require.NoError(t, err)
   371  	pubKey := [48]byte{}
   372  	copy(pubKey[:], privKey.PublicKey().Marshal())
   373  	km := &mockKeymanager{
   374  		keysMap: map[[48]byte]bls.SecretKey{
   375  			pubKey: privKey,
   376  		},
   377  	}
   378  	v := validator{
   379  		validatorClient: client,
   380  		keyManager:      km,
   381  		genesisTime:     1,
   382  	}
   383  	resp := generateMockStatusResponse([][]byte{pubKey[:]})
   384  	resp.Statuses[0].Status.Status = ethpb.ValidatorStatus_ACTIVE
   385  	clientStream := mock.NewMockBeaconNodeValidator_WaitForActivationClient(ctrl)
   386  	client.EXPECT().WaitForActivation(
   387  		gomock.Any(),
   388  		gomock.Any(),
   389  	).Return(clientStream, nil)
   390  	clientStream.EXPECT().Recv().Return(
   391  		&ethpb.ValidatorActivationResponse{},
   392  		nil,
   393  	)
   394  	clientStream.EXPECT().Recv().Return(
   395  		resp,
   396  		nil,
   397  	)
   398  	assert.NoError(t, v.WaitForActivation(context.Background(), nil), "Could not wait for activation")
   399  }
   400  
   401  func TestWaitSync_ContextCanceled(t *testing.T) {
   402  	ctrl := gomock.NewController(t)
   403  	defer ctrl.Finish()
   404  	n := mock.NewMockNodeClient(ctrl)
   405  
   406  	v := validator{
   407  		node: n,
   408  	}
   409  
   410  	ctx, cancel := context.WithCancel(context.Background())
   411  	cancel()
   412  
   413  	n.EXPECT().GetSyncStatus(
   414  		gomock.Any(),
   415  		gomock.Any(),
   416  	).Return(&ethpb.SyncStatus{Syncing: true}, nil)
   417  
   418  	assert.ErrorContains(t, cancelledCtx, v.WaitForSync(ctx))
   419  }
   420  
   421  func TestWaitSync_NotSyncing(t *testing.T) {
   422  	ctrl := gomock.NewController(t)
   423  	defer ctrl.Finish()
   424  	n := mock.NewMockNodeClient(ctrl)
   425  
   426  	v := validator{
   427  		node: n,
   428  	}
   429  
   430  	n.EXPECT().GetSyncStatus(
   431  		gomock.Any(),
   432  		gomock.Any(),
   433  	).Return(&ethpb.SyncStatus{Syncing: false}, nil)
   434  
   435  	require.NoError(t, v.WaitForSync(context.Background()))
   436  }
   437  
   438  func TestWaitSync_Syncing(t *testing.T) {
   439  	ctrl := gomock.NewController(t)
   440  	defer ctrl.Finish()
   441  	n := mock.NewMockNodeClient(ctrl)
   442  
   443  	v := validator{
   444  		node: n,
   445  	}
   446  
   447  	n.EXPECT().GetSyncStatus(
   448  		gomock.Any(),
   449  		gomock.Any(),
   450  	).Return(&ethpb.SyncStatus{Syncing: true}, nil)
   451  
   452  	n.EXPECT().GetSyncStatus(
   453  		gomock.Any(),
   454  		gomock.Any(),
   455  	).Return(&ethpb.SyncStatus{Syncing: false}, nil)
   456  
   457  	require.NoError(t, v.WaitForSync(context.Background()))
   458  }
   459  
   460  func TestUpdateDuties_DoesNothingWhenNotEpochStart_AlreadyExistingAssignments(t *testing.T) {
   461  	ctrl := gomock.NewController(t)
   462  	defer ctrl.Finish()
   463  	client := mock.NewMockBeaconNodeValidatorClient(ctrl)
   464  
   465  	slot := types.Slot(1)
   466  	v := validator{
   467  		validatorClient: client,
   468  		duties: &ethpb.DutiesResponse{
   469  			Duties: []*ethpb.DutiesResponse_Duty{
   470  				{
   471  					Committee:      []types.ValidatorIndex{},
   472  					AttesterSlot:   10,
   473  					CommitteeIndex: 20,
   474  				},
   475  			},
   476  		},
   477  	}
   478  	client.EXPECT().GetDuties(
   479  		gomock.Any(),
   480  		gomock.Any(),
   481  	).Times(0)
   482  
   483  	assert.NoError(t, v.UpdateDuties(context.Background(), slot), "Could not update assignments")
   484  }
   485  
   486  func TestUpdateDuties_ReturnsError(t *testing.T) {
   487  	ctrl := gomock.NewController(t)
   488  	defer ctrl.Finish()
   489  	client := mock.NewMockBeaconNodeValidatorClient(ctrl)
   490  
   491  	privKey, err := bls.RandKey()
   492  	require.NoError(t, err)
   493  	pubKey := [48]byte{}
   494  	copy(pubKey[:], privKey.PublicKey().Marshal())
   495  	km := &mockKeymanager{
   496  		keysMap: map[[48]byte]bls.SecretKey{
   497  			pubKey: privKey,
   498  		},
   499  	}
   500  	v := validator{
   501  		validatorClient: client,
   502  		keyManager:      km,
   503  		duties: &ethpb.DutiesResponse{
   504  			Duties: []*ethpb.DutiesResponse_Duty{
   505  				{
   506  					CommitteeIndex: 1,
   507  				},
   508  			},
   509  		},
   510  	}
   511  
   512  	expected := errors.New("bad")
   513  
   514  	client.EXPECT().GetDuties(
   515  		gomock.Any(),
   516  		gomock.Any(),
   517  	).Return(nil, expected)
   518  
   519  	assert.ErrorContains(t, expected.Error(), v.UpdateDuties(context.Background(), params.BeaconConfig().SlotsPerEpoch))
   520  	assert.Equal(t, (*ethpb.DutiesResponse)(nil), v.duties, "Assignments should have been cleared on failure")
   521  }
   522  
   523  func TestUpdateDuties_OK(t *testing.T) {
   524  	ctrl := gomock.NewController(t)
   525  	defer ctrl.Finish()
   526  	client := mock.NewMockBeaconNodeValidatorClient(ctrl)
   527  
   528  	slot := params.BeaconConfig().SlotsPerEpoch
   529  	privKey, err := bls.RandKey()
   530  	require.NoError(t, err)
   531  	pubKey := [48]byte{}
   532  	copy(pubKey[:], privKey.PublicKey().Marshal())
   533  	km := &mockKeymanager{
   534  		keysMap: map[[48]byte]bls.SecretKey{
   535  			pubKey: privKey,
   536  		},
   537  	}
   538  	resp := &ethpb.DutiesResponse{
   539  		Duties: []*ethpb.DutiesResponse_Duty{
   540  			{
   541  				AttesterSlot:   params.BeaconConfig().SlotsPerEpoch,
   542  				ValidatorIndex: 200,
   543  				CommitteeIndex: 100,
   544  				Committee:      []types.ValidatorIndex{0, 1, 2, 3},
   545  				PublicKey:      []byte("testPubKey_1"),
   546  				ProposerSlots:  []types.Slot{params.BeaconConfig().SlotsPerEpoch + 1},
   547  			},
   548  		},
   549  	}
   550  	v := validator{
   551  		keyManager:      km,
   552  		validatorClient: client,
   553  	}
   554  	client.EXPECT().GetDuties(
   555  		gomock.Any(),
   556  		gomock.Any(),
   557  	).Return(resp, nil)
   558  
   559  	var wg sync.WaitGroup
   560  	wg.Add(1)
   561  
   562  	client.EXPECT().SubscribeCommitteeSubnets(
   563  		gomock.Any(),
   564  		gomock.Any(),
   565  	).DoAndReturn(func(_ context.Context, _ *ethpb.CommitteeSubnetsSubscribeRequest, arg2 ...grpc.CallOption) (*emptypb.Empty, error) {
   566  		wg.Done()
   567  		return nil, nil
   568  	})
   569  
   570  	require.NoError(t, v.UpdateDuties(context.Background(), slot), "Could not update assignments")
   571  
   572  	testutil.WaitTimeout(&wg, 3*time.Second)
   573  
   574  	assert.Equal(t, params.BeaconConfig().SlotsPerEpoch+1, v.duties.Duties[0].ProposerSlots[0], "Unexpected validator assignments")
   575  	assert.Equal(t, params.BeaconConfig().SlotsPerEpoch, v.duties.Duties[0].AttesterSlot, "Unexpected validator assignments")
   576  	assert.Equal(t, resp.Duties[0].CommitteeIndex, v.duties.Duties[0].CommitteeIndex, "Unexpected validator assignments")
   577  	assert.Equal(t, resp.Duties[0].ValidatorIndex, v.duties.Duties[0].ValidatorIndex, "Unexpected validator assignments")
   578  }
   579  
   580  func TestUpdateDuties_OK_FilterBlacklistedPublicKeys(t *testing.T) {
   581  	hook := logTest.NewGlobal()
   582  	ctrl := gomock.NewController(t)
   583  	defer ctrl.Finish()
   584  	client := mock.NewMockBeaconNodeValidatorClient(ctrl)
   585  	slot := params.BeaconConfig().SlotsPerEpoch
   586  
   587  	numValidators := 10
   588  	keysMap := make(map[[48]byte]bls.SecretKey)
   589  	blacklistedPublicKeys := make(map[[48]byte]bool)
   590  	for i := 0; i < numValidators; i++ {
   591  		priv, err := bls.RandKey()
   592  		require.NoError(t, err)
   593  		pubKey := [48]byte{}
   594  		copy(pubKey[:], priv.PublicKey().Marshal())
   595  		keysMap[pubKey] = priv
   596  		blacklistedPublicKeys[pubKey] = true
   597  	}
   598  
   599  	km := &mockKeymanager{
   600  		keysMap: keysMap,
   601  	}
   602  	resp := &ethpb.DutiesResponse{
   603  		Duties: []*ethpb.DutiesResponse_Duty{},
   604  	}
   605  	v := validator{
   606  		keyManager:                     km,
   607  		validatorClient:                client,
   608  		eipImportBlacklistedPublicKeys: blacklistedPublicKeys,
   609  	}
   610  	client.EXPECT().GetDuties(
   611  		gomock.Any(),
   612  		gomock.Any(),
   613  	).Return(resp, nil)
   614  
   615  	var wg sync.WaitGroup
   616  	wg.Add(1)
   617  	client.EXPECT().SubscribeCommitteeSubnets(
   618  		gomock.Any(),
   619  		gomock.Any(),
   620  	).DoAndReturn(func(_ context.Context, _ *ethpb.CommitteeSubnetsSubscribeRequest, arg2 ...grpc.CallOption) (*emptypb.Empty, error) {
   621  		wg.Done()
   622  		return nil, nil
   623  	})
   624  
   625  	require.NoError(t, v.UpdateDuties(context.Background(), slot), "Could not update assignments")
   626  
   627  	testutil.WaitTimeout(&wg, 3*time.Second)
   628  
   629  	for range blacklistedPublicKeys {
   630  		assert.LogsContain(t, hook, "Not including slashable public key")
   631  	}
   632  }
   633  
   634  func TestRolesAt_OK(t *testing.T) {
   635  	v, m, validatorKey, finish := setup(t)
   636  	defer finish()
   637  
   638  	v.duties = &ethpb.DutiesResponse{
   639  		Duties: []*ethpb.DutiesResponse_Duty{
   640  			{
   641  				CommitteeIndex: 1,
   642  				AttesterSlot:   1,
   643  				PublicKey:      validatorKey.PublicKey().Marshal(),
   644  			},
   645  		},
   646  	}
   647  
   648  	m.validatorClient.EXPECT().DomainData(
   649  		gomock.Any(), // ctx
   650  		gomock.Any(), // epoch
   651  	).Return(&ethpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
   652  
   653  	roleMap, err := v.RolesAt(context.Background(), 1)
   654  	require.NoError(t, err)
   655  
   656  	assert.Equal(t, iface.RoleAttester, roleMap[bytesutil.ToBytes48(validatorKey.PublicKey().Marshal())][0])
   657  }
   658  
   659  func TestRolesAt_DoesNotAssignProposer_Slot0(t *testing.T) {
   660  	v, m, validatorKey, finish := setup(t)
   661  	defer finish()
   662  
   663  	v.duties = &ethpb.DutiesResponse{
   664  		Duties: []*ethpb.DutiesResponse_Duty{
   665  			{
   666  				CommitteeIndex: 1,
   667  				AttesterSlot:   0,
   668  				ProposerSlots:  []types.Slot{0},
   669  				PublicKey:      validatorKey.PublicKey().Marshal(),
   670  			},
   671  		},
   672  	}
   673  
   674  	m.validatorClient.EXPECT().DomainData(
   675  		gomock.Any(), // ctx
   676  		gomock.Any(), // epoch
   677  	).Return(&ethpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
   678  
   679  	roleMap, err := v.RolesAt(context.Background(), 0)
   680  	require.NoError(t, err)
   681  
   682  	assert.Equal(t, iface.RoleAttester, roleMap[bytesutil.ToBytes48(validatorKey.PublicKey().Marshal())][0])
   683  }
   684  
   685  func TestCheckAndLogValidatorStatus_OK(t *testing.T) {
   686  	nonexistentIndex := types.ValidatorIndex(^uint64(0))
   687  	type statusTest struct {
   688  		name   string
   689  		status *validatorStatus
   690  		log    string
   691  		active bool
   692  	}
   693  	pubKeys := [][]byte{bytesutil.Uint64ToBytesLittleEndian(0)}
   694  	tests := []statusTest{
   695  		{
   696  			name: "UNKNOWN_STATUS, no deposit found yet",
   697  			status: &validatorStatus{
   698  				publicKey: pubKeys[0],
   699  				index:     nonexistentIndex,
   700  				status: &ethpb.ValidatorStatusResponse{
   701  					Status: ethpb.ValidatorStatus_UNKNOWN_STATUS,
   702  				},
   703  			},
   704  			log:    "Waiting for deposit to be observed by beacon node",
   705  			active: false,
   706  		},
   707  		{
   708  			name: "DEPOSITED into state",
   709  			status: &validatorStatus{
   710  				publicKey: pubKeys[0],
   711  				index:     30,
   712  				status: &ethpb.ValidatorStatusResponse{
   713  					Status:                    ethpb.ValidatorStatus_DEPOSITED,
   714  					PositionInActivationQueue: 30,
   715  				},
   716  			},
   717  			log:    "Deposit processed, entering activation queue after finalization\" index=30 positionInActivationQueue=30",
   718  			active: false,
   719  		},
   720  		{
   721  			name: "PENDING",
   722  			status: &validatorStatus{
   723  				publicKey: pubKeys[0],
   724  				index:     50,
   725  				status: &ethpb.ValidatorStatusResponse{
   726  					Status:                    ethpb.ValidatorStatus_PENDING,
   727  					ActivationEpoch:           params.BeaconConfig().FarFutureEpoch,
   728  					PositionInActivationQueue: 6,
   729  				},
   730  			},
   731  			log:    "Waiting to be assigned activation epoch\" index=50 positionInActivationQueue=6",
   732  			active: false,
   733  		},
   734  		{
   735  			name: "PENDING",
   736  			status: &validatorStatus{
   737  				publicKey: pubKeys[0],
   738  				index:     89,
   739  				status: &ethpb.ValidatorStatusResponse{
   740  					Status:                    ethpb.ValidatorStatus_PENDING,
   741  					ActivationEpoch:           60,
   742  					PositionInActivationQueue: 5,
   743  				},
   744  			},
   745  			log:    "Waiting for activation\" activationEpoch=60 index=89",
   746  			active: false,
   747  		},
   748  		{
   749  			name: "ACTIVE",
   750  			status: &validatorStatus{
   751  				publicKey: pubKeys[0],
   752  				index:     89,
   753  				status: &ethpb.ValidatorStatusResponse{
   754  					Status: ethpb.ValidatorStatus_ACTIVE,
   755  				},
   756  			},
   757  			active: true,
   758  		},
   759  		{
   760  			name: "EXITING",
   761  			status: &validatorStatus{
   762  				publicKey: pubKeys[0],
   763  				index:     89,
   764  				status: &ethpb.ValidatorStatusResponse{
   765  					Status: ethpb.ValidatorStatus_EXITING,
   766  				},
   767  			},
   768  			active: true,
   769  		},
   770  		{
   771  			name: "EXITED",
   772  			status: &validatorStatus{
   773  				publicKey: pubKeys[0],
   774  				status: &ethpb.ValidatorStatusResponse{
   775  					Status: ethpb.ValidatorStatus_EXITED,
   776  				},
   777  			},
   778  			log:    "Validator exited",
   779  			active: false,
   780  		},
   781  	}
   782  	for _, test := range tests {
   783  		t.Run(test.name, func(t *testing.T) {
   784  			hook := logTest.NewGlobal()
   785  			ctrl := gomock.NewController(t)
   786  			defer ctrl.Finish()
   787  			client := mock.NewMockBeaconNodeValidatorClient(ctrl)
   788  			v := validator{
   789  				validatorClient: client,
   790  				duties: &ethpb.DutiesResponse{
   791  					Duties: []*ethpb.DutiesResponse_Duty{
   792  						{
   793  							CommitteeIndex: 1,
   794  						},
   795  					},
   796  				},
   797  			}
   798  
   799  			active := v.checkAndLogValidatorStatus([]*validatorStatus{test.status})
   800  			require.Equal(t, test.active, active)
   801  			if test.log != "" {
   802  				require.LogsContain(t, hook, test.log)
   803  			}
   804  		})
   805  	}
   806  }
   807  
   808  func TestAllValidatorsAreExited_AllExited(t *testing.T) {
   809  	ctrl := gomock.NewController(t)
   810  	defer ctrl.Finish()
   811  	client := mock.NewMockBeaconNodeValidatorClient(ctrl)
   812  
   813  	statuses := []*ethpb.ValidatorStatusResponse{
   814  		{Status: ethpb.ValidatorStatus_EXITED},
   815  		{Status: ethpb.ValidatorStatus_EXITED},
   816  	}
   817  
   818  	client.EXPECT().MultipleValidatorStatus(
   819  		gomock.Any(), // ctx
   820  		gomock.Any(), // request
   821  	).Return(&ethpb.MultipleValidatorStatusResponse{Statuses: statuses}, nil /*err*/)
   822  
   823  	v := validator{keyManager: genMockKeymanager(2), validatorClient: client}
   824  	exited, err := v.AllValidatorsAreExited(context.Background())
   825  	require.NoError(t, err)
   826  	assert.Equal(t, true, exited)
   827  }
   828  
   829  func TestAllValidatorsAreExited_NotAllExited(t *testing.T) {
   830  	ctrl := gomock.NewController(t)
   831  	defer ctrl.Finish()
   832  	client := mock.NewMockBeaconNodeValidatorClient(ctrl)
   833  
   834  	statuses := []*ethpb.ValidatorStatusResponse{
   835  		{Status: ethpb.ValidatorStatus_ACTIVE},
   836  		{Status: ethpb.ValidatorStatus_EXITED},
   837  	}
   838  
   839  	client.EXPECT().MultipleValidatorStatus(
   840  		gomock.Any(), // ctx
   841  		gomock.Any(), // request
   842  	).Return(&ethpb.MultipleValidatorStatusResponse{Statuses: statuses}, nil /*err*/)
   843  
   844  	v := validator{keyManager: genMockKeymanager(2), validatorClient: client}
   845  	exited, err := v.AllValidatorsAreExited(context.Background())
   846  	require.NoError(t, err)
   847  	assert.Equal(t, false, exited)
   848  }
   849  
   850  func TestAllValidatorsAreExited_PartialResult(t *testing.T) {
   851  	ctrl := gomock.NewController(t)
   852  	defer ctrl.Finish()
   853  	client := mock.NewMockBeaconNodeValidatorClient(ctrl)
   854  
   855  	statuses := []*ethpb.ValidatorStatusResponse{
   856  		{Status: ethpb.ValidatorStatus_EXITED},
   857  	}
   858  
   859  	client.EXPECT().MultipleValidatorStatus(
   860  		gomock.Any(), // ctx
   861  		gomock.Any(), // request
   862  	).Return(&ethpb.MultipleValidatorStatusResponse{Statuses: statuses}, nil /*err*/)
   863  
   864  	v := validator{keyManager: genMockKeymanager(2), validatorClient: client}
   865  	exited, err := v.AllValidatorsAreExited(context.Background())
   866  	require.ErrorContains(t, "number of status responses did not match number of requested keys", err)
   867  	assert.Equal(t, false, exited)
   868  }
   869  
   870  func TestAllValidatorsAreExited_NoKeys(t *testing.T) {
   871  	ctrl := gomock.NewController(t)
   872  	defer ctrl.Finish()
   873  	client := mock.NewMockBeaconNodeValidatorClient(ctrl)
   874  	v := validator{keyManager: genMockKeymanager(0), validatorClient: client}
   875  	exited, err := v.AllValidatorsAreExited(context.Background())
   876  	require.NoError(t, err)
   877  	assert.Equal(t, false, exited)
   878  }
   879  
   880  // TestAllValidatorsAreExited_CorrectRequest is a regression test that checks if the request contains the correct keys
   881  func TestAllValidatorsAreExited_CorrectRequest(t *testing.T) {
   882  	ctrl := gomock.NewController(t)
   883  	defer ctrl.Finish()
   884  	client := mock.NewMockBeaconNodeValidatorClient(ctrl)
   885  
   886  	// Create two different public keys
   887  	pubKey0 := [48]byte{1, 2, 3, 4}
   888  	pubKey1 := [48]byte{6, 7, 8, 9}
   889  	// This is the request expected from AllValidatorsAreExited()
   890  	request := &ethpb.MultipleValidatorStatusRequest{
   891  		PublicKeys: [][]byte{
   892  			pubKey0[:],
   893  			pubKey1[:],
   894  		},
   895  	}
   896  	statuses := []*ethpb.ValidatorStatusResponse{
   897  		{Status: ethpb.ValidatorStatus_ACTIVE},
   898  		{Status: ethpb.ValidatorStatus_EXITED},
   899  	}
   900  
   901  	client.EXPECT().MultipleValidatorStatus(
   902  		gomock.Any(), // ctx
   903  		request,      // request
   904  	).Return(&ethpb.MultipleValidatorStatusResponse{Statuses: statuses}, nil /*err*/)
   905  
   906  	keysMap := make(map[[48]byte]bls.SecretKey)
   907  	// secretKey below is just filler and is used multiple times
   908  	secretKeyBytes := [32]byte{1}
   909  	secretKey, err := bls.SecretKeyFromBytes(secretKeyBytes[:])
   910  	require.NoError(t, err)
   911  	keysMap[pubKey0] = secretKey
   912  	keysMap[pubKey1] = secretKey
   913  
   914  	// If AllValidatorsAreExited does not create the expected request, this test will fail
   915  	v := validator{keyManager: &mockKeymanager{keysMap: keysMap}, validatorClient: client}
   916  	exited, err := v.AllValidatorsAreExited(context.Background())
   917  	require.NoError(t, err)
   918  	assert.Equal(t, false, exited)
   919  }
   920  
   921  func TestService_ReceiveBlocks_NilBlock(t *testing.T) {
   922  	ctrl := gomock.NewController(t)
   923  	defer ctrl.Finish()
   924  	client := mock.NewMockBeaconChainClient(ctrl)
   925  
   926  	v := validator{
   927  		beaconClient: client,
   928  		blockFeed:    new(event.Feed),
   929  	}
   930  	stream := mock.NewMockBeaconChain_StreamBlocksClient(ctrl)
   931  	ctx, cancel := context.WithCancel(context.Background())
   932  	client.EXPECT().StreamBlocks(
   933  		gomock.Any(),
   934  		&ethpb.StreamBlocksRequest{VerifiedOnly: true},
   935  	).Return(stream, nil)
   936  	stream.EXPECT().Context().Return(ctx).AnyTimes()
   937  	stream.EXPECT().Recv().Return(
   938  		&ethpb.SignedBeaconBlock{},
   939  		nil,
   940  	).Do(func() {
   941  		cancel()
   942  	})
   943  	connectionErrorChannel := make(chan error)
   944  	v.ReceiveBlocks(ctx, connectionErrorChannel)
   945  	require.Equal(t, types.Slot(0), v.highestValidSlot)
   946  }
   947  
   948  func TestService_ReceiveBlocks_SetHighest(t *testing.T) {
   949  	ctrl := gomock.NewController(t)
   950  	defer ctrl.Finish()
   951  	client := mock.NewMockBeaconChainClient(ctrl)
   952  
   953  	v := validator{
   954  		beaconClient: client,
   955  		blockFeed:    new(event.Feed),
   956  	}
   957  	stream := mock.NewMockBeaconChain_StreamBlocksClient(ctrl)
   958  	ctx, cancel := context.WithCancel(context.Background())
   959  	client.EXPECT().StreamBlocks(
   960  		gomock.Any(),
   961  		&ethpb.StreamBlocksRequest{VerifiedOnly: true},
   962  	).Return(stream, nil)
   963  	stream.EXPECT().Context().Return(ctx).AnyTimes()
   964  	slot := types.Slot(100)
   965  	stream.EXPECT().Recv().Return(
   966  		&ethpb.SignedBeaconBlock{Block: &ethpb.BeaconBlock{Slot: slot}},
   967  		nil,
   968  	).Do(func() {
   969  		cancel()
   970  	})
   971  	connectionErrorChannel := make(chan error)
   972  	v.ReceiveBlocks(ctx, connectionErrorChannel)
   973  	require.Equal(t, slot, v.highestValidSlot)
   974  }
   975  
   976  type doppelGangerRequestMatcher struct {
   977  	req *ethpb.DoppelGangerRequest
   978  }
   979  
   980  var _ gomock.Matcher = (*doppelGangerRequestMatcher)(nil)
   981  
   982  func (m *doppelGangerRequestMatcher) Matches(x interface{}) bool {
   983  	r, ok := x.(*ethpb.DoppelGangerRequest)
   984  	if !ok {
   985  		panic("Invalid match type")
   986  	}
   987  	return gomock.InAnyOrder(m.req.ValidatorRequests).Matches(r.ValidatorRequests)
   988  }
   989  
   990  func (m *doppelGangerRequestMatcher) String() string {
   991  	return fmt.Sprintf("%#v", m.req.ValidatorRequests)
   992  }
   993  
   994  func TestValidator_CheckDoppelGanger(t *testing.T) {
   995  	ctrl := gomock.NewController(t)
   996  	defer ctrl.Finish()
   997  	flgs := featureconfig.Get()
   998  	flgs.EnableDoppelGanger = true
   999  	reset := featureconfig.InitWithReset(flgs)
  1000  	defer reset()
  1001  	tests := []struct {
  1002  		name            string
  1003  		validatorSetter func(t *testing.T) *validator
  1004  		err             string
  1005  	}{
  1006  		{
  1007  			name: "no doppelganger",
  1008  			validatorSetter: func(t *testing.T) *validator {
  1009  				client := mock.NewMockBeaconNodeValidatorClient(ctrl)
  1010  				km := genMockKeymanager(10)
  1011  				keys, err := km.FetchValidatingPublicKeys(context.Background())
  1012  				assert.NoError(t, err)
  1013  				db := dbTest.SetupDB(t, keys)
  1014  				req := &ethpb.DoppelGangerRequest{ValidatorRequests: []*ethpb.DoppelGangerRequest_ValidatorRequest{}}
  1015  				resp := &ethpb.DoppelGangerRequest{ValidatorRequests: []*ethpb.DoppelGangerRequest_ValidatorRequest{}}
  1016  				for _, k := range keys {
  1017  					pkey := k
  1018  					att := createAttestation(10, 12)
  1019  					rt, err := att.Data.HashTreeRoot()
  1020  					assert.NoError(t, err)
  1021  					assert.NoError(t, db.SaveAttestationForPubKey(context.Background(), pkey, rt, att))
  1022  					resp.ValidatorRequests = append(resp.ValidatorRequests, &ethpb.DoppelGangerRequest_ValidatorRequest{PublicKey: pkey[:], Epoch: att.Data.Target.Epoch, SignedRoot: rt[:]})
  1023  					req.ValidatorRequests = append(req.ValidatorRequests, &ethpb.DoppelGangerRequest_ValidatorRequest{PublicKey: pkey[:], Epoch: att.Data.Target.Epoch, SignedRoot: rt[:]})
  1024  				}
  1025  				v := &validator{
  1026  					validatorClient: client,
  1027  					keyManager:      km,
  1028  					db:              db,
  1029  				}
  1030  				client.EXPECT().CheckDoppelGanger(
  1031  					gomock.Any(),                     // ctx
  1032  					&doppelGangerRequestMatcher{req}, // request
  1033  				).Return(nil, nil /*err*/)
  1034  
  1035  				return v
  1036  			},
  1037  		},
  1038  		{
  1039  			name: "multiple doppelganger exists",
  1040  			validatorSetter: func(t *testing.T) *validator {
  1041  				client := mock.NewMockBeaconNodeValidatorClient(ctrl)
  1042  				km := genMockKeymanager(10)
  1043  				keys, err := km.FetchValidatingPublicKeys(context.Background())
  1044  				assert.NoError(t, err)
  1045  				db := dbTest.SetupDB(t, keys)
  1046  				req := &ethpb.DoppelGangerRequest{ValidatorRequests: []*ethpb.DoppelGangerRequest_ValidatorRequest{}}
  1047  				resp := &ethpb.DoppelGangerResponse{Responses: []*ethpb.DoppelGangerResponse_ValidatorResponse{}}
  1048  				for i, k := range keys {
  1049  					pkey := k
  1050  					att := createAttestation(10, 12)
  1051  					rt, err := att.Data.HashTreeRoot()
  1052  					assert.NoError(t, err)
  1053  					assert.NoError(t, db.SaveAttestationForPubKey(context.Background(), pkey, rt, att))
  1054  					if i%3 == 0 {
  1055  						resp.Responses = append(resp.Responses, &ethpb.DoppelGangerResponse_ValidatorResponse{PublicKey: pkey[:], DuplicateExists: true})
  1056  					}
  1057  					req.ValidatorRequests = append(req.ValidatorRequests, &ethpb.DoppelGangerRequest_ValidatorRequest{PublicKey: pkey[:], Epoch: att.Data.Target.Epoch, SignedRoot: rt[:]})
  1058  				}
  1059  				v := &validator{
  1060  					validatorClient: client,
  1061  					keyManager:      km,
  1062  					db:              db,
  1063  				}
  1064  				client.EXPECT().CheckDoppelGanger(
  1065  					gomock.Any(),                     // ctx
  1066  					&doppelGangerRequestMatcher{req}, // request
  1067  				).Return(resp, nil /*err*/)
  1068  				return v
  1069  			},
  1070  			err: "Duplicate instances exists in the network for validator keys",
  1071  		},
  1072  		{
  1073  			name: "single doppelganger exists",
  1074  			validatorSetter: func(t *testing.T) *validator {
  1075  				client := mock.NewMockBeaconNodeValidatorClient(ctrl)
  1076  				km := genMockKeymanager(10)
  1077  				keys, err := km.FetchValidatingPublicKeys(context.Background())
  1078  				assert.NoError(t, err)
  1079  				db := dbTest.SetupDB(t, keys)
  1080  				req := &ethpb.DoppelGangerRequest{ValidatorRequests: []*ethpb.DoppelGangerRequest_ValidatorRequest{}}
  1081  				resp := &ethpb.DoppelGangerResponse{Responses: []*ethpb.DoppelGangerResponse_ValidatorResponse{}}
  1082  				for i, k := range keys {
  1083  					pkey := k
  1084  					att := createAttestation(10, 12)
  1085  					rt, err := att.Data.HashTreeRoot()
  1086  					assert.NoError(t, err)
  1087  					assert.NoError(t, db.SaveAttestationForPubKey(context.Background(), pkey, rt, att))
  1088  					if i%9 == 0 {
  1089  						resp.Responses = append(resp.Responses, &ethpb.DoppelGangerResponse_ValidatorResponse{PublicKey: pkey[:], DuplicateExists: true})
  1090  					}
  1091  					req.ValidatorRequests = append(req.ValidatorRequests, &ethpb.DoppelGangerRequest_ValidatorRequest{PublicKey: pkey[:], Epoch: att.Data.Target.Epoch, SignedRoot: rt[:]})
  1092  				}
  1093  				v := &validator{
  1094  					validatorClient: client,
  1095  					keyManager:      km,
  1096  					db:              db,
  1097  				}
  1098  				client.EXPECT().CheckDoppelGanger(
  1099  					gomock.Any(),                     // ctx
  1100  					&doppelGangerRequestMatcher{req}, // request
  1101  				).Return(resp, nil /*err*/)
  1102  				return v
  1103  			},
  1104  			err: "Duplicate instances exists in the network for validator keys",
  1105  		},
  1106  		{
  1107  			name: "multiple attestations saved",
  1108  			validatorSetter: func(t *testing.T) *validator {
  1109  				client := mock.NewMockBeaconNodeValidatorClient(ctrl)
  1110  				km := genMockKeymanager(10)
  1111  				keys, err := km.FetchValidatingPublicKeys(context.Background())
  1112  				assert.NoError(t, err)
  1113  				db := dbTest.SetupDB(t, keys)
  1114  				req := &ethpb.DoppelGangerRequest{ValidatorRequests: []*ethpb.DoppelGangerRequest_ValidatorRequest{}}
  1115  				resp := &ethpb.DoppelGangerResponse{Responses: []*ethpb.DoppelGangerResponse_ValidatorResponse{}}
  1116  				attLimit := 5
  1117  				for i, k := range keys {
  1118  					pkey := k
  1119  					for j := 0; j < attLimit; j++ {
  1120  						att := createAttestation(10+types.Epoch(j), 12+types.Epoch(j))
  1121  						rt, err := att.Data.HashTreeRoot()
  1122  						assert.NoError(t, err)
  1123  						assert.NoError(t, db.SaveAttestationForPubKey(context.Background(), pkey, rt, att))
  1124  						if j == attLimit-1 {
  1125  							req.ValidatorRequests = append(req.ValidatorRequests, &ethpb.DoppelGangerRequest_ValidatorRequest{PublicKey: pkey[:], Epoch: att.Data.Target.Epoch, SignedRoot: rt[:]})
  1126  						}
  1127  					}
  1128  					if i%3 == 0 {
  1129  						resp.Responses = append(resp.Responses, &ethpb.DoppelGangerResponse_ValidatorResponse{PublicKey: pkey[:], DuplicateExists: true})
  1130  					}
  1131  				}
  1132  				v := &validator{
  1133  					validatorClient: client,
  1134  					keyManager:      km,
  1135  					db:              db,
  1136  				}
  1137  				client.EXPECT().CheckDoppelGanger(
  1138  					gomock.Any(),                     // ctx
  1139  					&doppelGangerRequestMatcher{req}, // request
  1140  				).Return(resp, nil /*err*/)
  1141  				return v
  1142  			},
  1143  			err: "Duplicate instances exists in the network for validator keys",
  1144  		},
  1145  		{
  1146  			name: "no history exists",
  1147  			validatorSetter: func(t *testing.T) *validator {
  1148  				client := mock.NewMockBeaconNodeValidatorClient(ctrl)
  1149  				// Use only 1 key for deterministic order.
  1150  				km := genMockKeymanager(1)
  1151  				keys, err := km.FetchValidatingPublicKeys(context.Background())
  1152  				assert.NoError(t, err)
  1153  				db := dbTest.SetupDB(t, keys)
  1154  				resp := &ethpb.DoppelGangerResponse{Responses: []*ethpb.DoppelGangerResponse_ValidatorResponse{}}
  1155  				req := &ethpb.DoppelGangerRequest{ValidatorRequests: []*ethpb.DoppelGangerRequest_ValidatorRequest{}}
  1156  				for _, k := range keys {
  1157  					resp.Responses = append(resp.Responses, &ethpb.DoppelGangerResponse_ValidatorResponse{PublicKey: k[:], DuplicateExists: false})
  1158  					req.ValidatorRequests = append(req.ValidatorRequests, &ethpb.DoppelGangerRequest_ValidatorRequest{PublicKey: k[:], SignedRoot: make([]byte, 32), Epoch: 0})
  1159  				}
  1160  				v := &validator{
  1161  					validatorClient: client,
  1162  					keyManager:      km,
  1163  					db:              db,
  1164  				}
  1165  				client.EXPECT().CheckDoppelGanger(
  1166  					gomock.Any(), // ctx
  1167  					req,          // request
  1168  				).Return(resp, nil /*err*/)
  1169  				return v
  1170  			},
  1171  			err: "",
  1172  		},
  1173  	}
  1174  	for _, tt := range tests {
  1175  		t.Run(tt.name, func(t *testing.T) {
  1176  			v := tt.validatorSetter(t)
  1177  			if err := v.CheckDoppelGanger(context.Background()); tt.err != "" {
  1178  				assert.ErrorContains(t, tt.err, err)
  1179  			}
  1180  		})
  1181  	}
  1182  }
  1183  
  1184  func TestValidatorAttestationsAreOrdered(t *testing.T) {
  1185  	km := genMockKeymanager(10)
  1186  	keys, err := km.FetchValidatingPublicKeys(context.Background())
  1187  	assert.NoError(t, err)
  1188  	db := dbTest.SetupDB(t, keys)
  1189  
  1190  	k := keys[0]
  1191  	att := createAttestation(10, 14)
  1192  	rt, err := att.Data.HashTreeRoot()
  1193  	assert.NoError(t, err)
  1194  	assert.NoError(t, db.SaveAttestationForPubKey(context.Background(), k, rt, att))
  1195  
  1196  	att = createAttestation(6, 8)
  1197  	rt, err = att.Data.HashTreeRoot()
  1198  	assert.NoError(t, err)
  1199  	assert.NoError(t, db.SaveAttestationForPubKey(context.Background(), k, rt, att))
  1200  
  1201  	att = createAttestation(10, 12)
  1202  	rt, err = att.Data.HashTreeRoot()
  1203  	assert.NoError(t, err)
  1204  	assert.NoError(t, db.SaveAttestationForPubKey(context.Background(), k, rt, att))
  1205  
  1206  	att = createAttestation(2, 3)
  1207  	rt, err = att.Data.HashTreeRoot()
  1208  	assert.NoError(t, err)
  1209  	assert.NoError(t, db.SaveAttestationForPubKey(context.Background(), k, rt, att))
  1210  
  1211  	histories, err := db.AttestationHistoryForPubKey(context.Background(), k)
  1212  	assert.NoError(t, err)
  1213  	r := retrieveLatestRecord(histories)
  1214  	assert.Equal(t, r.Target, types.Epoch(14))
  1215  }
  1216  
  1217  func createAttestation(source, target types.Epoch) *ethpb.IndexedAttestation {
  1218  	return &ethpb.IndexedAttestation{
  1219  		Data: &ethpb.AttestationData{
  1220  			Source: &ethpb.Checkpoint{
  1221  				Epoch: source,
  1222  				Root:  make([]byte, 32),
  1223  			},
  1224  			Target: &ethpb.Checkpoint{
  1225  				Epoch: target,
  1226  				Root:  make([]byte, 32),
  1227  			},
  1228  			BeaconBlockRoot: make([]byte, 32),
  1229  		},
  1230  		Signature: make([]byte, 96),
  1231  	}
  1232  }