2019-11-28 16:06:09 +00:00
|
|
|
package state
|
2018-03-21 16:11:04 +00:00
|
|
|
|
|
|
|
import (
|
2019-08-27 13:29:42 +00:00
|
|
|
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
2019-11-11 15:25:28 +00:00
|
|
|
"github.com/CityOfZion/neo-go/pkg/io"
|
2018-03-21 16:11:04 +00:00
|
|
|
"github.com/CityOfZion/neo-go/pkg/util"
|
|
|
|
)
|
|
|
|
|
2019-11-28 16:06:09 +00:00
|
|
|
// Validator holds the state of a validator.
|
|
|
|
type Validator struct {
|
2019-08-27 13:29:42 +00:00
|
|
|
PublicKey *keys.PublicKey
|
2018-03-21 16:11:04 +00:00
|
|
|
Registered bool
|
|
|
|
Votes util.Fixed8
|
|
|
|
}
|
2019-11-11 15:25:28 +00:00
|
|
|
|
|
|
|
// RegisteredAndHasVotes returns true or false whether Validator is registered and has votes.
|
2019-11-28 16:06:09 +00:00
|
|
|
func (vs *Validator) RegisteredAndHasVotes() bool {
|
2019-11-11 15:25:28 +00:00
|
|
|
return vs.Registered && vs.Votes > util.Fixed8(0)
|
|
|
|
}
|
|
|
|
|
2019-11-28 16:06:09 +00:00
|
|
|
// EncodeBinary encodes Validator to the given BinWriter.
|
|
|
|
func (vs *Validator) EncodeBinary(bw *io.BinWriter) {
|
2019-11-11 15:25:28 +00:00
|
|
|
vs.PublicKey.EncodeBinary(bw)
|
2019-12-12 15:52:23 +00:00
|
|
|
bw.WriteBool(vs.Registered)
|
|
|
|
vs.Votes.EncodeBinary(bw)
|
2019-11-11 15:25:28 +00:00
|
|
|
}
|
|
|
|
|
2019-11-28 16:06:09 +00:00
|
|
|
// DecodeBinary decodes Validator from the given BinReader.
|
|
|
|
func (vs *Validator) DecodeBinary(reader *io.BinReader) {
|
2019-11-11 15:25:28 +00:00
|
|
|
vs.PublicKey = &keys.PublicKey{}
|
|
|
|
vs.PublicKey.DecodeBinary(reader)
|
2019-12-12 15:52:23 +00:00
|
|
|
vs.Registered = reader.ReadBool()
|
|
|
|
vs.Votes.DecodeBinary(reader)
|
2019-11-11 15:25:28 +00:00
|
|
|
}
|
2019-11-18 14:59:43 +00:00
|
|
|
|
|
|
|
// 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.
|
2019-11-28 16:06:09 +00:00
|
|
|
func GetValidatorsWeightedAverage(validators []*Validator) int {
|
2019-11-18 14:59:43 +00:00
|
|
|
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
|
2019-11-28 16:06:09 +00:00
|
|
|
func applyWeightedFilter(validators []*Validator) map[*Validator]float64 {
|
|
|
|
var validatorsWithVotes []*Validator
|
2019-11-18 14:59:43 +00:00
|
|
|
var amount float64
|
|
|
|
|
2019-11-28 16:06:09 +00:00
|
|
|
weightedVotes := make(map[*Validator]float64)
|
2019-11-18 14:59:43 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2019-11-28 16:06:09 +00:00
|
|
|
func weightedAverage(weightedVotes map[*Validator]float64) float64 {
|
2019-11-18 14:59:43 +00:00
|
|
|
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
|
|
|
|
}
|