mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-05-02 03:02:54 -04:00
Core: sync committee helpers (#9269)
* Add sync committee helpers * Create BUILD.bazel * Update BUILD.bazel * Update BUILD.bazel * Fix visibility * Preston's review
This commit is contained in:
@@ -2,7 +2,10 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["deposit.go"],
|
||||
srcs = [
|
||||
"deposit.go",
|
||||
"sync_committee.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/altair",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
@@ -10,9 +13,13 @@ go_library(
|
||||
],
|
||||
deps = [
|
||||
"//beacon-chain/core/blocks:go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//shared/bls:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/mathutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
@@ -24,6 +31,7 @@ go_test(
|
||||
srcs = [
|
||||
"deposit_fuzz_test.go",
|
||||
"deposit_test.go",
|
||||
"sync_committee_test.go",
|
||||
],
|
||||
deps = [
|
||||
":go_default_library",
|
||||
@@ -37,5 +45,6 @@ go_test(
|
||||
"//shared/testutil/require:go_default_library",
|
||||
"//shared/trieutil:go_default_library",
|
||||
"@com_github_google_gofuzz//:go_default_library",
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
181
beacon-chain/core/altair/sync_committee.go
Normal file
181
beacon-chain/core/altair/sync_committee.go
Normal file
@@ -0,0 +1,181 @@
|
||||
package altair
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/mathutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
const maxRandomByte = uint64(1<<8 - 1)
|
||||
|
||||
// ValidateNilSyncContribution validates the following fields are not nil:
|
||||
// -the contribution and proof itself
|
||||
// -the message within contribution and proof
|
||||
// -the contribution within contribution and proof
|
||||
// -the aggregation bits within contribution
|
||||
func ValidateNilSyncContribution(s *ethpb.SignedContributionAndProof) error {
|
||||
if s == nil {
|
||||
return errors.New("signed message can't be nil")
|
||||
}
|
||||
if s.Message == nil {
|
||||
return errors.New("signed contribution's message can't be nil")
|
||||
}
|
||||
if s.Message.Contribution == nil {
|
||||
return errors.New("inner contribution can't be nil")
|
||||
}
|
||||
if s.Message.Contribution.AggregationBits == nil {
|
||||
return errors.New("contribution's bitfield can't be nil")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NextSyncCommittee returns the next sync committee for a given state.
|
||||
//
|
||||
// Spec code:
|
||||
// def get_next_sync_committee(state: BeaconState) -> SyncCommittee:
|
||||
// """
|
||||
// Return the next sync committee, with possible pubkey duplicates.
|
||||
// """
|
||||
// indices = get_next_sync_committee_indices(state)
|
||||
// pubkeys = [state.validators[index].pubkey for index in indices]
|
||||
// aggregate_pubkey = bls.AggregatePKs(pubkeys)
|
||||
// return SyncCommittee(pubkeys=pubkeys, aggregate_pubkey=aggregate_pubkey)
|
||||
func NextSyncCommittee(ctx context.Context, s state.BeaconStateAltair) (*ethpb.SyncCommittee, error) {
|
||||
indices, err := NextSyncCommitteeIndices(ctx, s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pubkeys := make([][]byte, len(indices))
|
||||
for i, index := range indices {
|
||||
p := s.PubkeyAtIndex(index)
|
||||
pubkeys[i] = p[:]
|
||||
}
|
||||
aggregated, err := bls.AggregatePublicKeys(pubkeys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ðpb.SyncCommittee{
|
||||
Pubkeys: pubkeys,
|
||||
AggregatePubkey: aggregated.Marshal(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NextSyncCommitteeIndices returns the next sync committee indices for a given state.
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// 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
|
||||
func NextSyncCommitteeIndices(ctx context.Context, s state.BeaconStateAltair) ([]types.ValidatorIndex, error) {
|
||||
epoch := helpers.NextEpoch(s)
|
||||
indices, err := helpers.ActiveValidatorIndices(s, epoch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
seed, err := helpers.Seed(s, epoch, params.BeaconConfig().DomainSyncCommittee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
count := uint64(len(indices))
|
||||
cfg := params.BeaconConfig()
|
||||
syncCommitteeSize := cfg.SyncCommitteeSize
|
||||
cIndices := make([]types.ValidatorIndex, 0, syncCommitteeSize)
|
||||
hashFunc := hashutil.CustomSHA256Hasher()
|
||||
|
||||
for i := types.ValidatorIndex(0); uint64(len(cIndices)) < params.BeaconConfig().SyncCommitteeSize; i++ {
|
||||
if ctx.Err() != nil {
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
|
||||
sIndex, err := helpers.ComputeShuffledIndex(i.Mod(count), count, seed, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b := append(seed[:], bytesutil.Bytes8(uint64(i.Div(32)))...)
|
||||
randomByte := hashFunc(b)[i%32]
|
||||
cIndex := indices[sIndex]
|
||||
v, err := s.ValidatorAtIndexReadOnly(cIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
effectiveBal := v.EffectiveBalance()
|
||||
if effectiveBal*maxRandomByte >= cfg.MaxEffectiveBalance*uint64(randomByte) {
|
||||
cIndices = append(cIndices, cIndex)
|
||||
}
|
||||
}
|
||||
|
||||
return cIndices, nil
|
||||
}
|
||||
|
||||
// SyncSubCommitteePubkeys returns the pubkeys participating in a sync subcommittee.
|
||||
//
|
||||
// def get_sync_subcommittee_pubkeys(state: BeaconState, subcommittee_index: uint64) -> Sequence[BLSPubkey]:
|
||||
// # Committees assigned to `slot` sign for `slot - 1`
|
||||
// # This creates the exceptional logic below when transitioning between sync committee periods
|
||||
// next_slot_epoch = compute_epoch_at_slot(Slot(state.slot + 1))
|
||||
// if compute_sync_committee_period(get_current_epoch(state)) == compute_sync_committee_period(next_slot_epoch):
|
||||
// sync_committee = state.current_sync_committee
|
||||
// else:
|
||||
// sync_committee = state.next_sync_committee
|
||||
//
|
||||
// # Return pubkeys for the subcommittee index
|
||||
// sync_subcommittee_size = SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT
|
||||
// i = subcommittee_index * sync_subcommittee_size
|
||||
// return sync_committee.pubkeys[i:i + sync_subcommittee_size]
|
||||
func SyncSubCommitteePubkeys(syncCommittee *ethpb.SyncCommittee, subComIdx types.CommitteeIndex) ([][]byte, error) {
|
||||
cfg := params.BeaconConfig()
|
||||
subCommSize := cfg.SyncCommitteeSize / cfg.SyncCommitteeSubnetCount
|
||||
i := uint64(subComIdx) * subCommSize
|
||||
endOfSubCom := i + subCommSize
|
||||
pubkeyLen := uint64(len(syncCommittee.Pubkeys))
|
||||
if endOfSubCom > pubkeyLen {
|
||||
return nil, errors.Errorf("end index is larger than array length: %d > %d", endOfSubCom, pubkeyLen)
|
||||
}
|
||||
return syncCommittee.Pubkeys[i:endOfSubCom], nil
|
||||
}
|
||||
|
||||
// IsSyncCommitteeAggregator checks whether the provided signature is for a valid
|
||||
// aggregator.
|
||||
//
|
||||
// def is_sync_committee_aggregator(signature: BLSSignature) -> bool:
|
||||
// modulo = max(1, SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT // TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE)
|
||||
// return bytes_to_uint64(hash(signature)[0:8]) % modulo == 0
|
||||
func IsSyncCommitteeAggregator(sig []byte) (bool, error) {
|
||||
if len(sig) != params.BeaconConfig().BLSPubkeyLength {
|
||||
return false, errors.New("incorrect sig length")
|
||||
}
|
||||
|
||||
cfg := params.BeaconConfig()
|
||||
modulo := mathutil.Max(1, cfg.SyncCommitteeSize/cfg.SyncCommitteeSubnetCount/cfg.TargetAggregatorsPerSyncSubcommittee)
|
||||
hashedSig := hashutil.Hash(sig)
|
||||
return bytesutil.FromBytes8(hashedSig[:8])%modulo == 0, nil
|
||||
}
|
||||
298
beacon-chain/core/altair/sync_committee_test.go
Normal file
298
beacon-chain/core/altair/sync_committee_test.go
Normal file
@@ -0,0 +1,298 @@
|
||||
package altair_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
types "github.com/prysmaticlabs/eth2-types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/altair"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
stateAltair "github.com/prysmaticlabs/prysm/beacon-chain/state/v2"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bls"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
|
||||
func TestSyncCommitteeIndices_CanGet(t *testing.T) {
|
||||
getState := func(t *testing.T, count uint64) *stateAltair.BeaconState {
|
||||
validators := make([]*ethpb.Validator, count)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = ðpb.Validator{
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
EffectiveBalance: params.BeaconConfig().MinDepositAmount,
|
||||
}
|
||||
}
|
||||
state, err := stateAltair.InitializeFromProto(ðpb.BeaconStateAltair{
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return state
|
||||
}
|
||||
|
||||
type args struct {
|
||||
state *stateAltair.BeaconState
|
||||
epoch types.Epoch
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
errString string
|
||||
}{
|
||||
{
|
||||
name: "nil state",
|
||||
args: args{
|
||||
state: nil,
|
||||
},
|
||||
wantErr: true,
|
||||
errString: "nil inner state",
|
||||
},
|
||||
{
|
||||
name: "genesis validator count, epoch 0",
|
||||
args: args{
|
||||
state: getState(t, params.BeaconConfig().MinGenesisActiveValidatorCount),
|
||||
epoch: 0,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "genesis validator count, epoch 100",
|
||||
args: args{
|
||||
state: getState(t, 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,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
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))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncCommitteeIndices_DifferentPeriods(t *testing.T) {
|
||||
helpers.ClearCache()
|
||||
getState := func(t *testing.T, count uint64) *stateAltair.BeaconState {
|
||||
validators := make([]*ethpb.Validator, count)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
validators[i] = ðpb.Validator{
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
EffectiveBalance: params.BeaconConfig().MinDepositAmount,
|
||||
}
|
||||
}
|
||||
state, err := stateAltair.InitializeFromProto(ðpb.BeaconStateAltair{
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return state
|
||||
}
|
||||
|
||||
state := getState(t, params.BeaconConfig().MaxValidatorsPerCommittee)
|
||||
got1, err := altair.NextSyncCommitteeIndices(context.Background(), state)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, state.SetSlot(params.BeaconConfig().SlotsPerEpoch))
|
||||
got2, err := altair.NextSyncCommitteeIndices(context.Background(), state)
|
||||
require.NoError(t, err)
|
||||
require.DeepNotEqual(t, got1, got2)
|
||||
require.NoError(t, state.SetSlot(params.BeaconConfig().SlotsPerEpoch*types.Slot(params.BeaconConfig().EpochsPerSyncCommitteePeriod)))
|
||||
got2, err = altair.NextSyncCommitteeIndices(context.Background(), state)
|
||||
require.NoError(t, err)
|
||||
require.DeepNotEqual(t, got1, got2)
|
||||
require.NoError(t, state.SetSlot(params.BeaconConfig().SlotsPerEpoch*types.Slot(2*params.BeaconConfig().EpochsPerSyncCommitteePeriod)))
|
||||
got2, err = altair.NextSyncCommitteeIndices(context.Background(), state)
|
||||
require.NoError(t, err)
|
||||
require.DeepNotEqual(t, got1, got2)
|
||||
}
|
||||
|
||||
func TestSyncCommittee_CanGet(t *testing.T) {
|
||||
getState := func(t *testing.T, count uint64) *stateAltair.BeaconState {
|
||||
validators := make([]*ethpb.Validator, count)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
blsKey, err := bls.RandKey()
|
||||
require.NoError(t, err)
|
||||
validators[i] = ðpb.Validator{
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
EffectiveBalance: params.BeaconConfig().MinDepositAmount,
|
||||
PublicKey: blsKey.PublicKey().Marshal(),
|
||||
}
|
||||
}
|
||||
state, err := stateAltair.InitializeFromProto(ðpb.BeaconStateAltair{
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return state
|
||||
}
|
||||
|
||||
type args struct {
|
||||
state *stateAltair.BeaconState
|
||||
epoch types.Epoch
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
errString string
|
||||
}{
|
||||
{
|
||||
name: "nil state",
|
||||
args: args{
|
||||
state: nil,
|
||||
},
|
||||
wantErr: true,
|
||||
errString: "nil inner state",
|
||||
},
|
||||
{
|
||||
name: "genesis validator count, epoch 0",
|
||||
args: args{
|
||||
state: getState(t, params.BeaconConfig().MinGenesisActiveValidatorCount),
|
||||
epoch: 0,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "genesis validator count, epoch 100",
|
||||
args: args{
|
||||
state: getState(t, 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,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
helpers.ClearCache()
|
||||
if !tt.wantErr {
|
||||
require.NoError(t, tt.args.state.SetSlot(types.Slot(tt.args.epoch)*params.BeaconConfig().SlotsPerEpoch))
|
||||
}
|
||||
got, err := altair.NextSyncCommittee(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.Pubkeys))
|
||||
require.Equal(t, params.BeaconConfig().BLSPubkeyLength, len(got.AggregatePubkey))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateNilSyncContribution(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
s *ethpb.SignedContributionAndProof
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "nil object",
|
||||
s: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "nil message",
|
||||
s: ðpb.SignedContributionAndProof{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "nil contribution",
|
||||
s: ðpb.SignedContributionAndProof{Message: ðpb.ContributionAndProof{}},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "nil bitfield",
|
||||
s: ðpb.SignedContributionAndProof{
|
||||
Message: ðpb.ContributionAndProof{
|
||||
Contribution: ðpb.SyncCommitteeContribution{},
|
||||
}},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "non nil sync contribution",
|
||||
s: ðpb.SignedContributionAndProof{
|
||||
Message: ðpb.ContributionAndProof{
|
||||
Contribution: ðpb.SyncCommitteeContribution{
|
||||
AggregationBits: []byte{},
|
||||
},
|
||||
}},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := altair.ValidateNilSyncContribution(tt.s); (err != nil) != tt.wantErr {
|
||||
t.Errorf("ValidateNilSyncContribution() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncSubCommitteePubkeys_CanGet(t *testing.T) {
|
||||
helpers.ClearCache()
|
||||
state := getState(t, params.BeaconConfig().MaxValidatorsPerCommittee)
|
||||
com, err := altair.NextSyncCommittee(context.Background(), state)
|
||||
require.NoError(t, err)
|
||||
sub, err := altair.SyncSubCommitteePubkeys(com, 0)
|
||||
require.NoError(t, err)
|
||||
subCommSize := params.BeaconConfig().SyncCommitteeSize / params.BeaconConfig().SyncCommitteeSubnetCount
|
||||
require.Equal(t, int(subCommSize), len(sub))
|
||||
require.DeepSSZEqual(t, com.Pubkeys[0:subCommSize], sub)
|
||||
|
||||
sub, err = altair.SyncSubCommitteePubkeys(com, 1)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, com.Pubkeys[subCommSize:2*subCommSize], sub)
|
||||
|
||||
sub, err = altair.SyncSubCommitteePubkeys(com, 2)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, com.Pubkeys[2*subCommSize:3*subCommSize], sub)
|
||||
|
||||
sub, err = altair.SyncSubCommitteePubkeys(com, 3)
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, com.Pubkeys[3*subCommSize:], sub)
|
||||
|
||||
}
|
||||
|
||||
func getState(t *testing.T, count uint64) *stateAltair.BeaconState {
|
||||
validators := make([]*ethpb.Validator, count)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
blsKey, err := bls.RandKey()
|
||||
require.NoError(t, err)
|
||||
validators[i] = ðpb.Validator{
|
||||
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
||||
EffectiveBalance: params.BeaconConfig().MinDepositAmount,
|
||||
PublicKey: blsKey.PublicKey().Marshal(),
|
||||
}
|
||||
}
|
||||
state, err := stateAltair.InitializeFromProto(ðpb.BeaconStateAltair{
|
||||
Validators: validators,
|
||||
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return state
|
||||
}
|
||||
Reference in New Issue
Block a user