forked from TrueCloudLab/neoneo-go
core/native: move ValidatorsCount processing into native NEO contract
This commit is contained in:
parent
3476a18fa9
commit
064636768b
7 changed files with 154 additions and 168 deletions
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if len(validatorsCount) == 0 {
|
||||
sb, err := bc.GetStandByValidators()
|
||||
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
|
||||
}
|
||||
return sb, nil
|
||||
}
|
||||
|
||||
validatorsBytes, err := n.getRegisteredValidators(d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
106
pkg/core/native/validators_count.go
Normal file
106
pkg/core/native/validators_count.go
Normal 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)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ const (
|
|||
STNEP5Transfers KeyPrefix = 0x72
|
||||
STNEP5Balances KeyPrefix = 0x73
|
||||
IXHeaderHashList KeyPrefix = 0x80
|
||||
IXValidatorsCount KeyPrefix = 0x90
|
||||
SYSCurrentBlock KeyPrefix = 0xc0
|
||||
SYSCurrentHeader KeyPrefix = 0xc1
|
||||
SYSVersion KeyPrefix = 0xf0
|
||||
|
|
|
@ -17,7 +17,6 @@ var (
|
|||
STContract,
|
||||
STStorage,
|
||||
IXHeaderHashList,
|
||||
IXValidatorsCount,
|
||||
SYSCurrentBlock,
|
||||
SYSCurrentHeader,
|
||||
SYSVersion,
|
||||
|
@ -33,7 +32,6 @@ var (
|
|||
0x50,
|
||||
0x70,
|
||||
0x80,
|
||||
0x90,
|
||||
0xc0,
|
||||
0xc1,
|
||||
0xf0,
|
||||
|
|
Loading…
Reference in a new issue