core: add weighted function for validators

This commit is contained in:
Vsevolod Brekelov 2019-11-18 17:59:43 +03:00
parent 672f314be0
commit 419b031e96
2 changed files with 67 additions and 17 deletions

View file

@ -1289,23 +1289,8 @@ func (bc *Blockchain) GetValidators(txes... *transaction.Transaction) ([]*keys.P
} }
validators := getValidatorsFromStore(chainState.store) validators := getValidatorsFromStore(chainState.store)
for _, validator := range validators {
if validator.Votes > util.Fixed8(0) { count := GetValidatorsWeightedAverage(validators)
// Select count of votes with strange logic:
// int count = (int)snapshot.ValidatorsCount.Get().Votes.Select((p, i) => new
// {
// Count = i,
// Votes = p
// }).Where(p => p.Votes > Fixed8.Zero).ToArray().WeightedFilter(0.25, 0.75, p => p.Votes.GetData(), (p, w) => new
// {
// p.Count,
// Weight = w
// }).WeightedAverage(p => p.Count, p => p.Weight);
// count = Math.Max(count, Blockchain.StandbyValidators.Length);
}
}
var count int
count = 0
standByValidators, err := bc.GetStandByValidators() standByValidators, err := bc.GetStandByValidators()
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -107,3 +107,68 @@ func (vs *ValidatorState) DecodeBinary(reader *io.BinReader) {
reader.ReadLE(&vs.Registered) reader.ReadLE(&vs.Registered)
reader.ReadLE(&vs.Votes) reader.ReadLE(&vs.Votes)
} }
// GetValidatorsWeightedAverage applies weighted filter based on votes for validator and returns number of validators.
// Get back to it with further investigation in https://github.com/nspcc-dev/neo-go/issues/512.
func GetValidatorsWeightedAverage(validators []*ValidatorState) int {
return int(weightedAverage(applyWeightedFilter(validators)))
}
// applyWeightedFilter is an implementation of the filter for validators votes.
// C# reference https://github.com/neo-project/neo/blob/41caff115c28d6c7665b2a7ac72967e7ce82e921/neo/Helper.cs#L273
func applyWeightedFilter(validators []*ValidatorState) map[*ValidatorState]float64 {
var validatorsWithVotes []*ValidatorState
var amount float64
weightedVotes := make(map[*ValidatorState]float64)
start := 0.25
end := 0.75
sum := float64(0)
current := float64(0)
for _, validator := range validators {
if validator.Votes > util.Fixed8(0) {
validatorsWithVotes = append(validatorsWithVotes, validator)
amount += validator.Votes.FloatValue()
}
}
for _, validator := range validatorsWithVotes {
if current >= end {
break
}
weight := validator.Votes.FloatValue()
sum += weight
old := current
current = sum / amount
if current <= start {
continue
}
if old < start {
if current > end {
weight = (end - start) * amount
} else {
weight = (current - start) * amount
}
} else if current > end {
weight = (end - old) * amount
}
weightedVotes[validator] = weight
}
return weightedVotes
}
func weightedAverage(weightedVotes map[*ValidatorState]float64) float64 {
sumWeight := float64(0)
sumValue := float64(0)
for vState, weight := range weightedVotes {
sumWeight += weight
sumValue += vState.Votes.FloatValue() * weight
}
if sumValue == 0 || sumWeight == 0 {
return 0
}
return sumValue / sumWeight
}