prysm-pulse/beacon-chain/casper/sharding.go
2018-10-15 06:17:07 -07:00

85 lines
3.3 KiB
Go

package casper
import (
"github.com/ethereum/go-ethereum/common"
"github.com/prysmaticlabs/prysm/beacon-chain/params"
"github.com/prysmaticlabs/prysm/beacon-chain/utils"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
)
// ShuffleValidatorsToCommittees shuffles validator indices and splits them by slot and shard.
func ShuffleValidatorsToCommittees(seed common.Hash, activeValidators []*pb.ValidatorRecord, crosslinkStartShard uint64) ([]*pb.ShardAndCommitteeArray, error) {
indices := ActiveValidatorIndices(activeValidators)
// split the shuffled list for slot.
shuffledValidators, err := utils.ShuffleIndices(seed, indices)
if err != nil {
return nil, err
}
return splitBySlotShard(shuffledValidators, crosslinkStartShard), nil
}
// InitialShardAndCommitteesForSlots initialises the committees for shards by shuffling the validators
// and assigning them to specific shards.
func InitialShardAndCommitteesForSlots(validators []*pb.ValidatorRecord) ([]*pb.ShardAndCommitteeArray, error) {
seed := make([]byte, 0, 32)
committees, err := ShuffleValidatorsToCommittees(common.BytesToHash(seed), validators, 1)
if err != nil {
return nil, err
}
// Starting with 2 cycles (128 slots) with the same committees.
return append(committees, committees...), nil
}
// splitBySlotShard splits the validator list into evenly sized committees and assigns each
// committee to a slot and a shard. If the validator set is large, multiple committees are assigned
// to a single slot and shard. See getCommitteesPerSlot for more details.
func splitBySlotShard(shuffledValidators []uint32, crosslinkStartShard uint64) []*pb.ShardAndCommitteeArray {
committeesPerSlot := getCommitteesPerSlot(len(shuffledValidators))
committeBySlotAndShard := []*pb.ShardAndCommitteeArray{}
// split the validator indices by slot.
validatorsBySlot := utils.SplitIndices(shuffledValidators, int(params.GetConfig().CycleLength))
for i, validatorsForSlot := range validatorsBySlot {
shardCommittees := []*pb.ShardAndCommittee{}
validatorsByShard := utils.SplitIndices(validatorsForSlot, committeesPerSlot)
shardStart := int(crosslinkStartShard) + i*committeesPerSlot
for j, validatorsForShard := range validatorsByShard {
shardID := (shardStart + j) % params.GetConfig().ShardCount
shardCommittees = append(shardCommittees, &pb.ShardAndCommittee{
Shard: uint64(shardID),
Committee: validatorsForShard,
})
}
committeBySlotAndShard = append(committeBySlotAndShard, &pb.ShardAndCommitteeArray{
ArrayShardAndCommittee: shardCommittees,
})
}
return committeBySlotAndShard
}
// getCommitteesPerSlot calculates the parameters for ShuffleValidatorsToCommittees.
// The minimum value for committeesPerSlot is 1.
// Otherwise, the value for committeesPerSlot is the smaller of
// numActiveValidators / CycleLength / (MinCommitteeSize*2) + 1 or
// ShardCount / CycleLength.
func getCommitteesPerSlot(numActiveValidators int) int {
cycleLength := int(params.GetConfig().CycleLength)
boundOnValidators := numActiveValidators/cycleLength/int(params.GetConfig().MinCommiteeSize*2) + 1
boundOnShardCount := params.GetConfig().ShardCount / cycleLength
// Ensure that comitteesPerSlot is at least 1.
if boundOnShardCount == 0 {
return 1
} else if boundOnValidators > boundOnShardCount {
return boundOnShardCount
}
return boundOnValidators
}