package client import ( "context" "testing" "time" "github.com/golang/mock/gomock" "github.com/pkg/errors" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/prysm/shared/bls" "github.com/prysmaticlabs/prysm/shared/mock" "github.com/prysmaticlabs/prysm/shared/testutil/assert" "github.com/prysmaticlabs/prysm/shared/testutil/require" logTest "github.com/sirupsen/logrus/hooks/test" ) func TestWaitActivation_ContextCanceled(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mock.NewMockBeaconNodeValidatorClient(ctrl) privKey, err := bls.RandKey() require.NoError(t, err) pubKey := [48]byte{} copy(pubKey[:], privKey.PublicKey().Marshal()) km := &mockKeymanager{ keysMap: map[[48]byte]bls.SecretKey{ pubKey: privKey, }, } v := validator{ validatorClient: client, keyManager: km, } clientStream := mock.NewMockBeaconNodeValidator_WaitForActivationClient(ctrl) client.EXPECT().WaitForActivation( gomock.Any(), ðpb.ValidatorActivationRequest{ PublicKeys: [][]byte{pubKey[:]}, }, ).Return(clientStream, nil) clientStream.EXPECT().Recv().Return( ðpb.ValidatorActivationResponse{}, nil, ) ctx, cancel := context.WithCancel(context.Background()) cancel() assert.ErrorContains(t, cancelledCtx, v.WaitForActivation(ctx)) } func TestWaitActivation_StreamSetupFails_AttemptsToReconnect(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mock.NewMockBeaconNodeValidatorClient(ctrl) privKey, err := bls.RandKey() require.NoError(t, err) pubKey := [48]byte{} copy(pubKey[:], privKey.PublicKey().Marshal()) km := &mockKeymanager{ keysMap: map[[48]byte]bls.SecretKey{ pubKey: privKey, }, } v := validator{ validatorClient: client, keyManager: km, } clientStream := mock.NewMockBeaconNodeValidator_WaitForActivationClient(ctrl) client.EXPECT().WaitForActivation( gomock.Any(), ðpb.ValidatorActivationRequest{ PublicKeys: [][]byte{pubKey[:]}, }, ).Return(clientStream, errors.New("failed stream")).Return(clientStream, nil) resp := generateMockStatusResponse([][]byte{pubKey[:]}) resp.Statuses[0].Status.Status = ethpb.ValidatorStatus_ACTIVE clientStream.EXPECT().Recv().Return(resp, nil) assert.NoError(t, v.WaitForActivation(context.Background())) } func TestWaitForActivation_ReceiveErrorFromStream_AttemptsReconnection(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mock.NewMockBeaconNodeValidatorClient(ctrl) privKey, err := bls.RandKey() require.NoError(t, err) pubKey := [48]byte{} copy(pubKey[:], privKey.PublicKey().Marshal()) km := &mockKeymanager{ keysMap: map[[48]byte]bls.SecretKey{ pubKey: privKey, }, } v := validator{ validatorClient: client, keyManager: km, } clientStream := mock.NewMockBeaconNodeValidator_WaitForActivationClient(ctrl) client.EXPECT().WaitForActivation( gomock.Any(), ðpb.ValidatorActivationRequest{ PublicKeys: [][]byte{pubKey[:]}, }, ).Return(clientStream, nil) // A stream fails the first time, but succeeds the second time. resp := generateMockStatusResponse([][]byte{pubKey[:]}) resp.Statuses[0].Status.Status = ethpb.ValidatorStatus_ACTIVE clientStream.EXPECT().Recv().Return( nil, errors.New("fails"), ).Return(resp, nil) assert.NoError(t, v.WaitForActivation(context.Background())) } func TestWaitActivation_LogsActivationEpochOK(t *testing.T) { hook := logTest.NewGlobal() ctrl := gomock.NewController(t) defer ctrl.Finish() client := mock.NewMockBeaconNodeValidatorClient(ctrl) privKey, err := bls.RandKey() require.NoError(t, err) pubKey := [48]byte{} copy(pubKey[:], privKey.PublicKey().Marshal()) km := &mockKeymanager{ keysMap: map[[48]byte]bls.SecretKey{ pubKey: privKey, }, } v := validator{ validatorClient: client, keyManager: km, genesisTime: 1, } resp := generateMockStatusResponse([][]byte{pubKey[:]}) resp.Statuses[0].Status.Status = ethpb.ValidatorStatus_ACTIVE clientStream := mock.NewMockBeaconNodeValidator_WaitForActivationClient(ctrl) client.EXPECT().WaitForActivation( gomock.Any(), ðpb.ValidatorActivationRequest{ PublicKeys: [][]byte{pubKey[:]}, }, ).Return(clientStream, nil) clientStream.EXPECT().Recv().Return( resp, nil, ) assert.NoError(t, v.WaitForActivation(context.Background()), "Could not wait for activation") require.LogsContain(t, hook, "Validator activated") } func TestWaitForActivation_Exiting(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() client := mock.NewMockBeaconNodeValidatorClient(ctrl) privKey, err := bls.RandKey() require.NoError(t, err) pubKey := [48]byte{} copy(pubKey[:], privKey.PublicKey().Marshal()) km := &mockKeymanager{ keysMap: map[[48]byte]bls.SecretKey{ pubKey: privKey, }, } v := validator{ validatorClient: client, keyManager: km, genesisTime: 1, } resp := generateMockStatusResponse([][]byte{pubKey[:]}) resp.Statuses[0].Status.Status = ethpb.ValidatorStatus_EXITING clientStream := mock.NewMockBeaconNodeValidator_WaitForActivationClient(ctrl) client.EXPECT().WaitForActivation( gomock.Any(), ðpb.ValidatorActivationRequest{ PublicKeys: [][]byte{pubKey[:]}, }, ).Return(clientStream, nil) clientStream.EXPECT().Recv().Return( resp, nil, ) require.NoError(t, v.WaitForActivation(context.Background())) } func TestWaitForActivation_RefetchKeys(t *testing.T) { originalPeriod := keyRefetchPeriod defer func() { keyRefetchPeriod = originalPeriod }() keyRefetchPeriod = 5 * time.Second hook := logTest.NewGlobal() ctrl := gomock.NewController(t) defer ctrl.Finish() client := mock.NewMockBeaconNodeValidatorClient(ctrl) privKey, err := bls.RandKey() require.NoError(t, err) pubKey := [48]byte{} copy(pubKey[:], privKey.PublicKey().Marshal()) km := &mockKeymanager{ keysMap: map[[48]byte]bls.SecretKey{ pubKey: privKey, }, fetchNoKeys: true, } v := validator{ validatorClient: client, keyManager: km, genesisTime: 1, } resp := generateMockStatusResponse([][]byte{pubKey[:]}) resp.Statuses[0].Status.Status = ethpb.ValidatorStatus_ACTIVE clientStream := mock.NewMockBeaconNodeValidator_WaitForActivationClient(ctrl) client.EXPECT().WaitForActivation( gomock.Any(), ðpb.ValidatorActivationRequest{ PublicKeys: [][]byte{pubKey[:]}, }, ).Return(clientStream, nil) clientStream.EXPECT().Recv().Return( resp, nil, ) assert.NoError(t, v.WaitForActivation(context.Background()), "Could not wait for activation") assert.LogsContain(t, hook, msgNoKeysFetched) assert.LogsContain(t, hook, "Validator activated") }