github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/rpc/eth/v1/beacon/validator_test.go (about) 1 package beacon 2 3 import ( 4 "bytes" 5 "context" 6 "strconv" 7 "strings" 8 "testing" 9 10 types "github.com/prysmaticlabs/eth2-types" 11 chainMock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" 12 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 13 "github.com/prysmaticlabs/prysm/beacon-chain/rpc/statefetcher" 14 "github.com/prysmaticlabs/prysm/beacon-chain/rpc/testutil" 15 iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface" 16 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1" 17 ethpb_alpha "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 18 "github.com/prysmaticlabs/prysm/shared/params" 19 sharedtestutil "github.com/prysmaticlabs/prysm/shared/testutil" 20 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 21 "github.com/prysmaticlabs/prysm/shared/testutil/require" 22 ) 23 24 func TestGetValidator(t *testing.T) { 25 ctx := context.Background() 26 27 var state iface.BeaconState 28 state, _ = sharedtestutil.DeterministicGenesisState(t, 8192) 29 30 t.Run("Head Get Validator by index", func(t *testing.T) { 31 s := Server{ 32 StateFetcher: &testutil.MockFetcher{ 33 BeaconState: state, 34 }, 35 } 36 37 resp, err := s.GetValidator(ctx, ðpb.StateValidatorRequest{ 38 StateId: []byte("head"), 39 ValidatorId: []byte("15"), 40 }) 41 require.NoError(t, err) 42 assert.Equal(t, types.ValidatorIndex(15), resp.Data.Index) 43 }) 44 45 t.Run("Head Get Validator by pubkey", func(t *testing.T) { 46 s := Server{ 47 StateFetcher: &testutil.MockFetcher{ 48 BeaconState: state, 49 }, 50 } 51 52 pubKey := state.PubkeyAtIndex(types.ValidatorIndex(20)) 53 resp, err := s.GetValidator(ctx, ðpb.StateValidatorRequest{ 54 StateId: []byte("head"), 55 ValidatorId: pubKey[:], 56 }) 57 require.NoError(t, err) 58 assert.Equal(t, types.ValidatorIndex(20), resp.Data.Index) 59 assert.Equal(t, true, bytes.Equal(pubKey[:], resp.Data.Validator.Pubkey)) 60 }) 61 62 t.Run("Validator ID required", func(t *testing.T) { 63 s := Server{ 64 StateFetcher: &testutil.MockFetcher{ 65 BeaconState: state, 66 }, 67 } 68 _, err := s.GetValidator(ctx, ðpb.StateValidatorRequest{ 69 StateId: []byte("head"), 70 }) 71 require.ErrorContains(t, "Validator ID is required", err) 72 }) 73 } 74 75 func TestListValidators(t *testing.T) { 76 ctx := context.Background() 77 78 var state iface.BeaconState 79 state, _ = sharedtestutil.DeterministicGenesisState(t, 8192) 80 81 t.Run("Head List All Validators", func(t *testing.T) { 82 s := Server{ 83 StateFetcher: &testutil.MockFetcher{ 84 BeaconState: state, 85 }, 86 } 87 88 resp, err := s.ListValidators(ctx, ðpb.StateValidatorsRequest{ 89 StateId: []byte("head"), 90 }) 91 require.NoError(t, err) 92 assert.Equal(t, len(resp.Data), 8192) 93 for _, val := range resp.Data { 94 assert.Equal(t, ethpb.ValidatorStatus_ACTIVE_ONGOING, val.Status) 95 } 96 }) 97 98 t.Run("Head List Validators by index", func(t *testing.T) { 99 s := Server{ 100 StateFetcher: &testutil.MockFetcher{ 101 BeaconState: state, 102 }, 103 } 104 105 ids := [][]byte{[]byte("15"), []byte("26"), []byte("400")} 106 idNums := []types.ValidatorIndex{15, 26, 400} 107 resp, err := s.ListValidators(ctx, ðpb.StateValidatorsRequest{ 108 StateId: []byte("head"), 109 Id: ids, 110 }) 111 require.NoError(t, err) 112 for i, val := range resp.Data { 113 assert.Equal(t, idNums[i], val.Index) 114 assert.Equal(t, ethpb.ValidatorStatus_ACTIVE_ONGOING, val.Status) 115 } 116 }) 117 118 t.Run("Head List Validators by pubkey", func(t *testing.T) { 119 s := Server{ 120 StateFetcher: &testutil.MockFetcher{ 121 BeaconState: state, 122 }, 123 } 124 idNums := []types.ValidatorIndex{20, 66, 90, 100} 125 pubkey1 := state.PubkeyAtIndex(types.ValidatorIndex(20)) 126 pubkey2 := state.PubkeyAtIndex(types.ValidatorIndex(66)) 127 pubkey3 := state.PubkeyAtIndex(types.ValidatorIndex(90)) 128 pubkey4 := state.PubkeyAtIndex(types.ValidatorIndex(100)) 129 pubKeys := [][]byte{pubkey1[:], pubkey2[:], pubkey3[:], pubkey4[:]} 130 resp, err := s.ListValidators(ctx, ðpb.StateValidatorsRequest{ 131 StateId: []byte("head"), 132 Id: pubKeys, 133 }) 134 require.NoError(t, err) 135 for i, val := range resp.Data { 136 assert.Equal(t, idNums[i], val.Index) 137 assert.Equal(t, true, bytes.Equal(pubKeys[i], val.Validator.Pubkey)) 138 assert.Equal(t, ethpb.ValidatorStatus_ACTIVE_ONGOING, val.Status) 139 } 140 }) 141 142 t.Run("Head List Validators by both index and pubkey", func(t *testing.T) { 143 s := Server{ 144 StateFetcher: &testutil.MockFetcher{ 145 BeaconState: state, 146 }, 147 } 148 149 idNums := []types.ValidatorIndex{20, 90, 170, 129} 150 pubkey1 := state.PubkeyAtIndex(types.ValidatorIndex(20)) 151 pubkey2 := state.PubkeyAtIndex(types.ValidatorIndex(90)) 152 pubkey3 := state.PubkeyAtIndex(types.ValidatorIndex(170)) 153 pubkey4 := state.PubkeyAtIndex(types.ValidatorIndex(129)) 154 pubkeys := [][]byte{pubkey1[:], pubkey2[:], pubkey3[:], pubkey4[:]} 155 ids := [][]byte{pubkey1[:], []byte("90"), pubkey3[:], []byte("129")} 156 resp, err := s.ListValidators(ctx, ðpb.StateValidatorsRequest{ 157 StateId: []byte("head"), 158 Id: ids, 159 }) 160 require.NoError(t, err) 161 for i, val := range resp.Data { 162 assert.Equal(t, idNums[i], val.Index) 163 assert.Equal(t, true, bytes.Equal(pubkeys[i], val.Validator.Pubkey)) 164 assert.Equal(t, ethpb.ValidatorStatus_ACTIVE_ONGOING, val.Status) 165 } 166 }) 167 168 t.Run("Unknown public key is ignored", func(t *testing.T) { 169 s := Server{ 170 StateFetcher: &testutil.MockFetcher{ 171 BeaconState: state, 172 }, 173 } 174 175 existingKey := state.PubkeyAtIndex(types.ValidatorIndex(1)) 176 pubkeys := [][]byte{existingKey[:], []byte(strings.Repeat("f", 48))} 177 resp, err := s.ListValidators(ctx, ðpb.StateValidatorsRequest{ 178 StateId: []byte("head"), 179 Id: pubkeys, 180 }) 181 require.NoError(t, err) 182 require.Equal(t, 1, len(resp.Data)) 183 assert.Equal(t, types.ValidatorIndex(1), resp.Data[0].Index) 184 }) 185 186 t.Run("Unknown index is ignored", func(t *testing.T) { 187 s := Server{ 188 StateFetcher: &testutil.MockFetcher{ 189 BeaconState: state, 190 }, 191 } 192 193 ids := [][]byte{[]byte("1"), []byte("99999")} 194 resp, err := s.ListValidators(ctx, ðpb.StateValidatorsRequest{ 195 StateId: []byte("head"), 196 Id: ids, 197 }) 198 require.NoError(t, err) 199 require.Equal(t, 1, len(resp.Data)) 200 assert.Equal(t, types.ValidatorIndex(1), resp.Data[0].Index) 201 }) 202 } 203 204 func TestListValidators_Status(t *testing.T) { 205 ctx := context.Background() 206 207 var state iface.BeaconState 208 state, _ = sharedtestutil.DeterministicGenesisState(t, 8192) 209 210 farFutureEpoch := params.BeaconConfig().FarFutureEpoch 211 validators := []*ethpb_alpha.Validator{ 212 // Pending initialized. 213 { 214 ActivationEpoch: farFutureEpoch, 215 ActivationEligibilityEpoch: farFutureEpoch, 216 }, 217 // Pending queued. 218 { 219 ActivationEpoch: 10, 220 ActivationEligibilityEpoch: 4, 221 }, 222 // Active ongoing. 223 { 224 ActivationEpoch: 0, 225 ExitEpoch: farFutureEpoch, 226 }, 227 // Active slashed. 228 { 229 ActivationEpoch: 0, 230 ExitEpoch: 30, 231 Slashed: true, 232 }, 233 // Active exiting. 234 { 235 ActivationEpoch: 3, 236 ExitEpoch: 30, 237 Slashed: false, 238 }, 239 // Exit slashed (at epoch 35). 240 { 241 ActivationEpoch: 3, 242 ExitEpoch: 30, 243 WithdrawableEpoch: 40, 244 Slashed: true, 245 }, 246 // Exit unslashed (at epoch 35). 247 { 248 ActivationEpoch: 3, 249 ExitEpoch: 30, 250 WithdrawableEpoch: 40, 251 Slashed: false, 252 }, 253 // Withdrawable (at epoch 45). 254 { 255 ActivationEpoch: 3, 256 ExitEpoch: 30, 257 WithdrawableEpoch: 40, 258 EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance, 259 Slashed: false, 260 }, 261 // Withdrawal done (at epoch 45). 262 { 263 ActivationEpoch: 3, 264 ExitEpoch: 30, 265 WithdrawableEpoch: 40, 266 EffectiveBalance: 0, 267 Slashed: false, 268 }, 269 } 270 for _, validator := range validators { 271 require.NoError(t, state.AppendValidator(validator)) 272 require.NoError(t, state.AppendBalance(params.BeaconConfig().MaxEffectiveBalance)) 273 } 274 275 t.Run("Head List All ACTIVE Validators", func(t *testing.T) { 276 s := Server{ 277 StateFetcher: &statefetcher.StateProvider{ 278 ChainInfoFetcher: &chainMock.ChainService{State: state}, 279 }, 280 } 281 282 resp, err := s.ListValidators(ctx, ðpb.StateValidatorsRequest{ 283 StateId: []byte("head"), 284 Status: []ethpb.ValidatorStatus{ethpb.ValidatorStatus_ACTIVE}, 285 }) 286 require.NoError(t, err) 287 assert.Equal(t, len(resp.Data), 8192+2 /* 2 active */) 288 for _, datum := range resp.Data { 289 status, err := validatorStatus(datum.Validator, 0) 290 require.NoError(t, err) 291 require.Equal( 292 t, 293 true, 294 status == ethpb.ValidatorStatus_ACTIVE, 295 ) 296 require.Equal( 297 t, 298 true, 299 datum.Status == ethpb.ValidatorStatus_ACTIVE_ONGOING || 300 datum.Status == ethpb.ValidatorStatus_ACTIVE_EXITING || 301 datum.Status == ethpb.ValidatorStatus_ACTIVE_SLASHED, 302 ) 303 } 304 }) 305 306 t.Run("Head List All ACTIVE_ONGOING Validators", func(t *testing.T) { 307 s := Server{ 308 StateFetcher: &statefetcher.StateProvider{ 309 ChainInfoFetcher: &chainMock.ChainService{State: state}, 310 }, 311 } 312 313 resp, err := s.ListValidators(ctx, ðpb.StateValidatorsRequest{ 314 StateId: []byte("head"), 315 Status: []ethpb.ValidatorStatus{ethpb.ValidatorStatus_ACTIVE_ONGOING}, 316 }) 317 require.NoError(t, err) 318 assert.Equal(t, len(resp.Data), 8192+1 /* 1 active_ongoing */) 319 for _, datum := range resp.Data { 320 status, err := validatorSubStatus(datum.Validator, 0) 321 require.NoError(t, err) 322 require.Equal( 323 t, 324 true, 325 status == ethpb.ValidatorStatus_ACTIVE_ONGOING, 326 ) 327 require.Equal( 328 t, 329 true, 330 datum.Status == ethpb.ValidatorStatus_ACTIVE_ONGOING, 331 ) 332 } 333 }) 334 335 require.NoError(t, state.SetSlot(params.BeaconConfig().SlotsPerEpoch*35)) 336 t.Run("Head List All EXITED Validators", func(t *testing.T) { 337 s := Server{ 338 StateFetcher: &statefetcher.StateProvider{ 339 ChainInfoFetcher: &chainMock.ChainService{State: state}, 340 }, 341 } 342 343 resp, err := s.ListValidators(ctx, ðpb.StateValidatorsRequest{ 344 StateId: []byte("head"), 345 Status: []ethpb.ValidatorStatus{ethpb.ValidatorStatus_EXITED}, 346 }) 347 require.NoError(t, err) 348 assert.Equal(t, 4 /* 4 exited */, len(resp.Data)) 349 for _, datum := range resp.Data { 350 status, err := validatorStatus(datum.Validator, 35) 351 require.NoError(t, err) 352 require.Equal( 353 t, 354 true, 355 status == ethpb.ValidatorStatus_EXITED, 356 ) 357 require.Equal( 358 t, 359 true, 360 datum.Status == ethpb.ValidatorStatus_EXITED_UNSLASHED || datum.Status == ethpb.ValidatorStatus_EXITED_SLASHED, 361 ) 362 } 363 }) 364 365 t.Run("Head List All PENDING_INITIALIZED and EXITED_UNSLASHED Validators", func(t *testing.T) { 366 s := Server{ 367 StateFetcher: &statefetcher.StateProvider{ 368 ChainInfoFetcher: &chainMock.ChainService{State: state}, 369 }, 370 } 371 372 resp, err := s.ListValidators(ctx, ðpb.StateValidatorsRequest{ 373 StateId: []byte("head"), 374 Status: []ethpb.ValidatorStatus{ethpb.ValidatorStatus_PENDING_INITIALIZED, ethpb.ValidatorStatus_EXITED_UNSLASHED}, 375 }) 376 require.NoError(t, err) 377 assert.Equal(t, 4 /* 4 exited */, len(resp.Data)) 378 for _, datum := range resp.Data { 379 status, err := validatorSubStatus(datum.Validator, 35) 380 require.NoError(t, err) 381 require.Equal( 382 t, 383 true, 384 status == ethpb.ValidatorStatus_PENDING_INITIALIZED || status == ethpb.ValidatorStatus_EXITED_UNSLASHED, 385 ) 386 require.Equal( 387 t, 388 true, 389 datum.Status == ethpb.ValidatorStatus_PENDING_INITIALIZED || datum.Status == ethpb.ValidatorStatus_EXITED_UNSLASHED, 390 ) 391 } 392 }) 393 394 t.Run("Head List All PENDING and EXITED Validators", func(t *testing.T) { 395 s := Server{ 396 StateFetcher: &statefetcher.StateProvider{ 397 ChainInfoFetcher: &chainMock.ChainService{State: state}, 398 }, 399 } 400 401 resp, err := s.ListValidators(ctx, ðpb.StateValidatorsRequest{ 402 StateId: []byte("head"), 403 Status: []ethpb.ValidatorStatus{ethpb.ValidatorStatus_PENDING, ethpb.ValidatorStatus_EXITED_SLASHED}, 404 }) 405 require.NoError(t, err) 406 assert.Equal(t, 2 /* 1 pending, 1 exited */, len(resp.Data)) 407 for _, datum := range resp.Data { 408 status, err := validatorStatus(datum.Validator, 35) 409 require.NoError(t, err) 410 subStatus, err := validatorSubStatus(datum.Validator, 35) 411 require.NoError(t, err) 412 require.Equal( 413 t, 414 true, 415 status == ethpb.ValidatorStatus_PENDING || subStatus == ethpb.ValidatorStatus_EXITED_SLASHED, 416 ) 417 require.Equal( 418 t, 419 true, 420 datum.Status == ethpb.ValidatorStatus_PENDING_INITIALIZED || datum.Status == ethpb.ValidatorStatus_EXITED_SLASHED, 421 ) 422 } 423 }) 424 } 425 func TestListValidatorBalances(t *testing.T) { 426 ctx := context.Background() 427 428 var state iface.BeaconState 429 state, _ = sharedtestutil.DeterministicGenesisState(t, 8192) 430 431 t.Run("Head List Validators Balance by index", func(t *testing.T) { 432 s := Server{ 433 StateFetcher: &testutil.MockFetcher{ 434 BeaconState: state, 435 }, 436 } 437 438 ids := [][]byte{[]byte("15"), []byte("26"), []byte("400")} 439 idNums := []types.ValidatorIndex{15, 26, 400} 440 resp, err := s.ListValidatorBalances(ctx, ðpb.ValidatorBalancesRequest{ 441 StateId: []byte("head"), 442 Id: ids, 443 }) 444 require.NoError(t, err) 445 for i, val := range resp.Data { 446 assert.Equal(t, idNums[i], val.Index) 447 assert.Equal(t, params.BeaconConfig().MaxEffectiveBalance, val.Balance) 448 } 449 }) 450 451 t.Run("Head List Validators Balance by pubkey", func(t *testing.T) { 452 s := Server{ 453 StateFetcher: &testutil.MockFetcher{ 454 BeaconState: state, 455 }, 456 } 457 idNums := []types.ValidatorIndex{20, 66, 90, 100} 458 pubkey1 := state.PubkeyAtIndex(types.ValidatorIndex(20)) 459 pubkey2 := state.PubkeyAtIndex(types.ValidatorIndex(66)) 460 pubkey3 := state.PubkeyAtIndex(types.ValidatorIndex(90)) 461 pubkey4 := state.PubkeyAtIndex(types.ValidatorIndex(100)) 462 pubKeys := [][]byte{pubkey1[:], pubkey2[:], pubkey3[:], pubkey4[:]} 463 resp, err := s.ListValidatorBalances(ctx, ðpb.ValidatorBalancesRequest{ 464 StateId: []byte("head"), 465 Id: pubKeys, 466 }) 467 require.NoError(t, err) 468 for i, val := range resp.Data { 469 assert.Equal(t, idNums[i], val.Index) 470 assert.Equal(t, params.BeaconConfig().MaxEffectiveBalance, val.Balance) 471 } 472 }) 473 474 t.Run("Head List Validators Balance by both index and pubkey", func(t *testing.T) { 475 s := Server{ 476 StateFetcher: &testutil.MockFetcher{ 477 BeaconState: state, 478 }, 479 } 480 481 idNums := []types.ValidatorIndex{20, 90, 170, 129} 482 pubkey1 := state.PubkeyAtIndex(types.ValidatorIndex(20)) 483 pubkey3 := state.PubkeyAtIndex(types.ValidatorIndex(170)) 484 ids := [][]byte{pubkey1[:], []byte("90"), pubkey3[:], []byte("129")} 485 resp, err := s.ListValidatorBalances(ctx, ðpb.ValidatorBalancesRequest{ 486 StateId: []byte("head"), 487 Id: ids, 488 }) 489 require.NoError(t, err) 490 for i, val := range resp.Data { 491 assert.Equal(t, idNums[i], val.Index) 492 assert.Equal(t, params.BeaconConfig().MaxEffectiveBalance, val.Balance) 493 } 494 }) 495 } 496 497 func TestListCommittees(t *testing.T) { 498 ctx := context.Background() 499 500 var state iface.BeaconState 501 state, _ = sharedtestutil.DeterministicGenesisState(t, 8192) 502 epoch := helpers.SlotToEpoch(state.Slot()) 503 504 t.Run("Head All Committees", func(t *testing.T) { 505 s := Server{ 506 StateFetcher: &testutil.MockFetcher{ 507 BeaconState: state, 508 }, 509 } 510 511 resp, err := s.ListCommittees(ctx, ðpb.StateCommitteesRequest{ 512 StateId: []byte("head"), 513 }) 514 require.NoError(t, err) 515 assert.Equal(t, int(params.BeaconConfig().SlotsPerEpoch)*2, len(resp.Data)) 516 for _, datum := range resp.Data { 517 assert.Equal(t, true, datum.Index == types.CommitteeIndex(0) || datum.Index == types.CommitteeIndex(1)) 518 assert.Equal(t, epoch, helpers.SlotToEpoch(datum.Slot)) 519 } 520 }) 521 522 t.Run("Head All Committees of Epoch 10", func(t *testing.T) { 523 s := Server{ 524 StateFetcher: &testutil.MockFetcher{ 525 BeaconState: state, 526 }, 527 } 528 epoch := types.Epoch(10) 529 resp, err := s.ListCommittees(ctx, ðpb.StateCommitteesRequest{ 530 StateId: []byte("head"), 531 Epoch: &epoch, 532 }) 533 require.NoError(t, err) 534 for _, datum := range resp.Data { 535 assert.Equal(t, true, datum.Slot >= 320 && datum.Slot <= 351) 536 } 537 }) 538 539 t.Run("Head All Committees of Slot 4", func(t *testing.T) { 540 s := Server{ 541 StateFetcher: &testutil.MockFetcher{ 542 BeaconState: state, 543 }, 544 } 545 546 slot := types.Slot(4) 547 resp, err := s.ListCommittees(ctx, ðpb.StateCommitteesRequest{ 548 StateId: []byte("head"), 549 Slot: &slot, 550 }) 551 require.NoError(t, err) 552 assert.Equal(t, 2, len(resp.Data)) 553 index := types.CommitteeIndex(0) 554 for _, datum := range resp.Data { 555 assert.Equal(t, epoch, helpers.SlotToEpoch(datum.Slot)) 556 assert.Equal(t, slot, datum.Slot) 557 assert.Equal(t, index, datum.Index) 558 index++ 559 } 560 }) 561 562 t.Run("Head All Committees of Index 1", func(t *testing.T) { 563 s := Server{ 564 StateFetcher: &testutil.MockFetcher{ 565 BeaconState: state, 566 }, 567 } 568 569 index := types.CommitteeIndex(1) 570 resp, err := s.ListCommittees(ctx, ðpb.StateCommitteesRequest{ 571 StateId: []byte("head"), 572 Index: &index, 573 }) 574 require.NoError(t, err) 575 assert.Equal(t, int(params.BeaconConfig().SlotsPerEpoch), len(resp.Data)) 576 slot := types.Slot(0) 577 for _, datum := range resp.Data { 578 assert.Equal(t, epoch, helpers.SlotToEpoch(datum.Slot)) 579 assert.Equal(t, slot, datum.Slot) 580 assert.Equal(t, index, datum.Index) 581 slot++ 582 } 583 }) 584 585 t.Run("Head All Committees of Slot 2, Index 1", func(t *testing.T) { 586 s := Server{ 587 StateFetcher: &testutil.MockFetcher{ 588 BeaconState: state, 589 }, 590 } 591 592 index := types.CommitteeIndex(1) 593 slot := types.Slot(2) 594 resp, err := s.ListCommittees(ctx, ðpb.StateCommitteesRequest{ 595 StateId: []byte("head"), 596 Slot: &slot, 597 Index: &index, 598 }) 599 require.NoError(t, err) 600 assert.Equal(t, 1, len(resp.Data)) 601 for _, datum := range resp.Data { 602 assert.Equal(t, epoch, helpers.SlotToEpoch(datum.Slot)) 603 assert.Equal(t, slot, datum.Slot) 604 assert.Equal(t, index, datum.Index) 605 } 606 }) 607 } 608 609 func Test_validatorStatus(t *testing.T) { 610 farFutureEpoch := params.BeaconConfig().FarFutureEpoch 611 612 type args struct { 613 validator *ethpb.Validator 614 epoch types.Epoch 615 } 616 tests := []struct { 617 name string 618 args args 619 want ethpb.ValidatorStatus 620 wantErr bool 621 }{ 622 { 623 name: "pending initialized", 624 args: args{ 625 validator: ðpb.Validator{ 626 ActivationEpoch: farFutureEpoch, 627 ActivationEligibilityEpoch: farFutureEpoch, 628 }, 629 epoch: types.Epoch(5), 630 }, 631 want: ethpb.ValidatorStatus_PENDING, 632 }, 633 { 634 name: "pending queued", 635 args: args{ 636 validator: ðpb.Validator{ 637 ActivationEpoch: 10, 638 ActivationEligibilityEpoch: 2, 639 }, 640 epoch: types.Epoch(5), 641 }, 642 want: ethpb.ValidatorStatus_PENDING, 643 }, 644 { 645 name: "active ongoing", 646 args: args{ 647 validator: ðpb.Validator{ 648 ActivationEpoch: 3, 649 ExitEpoch: farFutureEpoch, 650 }, 651 epoch: types.Epoch(5), 652 }, 653 want: ethpb.ValidatorStatus_ACTIVE, 654 }, 655 { 656 name: "active slashed", 657 args: args{ 658 validator: ðpb.Validator{ 659 ActivationEpoch: 3, 660 ExitEpoch: 30, 661 Slashed: true, 662 }, 663 epoch: types.Epoch(5), 664 }, 665 want: ethpb.ValidatorStatus_ACTIVE, 666 }, 667 { 668 name: "active exiting", 669 args: args{ 670 validator: ðpb.Validator{ 671 ActivationEpoch: 3, 672 ExitEpoch: 30, 673 Slashed: false, 674 }, 675 epoch: types.Epoch(5), 676 }, 677 want: ethpb.ValidatorStatus_ACTIVE, 678 }, 679 { 680 name: "exited slashed", 681 args: args{ 682 validator: ðpb.Validator{ 683 ActivationEpoch: 3, 684 ExitEpoch: 30, 685 WithdrawableEpoch: 40, 686 Slashed: true, 687 }, 688 epoch: types.Epoch(35), 689 }, 690 want: ethpb.ValidatorStatus_EXITED, 691 }, 692 { 693 name: "exited unslashed", 694 args: args{ 695 validator: ðpb.Validator{ 696 ActivationEpoch: 3, 697 ExitEpoch: 30, 698 WithdrawableEpoch: 40, 699 Slashed: false, 700 }, 701 epoch: types.Epoch(35), 702 }, 703 want: ethpb.ValidatorStatus_EXITED, 704 }, 705 { 706 name: "withdrawal possible", 707 args: args{ 708 validator: ðpb.Validator{ 709 ActivationEpoch: 3, 710 ExitEpoch: 30, 711 WithdrawableEpoch: 40, 712 EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance, 713 Slashed: false, 714 }, 715 epoch: types.Epoch(45), 716 }, 717 want: ethpb.ValidatorStatus_WITHDRAWAL, 718 }, 719 { 720 name: "withdrawal done", 721 args: args{ 722 validator: ðpb.Validator{ 723 ActivationEpoch: 3, 724 ExitEpoch: 30, 725 WithdrawableEpoch: 40, 726 EffectiveBalance: 0, 727 Slashed: false, 728 }, 729 epoch: types.Epoch(45), 730 }, 731 want: ethpb.ValidatorStatus_WITHDRAWAL, 732 }, 733 } 734 for _, tt := range tests { 735 t.Run(tt.name, func(t *testing.T) { 736 got, err := validatorStatus(tt.args.validator, tt.args.epoch) 737 require.NoError(t, err) 738 if got != tt.want { 739 t.Errorf("validatorStatus() got = %v, want %v", got, tt.want) 740 } 741 }) 742 } 743 } 744 745 func Test_validatorSubStatus(t *testing.T) { 746 farFutureEpoch := params.BeaconConfig().FarFutureEpoch 747 748 type args struct { 749 validator *ethpb.Validator 750 epoch types.Epoch 751 } 752 tests := []struct { 753 name string 754 args args 755 want ethpb.ValidatorStatus 756 wantErr bool 757 }{ 758 { 759 name: "pending initialized", 760 args: args{ 761 validator: ðpb.Validator{ 762 ActivationEpoch: farFutureEpoch, 763 ActivationEligibilityEpoch: farFutureEpoch, 764 }, 765 epoch: types.Epoch(5), 766 }, 767 want: ethpb.ValidatorStatus_PENDING_INITIALIZED, 768 }, 769 { 770 name: "pending queued", 771 args: args{ 772 validator: ðpb.Validator{ 773 ActivationEpoch: 10, 774 ActivationEligibilityEpoch: 2, 775 }, 776 epoch: types.Epoch(5), 777 }, 778 want: ethpb.ValidatorStatus_PENDING_QUEUED, 779 }, 780 { 781 name: "active ongoing", 782 args: args{ 783 validator: ðpb.Validator{ 784 ActivationEpoch: 3, 785 ExitEpoch: farFutureEpoch, 786 }, 787 epoch: types.Epoch(5), 788 }, 789 want: ethpb.ValidatorStatus_ACTIVE_ONGOING, 790 }, 791 { 792 name: "active slashed", 793 args: args{ 794 validator: ðpb.Validator{ 795 ActivationEpoch: 3, 796 ExitEpoch: 30, 797 Slashed: true, 798 }, 799 epoch: types.Epoch(5), 800 }, 801 want: ethpb.ValidatorStatus_ACTIVE_SLASHED, 802 }, 803 { 804 name: "active exiting", 805 args: args{ 806 validator: ðpb.Validator{ 807 ActivationEpoch: 3, 808 ExitEpoch: 30, 809 Slashed: false, 810 }, 811 epoch: types.Epoch(5), 812 }, 813 want: ethpb.ValidatorStatus_ACTIVE_EXITING, 814 }, 815 { 816 name: "exited slashed", 817 args: args{ 818 validator: ðpb.Validator{ 819 ActivationEpoch: 3, 820 ExitEpoch: 30, 821 WithdrawableEpoch: 40, 822 Slashed: true, 823 }, 824 epoch: types.Epoch(35), 825 }, 826 want: ethpb.ValidatorStatus_EXITED_SLASHED, 827 }, 828 { 829 name: "exited unslashed", 830 args: args{ 831 validator: ðpb.Validator{ 832 ActivationEpoch: 3, 833 ExitEpoch: 30, 834 WithdrawableEpoch: 40, 835 Slashed: false, 836 }, 837 epoch: types.Epoch(35), 838 }, 839 want: ethpb.ValidatorStatus_EXITED_UNSLASHED, 840 }, 841 { 842 name: "withdrawal possible", 843 args: args{ 844 validator: ðpb.Validator{ 845 ActivationEpoch: 3, 846 ExitEpoch: 30, 847 WithdrawableEpoch: 40, 848 EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance, 849 Slashed: false, 850 }, 851 epoch: types.Epoch(45), 852 }, 853 want: ethpb.ValidatorStatus_WITHDRAWAL_POSSIBLE, 854 }, 855 { 856 name: "withdrawal done", 857 args: args{ 858 validator: ðpb.Validator{ 859 ActivationEpoch: 3, 860 ExitEpoch: 30, 861 WithdrawableEpoch: 40, 862 EffectiveBalance: 0, 863 Slashed: false, 864 }, 865 epoch: types.Epoch(45), 866 }, 867 want: ethpb.ValidatorStatus_WITHDRAWAL_DONE, 868 }, 869 } 870 for _, tt := range tests { 871 t.Run(tt.name, func(t *testing.T) { 872 got, err := validatorSubStatus(tt.args.validator, tt.args.epoch) 873 require.NoError(t, err) 874 if got != tt.want { 875 t.Errorf("validatorSubStatus() got = %v, want %v", got, tt.want) 876 } 877 }) 878 } 879 } 880 881 // This test verifies how many validator statuses have meaningful values. 882 // The first expected non-meaningful value will have x.String() equal to its numeric representation. 883 // This test assumes we start numbering from 0 and do not skip any values. 884 // Having a test like this allows us to use e.g. `if value < 10` for validity checks. 885 func TestNumberOfStatuses(t *testing.T) { 886 lastValidEnumValue := 12 887 x := ethpb.ValidatorStatus(lastValidEnumValue) 888 assert.NotEqual(t, strconv.Itoa(lastValidEnumValue), x.String()) 889 x = ethpb.ValidatorStatus(lastValidEnumValue + 1) 890 assert.Equal(t, strconv.Itoa(lastValidEnumValue+1), x.String()) 891 }