neo-go/pkg/core/state/validator.go
Roman Khimov 807309a0de core/state: only drop unregistered non-voted validators
Simple as that: UnregisteredAndHasNoVotes != !RegisteredAndHasVotes

Registered validators should stay in the DB, we might be in the process of
updating votes for them and that starts with subtraction.
2020-02-13 13:07:34 +03:00

104 lines
2.9 KiB
Go

package state
import (
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
"github.com/CityOfZion/neo-go/pkg/io"
"github.com/CityOfZion/neo-go/pkg/util"
)
// Validator holds the state of a validator.
type Validator struct {
PublicKey *keys.PublicKey
Registered bool
Votes util.Fixed8
}
// RegisteredAndHasVotes returns true or false whether Validator is registered and has votes.
func (vs *Validator) RegisteredAndHasVotes() bool {
return vs.Registered && vs.Votes > util.Fixed8(0)
}
// UnregisteredAndHasNoVotes returns true when Validator is not registered and has no votes.
func (vs *Validator) UnregisteredAndHasNoVotes() bool {
return !vs.Registered && vs.Votes == 0
}
// EncodeBinary encodes Validator to the given BinWriter.
func (vs *Validator) EncodeBinary(bw *io.BinWriter) {
vs.PublicKey.EncodeBinary(bw)
bw.WriteBool(vs.Registered)
vs.Votes.EncodeBinary(bw)
}
// DecodeBinary decodes Validator from the given BinReader.
func (vs *Validator) DecodeBinary(reader *io.BinReader) {
vs.PublicKey = &keys.PublicKey{}
vs.PublicKey.DecodeBinary(reader)
vs.Registered = reader.ReadBool()
vs.Votes.DecodeBinary(reader)
}
// 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 []*Validator) 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 []*Validator) map[*Validator]float64 {
var validatorsWithVotes []*Validator
var amount float64
weightedVotes := make(map[*Validator]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[*Validator]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
}