neoneo-go/pkg/core/native/validators_count.go
2020-06-08 13:29:35 +03:00

113 lines
2.8 KiB
Go

package native
import (
"errors"
"math/big"
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
"github.com/nspcc-dev/neo-go/pkg/io"
)
// MaxValidatorsVoted limits the number of validators that one can vote for.
const MaxValidatorsVoted = 1024
// ValidatorsCount represents votes with particular number of consensus nodes
// for this number to be changeable by the voting system.
type ValidatorsCount [MaxValidatorsVoted]big.Int
// ValidatorsCountFromBytes converts serialized ValidatorsCount to structure.
func ValidatorsCountFromBytes(b []byte) (*ValidatorsCount, error) {
vc := new(ValidatorsCount)
if len(b) == 0 {
return vc, nil
}
r := io.NewBinReaderFromBuf(b)
vc.DecodeBinary(r)
if r.Err != nil {
return nil, r.Err
}
return vc, nil
}
// Bytes returns serialized ValidatorsCount.
func (vc *ValidatorsCount) Bytes() []byte {
w := io.NewBufBinWriter()
vc.EncodeBinary(w.BinWriter)
if w.Err != nil {
panic(w.Err)
}
return w.Bytes()
}
// EncodeBinary implements io.Serializable interface.
func (vc *ValidatorsCount) EncodeBinary(w *io.BinWriter) {
w.WriteVarUint(uint64(MaxValidatorsVoted))
for i := range vc {
w.WriteVarBytes(bigint.ToBytes(&vc[i]))
}
}
// DecodeBinary implements io.Serializable interface.
func (vc *ValidatorsCount) DecodeBinary(r *io.BinReader) {
count := r.ReadVarUint()
if count < 0 || count > MaxValidatorsVoted {
r.Err = errors.New("invalid validators count")
return
}
for i := 0; i < int(count); i++ {
buf := r.ReadVarBytes()
if r.Err != nil {
return
}
vc[i] = *bigint.FromBytes(buf)
}
}
// 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 int64
slidingRatio float64
)
for i := range vc {
overallSum += vc[i].Int64()
}
for i := range vc {
if slidingRatio >= upperThreshold {
break
}
weight := vc[i].Int64()
slidingSum += weight
previousRatio := slidingRatio
slidingRatio = float64(slidingSum) / float64(overallSum)
if slidingRatio <= lowerThreshold {
continue
}
if previousRatio < lowerThreshold {
if slidingRatio > upperThreshold {
weight = int64((upperThreshold - lowerThreshold) * float64(overallSum))
} else {
weight = int64((slidingRatio - lowerThreshold) * float64(overallSum))
}
} else if slidingRatio > upperThreshold {
weight = int64((upperThreshold - previousRatio) * float64(overallSum))
}
sumWeight += weight
// Votes with N values get stored with N-1 index, thus +1 here.
sumValue += (int64(i) + 1) * weight
}
if sumValue == 0 || sumWeight == 0 {
return 0
}
return int(sumValue / sumWeight)
}