core/native: move ValidatorsCount processing into native NEO contract

This commit is contained in:
Roman Khimov 2020-04-26 10:15:59 +03:00
parent 3476a18fa9
commit 064636768b
7 changed files with 154 additions and 168 deletions

View file

@ -523,9 +523,6 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
if err != nil {
return err
}
if err = processTXWithValidatorsSubtract(prevTXOutput, account, cache); err != nil {
return err
}
}
balancesLen := len(account.Balances[prevTXOutput.AssetID])
@ -838,42 +835,6 @@ func processOutputs(tx *transaction.Transaction, dao *dao.Cached) error {
if err = dao.PutAccountState(account); err != nil {
return err
}
if err = processTXWithValidatorsAdd(&output, account, dao); err != nil {
return err
}
}
return nil
}
func processTXWithValidatorsAdd(output *transaction.Output, account *state.Account, dao *dao.Cached) error {
if output.AssetID.Equals(GoverningTokenID()) && len(account.Votes) > 0 {
return modAccountVotes(account, dao, output.Amount)
}
return nil
}
func processTXWithValidatorsSubtract(output *transaction.Output, account *state.Account, dao *dao.Cached) error {
if output.AssetID.Equals(GoverningTokenID()) && len(account.Votes) > 0 {
return modAccountVotes(account, dao, -output.Amount)
}
return nil
}
// modAccountVotes adds given value to given account voted validators.
func modAccountVotes(account *state.Account, dao *dao.Cached, value util.Fixed8) error {
// if err := native.ModifyAccountVotes(account, dao, value); err != nil {
// return err
// }
if len(account.Votes) > 0 {
vc, err := dao.GetValidatorsCount()
if err != nil {
return err
}
vc[len(account.Votes)-1] += value
err = dao.PutValidatorsCount(vc)
if err != nil {
return err
}
}
return nil
}
@ -1348,7 +1309,7 @@ func (bc *Blockchain) verifyTx(t *transaction.Transaction, block *block.Block) e
if err != nil {
return err
}
if len(votes) > state.MaxValidatorsVoted {
if len(votes) > native.MaxValidatorsVoted {
return errors.New("voting candidate limit exceeded")
}
hash, err := util.Uint160DecodeBytesBE(desc.Key)
@ -1690,9 +1651,6 @@ func (bc *Blockchain) GetValidators(txes ...*transaction.Transaction) ([]*keys.P
if err := cache.PutAccountState(accountState); err != nil {
return nil, err
}
if err = processTXWithValidatorsAdd(&output, accountState, cache); err != nil {
return nil, err
}
}
// group inputs by the same previous hash and iterate through inputs
@ -1715,10 +1673,6 @@ func (bc *Blockchain) GetValidators(txes ...*transaction.Transaction) ([]*keys.P
return nil, err
}
// process account state votes: if there are any -> validators will be updated.
if err = processTXWithValidatorsSubtract(prevOutput, accountState, cache); err != nil {
return nil, err
}
delete(accountState.Balances, prevOutput.AssetID)
if err = cache.PutAccountState(accountState); err != nil {
return nil, err

View file

@ -43,7 +43,6 @@ type DAO interface {
GetValidatorState(publicKey *keys.PublicKey) (*state.Validator, error)
GetValidatorStateOrNew(publicKey *keys.PublicKey) (*state.Validator, error)
GetValidators() []*state.Validator
GetValidatorsCount() (*state.ValidatorsCount, error)
GetVersion() (string, error)
GetWrapped() DAO
HasTransaction(hash util.Uint256) bool
@ -61,7 +60,6 @@ type DAO interface {
PutStorageItem(scripthash util.Uint160, key []byte, si *state.StorageItem) error
PutUnspentCoinState(hash util.Uint256, ucs *state.UnspentCoin) error
PutValidatorState(vs *state.Validator) error
PutValidatorsCount(vc *state.ValidatorsCount) error
PutVersion(v string) error
StoreAsBlock(block *block.Block, sysFee uint32) error
StoreAsCurrentBlock(block *block.Block) error
@ -396,24 +394,6 @@ func (dao *Simple) DeleteValidatorState(vs *state.Validator) error {
return dao.Store.Delete(key)
}
// GetValidatorsCount returns current ValidatorsCount or new one if there is none
// in the DB.
func (dao *Simple) GetValidatorsCount() (*state.ValidatorsCount, error) {
vc := &state.ValidatorsCount{}
key := []byte{byte(storage.IXValidatorsCount)}
err := dao.GetAndDecode(vc, key)
if err != nil && err != storage.ErrKeyNotFound {
return nil, err
}
return vc, nil
}
// PutValidatorsCount put given ValidatorsCount in the store.
func (dao *Simple) PutValidatorsCount(vc *state.ValidatorsCount) error {
key := []byte{byte(storage.IXValidatorsCount)}
return dao.Put(vc, key)
}
// -- end validator.
// -- start notification event.

View file

@ -45,6 +45,15 @@ const (
prefixValidator = 33
)
var (
// validatorsCountKey is a key used to store validators count
// used to determine the real number of validators.
validatorsCountKey = []byte{15}
// nextValidatorsKey is a key used to store validators for the
// next block.
nextValidatorsKey = []byte{14}
)
// makeValidatorKey creates a key from account script hash.
func makeValidatorKey(key *keys.PublicKey) []byte {
b := key.Bytes()
@ -102,6 +111,8 @@ func NewNEO() *NEO {
// Initialize initializes NEO contract.
func (n *NEO) Initialize(ic *interop.Context) error {
var si state.StorageItem
if err := n.nep5TokenNative.Initialize(ic); err != nil {
return err
}
@ -110,6 +121,11 @@ func (n *NEO) Initialize(ic *interop.Context) error {
return errors.New("already initialized")
}
vc := new(ValidatorsCount)
si.Value = vc.Bytes()
if err := ic.DAO.PutStorageItem(n.Hash, validatorsCountKey, &si); err != nil {
return err
}
h, vs, err := getStandbyValidatorsHash(ic)
if err != nil {
return err
@ -255,17 +271,22 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pubs keys.Public
newPubs = append(newPubs, pub)
}
if lp, lv := len(newPubs), len(oldAcc.Votes); lp != lv {
vc, err := ic.DAO.GetValidatorsCount()
si := ic.DAO.GetStorageItem(n.Hash, validatorsCountKey)
if si == nil {
return errors.New("validators count uninitialized")
}
vc, err := ValidatorsCountFromBytes(si.Value)
if err != nil {
return err
}
if lv > 0 {
vc[lv-1] -= util.Fixed8(acc.Balance.Int64())
vc[lv-1].Sub(&vc[lv-1], &acc.Balance)
}
if len(newPubs) > 0 {
vc[lp-1] += util.Fixed8(acc.Balance.Int64())
vc[lp-1].Add(&vc[lp-1], &acc.Balance)
}
if err := ic.DAO.PutValidatorsCount(vc); err != nil {
si.Value = vc.Bytes()
if err := ic.DAO.PutStorageItem(n.Hash, validatorsCountKey, si); err != nil {
return err
}
}
@ -330,17 +351,14 @@ func (n *NEO) getRegisteredValidatorsCall(ic *interop.Context, _ []vm.StackItem)
// GetValidatorsInternal returns a list of current validators.
func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) ([]*keys.PublicKey, error) {
validatorsCount, err := d.GetValidatorsCount()
si := d.GetStorageItem(n.Hash, validatorsCountKey)
if si == nil {
return nil, errors.New("validators count uninitialized")
}
validatorsCount, err := ValidatorsCountFromBytes(si.Value)
if err != nil {
return nil, err
} else if len(validatorsCount) == 0 {
sb, err := bc.GetStandByValidators()
if err != nil {
return nil, err
}
return sb, nil
}
validatorsBytes, err := n.getRegisteredValidators(d)
if err != nil {
return nil, err

View file

@ -0,0 +1,106 @@
package native
import (
"math/big"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
)
// 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) {
for i := range vc {
w.WriteVarBytes(emit.IntToBytes(&vc[i]))
}
}
// DecodeBinary implements io.Serializable interface.
func (vc *ValidatorsCount) DecodeBinary(r *io.BinReader) {
for i := range vc {
buf := r.ReadVarBytes()
if r.Err != nil {
return
}
vc[i] = *emit.BytesToInt(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)
}

View file

@ -6,9 +6,6 @@ import (
"github.com/nspcc-dev/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
@ -16,10 +13,6 @@ type Validator struct {
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)
@ -44,65 +37,3 @@ func (vs *Validator) DecodeBinary(reader *io.BinReader) {
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)
}

View file

@ -7,24 +7,23 @@ import (
// KeyPrefix constants.
const (
DataBlock KeyPrefix = 0x01
DataTransaction KeyPrefix = 0x02
STAccount KeyPrefix = 0x40
STCoin KeyPrefix = 0x44
STSpentCoin KeyPrefix = 0x45
STNextValidators KeyPrefix = 0x47
STValidator KeyPrefix = 0x48
STAsset KeyPrefix = 0x4c
STNotification KeyPrefix = 0x4d
STContract KeyPrefix = 0x50
STStorage KeyPrefix = 0x70
STNEP5Transfers KeyPrefix = 0x72
STNEP5Balances KeyPrefix = 0x73
IXHeaderHashList KeyPrefix = 0x80
IXValidatorsCount KeyPrefix = 0x90
SYSCurrentBlock KeyPrefix = 0xc0
SYSCurrentHeader KeyPrefix = 0xc1
SYSVersion KeyPrefix = 0xf0
DataBlock KeyPrefix = 0x01
DataTransaction KeyPrefix = 0x02
STAccount KeyPrefix = 0x40
STCoin KeyPrefix = 0x44
STSpentCoin KeyPrefix = 0x45
STNextValidators KeyPrefix = 0x47
STValidator KeyPrefix = 0x48
STAsset KeyPrefix = 0x4c
STNotification KeyPrefix = 0x4d
STContract KeyPrefix = 0x50
STStorage KeyPrefix = 0x70
STNEP5Transfers KeyPrefix = 0x72
STNEP5Balances KeyPrefix = 0x73
IXHeaderHashList KeyPrefix = 0x80
SYSCurrentBlock KeyPrefix = 0xc0
SYSCurrentHeader KeyPrefix = 0xc1
SYSVersion KeyPrefix = 0xf0
)
// ErrKeyNotFound is an error returned by Store implementations

View file

@ -17,7 +17,6 @@ var (
STContract,
STStorage,
IXHeaderHashList,
IXValidatorsCount,
SYSCurrentBlock,
SYSCurrentHeader,
SYSVersion,
@ -33,7 +32,6 @@ var (
0x50,
0x70,
0x80,
0x90,
0xc0,
0xc1,
0xf0,