diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index d5b44b7b5..8d4df662e 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -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 diff --git a/pkg/core/dao/dao.go b/pkg/core/dao/dao.go index 7ada3828d..4618808c9 100644 --- a/pkg/core/dao/dao.go +++ b/pkg/core/dao/dao.go @@ -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. diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index a70fe04d0..9e480e0ff 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -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 diff --git a/pkg/core/native/validators_count.go b/pkg/core/native/validators_count.go new file mode 100644 index 000000000..25cfe3c7c --- /dev/null +++ b/pkg/core/native/validators_count.go @@ -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) +} diff --git a/pkg/core/state/validator.go b/pkg/core/state/validator.go index 2a2d65fa1..ecdc8cc67 100644 --- a/pkg/core/state/validator.go +++ b/pkg/core/state/validator.go @@ -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) -} diff --git a/pkg/core/storage/store.go b/pkg/core/storage/store.go index ea48dc0b8..6726ff58c 100644 --- a/pkg/core/storage/store.go +++ b/pkg/core/storage/store.go @@ -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 diff --git a/pkg/core/storage/store_test.go b/pkg/core/storage/store_test.go index 4c028fbe4..9c9f6ef70 100644 --- a/pkg/core/storage/store_test.go +++ b/pkg/core/storage/store_test.go @@ -17,7 +17,6 @@ var ( STContract, STStorage, IXHeaderHashList, - IXValidatorsCount, SYSCurrentBlock, SYSCurrentHeader, SYSVersion, @@ -33,7 +32,6 @@ var ( 0x50, 0x70, 0x80, - 0x90, 0xc0, 0xc1, 0xf0,