diff --git a/beacon-chain/core/altair/sync_committee.go b/beacon-chain/core/altair/sync_committee.go index 9bfcd8434c6e..6ae80a370db4 100644 --- a/beacon-chain/core/altair/sync_committee.go +++ b/beacon-chain/core/altair/sync_committee.go @@ -18,6 +18,7 @@ import ( "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" "github.com/prysmaticlabs/prysm/v5/math" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v5/runtime/version" "github.com/prysmaticlabs/prysm/v5/time/slots" ) @@ -85,26 +86,27 @@ func NextSyncCommittee(ctx context.Context, s state.BeaconState) (*ethpb.SyncCom // Spec code: // def get_next_sync_committee_indices(state: BeaconState) -> Sequence[ValidatorIndex]: // -// """ -// Return the sync committee indices, with possible duplicates, for the next sync committee. -// """ -// epoch = Epoch(get_current_epoch(state) + 1) +// """ +// Return the sync committee indices, with possible duplicates, for the next sync committee. +// """ +// epoch = Epoch(get_current_epoch(state) + 1) // -// MAX_RANDOM_BYTE = 2**8 - 1 -// active_validator_indices = get_active_validator_indices(state, epoch) -// active_validator_count = uint64(len(active_validator_indices)) -// seed = get_seed(state, epoch, DOMAIN_SYNC_COMMITTEE) -// i = 0 -// sync_committee_indices: List[ValidatorIndex] = [] -// while len(sync_committee_indices) < SYNC_COMMITTEE_SIZE: -// shuffled_index = compute_shuffled_index(uint64(i % active_validator_count), active_validator_count, seed) -// candidate_index = active_validator_indices[shuffled_index] -// random_byte = hash(seed + uint_to_bytes(uint64(i // 32)))[i % 32] -// effective_balance = state.validators[candidate_index].effective_balance -// if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte: -// sync_committee_indices.append(candidate_index) -// i += 1 -// return sync_committee_indices +// MAX_RANDOM_BYTE = 2**8 - 1 +// active_validator_indices = get_active_validator_indices(state, epoch) +// active_validator_count = uint64(len(active_validator_indices)) +// seed = get_seed(state, epoch, DOMAIN_SYNC_COMMITTEE) +// i = 0 +// sync_committee_indices: List[ValidatorIndex] = [] +// while len(sync_committee_indices) < SYNC_COMMITTEE_SIZE: +// shuffled_index = compute_shuffled_index(uint64(i % active_validator_count), active_validator_count, seed) +// candidate_index = active_validator_indices[shuffled_index] +// random_byte = hash(seed + uint_to_bytes(uint64(i // 32)))[i % 32] +// effective_balance = state.validators[candidate_index].effective_balance +// # [Modified in Electra:EIP7251] +// if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE_ELECTRA * random_byte: +// sync_committee_indices.append(candidate_index) +// i += 1 +// return sync_committee_indices func NextSyncCommitteeIndices(ctx context.Context, s state.BeaconState) ([]primitives.ValidatorIndex, error) { epoch := coreTime.NextEpoch(s) indices, err := helpers.ActiveValidatorIndices(ctx, s, epoch) @@ -121,6 +123,11 @@ func NextSyncCommitteeIndices(ctx context.Context, s state.BeaconState) ([]primi cIndices := make([]primitives.ValidatorIndex, 0, syncCommitteeSize) hashFunc := hash.CustomSHA256Hasher() + maxEB := cfg.MaxEffectiveBalanceElectra + if s.Version() < version.Electra { + maxEB = cfg.MaxEffectiveBalance + } + for i := primitives.ValidatorIndex(0); uint64(len(cIndices)) < params.BeaconConfig().SyncCommitteeSize; i++ { if ctx.Err() != nil { return nil, ctx.Err() @@ -140,7 +147,7 @@ func NextSyncCommitteeIndices(ctx context.Context, s state.BeaconState) ([]primi } effectiveBal := v.EffectiveBalance() - if effectiveBal*maxRandomByte >= cfg.MaxEffectiveBalance*uint64(randomByte) { + if effectiveBal*maxRandomByte >= maxEB*uint64(randomByte) { cIndices = append(cIndices, cIndex) } } diff --git a/beacon-chain/core/altair/sync_committee_test.go b/beacon-chain/core/altair/sync_committee_test.go index c9406d876a8e..9e38b0065440 100644 --- a/beacon-chain/core/altair/sync_committee_test.go +++ b/beacon-chain/core/altair/sync_committee_test.go @@ -13,13 +13,14 @@ import ( "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/crypto/bls" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v5/runtime/version" "github.com/prysmaticlabs/prysm/v5/testing/assert" "github.com/prysmaticlabs/prysm/v5/testing/require" prysmTime "github.com/prysmaticlabs/prysm/v5/time" ) func TestSyncCommitteeIndices_CanGet(t *testing.T) { - getState := func(t *testing.T, count uint64) state.BeaconState { + getState := func(t *testing.T, count uint64, vers int) state.BeaconState { validators := make([]*ethpb.Validator, count) for i := 0; i < len(validators); i++ { validators[i] = ðpb.Validator{ @@ -27,17 +28,28 @@ func TestSyncCommitteeIndices_CanGet(t *testing.T) { EffectiveBalance: params.BeaconConfig().MinDepositAmount, } } - st, err := state_native.InitializeFromProtoAltair(ðpb.BeaconStateAltair{ - RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), - }) + var st state.BeaconState + var err error + switch vers { + case version.Altair: + st, err = state_native.InitializeFromProtoAltair(ðpb.BeaconStateAltair{ + RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), + }) + case version.Electra: + st, err = state_native.InitializeFromProtoElectra(ðpb.BeaconStateElectra{ + RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), + }) + default: + t.Fatal("Unknown version") + } require.NoError(t, err) require.NoError(t, st.SetValidators(validators)) return st } type args struct { - state state.BeaconState - epoch primitives.Epoch + validatorCount uint64 + epoch primitives.Epoch } tests := []struct { name string @@ -48,32 +60,32 @@ func TestSyncCommitteeIndices_CanGet(t *testing.T) { { name: "genesis validator count, epoch 0", args: args{ - state: getState(t, params.BeaconConfig().MinGenesisActiveValidatorCount), - epoch: 0, + validatorCount: params.BeaconConfig().MinGenesisActiveValidatorCount, + epoch: 0, }, wantErr: false, }, { name: "genesis validator count, epoch 100", args: args{ - state: getState(t, params.BeaconConfig().MinGenesisActiveValidatorCount), - epoch: 100, + validatorCount: params.BeaconConfig().MinGenesisActiveValidatorCount, + epoch: 100, }, wantErr: false, }, { name: "less than optimal validator count, epoch 100", args: args{ - state: getState(t, params.BeaconConfig().MaxValidatorsPerCommittee), - epoch: 100, + validatorCount: params.BeaconConfig().MaxValidatorsPerCommittee, + epoch: 100, }, wantErr: false, }, { name: "no active validators, epoch 100", args: args{ - state: getState(t, 0), // Regression test for divide by zero. Issue #13051. - epoch: 100, + validatorCount: 0, // Regression test for divide by zero. Issue #13051. + epoch: 100, }, wantErr: true, errString: "no active validator indices", @@ -81,13 +93,18 @@ func TestSyncCommitteeIndices_CanGet(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - helpers.ClearCache() - got, err := altair.NextSyncCommitteeIndices(context.Background(), tt.args.state) - if tt.wantErr { - require.ErrorContains(t, tt.errString, err) - } else { - require.NoError(t, err) - require.Equal(t, int(params.BeaconConfig().SyncCommitteeSize), len(got)) + for _, v := range []int{version.Altair, version.Electra} { + t.Run(version.String(v), func(t *testing.T) { + helpers.ClearCache() + st := getState(t, tt.args.validatorCount, v) + got, err := altair.NextSyncCommitteeIndices(context.Background(), st) + if tt.wantErr { + require.ErrorContains(t, tt.errString, err) + } else { + require.NoError(t, err) + require.Equal(t, int(params.BeaconConfig().SyncCommitteeSize), len(got)) + } + }) } }) }