From 803fb39bb072d08984bc4ca31777ad0213a44688 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 12 Feb 2020 14:54:15 +0300 Subject: [PATCH 01/11] transaction: fix wrong state descriptor serdes Wrong field order. --- pkg/core/transaction/state_descriptor.go | 4 ++-- pkg/core/transaction/state_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/core/transaction/state_descriptor.go b/pkg/core/transaction/state_descriptor.go index a839d17d2..05f0a7f12 100644 --- a/pkg/core/transaction/state_descriptor.go +++ b/pkg/core/transaction/state_descriptor.go @@ -26,14 +26,14 @@ func (s *StateDescriptor) DecodeBinary(r *io.BinReader) { s.Type = DescStateType(r.ReadB()) s.Key = r.ReadVarBytes() - s.Value = r.ReadVarBytes() s.Field = r.ReadString() + s.Value = r.ReadVarBytes() } // EncodeBinary implements Serializable interface. func (s *StateDescriptor) EncodeBinary(w *io.BinWriter) { w.WriteB(byte(s.Type)) w.WriteVarBytes(s.Key) - w.WriteVarBytes(s.Value) w.WriteString(s.Field) + w.WriteVarBytes(s.Value) } diff --git a/pkg/core/transaction/state_test.go b/pkg/core/transaction/state_test.go index 0c01406cd..53fb17e7c 100644 --- a/pkg/core/transaction/state_test.go +++ b/pkg/core/transaction/state_test.go @@ -25,8 +25,8 @@ func TestEncodeDecodeState(t *testing.T) { assert.Equal(t, 1, len(s.Descriptors)) descriptor := s.Descriptors[0] assert.Equal(t, "03c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c1", hex.EncodeToString(descriptor.Key)) - assert.Equal(t, "52656769737465726564", hex.EncodeToString(descriptor.Value)) - assert.Equal(t, "\x01", descriptor.Field) + assert.Equal(t, "Registered", descriptor.Field) + assert.Equal(t, []byte{0x01}, descriptor.Value) assert.Equal(t, Validator, descriptor.Type) // Encode From 141553da4cf05db8faf63beb2c53d7fd16b133de Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 12 Feb 2020 14:55:19 +0300 Subject: [PATCH 02/11] keys: fix PublicKeys decoding It changes the slice, thus it has to work via a pointer. --- pkg/crypto/keys/publickey.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/crypto/keys/publickey.go b/pkg/crypto/keys/publickey.go index 422efadca..be1435fea 100644 --- a/pkg/crypto/keys/publickey.go +++ b/pkg/crypto/keys/publickey.go @@ -36,7 +36,7 @@ func (keys PublicKeys) Less(i, j int) bool { } // DecodeBytes decodes a PublicKeys from the given slice of bytes. -func (keys PublicKeys) DecodeBytes(data []byte) error { +func (keys *PublicKeys) DecodeBytes(data []byte) error { b := io.NewBinReaderFromBuf(data) b.ReadArray(keys) return b.Err From 38d0efc96c65cfd1f5f05ef3804a8afb41d05db5 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 12 Feb 2020 14:56:40 +0300 Subject: [PATCH 03/11] core: fix Registered state descriptor processing It has simple boolean value encoded, no `strconv` needed (it actually chokes on such input). --- pkg/core/blockchain.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 43d716114..1f3866ee2 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -5,7 +5,6 @@ import ( "math" "math/big" "sort" - "strconv" "sync" "sync/atomic" "time" @@ -684,12 +683,11 @@ func processValidatorStateDescriptor(descriptor *transaction.StateDescriptor, da return err } if descriptor.Field == "Registered" { - isRegistered, err := strconv.ParseBool(string(descriptor.Value)) - if err != nil { - return err + if len(descriptor.Value) == 1 { + validatorState.Registered = descriptor.Value[0] != 0 + return dao.PutValidatorState(validatorState) } - validatorState.Registered = isRegistered - return dao.PutValidatorState(validatorState) + return errors.New("bad descriptor value") } return nil } From 32a064aa31e013bc8b70fcd10d809b6a755d6815 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 12 Feb 2020 18:08:33 +0300 Subject: [PATCH 04/11] keys: add Cmp method to PublicKey It can be used by code that doesn't operate with PublicKeys, but still needs to be able to compare keys for some purposes. --- pkg/crypto/keys/publickey.go | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/pkg/crypto/keys/publickey.go b/pkg/crypto/keys/publickey.go index be1435fea..b22e2f9f3 100644 --- a/pkg/crypto/keys/publickey.go +++ b/pkg/crypto/keys/publickey.go @@ -22,17 +22,7 @@ type PublicKeys []*PublicKey func (keys PublicKeys) Len() int { return len(keys) } func (keys PublicKeys) Swap(i, j int) { keys[i], keys[j] = keys[j], keys[i] } func (keys PublicKeys) Less(i, j int) bool { - if keys[i].X.Cmp(keys[j].X) == -1 { - return true - } - if keys[i].X.Cmp(keys[j].X) == 1 { - return false - } - if keys[i].X.Cmp(keys[j].X) == 0 { - return false - } - - return keys[i].Y.Cmp(keys[j].Y) == -1 + return keys[i].Cmp(keys[j]) == -1 } // DecodeBytes decodes a PublicKeys from the given slice of bytes. @@ -75,6 +65,15 @@ func (p *PublicKey) Equal(key *PublicKey) bool { return p.X.Cmp(key.X) == 0 && p.Y.Cmp(key.Y) == 0 } +// Cmp compares two keys. +func (p *PublicKey) Cmp(key *PublicKey) int { + xCmp := p.X.Cmp(key.X) + if xCmp != 0 { + return xCmp + } + return p.Y.Cmp(key.Y) +} + // NewPublicKeyFromString returns a public key created from the // given hex string. func NewPublicKeyFromString(s string) (*PublicKey, error) { From 807309a0de2e5a3fa834d02b92540180f456cfe6 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 12 Feb 2020 18:10:23 +0300 Subject: [PATCH 05/11] core/state: only drop unregistered non-voted validators Simple as that: UnregisteredAndHasNoVotes != !RegisteredAndHasVotes Registered validators should stay in the DB, we might be in the process of updating votes for them and that starts with subtraction. --- pkg/core/blockchain.go | 2 +- pkg/core/state/validator.go | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 1f3866ee2..e88cdd08b 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -659,7 +659,7 @@ func processTXWithValidatorsSubtract(account *state.Account, dao *cachedDao, toS return err } validator.Votes -= toSubtract - if !validator.RegisteredAndHasVotes() { + if validator.UnregisteredAndHasNoVotes() { if err := dao.DeleteValidatorState(validator); err != nil { return err } diff --git a/pkg/core/state/validator.go b/pkg/core/state/validator.go index bd3414a1f..3f8ba51d8 100644 --- a/pkg/core/state/validator.go +++ b/pkg/core/state/validator.go @@ -18,6 +18,11 @@ 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) From 8310b76d28705e3ef0301e870dc44ea1472f93a3 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 12 Feb 2020 18:14:28 +0300 Subject: [PATCH 06/11] core: properly save votes when processing Account descriptor They were ignored basically. --- pkg/core/blockchain.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index e88cdd08b..c723f2e50 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -713,8 +713,8 @@ func processAccountStateDescriptor(descriptor *transaction.StateDescriptor, dao if err != nil { return err } + account.Votes = votes if votes.Len() != len(account.Votes) { - account.Votes = votes for _, vote := range votes { validator, err := dao.GetValidatorStateOrNew(vote) if err != nil { @@ -725,6 +725,7 @@ func processAccountStateDescriptor(descriptor *transaction.StateDescriptor, dao } } } + return dao.PutAccountState(account) } return nil } From 2ff7ceb8cf6cb46d9a3d7bdc3bcc0d6f1e9e9ef2 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 12 Feb 2020 18:16:57 +0300 Subject: [PATCH 07/11] core: properly update validator votes for Account descriptor ValidatorsCount is not implemented yet, but we need to update validators properly, adding account's vote to their votes count. --- pkg/core/blockchain.go | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index c723f2e50..73b293564 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -714,15 +714,14 @@ func processAccountStateDescriptor(descriptor *transaction.StateDescriptor, dao return err } account.Votes = votes - if votes.Len() != len(account.Votes) { - for _, vote := range votes { - validator, err := dao.GetValidatorStateOrNew(vote) - if err != nil { - return err - } - if err := dao.PutValidatorState(validator); err != nil { - return err - } + for _, vote := range account.Votes { + validatorState, err := dao.GetValidatorStateOrNew(vote) + if err != nil { + return err + } + validatorState.Votes += balance + if err = dao.PutValidatorState(validatorState); err != nil { + return err } } return dao.PutAccountState(account) From f9c6a0d77c842896747249ceb7db7961215e5b18 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 12 Feb 2020 18:22:31 +0300 Subject: [PATCH 08/11] core: make voting choose the best validators They need to be sorted in order for voting system to work. --- pkg/core/blockchain.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 73b293564..8110c37cf 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -1344,6 +1344,18 @@ func (bc *Blockchain) GetValidators(txes ...*transaction.Transaction) ([]*keys.P } validators := cache.GetValidators() + sort.Slice(validators, func(i, j int) bool { + // Unregistered validators go to the end of the list. + if validators[i].Registered != validators[j].Registered { + return validators[i].Registered + } + // The most-voted validators should end up in the front of the list. + if validators[i].Votes != validators[j].Votes { + return validators[i].Votes > validators[j].Votes + } + // Ties are broken with public keys. + return validators[i].PublicKey.Cmp(validators[j].PublicKey) == -1 + }) count := state.GetValidatorsWeightedAverage(validators) standByValidators, err := bc.GetStandByValidators() @@ -1361,7 +1373,6 @@ func (bc *Blockchain) GetValidators(txes ...*transaction.Transaction) ([]*keys.P pubKeys = append(pubKeys, validator.PublicKey) } } - sort.Sort(sort.Reverse(pubKeys)) if pubKeys.Len() >= count { return pubKeys[:count], nil } From 357b675090b23c832965f206e6e6c82c9943d97c Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 12 Feb 2020 20:29:10 +0300 Subject: [PATCH 09/11] core: don't choose duplicating standby validators We may already have chosen some of standby validators, so don't repeat. --- pkg/core/blockchain.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 8110c37cf..33f0c81bc 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -1379,7 +1379,9 @@ func (bc *Blockchain) GetValidators(txes ...*transaction.Transaction) ([]*keys.P result := pubKeys.Unique() for i := 0; i < uniqueSBValidators.Len() && result.Len() < count; i++ { - result = append(result, uniqueSBValidators[i]) + if !result.Contains(uniqueSBValidators[i]) { + result = append(result, uniqueSBValidators[i]) + } } return result, nil } From c8a248596eec84438b322fb18ce2abbeb0b9555d Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 12 Feb 2020 20:54:48 +0300 Subject: [PATCH 10/11] 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. --- pkg/core/blockchain.go | 52 ++++++++++++++++++--- pkg/core/dao.go | 18 ++++++++ pkg/core/state/validator.go | 90 +++++++++++++++++++------------------ 3 files changed, 111 insertions(+), 49 deletions(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 33f0c81bc..8094a19c8 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -648,6 +648,15 @@ func processTXWithValidatorsAdd(output *transaction.Output, account *state.Accou return err } } + vc, err := dao.GetValidatorsCount() + if err != nil { + return err + } + vc[len(account.Votes)-1] += output.Amount + err = dao.PutValidatorsCount(vc) + if err != nil { + return err + } } return nil } @@ -669,6 +678,17 @@ func processTXWithValidatorsSubtract(account *state.Account, dao *cachedDao, toS } } } + if len(account.Votes) > 0 { + vc, err := dao.GetValidatorsCount() + if err != nil { + return err + } + vc[len(account.Votes)-1] -= toSubtract + err = dao.PutValidatorsCount(vc) + if err != nil { + return err + } + } return nil } @@ -713,16 +733,32 @@ func processAccountStateDescriptor(descriptor *transaction.StateDescriptor, dao if err != nil { return err } - account.Votes = votes - for _, vote := range account.Votes { - validatorState, err := dao.GetValidatorStateOrNew(vote) + if len(votes) > state.MaxValidatorsVoted { + return errors.New("voting candidate limit exceeded") + } + if len(votes) > 0 { + account.Votes = votes + for _, vote := range account.Votes { + validatorState, err := dao.GetValidatorStateOrNew(vote) + if err != nil { + return err + } + validatorState.Votes += balance + if err = dao.PutValidatorState(validatorState); err != nil { + return err + } + } + vc, err := dao.GetValidatorsCount() if err != nil { return err } - validatorState.Votes += balance - if err = dao.PutValidatorState(validatorState); err != nil { + vc[len(account.Votes)-1] += balance + err = dao.PutValidatorsCount(vc) + if err != nil { return err } + } else { + account.Votes = nil } return dao.PutAccountState(account) } @@ -1357,7 +1393,11 @@ func (bc *Blockchain) GetValidators(txes ...*transaction.Transaction) ([]*keys.P return validators[i].PublicKey.Cmp(validators[j].PublicKey) == -1 }) - count := state.GetValidatorsWeightedAverage(validators) + validatorsCount, err := cache.GetValidatorsCount() + if err != nil { + return nil, err + } + count := validatorsCount.GetWeightedAverage() standByValidators, err := bc.GetStandByValidators() if err != nil { return nil, err diff --git a/pkg/core/dao.go b/pkg/core/dao.go index 4111fba66..1ff5d5e5e 100644 --- a/pkg/core/dao.go +++ b/pkg/core/dao.go @@ -266,6 +266,24 @@ func (dao *dao) 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 *dao) 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 *dao) 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/state/validator.go b/pkg/core/state/validator.go index 3f8ba51d8..0fdb73c47 100644 --- a/pkg/core/state/validator.go +++ b/pkg/core/state/validator.go @@ -6,6 +6,9 @@ import ( "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 @@ -13,6 +16,10 @@ 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) @@ -38,67 +45,64 @@ func (vs *Validator) DecodeBinary(reader *io.BinReader) { vs.Votes.DecodeBinary(reader) } -// GetValidatorsWeightedAverage applies weighted filter based on votes for validator and returns number of validators. -// Get back to it with further investigation in https://github.com/nspcc-dev/neo-go/issues/512. -func GetValidatorsWeightedAverage(validators []*Validator) int { - return int(weightedAverage(applyWeightedFilter(validators))) +// EncodeBinary encodes ValidatorCount to the given BinWriter. +func (vc *ValidatorsCount) EncodeBinary(w *io.BinWriter) { + for i := range vc { + vc[i].EncodeBinary(w) + } } -// applyWeightedFilter is an implementation of the filter for validators votes. -// C# reference https://github.com/neo-project/neo/blob/41caff115c28d6c7665b2a7ac72967e7ce82e921/neo/Helper.cs#L273 -func applyWeightedFilter(validators []*Validator) map[*Validator]float64 { - var validatorsWithVotes []*Validator - var amount float64 +// DecodeBinary decodes ValidatorCount from the given BinReader. +func (vc *ValidatorsCount) DecodeBinary(r *io.BinReader) { + for i := range vc { + vc[i].DecodeBinary(r) + } +} - weightedVotes := make(map[*Validator]float64) - start := 0.25 - end := 0.75 - sum := float64(0) - current := float64(0) +// 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 _, validator := range validators { - if validator.Votes > util.Fixed8(0) { - validatorsWithVotes = append(validatorsWithVotes, validator) - amount += validator.Votes.FloatValue() - } + for i := range vc { + overallSum += vc[i] } - for _, validator := range validatorsWithVotes { - if current >= end { + for i := range vc { + if slidingRatio >= upperThreshold { break } - weight := validator.Votes.FloatValue() - sum += weight - old := current - current = sum / amount + weight := vc[i] + slidingSum += weight + previousRatio := slidingRatio + slidingRatio = slidingSum.FloatValue() / overallSum.FloatValue() - if current <= start { + if slidingRatio <= lowerThreshold { continue } - if old < start { - if current > end { - weight = (end - start) * amount + if previousRatio < lowerThreshold { + if slidingRatio > upperThreshold { + weight = util.Fixed8FromFloat((upperThreshold - lowerThreshold) * overallSum.FloatValue()) } else { - weight = (current - start) * amount + weight = util.Fixed8FromFloat((slidingRatio - lowerThreshold) * overallSum.FloatValue()) } - } else if current > end { - weight = (end - old) * amount + } else if slidingRatio > upperThreshold { + weight = util.Fixed8FromFloat((upperThreshold - previousRatio) * overallSum.FloatValue()) } - weightedVotes[validator] = weight - } - return weightedVotes -} - -func weightedAverage(weightedVotes map[*Validator]float64) float64 { - sumWeight := float64(0) - sumValue := float64(0) - for vState, weight := range weightedVotes { sumWeight += weight - sumValue += vState.Votes.FloatValue() * 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 sumValue / sumWeight + return int(sumValue / sumWeight) } From 26af9200b0e363af8c79f9fefb741ae5b9b876f6 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 12 Feb 2020 21:12:39 +0300 Subject: [PATCH 11/11] core: refactor voting processing, fix pending tx subtractions Deduplicate code and add missing token check for subtractions that are being done in GetValidators() for pending transactions. --- pkg/core/blockchain.go | 40 +++++++++++++++------------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 8094a19c8..0c1ad2495 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -436,7 +436,7 @@ func (bc *Blockchain) storeBlock(block *block.Block) error { if err = cache.PutSpentCoinState(input.PrevHash, spentCoin); err != nil { return err } - if err = processTXWithValidatorsSubtract(account, cache, prevTXOutput.Amount); err != nil { + if err = processTXWithValidatorsSubtract(&prevTXOutput, account, cache); err != nil { return err } } @@ -638,36 +638,26 @@ func processOutputs(tx *transaction.Transaction, dao *cachedDao) error { func processTXWithValidatorsAdd(output *transaction.Output, account *state.Account, dao *cachedDao) error { if output.AssetID.Equals(governingTokenTX().Hash()) && len(account.Votes) > 0 { - for _, vote := range account.Votes { - validatorState, err := dao.GetValidatorStateOrNew(vote) - if err != nil { - return err - } - validatorState.Votes += output.Amount - if err = dao.PutValidatorState(validatorState); err != nil { - return err - } - } - vc, err := dao.GetValidatorsCount() - if err != nil { - return err - } - vc[len(account.Votes)-1] += output.Amount - err = dao.PutValidatorsCount(vc) - if err != nil { - return err - } + return modAccountVotes(account, dao, output.Amount) } return nil } -func processTXWithValidatorsSubtract(account *state.Account, dao *cachedDao, toSubtract util.Fixed8) error { +func processTXWithValidatorsSubtract(output *transaction.Output, account *state.Account, dao *cachedDao) error { + if output.AssetID.Equals(governingTokenTX().Hash()) && 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 *cachedDao, value util.Fixed8) error { for _, vote := range account.Votes { validator, err := dao.GetValidatorStateOrNew(vote) if err != nil { return err } - validator.Votes -= toSubtract + validator.Votes += value if validator.UnregisteredAndHasNoVotes() { if err := dao.DeleteValidatorState(validator); err != nil { return err @@ -683,7 +673,7 @@ func processTXWithValidatorsSubtract(account *state.Account, dao *cachedDao, toS if err != nil { return err } - vc[len(account.Votes)-1] -= toSubtract + vc[len(account.Votes)-1] += value err = dao.PutValidatorsCount(vc) if err != nil { return err @@ -724,7 +714,7 @@ func processAccountStateDescriptor(descriptor *transaction.StateDescriptor, dao if descriptor.Field == "Votes" { balance := account.GetBalanceValues()[governingTokenTX().Hash()] - if err = processTXWithValidatorsSubtract(account, dao, balance); err != nil { + if err = modAccountVotes(account, dao, -balance); err != nil { return err } @@ -1356,7 +1346,7 @@ func (bc *Blockchain) GetValidators(txes ...*transaction.Transaction) ([]*keys.P } // process account state votes: if there are any -> validators will be updated. - if err = processTXWithValidatorsSubtract(accountState, cache, prevOutput.Amount); err != nil { + if err = processTXWithValidatorsSubtract(&prevOutput, accountState, cache); err != nil { return nil, err } delete(accountState.Balances, prevOutput.AssetID)