Implement Validator Cutoff Algorithm (#339)

This commit is contained in:
terence tsao
2018-07-29 10:03:03 -07:00
committed by GitHub
parent d7a88dffa4
commit 1cb300565d
3 changed files with 119 additions and 15 deletions

View File

@@ -129,7 +129,7 @@ func (b *BeaconChain) CanProcessBlock(fetcher types.POWBlockFetcher, block *type
// TODO: check if the parentHash pointed by the beacon block is in the beaconDB.
// Calculate the timestamp validity condition.
slotDuration := time.Duration(block.SlotNumber()*params.SlotLength) * time.Second
slotDuration := time.Duration(block.SlotNumber()*params.SlotDuration) * time.Second
genesis, err := b.GenesisBlock()
if err != nil {
return false, err
@@ -242,6 +242,16 @@ func (b *BeaconChain) computeNewActiveState(seed common.Hash) (*types.ActiveStat
}, nil
}
// getAttestersProposer returns lists of random sampled attesters and proposer indices.
func (b *BeaconChain) getAttestersProposer(seed common.Hash) ([]int, int, error) {
attesterCount := math.Min(params.AttesterCount, float64(len(b.CrystallizedState().ActiveValidators)))
indices, err := utils.ShuffleIndices(seed, len(b.CrystallizedState().ActiveValidators))
if err != nil {
return nil, -1, err
}
return indices[:int(attesterCount)], indices[len(indices)-1], nil
}
// hashActiveState serializes the active state object then uses
// blake2b to hash the serialized object.
func hashActiveState(state *types.ActiveState) ([32]byte, error) {
@@ -262,14 +272,50 @@ func hashCrystallizedState(state *types.CrystallizedState) ([32]byte, error) {
return blake2b.Sum256(serializedState), nil
}
// getAttestersProposer returns lists of random sampled attesters and proposer indices.
func (b *BeaconChain) getAttestersProposer(seed common.Hash) ([]int, int, error) {
attesterCount := math.Min(params.AttesterCount, float64(len(b.CrystallizedState().ActiveValidators)))
indices, err := utils.ShuffleIndices(seed, len(b.CrystallizedState().ActiveValidators))
if err != nil {
return nil, -1, err
// GetCutoffs is used to split up validators into groups at the start
// of every epoch. It determines at what height validators can make
// attestations and crosslinks. It returns lists of cutoff indices.
func GetCutoffs(validatorCount int) []int {
var heightCutoff = []int{0}
var heights []int
var heightCount float64
// Skip heights if there's not enough validators to fill in a min sized committee.
if validatorCount < params.EpochLength*params.MinCommiteeSize {
heightCount = math.Floor(float64(validatorCount) / params.MinCommiteeSize)
for i := 0; i < int(heightCount); i++ {
heights = append(heights, (i*params.Cofactor)%params.EpochLength)
}
// Enough validators, fill in all the heights.
} else {
heightCount = params.EpochLength
for i := 0; i < int(heightCount); i++ {
heights = append(heights, i)
}
}
return indices[:int(attesterCount)], indices[len(indices)-1], nil
filled := 0
appendHeight := false
for i := 0; i < params.EpochLength-1; i++ {
appendHeight = false
for _, height := range heights {
if i == height {
appendHeight = true
}
}
if appendHeight {
filled++
heightCutoff = append(heightCutoff, filled*validatorCount/int(heightCount))
} else {
heightCutoff = append(heightCutoff, heightCutoff[len(heightCutoff)-1])
}
}
heightCutoff = append(heightCutoff, validatorCount)
// TODO: For the validators assigned to each height, split them up into
// committees for different shards. Do not assign the last END_EPOCH_GRACE_PERIOD
// heights in a epoch to any shards.
return heightCutoff
}
// hasVoted checks if the attester has voted by looking at the bitfield.

View File

@@ -306,6 +306,50 @@ func TestRotateValidatorSet(t *testing.T) {
}
}
func TestCutOffValidatorSet(t *testing.T) {
// Test scenario #1: Assume there's enough validators to fill in all the heights.
validatorCount := params.EpochLength * params.MinCommiteeSize
cutoffsValidators := GetCutoffs(validatorCount)
// The length of cutoff list should be 65. Since there is 64 heights per epoch,
// it means during every height, a new set of 128 validators will form a committee.
expectedCount := int(math.Ceil(float64(validatorCount)/params.MinCommiteeSize)) + 1
if len(cutoffsValidators) != expectedCount {
t.Errorf("Incorrect count for cutoffs validator. Wanted: %v, Got: %v", expectedCount, len(cutoffsValidators))
}
// Verify each cutoff is an increment of MinCommiteeSize, it means 128 validators forms a
// a committee and get to attest per height.
count := 0
for _, cutoff := range cutoffsValidators {
if cutoff != count {
t.Errorf("cutoffsValidators did not get 128 increment. Wanted: count, Got: %v", cutoff)
}
count += params.MinCommiteeSize
}
// Test scenario #2: Assume there's not enough validators to fill in all the heights.
validatorCount = 1000
cutoffsValidators = unique(GetCutoffs(validatorCount))
// With 1000 validators, we can't attest every height. Given min committee size is 128,
// we can only attest 7 heights. round down 1000 / 128 equals to 7, means the length is 8.
expectedCount = int(math.Ceil(float64(validatorCount) / params.MinCommiteeSize))
if len(unique(cutoffsValidators)) != expectedCount {
t.Errorf("Incorrect count for cutoffs validator. Wanted: %v, Got: %v", expectedCount, validatorCount/params.MinCommiteeSize)
}
// Verify each cutoff is an increment of 142~143 (1000 / 7).
count = 0
for _, cutoff := range cutoffsValidators {
num := count * validatorCount / (len(cutoffsValidators) - 1)
if cutoff != num {
t.Errorf("cutoffsValidators did not get correct increment. Wanted: %v, Got: %v", num, cutoff)
}
count++
}
}
func TestIsEpochTransition(t *testing.T) {
beaconChain, db := startInMemoryBeaconChain(t)
defer db.Stop()
@@ -652,3 +696,17 @@ func TestComputeValidatorRewardsAndPenalties(t *testing.T) {
t.Fatalf("attester bitfields are not zeroed out: %v", beaconChain.state.ActiveState.AttesterBitfields)
}
}
// helper function to remove duplicates in a int slice.
func unique(ints []int) []int {
keys := make(map[int]bool)
list := []int{}
for _, int := range ints {
if _, value := keys[int]; !value {
keys[int] = true
list = append(list, int)
}
}
return list
}

View File

@@ -8,15 +8,15 @@ const (
// EpochLength is the beacon chain epoch length in slots.
EpochLength = 64
// ShardCount is a fixed number.
ShardCount = 20
ShardCount = 1024
// DefaultBalance of a validator.
DefaultBalance = 32000
// DefaultSwitchDynasty value.
DefaultSwitchDynasty = 9999999999999999999
// MaxValidators in the protocol.
MaxValidators = 4194304
// NotariesPerCrosslink fixed to 100.
NotariesPerCrosslink = 100
// SlotLength in seconds.
SlotLength = 8
// SlotDuration in seconds.
SlotDuration = 8
// Cofactor is used cutoff algorithm to select height and shard cutoffs.
Cofactor = 19
// MinCommiteeSize is the minimal number of validator needs to be in a committee.
MinCommiteeSize = 128
)