github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/rpc/prysm/v1alpha1/validator/server_test.go (about)

     1  package validator
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/golang/mock/gomock"
    10  	mockChain "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
    11  	"github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache"
    12  	"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
    13  	statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state"
    14  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
    15  	dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
    16  	mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing"
    17  	v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
    18  	pbp2p "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
    19  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    20  	"github.com/prysmaticlabs/prysm/proto/eth/v1alpha1/wrapper"
    21  	"github.com/prysmaticlabs/prysm/shared/bls"
    22  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    23  	"github.com/prysmaticlabs/prysm/shared/event"
    24  	"github.com/prysmaticlabs/prysm/shared/mock"
    25  	"github.com/prysmaticlabs/prysm/shared/params"
    26  	"github.com/prysmaticlabs/prysm/shared/testutil"
    27  	"github.com/prysmaticlabs/prysm/shared/testutil/assert"
    28  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    29  	"github.com/prysmaticlabs/prysm/shared/trieutil"
    30  	logTest "github.com/sirupsen/logrus/hooks/test"
    31  	"google.golang.org/protobuf/types/known/emptypb"
    32  )
    33  
    34  func TestValidatorIndex_OK(t *testing.T) {
    35  	db := dbutil.SetupDB(t)
    36  	ctx := context.Background()
    37  	st, err := testutil.NewBeaconState()
    38  	require.NoError(t, err)
    39  	require.NoError(t, db.SaveState(ctx, st.Copy(), [32]byte{}))
    40  
    41  	pubKey := pubKey(1)
    42  
    43  	err = st.SetValidators([]*ethpb.Validator{{PublicKey: pubKey}})
    44  	require.NoError(t, err)
    45  
    46  	Server := &Server{
    47  		BeaconDB:    db,
    48  		HeadFetcher: &mockChain.ChainService{State: st},
    49  	}
    50  
    51  	req := &ethpb.ValidatorIndexRequest{
    52  		PublicKey: pubKey,
    53  	}
    54  	_, err = Server.ValidatorIndex(context.Background(), req)
    55  	assert.NoError(t, err, "Could not get validator index")
    56  }
    57  
    58  func TestWaitForActivation_ContextClosed(t *testing.T) {
    59  	db := dbutil.SetupDB(t)
    60  	ctx := context.Background()
    61  
    62  	beaconState, err := v1.InitializeFromProto(&pbp2p.BeaconState{
    63  		Slot:       0,
    64  		Validators: []*ethpb.Validator{},
    65  	})
    66  	require.NoError(t, err)
    67  	block := testutil.NewBeaconBlock()
    68  	require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(block)), "Could not save genesis block")
    69  	genesisRoot, err := block.Block.HashTreeRoot()
    70  	require.NoError(t, err, "Could not get signing root")
    71  
    72  	ctx, cancel := context.WithCancel(context.Background())
    73  	depositCache, err := depositcache.New()
    74  	require.NoError(t, err)
    75  
    76  	vs := &Server{
    77  		BeaconDB:           db,
    78  		Ctx:                ctx,
    79  		ChainStartFetcher:  &mockPOW.POWChain{},
    80  		BlockFetcher:       &mockPOW.POWChain{},
    81  		Eth1InfoFetcher:    &mockPOW.POWChain{},
    82  		CanonicalStateChan: make(chan *pbp2p.BeaconState, 1),
    83  		DepositFetcher:     depositCache,
    84  		HeadFetcher:        &mockChain.ChainService{State: beaconState, Root: genesisRoot[:]},
    85  	}
    86  	req := &ethpb.ValidatorActivationRequest{
    87  		PublicKeys: [][]byte{pubKey(1)},
    88  	}
    89  
    90  	ctrl := gomock.NewController(t)
    91  	defer ctrl.Finish()
    92  	mockChainStream := mock.NewMockBeaconNodeValidator_WaitForActivationServer(ctrl)
    93  	mockChainStream.EXPECT().Context().Return(context.Background())
    94  	mockChainStream.EXPECT().Send(gomock.Any()).Return(nil)
    95  	mockChainStream.EXPECT().Context().Return(context.Background())
    96  	exitRoutine := make(chan bool)
    97  	go func(tt *testing.T) {
    98  		want := "context canceled"
    99  		assert.ErrorContains(tt, want, vs.WaitForActivation(req, mockChainStream))
   100  		<-exitRoutine
   101  	}(t)
   102  	cancel()
   103  	exitRoutine <- true
   104  }
   105  
   106  func TestWaitForActivation_ValidatorOriginallyExists(t *testing.T) {
   107  	db := dbutil.SetupDB(t)
   108  	// This test breaks if it doesnt use mainnet config
   109  	params.SetupTestConfigCleanup(t)
   110  	params.OverrideBeaconConfig(params.MainnetConfig())
   111  	ctx := context.Background()
   112  
   113  	priv1, err := bls.RandKey()
   114  	require.NoError(t, err)
   115  	priv2, err := bls.RandKey()
   116  	require.NoError(t, err)
   117  
   118  	pubKey1 := priv1.PublicKey().Marshal()
   119  	pubKey2 := priv2.PublicKey().Marshal()
   120  
   121  	beaconState := &pbp2p.BeaconState{
   122  		Slot: 4000,
   123  		Validators: []*ethpb.Validator{
   124  			{
   125  				ActivationEpoch:       0,
   126  				ExitEpoch:             params.BeaconConfig().FarFutureEpoch,
   127  				PublicKey:             pubKey1,
   128  				WithdrawalCredentials: make([]byte, 32),
   129  			},
   130  		},
   131  	}
   132  	block := testutil.NewBeaconBlock()
   133  	genesisRoot, err := block.Block.HashTreeRoot()
   134  	require.NoError(t, err, "Could not get signing root")
   135  	depData := &ethpb.Deposit_Data{
   136  		PublicKey:             pubKey1,
   137  		WithdrawalCredentials: bytesutil.PadTo([]byte("hey"), 32),
   138  		Signature:             make([]byte, 96),
   139  	}
   140  	domain, err := helpers.ComputeDomain(params.BeaconConfig().DomainDeposit, nil, nil)
   141  	require.NoError(t, err)
   142  	signingRoot, err := helpers.ComputeSigningRoot(depData, domain)
   143  	require.NoError(t, err)
   144  	depData.Signature = priv1.Sign(signingRoot[:]).Marshal()
   145  
   146  	deposit := &ethpb.Deposit{
   147  		Data: depData,
   148  	}
   149  	depositTrie, err := trieutil.NewTrie(params.BeaconConfig().DepositContractTreeDepth)
   150  	require.NoError(t, err, "Could not setup deposit trie")
   151  	depositCache, err := depositcache.New()
   152  	require.NoError(t, err)
   153  
   154  	assert.NoError(t, depositCache.InsertDeposit(ctx, deposit, 10 /*blockNum*/, 0, depositTrie.Root()))
   155  	trie, err := v1.InitializeFromProtoUnsafe(beaconState)
   156  	require.NoError(t, err)
   157  	vs := &Server{
   158  		BeaconDB:           db,
   159  		Ctx:                context.Background(),
   160  		CanonicalStateChan: make(chan *pbp2p.BeaconState, 1),
   161  		ChainStartFetcher:  &mockPOW.POWChain{},
   162  		BlockFetcher:       &mockPOW.POWChain{},
   163  		Eth1InfoFetcher:    &mockPOW.POWChain{},
   164  		DepositFetcher:     depositCache,
   165  		HeadFetcher:        &mockChain.ChainService{State: trie, Root: genesisRoot[:]},
   166  	}
   167  	req := &ethpb.ValidatorActivationRequest{
   168  		PublicKeys: [][]byte{pubKey1, pubKey2},
   169  	}
   170  	ctrl := gomock.NewController(t)
   171  
   172  	defer ctrl.Finish()
   173  	mockChainStream := mock.NewMockBeaconNodeValidator_WaitForActivationServer(ctrl)
   174  	mockChainStream.EXPECT().Context().Return(context.Background())
   175  	mockChainStream.EXPECT().Send(
   176  		&ethpb.ValidatorActivationResponse{
   177  			Statuses: []*ethpb.ValidatorActivationResponse_Status{
   178  				{
   179  					PublicKey: pubKey1,
   180  					Status: &ethpb.ValidatorStatusResponse{
   181  						Status: ethpb.ValidatorStatus_ACTIVE,
   182  					},
   183  					Index: 0,
   184  				},
   185  				{
   186  					PublicKey: pubKey2,
   187  					Status: &ethpb.ValidatorStatusResponse{
   188  						ActivationEpoch: params.BeaconConfig().FarFutureEpoch,
   189  					},
   190  					Index: nonExistentIndex,
   191  				},
   192  			},
   193  		},
   194  	).Return(nil)
   195  
   196  	require.NoError(t, vs.WaitForActivation(req, mockChainStream), "Could not setup wait for activation stream")
   197  }
   198  
   199  func TestWaitForActivation_MultipleStatuses(t *testing.T) {
   200  	db := dbutil.SetupDB(t)
   201  
   202  	priv1, err := bls.RandKey()
   203  	require.NoError(t, err)
   204  	priv2, err := bls.RandKey()
   205  	require.NoError(t, err)
   206  	priv3, err := bls.RandKey()
   207  	require.NoError(t, err)
   208  
   209  	pubKey1 := priv1.PublicKey().Marshal()
   210  	pubKey2 := priv2.PublicKey().Marshal()
   211  	pubKey3 := priv3.PublicKey().Marshal()
   212  
   213  	beaconState := &pbp2p.BeaconState{
   214  		Slot: 4000,
   215  		Validators: []*ethpb.Validator{
   216  			{
   217  				PublicKey:       pubKey1,
   218  				ActivationEpoch: 1,
   219  				ExitEpoch:       params.BeaconConfig().FarFutureEpoch,
   220  			},
   221  			{
   222  				PublicKey:                  pubKey2,
   223  				ActivationEpoch:            params.BeaconConfig().FarFutureEpoch,
   224  				ActivationEligibilityEpoch: 6,
   225  				ExitEpoch:                  params.BeaconConfig().FarFutureEpoch,
   226  			},
   227  			{
   228  				PublicKey:                  pubKey3,
   229  				ActivationEpoch:            0,
   230  				ActivationEligibilityEpoch: 0,
   231  				ExitEpoch:                  0,
   232  			},
   233  		},
   234  	}
   235  	block := testutil.NewBeaconBlock()
   236  	genesisRoot, err := block.Block.HashTreeRoot()
   237  	require.NoError(t, err, "Could not get signing root")
   238  	trie, err := v1.InitializeFromProtoUnsafe(beaconState)
   239  	require.NoError(t, err)
   240  	vs := &Server{
   241  		BeaconDB:           db,
   242  		Ctx:                context.Background(),
   243  		CanonicalStateChan: make(chan *pbp2p.BeaconState, 1),
   244  		ChainStartFetcher:  &mockPOW.POWChain{},
   245  		HeadFetcher:        &mockChain.ChainService{State: trie, Root: genesisRoot[:]},
   246  	}
   247  	req := &ethpb.ValidatorActivationRequest{
   248  		PublicKeys: [][]byte{pubKey1, pubKey2, pubKey3},
   249  	}
   250  	ctrl := gomock.NewController(t)
   251  
   252  	defer ctrl.Finish()
   253  	mockChainStream := mock.NewMockBeaconNodeValidator_WaitForActivationServer(ctrl)
   254  	mockChainStream.EXPECT().Context().Return(context.Background())
   255  	mockChainStream.EXPECT().Send(
   256  		&ethpb.ValidatorActivationResponse{
   257  			Statuses: []*ethpb.ValidatorActivationResponse_Status{
   258  				{
   259  					PublicKey: pubKey1,
   260  					Status: &ethpb.ValidatorStatusResponse{
   261  						Status:          ethpb.ValidatorStatus_ACTIVE,
   262  						ActivationEpoch: 1,
   263  					},
   264  					Index: 0,
   265  				},
   266  				{
   267  					PublicKey: pubKey2,
   268  					Status: &ethpb.ValidatorStatusResponse{
   269  						Status:                    ethpb.ValidatorStatus_PENDING,
   270  						ActivationEpoch:           params.BeaconConfig().FarFutureEpoch,
   271  						PositionInActivationQueue: 1,
   272  					},
   273  					Index: 1,
   274  				},
   275  				{
   276  					PublicKey: pubKey3,
   277  					Status: &ethpb.ValidatorStatusResponse{
   278  						Status: ethpb.ValidatorStatus_EXITED,
   279  					},
   280  					Index: 2,
   281  				},
   282  			},
   283  		},
   284  	).Return(nil)
   285  
   286  	require.NoError(t, vs.WaitForActivation(req, mockChainStream), "Could not setup wait for activation stream")
   287  }
   288  
   289  func TestWaitForChainStart_ContextClosed(t *testing.T) {
   290  	db := dbutil.SetupDB(t)
   291  	ctx, cancel := context.WithCancel(context.Background())
   292  	chainService := &mockChain.ChainService{}
   293  	Server := &Server{
   294  		Ctx: ctx,
   295  		ChainStartFetcher: &mockPOW.FaultyMockPOWChain{
   296  			ChainFeed: new(event.Feed),
   297  		},
   298  		StateNotifier: chainService.StateNotifier(),
   299  		BeaconDB:      db,
   300  		HeadFetcher:   chainService,
   301  	}
   302  
   303  	exitRoutine := make(chan bool)
   304  	ctrl := gomock.NewController(t)
   305  	defer ctrl.Finish()
   306  	mockStream := mock.NewMockBeaconNodeValidator_WaitForChainStartServer(ctrl)
   307  	mockStream.EXPECT().Context().Return(ctx)
   308  	go func(tt *testing.T) {
   309  		err := Server.WaitForChainStart(&emptypb.Empty{}, mockStream)
   310  		assert.ErrorContains(tt, "Context canceled", err)
   311  		<-exitRoutine
   312  	}(t)
   313  	cancel()
   314  	exitRoutine <- true
   315  }
   316  
   317  func TestWaitForChainStart_AlreadyStarted(t *testing.T) {
   318  	db := dbutil.SetupDB(t)
   319  	ctx := context.Background()
   320  	headBlockRoot := [32]byte{0x01, 0x02}
   321  	st, err := testutil.NewBeaconState()
   322  	require.NoError(t, err)
   323  	require.NoError(t, st.SetSlot(3))
   324  	require.NoError(t, db.SaveState(ctx, st, headBlockRoot))
   325  	require.NoError(t, db.SaveHeadBlockRoot(ctx, headBlockRoot))
   326  	genesisValidatorsRoot := bytesutil.ToBytes32([]byte("validators"))
   327  	require.NoError(t, st.SetGenesisValidatorRoot(genesisValidatorsRoot[:]))
   328  
   329  	chainService := &mockChain.ChainService{State: st, ValidatorsRoot: genesisValidatorsRoot}
   330  	Server := &Server{
   331  		Ctx: context.Background(),
   332  		ChainStartFetcher: &mockPOW.POWChain{
   333  			ChainFeed: new(event.Feed),
   334  		},
   335  		BeaconDB:      db,
   336  		StateNotifier: chainService.StateNotifier(),
   337  		HeadFetcher:   chainService,
   338  	}
   339  	ctrl := gomock.NewController(t)
   340  	defer ctrl.Finish()
   341  	mockStream := mock.NewMockBeaconNodeValidator_WaitForChainStartServer(ctrl)
   342  	mockStream.EXPECT().Send(
   343  		&ethpb.ChainStartResponse{
   344  			Started:               true,
   345  			GenesisTime:           uint64(time.Unix(0, 0).Unix()),
   346  			GenesisValidatorsRoot: genesisValidatorsRoot[:],
   347  		},
   348  	).Return(nil)
   349  	mockStream.EXPECT().Context().Return(context.Background())
   350  	assert.NoError(t, Server.WaitForChainStart(&emptypb.Empty{}, mockStream), "Could not call RPC method")
   351  }
   352  
   353  func TestWaitForChainStart_HeadStateDoesNotExist(t *testing.T) {
   354  	db := dbutil.SetupDB(t)
   355  	genesisValidatorRoot := params.BeaconConfig().ZeroHash
   356  
   357  	// Set head state to nil
   358  	chainService := &mockChain.ChainService{State: nil}
   359  	notifier := chainService.StateNotifier()
   360  	Server := &Server{
   361  		Ctx: context.Background(),
   362  		ChainStartFetcher: &mockPOW.POWChain{
   363  			ChainFeed: new(event.Feed),
   364  		},
   365  		BeaconDB:      db,
   366  		StateNotifier: chainService.StateNotifier(),
   367  		HeadFetcher:   chainService,
   368  	}
   369  	ctrl := gomock.NewController(t)
   370  	defer ctrl.Finish()
   371  	mockStream := mock.NewMockBeaconNodeValidator_WaitForChainStartServer(ctrl)
   372  	mockStream.EXPECT().Context().Return(context.Background())
   373  
   374  	wg := new(sync.WaitGroup)
   375  	wg.Add(1)
   376  	go func() {
   377  		assert.NoError(t, Server.WaitForChainStart(&emptypb.Empty{}, mockStream), "Could not call RPC method")
   378  		wg.Done()
   379  	}()
   380  	// Simulate a late state initialization event, so that
   381  	// method is able to handle race condition here.
   382  	notifier.StateFeed().Send(&feed.Event{
   383  		Type: statefeed.Initialized,
   384  		Data: &statefeed.InitializedData{
   385  			StartTime:             time.Unix(0, 0),
   386  			GenesisValidatorsRoot: genesisValidatorRoot[:],
   387  		},
   388  	})
   389  	testutil.WaitTimeout(wg, time.Second)
   390  }
   391  
   392  func TestWaitForChainStart_NotStartedThenLogFired(t *testing.T) {
   393  	db := dbutil.SetupDB(t)
   394  	hook := logTest.NewGlobal()
   395  
   396  	genesisValidatorsRoot := bytesutil.ToBytes32([]byte("validators"))
   397  	chainService := &mockChain.ChainService{}
   398  	Server := &Server{
   399  		Ctx: context.Background(),
   400  		ChainStartFetcher: &mockPOW.FaultyMockPOWChain{
   401  			ChainFeed: new(event.Feed),
   402  		},
   403  		BeaconDB:      db,
   404  		StateNotifier: chainService.StateNotifier(),
   405  		HeadFetcher:   chainService,
   406  	}
   407  	exitRoutine := make(chan bool)
   408  	ctrl := gomock.NewController(t)
   409  	defer ctrl.Finish()
   410  	mockStream := mock.NewMockBeaconNodeValidator_WaitForChainStartServer(ctrl)
   411  	mockStream.EXPECT().Send(
   412  		&ethpb.ChainStartResponse{
   413  			Started:               true,
   414  			GenesisTime:           uint64(time.Unix(0, 0).Unix()),
   415  			GenesisValidatorsRoot: genesisValidatorsRoot[:],
   416  		},
   417  	).Return(nil)
   418  	mockStream.EXPECT().Context().Return(context.Background())
   419  	go func(tt *testing.T) {
   420  		assert.NoError(tt, Server.WaitForChainStart(&emptypb.Empty{}, mockStream))
   421  		<-exitRoutine
   422  	}(t)
   423  
   424  	// Send in a loop to ensure it is delivered (busy wait for the service to subscribe to the state feed).
   425  	for sent := 0; sent == 0; {
   426  		sent = Server.StateNotifier.StateFeed().Send(&feed.Event{
   427  			Type: statefeed.Initialized,
   428  			Data: &statefeed.InitializedData{
   429  				StartTime:             time.Unix(0, 0),
   430  				GenesisValidatorsRoot: genesisValidatorsRoot[:],
   431  			},
   432  		})
   433  	}
   434  
   435  	exitRoutine <- true
   436  	require.LogsContain(t, hook, "Sending genesis time")
   437  }