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 := ðpb.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 := ðpb.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 := ðpb.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 := ðpb.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 := ðpb.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 ðpb.ValidatorActivationResponse{ 177 Statuses: []*ethpb.ValidatorActivationResponse_Status{ 178 { 179 PublicKey: pubKey1, 180 Status: ðpb.ValidatorStatusResponse{ 181 Status: ethpb.ValidatorStatus_ACTIVE, 182 }, 183 Index: 0, 184 }, 185 { 186 PublicKey: pubKey2, 187 Status: ðpb.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 := ðpb.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 ðpb.ValidatorActivationResponse{ 257 Statuses: []*ethpb.ValidatorActivationResponse_Status{ 258 { 259 PublicKey: pubKey1, 260 Status: ðpb.ValidatorStatusResponse{ 261 Status: ethpb.ValidatorStatus_ACTIVE, 262 ActivationEpoch: 1, 263 }, 264 Index: 0, 265 }, 266 { 267 PublicKey: pubKey2, 268 Status: ðpb.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: ðpb.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 ðpb.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 ðpb.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 }