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 := &ethpb.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 := &ethpb.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: &ethpb.Validator{
    67  				WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
    68  			},
    69  			epoch:     0,
    70  			slashable: true,
    71  		},
    72  		{
    73  			name: "before withdrawable, slashable",
    74  			validator: &ethpb.Validator{
    75  				WithdrawableEpoch: 5,
    76  			},
    77  			epoch:     3,
    78  			slashable: true,
    79  		},
    80  		{
    81  			name: "inactive, not slashable",
    82  			validator: &ethpb.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: &ethpb.Validator{
    92  				WithdrawableEpoch: 3,
    93  			},
    94  			epoch:     3,
    95  			slashable: false,
    96  		},
    97  		{
    98  			name: "slashed and withdrawable, not slashable",
    99  			validator: &ethpb.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: &ethpb.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: &ethpb.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: &ethpb.Validator{
   149  				WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
   150  			},
   151  			epoch:     0,
   152  			slashable: true,
   153  		},
   154  		{
   155  			name: "before withdrawable, slashable",
   156  			validator: &ethpb.Validator{
   157  				WithdrawableEpoch: 5,
   158  			},
   159  			epoch:     3,
   160  			slashable: true,
   161  		},
   162  		{
   163  			name: "inactive, not slashable",
   164  			validator: &ethpb.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: &ethpb.Validator{
   174  				WithdrawableEpoch: 3,
   175  			},
   176  			epoch:     3,
   177  			slashable: false,
   178  		},
   179  		{
   180  			name: "slashed and withdrawable, not slashable",
   181  			validator: &ethpb.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: &ethpb.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: &ethpb.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] = &ethpb.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] = &ethpb.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] = &ethpb.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] = &ethpb.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] = &ethpb.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  			&ethpb.Validator{ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch, EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance},
   737  			true},
   738  		{"Incorrect activation eligibility epoch",
   739  			&ethpb.Validator{ActivationEligibilityEpoch: 1, EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance},
   740  			false},
   741  		{"Not enough balance",
   742  			&ethpb.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  			&ethpb.Validator{ActivationEligibilityEpoch: 1, ActivationEpoch: params.BeaconConfig().FarFutureEpoch},
   761  			&pb.BeaconState{FinalizedCheckpoint: &ethpb.Checkpoint{Epoch: 2}},
   762  			true},
   763  		{"Not yet finalized",
   764  			&ethpb.Validator{ActivationEligibilityEpoch: 1, ActivationEpoch: params.BeaconConfig().FarFutureEpoch},
   765  			&pb.BeaconState{FinalizedCheckpoint: &ethpb.Checkpoint{Root: make([]byte, 32)}},
   766  			false},
   767  		{"Incorrect activation epoch",
   768  			&ethpb.Validator{ActivationEligibilityEpoch: 1},
   769  			&pb.BeaconState{FinalizedCheckpoint: &ethpb.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  }