github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/core/helpers/validators_test.go (about) 1 package helpers 2 3 import ( 4 "errors" 5 "testing" 6 7 types "github.com/prysmaticlabs/eth2-types" 8 "github.com/prysmaticlabs/prysm/beacon-chain/cache" 9 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1" 10 pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" 11 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 12 "github.com/prysmaticlabs/prysm/shared/bytesutil" 13 "github.com/prysmaticlabs/prysm/shared/hashutil" 14 "github.com/prysmaticlabs/prysm/shared/params" 15 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 16 "github.com/prysmaticlabs/prysm/shared/testutil/require" 17 ) 18 19 func TestIsActiveValidator_OK(t *testing.T) { 20 tests := []struct { 21 a types.Epoch 22 b bool 23 }{ 24 {a: 0, b: false}, 25 {a: 10, b: true}, 26 {a: 100, b: false}, 27 {a: 1000, b: false}, 28 {a: 64, b: true}, 29 } 30 for _, test := range tests { 31 validator := ðpb.Validator{ActivationEpoch: 10, ExitEpoch: 100} 32 assert.Equal(t, test.b, IsActiveValidator(validator, test.a), "IsActiveValidator(%d)", test.a) 33 } 34 } 35 36 func TestIsActiveValidatorUsingTrie_OK(t *testing.T) { 37 tests := []struct { 38 a types.Epoch 39 b bool 40 }{ 41 {a: 0, b: false}, 42 {a: 10, b: true}, 43 {a: 100, b: false}, 44 {a: 1000, b: false}, 45 {a: 64, b: true}, 46 } 47 val := ðpb.Validator{ActivationEpoch: 10, ExitEpoch: 100} 48 beaconState, err := v1.InitializeFromProto(&pb.BeaconState{Validators: []*ethpb.Validator{val}}) 49 require.NoError(t, err) 50 for _, test := range tests { 51 readOnlyVal, err := beaconState.ValidatorAtIndexReadOnly(0) 52 require.NoError(t, err) 53 assert.Equal(t, test.b, IsActiveValidatorUsingTrie(readOnlyVal, test.a), "IsActiveValidatorUsingTrie(%d)", test.a) 54 } 55 } 56 57 func TestIsSlashableValidator_OK(t *testing.T) { 58 tests := []struct { 59 name string 60 validator *ethpb.Validator 61 epoch types.Epoch 62 slashable bool 63 }{ 64 { 65 name: "Unset withdrawable, slashable", 66 validator: ðpb.Validator{ 67 WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch, 68 }, 69 epoch: 0, 70 slashable: true, 71 }, 72 { 73 name: "before withdrawable, slashable", 74 validator: ðpb.Validator{ 75 WithdrawableEpoch: 5, 76 }, 77 epoch: 3, 78 slashable: true, 79 }, 80 { 81 name: "inactive, not slashable", 82 validator: ðpb.Validator{ 83 ActivationEpoch: 5, 84 WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch, 85 }, 86 epoch: 2, 87 slashable: false, 88 }, 89 { 90 name: "after withdrawable, not slashable", 91 validator: ðpb.Validator{ 92 WithdrawableEpoch: 3, 93 }, 94 epoch: 3, 95 slashable: false, 96 }, 97 { 98 name: "slashed and withdrawable, not slashable", 99 validator: ðpb.Validator{ 100 Slashed: true, 101 ExitEpoch: params.BeaconConfig().FarFutureEpoch, 102 WithdrawableEpoch: 1, 103 }, 104 epoch: 2, 105 slashable: false, 106 }, 107 { 108 name: "slashed, not slashable", 109 validator: ðpb.Validator{ 110 Slashed: true, 111 ExitEpoch: params.BeaconConfig().FarFutureEpoch, 112 WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch, 113 }, 114 epoch: 2, 115 slashable: false, 116 }, 117 { 118 name: "inactive and slashed, not slashable", 119 validator: ðpb.Validator{ 120 Slashed: true, 121 ActivationEpoch: 4, 122 ExitEpoch: params.BeaconConfig().FarFutureEpoch, 123 WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch, 124 }, 125 epoch: 2, 126 slashable: false, 127 }, 128 } 129 130 for _, test := range tests { 131 t.Run(test.name, func(t *testing.T) { 132 slashableValidator := IsSlashableValidator(test.validator.ActivationEpoch, 133 test.validator.WithdrawableEpoch, test.validator.Slashed, test.epoch) 134 assert.Equal(t, test.slashable, slashableValidator, "Expected active validator slashable to be %t", test.slashable) 135 }) 136 } 137 } 138 139 func TestIsSlashableValidatorUsingTrie_OK(t *testing.T) { 140 tests := []struct { 141 name string 142 validator *ethpb.Validator 143 epoch types.Epoch 144 slashable bool 145 }{ 146 { 147 name: "Unset withdrawable, slashable", 148 validator: ðpb.Validator{ 149 WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch, 150 }, 151 epoch: 0, 152 slashable: true, 153 }, 154 { 155 name: "before withdrawable, slashable", 156 validator: ðpb.Validator{ 157 WithdrawableEpoch: 5, 158 }, 159 epoch: 3, 160 slashable: true, 161 }, 162 { 163 name: "inactive, not slashable", 164 validator: ðpb.Validator{ 165 ActivationEpoch: 5, 166 WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch, 167 }, 168 epoch: 2, 169 slashable: false, 170 }, 171 { 172 name: "after withdrawable, not slashable", 173 validator: ðpb.Validator{ 174 WithdrawableEpoch: 3, 175 }, 176 epoch: 3, 177 slashable: false, 178 }, 179 { 180 name: "slashed and withdrawable, not slashable", 181 validator: ðpb.Validator{ 182 Slashed: true, 183 ExitEpoch: params.BeaconConfig().FarFutureEpoch, 184 WithdrawableEpoch: 1, 185 }, 186 epoch: 2, 187 slashable: false, 188 }, 189 { 190 name: "slashed, not slashable", 191 validator: ðpb.Validator{ 192 Slashed: true, 193 ExitEpoch: params.BeaconConfig().FarFutureEpoch, 194 WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch, 195 }, 196 epoch: 2, 197 slashable: false, 198 }, 199 { 200 name: "inactive and slashed, not slashable", 201 validator: ðpb.Validator{ 202 Slashed: true, 203 ActivationEpoch: 4, 204 ExitEpoch: params.BeaconConfig().FarFutureEpoch, 205 WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch, 206 }, 207 epoch: 2, 208 slashable: false, 209 }, 210 } 211 212 for _, test := range tests { 213 beaconState, err := v1.InitializeFromProto(&pb.BeaconState{Validators: []*ethpb.Validator{test.validator}}) 214 require.NoError(t, err) 215 readOnlyVal, err := beaconState.ValidatorAtIndexReadOnly(0) 216 require.NoError(t, err) 217 t.Run(test.name, func(t *testing.T) { 218 slashableValidator := IsSlashableValidatorUsingTrie(readOnlyVal, test.epoch) 219 assert.Equal(t, test.slashable, slashableValidator, "Expected active validator slashable to be %t", test.slashable) 220 }) 221 } 222 } 223 224 func TestBeaconProposerIndex_OK(t *testing.T) { 225 params.SetupTestConfigCleanup(t) 226 ClearCache() 227 c := params.BeaconConfig() 228 c.MinGenesisActiveValidatorCount = 16384 229 params.OverrideBeaconConfig(c) 230 validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount/8) 231 for i := 0; i < len(validators); i++ { 232 validators[i] = ðpb.Validator{ 233 ExitEpoch: params.BeaconConfig().FarFutureEpoch, 234 } 235 } 236 237 state, err := v1.InitializeFromProto(&pb.BeaconState{ 238 Validators: validators, 239 Slot: 0, 240 RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), 241 }) 242 require.NoError(t, err) 243 244 tests := []struct { 245 slot types.Slot 246 index types.ValidatorIndex 247 }{ 248 { 249 slot: 1, 250 index: 2039, 251 }, 252 { 253 slot: 5, 254 index: 1895, 255 }, 256 { 257 slot: 19, 258 index: 1947, 259 }, 260 { 261 slot: 30, 262 index: 369, 263 }, 264 { 265 slot: 43, 266 index: 464, 267 }, 268 } 269 270 for _, tt := range tests { 271 ClearCache() 272 require.NoError(t, state.SetSlot(tt.slot)) 273 result, err := BeaconProposerIndex(state) 274 require.NoError(t, err, "Failed to get shard and committees at slot") 275 assert.Equal(t, tt.index, result, "Result index was an unexpected value") 276 } 277 } 278 279 func TestBeaconProposerIndex_BadState(t *testing.T) { 280 params.SetupTestConfigCleanup(t) 281 ClearCache() 282 c := params.BeaconConfig() 283 c.MinGenesisActiveValidatorCount = 16384 284 params.OverrideBeaconConfig(c) 285 validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount/8) 286 for i := 0; i < len(validators); i++ { 287 validators[i] = ðpb.Validator{ 288 ExitEpoch: params.BeaconConfig().FarFutureEpoch, 289 } 290 } 291 roots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot) 292 for i := uint64(0); i < uint64(params.BeaconConfig().SlotsPerHistoricalRoot); i++ { 293 roots[i] = make([]byte, 32) 294 } 295 296 state, err := v1.InitializeFromProto(&pb.BeaconState{ 297 Validators: validators, 298 Slot: 0, 299 RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), 300 BlockRoots: roots, 301 StateRoots: roots, 302 }) 303 require.NoError(t, err) 304 // Set a very high slot, so that retrieved block root will be 305 // non existent for the proposer cache. 306 require.NoError(t, state.SetSlot(100)) 307 _, err = BeaconProposerIndex(state) 308 require.NoError(t, err) 309 assert.Equal(t, 0, len(proposerIndicesCache.ProposerIndicesCache.ListKeys())) 310 } 311 312 func TestComputeProposerIndex_Compatibility(t *testing.T) { 313 validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount) 314 for i := 0; i < len(validators); i++ { 315 validators[i] = ðpb.Validator{ 316 ExitEpoch: params.BeaconConfig().FarFutureEpoch, 317 } 318 } 319 320 state, err := v1.InitializeFromProto(&pb.BeaconState{ 321 Validators: validators, 322 RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), 323 }) 324 require.NoError(t, err) 325 326 indices, err := ActiveValidatorIndices(state, 0) 327 require.NoError(t, err) 328 329 var proposerIndices []types.ValidatorIndex 330 seed, err := Seed(state, 0, params.BeaconConfig().DomainBeaconProposer) 331 require.NoError(t, err) 332 for i := uint64(0); i < uint64(params.BeaconConfig().SlotsPerEpoch); i++ { 333 seedWithSlot := append(seed[:], bytesutil.Bytes8(i)...) 334 seedWithSlotHash := hashutil.Hash(seedWithSlot) 335 index, err := ComputeProposerIndex(state, indices, seedWithSlotHash) 336 require.NoError(t, err) 337 proposerIndices = append(proposerIndices, index) 338 } 339 340 var wantedProposerIndices []types.ValidatorIndex 341 seed, err = Seed(state, 0, params.BeaconConfig().DomainBeaconProposer) 342 require.NoError(t, err) 343 for i := uint64(0); i < uint64(params.BeaconConfig().SlotsPerEpoch); i++ { 344 seedWithSlot := append(seed[:], bytesutil.Bytes8(i)...) 345 seedWithSlotHash := hashutil.Hash(seedWithSlot) 346 index, err := computeProposerIndexWithValidators(state.Validators(), indices, seedWithSlotHash) 347 require.NoError(t, err) 348 wantedProposerIndices = append(wantedProposerIndices, index) 349 } 350 assert.DeepEqual(t, wantedProposerIndices, proposerIndices, "Wanted proposer indices from ComputeProposerIndexWithValidators does not match") 351 } 352 353 func TestDelayedActivationExitEpoch_OK(t *testing.T) { 354 epoch := types.Epoch(9999) 355 wanted := epoch + 1 + params.BeaconConfig().MaxSeedLookahead 356 assert.Equal(t, wanted, ActivationExitEpoch(epoch)) 357 } 358 359 func TestActiveValidatorCount_Genesis(t *testing.T) { 360 c := 1000 361 validators := make([]*ethpb.Validator, c) 362 for i := 0; i < len(validators); i++ { 363 validators[i] = ðpb.Validator{ 364 ExitEpoch: params.BeaconConfig().FarFutureEpoch, 365 } 366 } 367 beaconState, err := v1.InitializeFromProto(&pb.BeaconState{ 368 Slot: 0, 369 Validators: validators, 370 RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), 371 }) 372 require.NoError(t, err) 373 374 // Preset cache to a bad count. 375 seed, err := Seed(beaconState, 0, params.BeaconConfig().DomainBeaconAttester) 376 require.NoError(t, err) 377 require.NoError(t, committeeCache.AddCommitteeShuffledList(&cache.Committees{Seed: seed, ShuffledIndices: []types.ValidatorIndex{1, 2, 3}})) 378 validatorCount, err := ActiveValidatorCount(beaconState, CurrentEpoch(beaconState)) 379 require.NoError(t, err) 380 assert.Equal(t, uint64(c), validatorCount, "Did not get the correct validator count") 381 } 382 383 func TestChurnLimit_OK(t *testing.T) { 384 tests := []struct { 385 validatorCount int 386 wantedChurn uint64 387 }{ 388 {validatorCount: 1000, wantedChurn: 4}, 389 {validatorCount: 100000, wantedChurn: 4}, 390 {validatorCount: 1000000, wantedChurn: 15 /* validatorCount/churnLimitQuotient */}, 391 {validatorCount: 2000000, wantedChurn: 30 /* validatorCount/churnLimitQuotient */}, 392 } 393 for _, test := range tests { 394 ClearCache() 395 396 validators := make([]*ethpb.Validator, test.validatorCount) 397 for i := 0; i < len(validators); i++ { 398 validators[i] = ðpb.Validator{ 399 ExitEpoch: params.BeaconConfig().FarFutureEpoch, 400 } 401 } 402 403 beaconState, err := v1.InitializeFromProto(&pb.BeaconState{ 404 Slot: 1, 405 Validators: validators, 406 RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), 407 }) 408 require.NoError(t, err) 409 validatorCount, err := ActiveValidatorCount(beaconState, CurrentEpoch(beaconState)) 410 require.NoError(t, err) 411 resultChurn, err := ValidatorChurnLimit(validatorCount) 412 require.NoError(t, err) 413 assert.Equal(t, test.wantedChurn, resultChurn, "ValidatorChurnLimit(%d)", test.validatorCount) 414 } 415 } 416 417 func TestDomain_OK(t *testing.T) { 418 state := &pb.BeaconState{ 419 Fork: &pb.Fork{ 420 Epoch: 3, 421 PreviousVersion: []byte{0, 0, 0, 2}, 422 CurrentVersion: []byte{0, 0, 0, 3}, 423 }, 424 } 425 tests := []struct { 426 epoch types.Epoch 427 domainType [4]byte 428 result []byte 429 }{ 430 {epoch: 1, domainType: bytesutil.ToBytes4(bytesutil.Bytes4(4)), result: bytesutil.ToBytes(947067381421703172, 32)}, 431 {epoch: 2, domainType: bytesutil.ToBytes4(bytesutil.Bytes4(4)), result: bytesutil.ToBytes(947067381421703172, 32)}, 432 {epoch: 2, domainType: bytesutil.ToBytes4(bytesutil.Bytes4(5)), result: bytesutil.ToBytes(947067381421703173, 32)}, 433 {epoch: 3, domainType: bytesutil.ToBytes4(bytesutil.Bytes4(4)), result: bytesutil.ToBytes(9369798235163459588, 32)}, 434 {epoch: 3, domainType: bytesutil.ToBytes4(bytesutil.Bytes4(5)), result: bytesutil.ToBytes(9369798235163459589, 32)}, 435 } 436 for _, tt := range tests { 437 domain, err := Domain(state.Fork, tt.epoch, tt.domainType, nil) 438 require.NoError(t, err) 439 assert.DeepEqual(t, tt.result[:8], domain[:8], "Unexpected domain version") 440 } 441 } 442 443 // Test basic functionality of ActiveValidatorIndices without caching. This test will need to be 444 // rewritten when releasing some cache flag. 445 func TestActiveValidatorIndices(t *testing.T) { 446 farFutureEpoch := params.BeaconConfig().FarFutureEpoch 447 type args struct { 448 state *pb.BeaconState 449 epoch types.Epoch 450 } 451 tests := []struct { 452 name string 453 args args 454 want []types.ValidatorIndex 455 wantedErr string 456 }{ 457 { 458 name: "all_active_epoch_10", 459 args: args{ 460 state: &pb.BeaconState{ 461 RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), 462 Validators: []*ethpb.Validator{ 463 { 464 ActivationEpoch: 0, 465 ExitEpoch: farFutureEpoch, 466 }, 467 { 468 ActivationEpoch: 0, 469 ExitEpoch: farFutureEpoch, 470 }, 471 { 472 ActivationEpoch: 0, 473 ExitEpoch: farFutureEpoch, 474 }, 475 }, 476 }, 477 epoch: 10, 478 }, 479 want: []types.ValidatorIndex{0, 1, 2}, 480 }, 481 { 482 name: "some_active_epoch_10", 483 args: args{ 484 state: &pb.BeaconState{ 485 RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), 486 Validators: []*ethpb.Validator{ 487 { 488 ActivationEpoch: 0, 489 ExitEpoch: farFutureEpoch, 490 }, 491 { 492 ActivationEpoch: 0, 493 ExitEpoch: farFutureEpoch, 494 }, 495 { 496 ActivationEpoch: 0, 497 ExitEpoch: 1, 498 }, 499 }, 500 }, 501 epoch: 10, 502 }, 503 want: []types.ValidatorIndex{0, 1}, 504 }, 505 { 506 name: "some_active_with_recent_new_epoch_10", 507 args: args{ 508 state: &pb.BeaconState{ 509 RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), 510 Validators: []*ethpb.Validator{ 511 { 512 ActivationEpoch: 0, 513 ExitEpoch: farFutureEpoch, 514 }, 515 { 516 ActivationEpoch: 0, 517 ExitEpoch: farFutureEpoch, 518 }, 519 { 520 ActivationEpoch: 0, 521 ExitEpoch: 1, 522 }, 523 { 524 ActivationEpoch: 0, 525 ExitEpoch: farFutureEpoch, 526 }, 527 }, 528 }, 529 epoch: 10, 530 }, 531 want: []types.ValidatorIndex{0, 1, 3}, 532 }, 533 { 534 name: "some_active_with_recent_new_epoch_10", 535 args: args{ 536 state: &pb.BeaconState{ 537 RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), 538 Validators: []*ethpb.Validator{ 539 { 540 ActivationEpoch: 0, 541 ExitEpoch: farFutureEpoch, 542 }, 543 { 544 ActivationEpoch: 0, 545 ExitEpoch: farFutureEpoch, 546 }, 547 { 548 ActivationEpoch: 0, 549 ExitEpoch: 1, 550 }, 551 { 552 ActivationEpoch: 0, 553 ExitEpoch: farFutureEpoch, 554 }, 555 }, 556 }, 557 epoch: 10, 558 }, 559 want: []types.ValidatorIndex{0, 1, 3}, 560 }, 561 { 562 name: "some_active_with_recent_new_epoch_10", 563 args: args{ 564 state: &pb.BeaconState{ 565 RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), 566 Validators: []*ethpb.Validator{ 567 { 568 ActivationEpoch: 0, 569 ExitEpoch: farFutureEpoch, 570 }, 571 { 572 ActivationEpoch: 0, 573 ExitEpoch: 1, 574 }, 575 { 576 ActivationEpoch: 0, 577 ExitEpoch: farFutureEpoch, 578 }, 579 { 580 ActivationEpoch: 0, 581 ExitEpoch: farFutureEpoch, 582 }, 583 }, 584 }, 585 epoch: 10, 586 }, 587 want: []types.ValidatorIndex{0, 2, 3}, 588 }, 589 } 590 for _, tt := range tests { 591 t.Run(tt.name, func(t *testing.T) { 592 s, err := v1.InitializeFromProto(tt.args.state) 593 require.NoError(t, err) 594 got, err := ActiveValidatorIndices(s, tt.args.epoch) 595 if tt.wantedErr != "" { 596 assert.ErrorContains(t, tt.wantedErr, err) 597 return 598 } 599 assert.DeepEqual(t, tt.want, got, "ActiveValidatorIndices()") 600 ClearCache() 601 }) 602 } 603 } 604 605 func TestComputeProposerIndex(t *testing.T) { 606 seed := bytesutil.ToBytes32([]byte("seed")) 607 type args struct { 608 validators []*ethpb.Validator 609 indices []types.ValidatorIndex 610 seed [32]byte 611 } 612 tests := []struct { 613 name string 614 args args 615 want types.ValidatorIndex 616 wantedErr string 617 }{ 618 { 619 name: "all_active_indices", 620 args: args{ 621 validators: []*ethpb.Validator{ 622 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 623 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 624 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 625 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 626 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 627 }, 628 indices: []types.ValidatorIndex{0, 1, 2, 3, 4}, 629 seed: seed, 630 }, 631 want: 2, 632 }, 633 { // Regression test for https://github.com/prysmaticlabs/prysm/issues/4259. 634 name: "1_active_index", 635 args: args{ 636 validators: []*ethpb.Validator{ 637 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 638 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 639 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 640 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 641 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 642 }, 643 indices: []types.ValidatorIndex{3}, 644 seed: seed, 645 }, 646 want: 3, 647 }, 648 { 649 name: "empty_active_indices", 650 args: args{ 651 validators: []*ethpb.Validator{ 652 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 653 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 654 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 655 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 656 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 657 }, 658 indices: []types.ValidatorIndex{}, 659 seed: seed, 660 }, 661 wantedErr: "empty active indices list", 662 }, 663 { 664 name: "active_indices_out_of_range", 665 args: args{ 666 validators: []*ethpb.Validator{ 667 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 668 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 669 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 670 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 671 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 672 }, 673 indices: []types.ValidatorIndex{100}, 674 seed: seed, 675 }, 676 wantedErr: "active index out of range", 677 }, 678 { 679 name: "second_half_active", 680 args: args{ 681 validators: []*ethpb.Validator{ 682 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 683 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 684 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 685 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 686 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 687 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 688 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 689 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 690 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 691 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 692 }, 693 indices: []types.ValidatorIndex{5, 6, 7, 8, 9}, 694 seed: seed, 695 }, 696 want: 7, 697 }, 698 { 699 name: "nil_validator", 700 args: args{ 701 validators: []*ethpb.Validator{ 702 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 703 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 704 nil, // Should never happen, but would cause a panic when it does happen. 705 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 706 {EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 707 }, 708 indices: []types.ValidatorIndex{0, 1, 2, 3, 4}, 709 seed: seed, 710 }, 711 want: 4, 712 }, 713 } 714 for _, tt := range tests { 715 t.Run(tt.name, func(t *testing.T) { 716 bState := &pb.BeaconState{Validators: tt.args.validators} 717 stTrie, err := v1.InitializeFromProtoUnsafe(bState) 718 require.NoError(t, err) 719 got, err := ComputeProposerIndex(stTrie, tt.args.indices, tt.args.seed) 720 if tt.wantedErr != "" { 721 assert.ErrorContains(t, tt.wantedErr, err) 722 return 723 } 724 assert.Equal(t, tt.want, got, "ComputeProposerIndex()") 725 }) 726 } 727 } 728 729 func TestIsEligibleForActivationQueue(t *testing.T) { 730 tests := []struct { 731 name string 732 validator *ethpb.Validator 733 want bool 734 }{ 735 {"Eligible", 736 ðpb.Validator{ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch, EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 737 true}, 738 {"Incorrect activation eligibility epoch", 739 ðpb.Validator{ActivationEligibilityEpoch: 1, EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance}, 740 false}, 741 {"Not enough balance", 742 ðpb.Validator{ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch, EffectiveBalance: 1}, 743 false}, 744 } 745 for _, tt := range tests { 746 t.Run(tt.name, func(t *testing.T) { 747 assert.Equal(t, tt.want, IsEligibleForActivationQueue(tt.validator), "IsEligibleForActivationQueue()") 748 }) 749 } 750 } 751 752 func TestIsIsEligibleForActivation(t *testing.T) { 753 tests := []struct { 754 name string 755 validator *ethpb.Validator 756 state *pb.BeaconState 757 want bool 758 }{ 759 {"Eligible", 760 ðpb.Validator{ActivationEligibilityEpoch: 1, ActivationEpoch: params.BeaconConfig().FarFutureEpoch}, 761 &pb.BeaconState{FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 2}}, 762 true}, 763 {"Not yet finalized", 764 ðpb.Validator{ActivationEligibilityEpoch: 1, ActivationEpoch: params.BeaconConfig().FarFutureEpoch}, 765 &pb.BeaconState{FinalizedCheckpoint: ðpb.Checkpoint{Root: make([]byte, 32)}}, 766 false}, 767 {"Incorrect activation epoch", 768 ðpb.Validator{ActivationEligibilityEpoch: 1}, 769 &pb.BeaconState{FinalizedCheckpoint: ðpb.Checkpoint{Epoch: 2}}, 770 false}, 771 } 772 for _, tt := range tests { 773 t.Run(tt.name, func(t *testing.T) { 774 s, err := v1.InitializeFromProto(tt.state) 775 require.NoError(t, err) 776 assert.Equal(t, tt.want, IsEligibleForActivation(s, tt.validator), "IsEligibleForActivation()") 777 }) 778 } 779 } 780 781 func computeProposerIndexWithValidators(validators []*ethpb.Validator, activeIndices []types.ValidatorIndex, seed [32]byte) (types.ValidatorIndex, error) { 782 length := uint64(len(activeIndices)) 783 if length == 0 { 784 return 0, errors.New("empty active indices list") 785 } 786 maxRandomByte := uint64(1<<8 - 1) 787 hashFunc := hashutil.CustomSHA256Hasher() 788 789 for i := uint64(0); ; i++ { 790 candidateIndex, err := ComputeShuffledIndex(types.ValidatorIndex(i%length), length, seed, true /* shuffle */) 791 if err != nil { 792 return 0, err 793 } 794 candidateIndex = activeIndices[candidateIndex] 795 if uint64(candidateIndex) >= uint64(len(validators)) { 796 return 0, errors.New("active index out of range") 797 } 798 b := append(seed[:], bytesutil.Bytes8(i/32)...) 799 randomByte := hashFunc(b)[i%32] 800 v := validators[candidateIndex] 801 var effectiveBal uint64 802 if v != nil { 803 effectiveBal = v.EffectiveBalance 804 } 805 if effectiveBal*maxRandomByte >= params.BeaconConfig().MaxEffectiveBalance*uint64(randomByte) { 806 return candidateIndex, nil 807 } 808 } 809 }