neo-go/pkg/core/state/validator.go
Roman Khimov c8a248596e core: introduce ValidatorsCount, make a proper count
We were completely lacking ValidatorsCount that is supposed to track the
number of votes with particular count of consensus nodes which in theory can
change the number of active consensus nodes (if it ever to exceed the number
of standby validators), so we were not producing the right count and based on
that not giving the right set of validators.

Fixes #512.
2020-02-13 13:07:34 +03:00

108 lines
3 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"
)
// MaxValidatorsVoted limits the number of validators that one can vote for.
const MaxValidatorsVoted = 1024
// Validator holds the state of a validator.
type Validator struct {
PublicKey *keys.PublicKey
Registered bool
Votes util.Fixed8
}
// ValidatorsCount represents votes with particular number of consensus nodes
// for this number to be changeable by the voting system.
type ValidatorsCount [MaxValidatorsVoted]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)
}
// EncodeBinary encodes ValidatorCount to the given BinWriter.
func (vc *ValidatorsCount) EncodeBinary(w *io.BinWriter) {
for i := range vc {
vc[i].EncodeBinary(w)
}
}
// DecodeBinary decodes ValidatorCount from the given BinReader.
func (vc *ValidatorsCount) DecodeBinary(r *io.BinReader) {
for i := range vc {
vc[i].DecodeBinary(r)
}
}
// GetWeightedAverage returns an average count of validators that's been voted
// for not counting 1/4 of minimum and maximum numbers.
func (vc *ValidatorsCount) GetWeightedAverage() int {
const (
lowerThreshold = 0.25
upperThreshold = 0.75
)
var (
sumWeight, sumValue, overallSum, slidingSum util.Fixed8
slidingRatio float64
)
for i := range vc {
overallSum += vc[i]
}
for i := range vc {
if slidingRatio >= upperThreshold {
break
}
weight := vc[i]
slidingSum += weight
previousRatio := slidingRatio
slidingRatio = slidingSum.FloatValue() / overallSum.FloatValue()
if slidingRatio <= lowerThreshold {
continue
}
if previousRatio < lowerThreshold {
if slidingRatio > upperThreshold {
weight = util.Fixed8FromFloat((upperThreshold - lowerThreshold) * overallSum.FloatValue())
} else {
weight = util.Fixed8FromFloat((slidingRatio - lowerThreshold) * overallSum.FloatValue())
}
} else if slidingRatio > upperThreshold {
weight = util.Fixed8FromFloat((upperThreshold - previousRatio) * overallSum.FloatValue())
}
sumWeight += weight
// Votes with N values get stored with N-1 index, thus +1 here.
sumValue += util.Fixed8(i+1) * weight
}
if sumValue == 0 || sumWeight == 0 {
return 0
}
return int(sumValue / sumWeight)
}