core/native: move Votes from account to native NEO state

This commit is contained in:
Roman Khimov 2020-04-26 14:00:17 +03:00
parent bc4a6a6bab
commit 2fa3bdf6a9
10 changed files with 18 additions and 72 deletions

View file

@ -404,24 +404,6 @@ func accountGetScriptHash(ic *interop.Context, v *vm.VM) error {
return nil return nil
} }
// accountGetVotes returns votes of a given account.
func accountGetVotes(ic *interop.Context, v *vm.VM) error {
accInterface := v.Estack().Pop().Value()
acc, ok := accInterface.(*state.Account)
if !ok {
return fmt.Errorf("%T is not an account state", acc)
}
if len(acc.Votes) > vm.MaxArraySize {
return errors.New("too many votes")
}
votes := make([]vm.StackItem, 0, len(acc.Votes))
for _, key := range acc.Votes {
votes = append(votes, vm.NewByteArrayItem(key.Bytes()))
}
v.Estack().PushVal(votes)
return nil
}
// accountIsStandard checks whether given account is standard. // accountIsStandard checks whether given account is standard.
func accountIsStandard(ic *interop.Context, v *vm.VM) error { func accountIsStandard(ic *interop.Context, v *vm.VM) error {
accbytes := v.Estack().Pop().Bytes() accbytes := v.Estack().Pop().Bytes()

View file

@ -417,17 +417,6 @@ func TestAccountGetScriptHash(t *testing.T) {
require.Equal(t, accState.ScriptHash.BytesBE(), hash) require.Equal(t, accState.ScriptHash.BytesBE(), hash)
} }
func TestAccountGetVotes(t *testing.T) {
v, accState, context, chain := createVMAndAccState(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(accState))
err := accountGetVotes(context, v)
require.NoError(t, err)
votes := v.Estack().Pop().Value().([]vm.StackItem)
require.Equal(t, vm.NewByteArrayItem(accState.Votes[0].Bytes()), votes[0])
}
func TestContractGetScript(t *testing.T) { func TestContractGetScript(t *testing.T) {
v, contractState, context, chain := createVMAndContractState(t) v, contractState, context, chain := createVMAndContractState(t)
defer chain.Close() defer chain.Close()
@ -603,9 +592,6 @@ func createVMAndAccState(t *testing.T) (*vm.VM, *state.Account, *interop.Context
hash, err := util.Uint160DecodeStringBE(rawHash) hash, err := util.Uint160DecodeStringBE(rawHash)
accountState := state.NewAccount(hash) accountState := state.NewAccount(hash)
key := &keys.PublicKey{X: big.NewInt(1), Y: big.NewInt(1)}
accountState.Votes = []*keys.PublicKey{key}
require.NoError(t, err) require.NoError(t, err)
chain := newTestChain(t) chain := newTestChain(t)
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), nil, nil) context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), nil, nil)

View file

@ -115,7 +115,6 @@ var systemInterops = []interop.Function{
var neoInterops = []interop.Function{ var neoInterops = []interop.Function{
{Name: "Neo.Account.GetBalance", Func: accountGetBalance, Price: 1}, {Name: "Neo.Account.GetBalance", Func: accountGetBalance, Price: 1},
{Name: "Neo.Account.GetScriptHash", Func: accountGetScriptHash, Price: 1}, {Name: "Neo.Account.GetScriptHash", Func: accountGetScriptHash, Price: 1},
{Name: "Neo.Account.GetVotes", Func: accountGetVotes, Price: 1},
{Name: "Neo.Account.IsStandard", Func: accountIsStandard, Price: 100}, {Name: "Neo.Account.IsStandard", Func: accountIsStandard, Price: 100},
{Name: "Neo.Asset.Create", Func: assetCreate, Price: 0}, {Name: "Neo.Asset.Create", Func: assetCreate, Price: 0},
{Name: "Neo.Asset.GetAdmin", Func: assetGetAdmin, Price: 1}, {Name: "Neo.Asset.GetAdmin", Func: assetGetAdmin, Price: 1},
@ -204,7 +203,6 @@ var neoInterops = []interop.Function{
// Old compatibility APIs. // Old compatibility APIs.
{Name: "AntShares.Account.GetBalance", Func: accountGetBalance, Price: 1}, {Name: "AntShares.Account.GetBalance", Func: accountGetBalance, Price: 1},
{Name: "AntShares.Account.GetScriptHash", Func: accountGetScriptHash, Price: 1}, {Name: "AntShares.Account.GetScriptHash", Func: accountGetScriptHash, Price: 1},
{Name: "AntShares.Account.GetVotes", Func: accountGetVotes, Price: 1},
{Name: "AntShares.Asset.Create", Func: assetCreate, Price: 0}, {Name: "AntShares.Asset.Create", Func: assetCreate, Price: 0},
{Name: "AntShares.Asset.GetAdmin", Func: assetGetAdmin, Price: 1}, {Name: "AntShares.Asset.GetAdmin", Func: assetGetAdmin, Price: 1},
{Name: "AntShares.Asset.GetAmount", Func: assetGetAmount, Price: 1}, {Name: "AntShares.Asset.GetAmount", Func: assetGetAmount, Price: 1},

View file

@ -34,7 +34,6 @@ func TestUnexpectedNonInterops(t *testing.T) {
funcs := []func(*interop.Context, *vm.VM) error{ funcs := []func(*interop.Context, *vm.VM) error{
accountGetBalance, accountGetBalance,
accountGetScriptHash, accountGetScriptHash,
accountGetVotes,
assetGetAdmin, assetGetAdmin,
assetGetAmount, assetGetAmount,
assetGetAssetID, assetGetAssetID,

View file

@ -166,12 +166,8 @@ func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.Sto
if amount.Sign() == 0 { if amount.Sign() == 0 {
return nil return nil
} }
oldAcc, err := ic.DAO.GetAccountState(h) if len(acc.Votes) > 0 {
if err != nil { if err := n.ModifyAccountVotes(acc, ic.DAO, new(big.Int).Neg(&acc.Balance)); err != nil {
return err
}
if len(oldAcc.Votes) > 0 {
if err := n.ModifyAccountVotes(oldAcc, ic.DAO, new(big.Int).Neg(&acc.Balance)); err != nil {
return err return err
} }
siVC := ic.DAO.GetStorageItem(n.Hash, validatorsCountKey) siVC := ic.DAO.GetStorageItem(n.Hash, validatorsCountKey)
@ -182,7 +178,7 @@ func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.Sto
if err != nil { if err != nil {
return err return err
} }
vc[len(oldAcc.Votes)-1].Add(&vc[len(oldAcc.Votes)-1], amount) vc[len(acc.Votes)-1].Add(&vc[len(acc.Votes)-1], amount)
siVC.Value = vc.Bytes() siVC.Value = vc.Bytes()
if err := ic.DAO.PutStorageItem(n.Hash, validatorsCountKey, siVC); err != nil { if err := ic.DAO.PutStorageItem(n.Hash, validatorsCountKey, siVC); err != nil {
return err return err
@ -276,11 +272,7 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pubs keys.Public
if err != nil { if err != nil {
return err return err
} }
oldAcc, err := ic.DAO.GetAccountState(h) if err := n.ModifyAccountVotes(acc, ic.DAO, new(big.Int).Neg(&acc.Balance)); err != nil {
if err != nil {
return err
}
if err := n.ModifyAccountVotes(oldAcc, ic.DAO, new(big.Int).Neg(&acc.Balance)); err != nil {
return err return err
} }
pubs = pubs.Unique() pubs = pubs.Unique()
@ -292,7 +284,7 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pubs keys.Public
} }
newPubs = append(newPubs, pub) newPubs = append(newPubs, pub)
} }
if lp, lv := len(newPubs), len(oldAcc.Votes); lp != lv { if lp, lv := len(newPubs), len(acc.Votes); lp != lv {
si := ic.DAO.GetStorageItem(n.Hash, validatorsCountKey) si := ic.DAO.GetStorageItem(n.Hash, validatorsCountKey)
if si == nil { if si == nil {
return errors.New("validators count uninitialized") return errors.New("validators count uninitialized")
@ -312,15 +304,16 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pubs keys.Public
return err return err
} }
} }
oldAcc.Votes = newPubs acc.Votes = newPubs
if err := n.ModifyAccountVotes(oldAcc, ic.DAO, &acc.Balance); err != nil { if err := n.ModifyAccountVotes(acc, ic.DAO, &acc.Balance); err != nil {
return err return err
} }
return ic.DAO.PutAccountState(oldAcc) si.Value = acc.Bytes()
return ic.DAO.PutStorageItem(n.Hash, key, si)
} }
// ModifyAccountVotes modifies votes of the specified account by value (can be negative). // ModifyAccountVotes modifies votes of the specified account by value (can be negative).
func (n *NEO) ModifyAccountVotes(acc *state.Account, d dao.DAO, value *big.Int) error { func (n *NEO) ModifyAccountVotes(acc *state.NEOBalanceState, d dao.DAO, value *big.Int) error {
for _, vote := range acc.Votes { for _, vote := range acc.Votes {
key := makeValidatorKey(vote) key := makeValidatorKey(vote)
si := d.GetStorageItem(n.Hash, key) si := d.GetStorageItem(n.Hash, key)

View file

@ -1,7 +1,6 @@
package state package state
import ( import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
) )
@ -32,7 +31,6 @@ type Account struct {
Version uint8 Version uint8
ScriptHash util.Uint160 ScriptHash util.Uint160
IsFrozen bool IsFrozen bool
Votes []*keys.PublicKey
Balances map[util.Uint256][]UnspentBalance Balances map[util.Uint256][]UnspentBalance
Unclaimed UnclaimedBalances Unclaimed UnclaimedBalances
} }
@ -43,7 +41,6 @@ func NewAccount(scriptHash util.Uint160) *Account {
Version: 0, Version: 0,
ScriptHash: scriptHash, ScriptHash: scriptHash,
IsFrozen: false, IsFrozen: false,
Votes: []*keys.PublicKey{},
Balances: make(map[util.Uint256][]UnspentBalance), Balances: make(map[util.Uint256][]UnspentBalance),
Unclaimed: UnclaimedBalances{Raw: []byte{}}, Unclaimed: UnclaimedBalances{Raw: []byte{}},
} }
@ -54,7 +51,6 @@ func (s *Account) DecodeBinary(br *io.BinReader) {
s.Version = uint8(br.ReadB()) s.Version = uint8(br.ReadB())
br.ReadBytes(s.ScriptHash[:]) br.ReadBytes(s.ScriptHash[:])
s.IsFrozen = br.ReadBool() s.IsFrozen = br.ReadBool()
br.ReadArray(&s.Votes)
s.Balances = make(map[util.Uint256][]UnspentBalance) s.Balances = make(map[util.Uint256][]UnspentBalance)
lenBalances := br.ReadVarUint() lenBalances := br.ReadVarUint()
@ -79,7 +75,6 @@ func (s *Account) EncodeBinary(bw *io.BinWriter) {
bw.WriteB(byte(s.Version)) bw.WriteB(byte(s.Version))
bw.WriteBytes(s.ScriptHash[:]) bw.WriteBytes(s.ScriptHash[:])
bw.WriteBool(s.IsFrozen) bw.WriteBool(s.IsFrozen)
bw.WriteArray(s.Votes)
bw.WriteVarUint(uint64(len(s.Balances))) bw.WriteVarUint(uint64(len(s.Balances)))
for k, v := range s.Balances { for k, v := range s.Balances {

View file

@ -3,7 +3,6 @@ package state
import ( import (
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/internal/random" "github.com/nspcc-dev/neo-go/pkg/internal/random"
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes" "github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
@ -14,7 +13,6 @@ func TestDecodeEncodeAccountState(t *testing.T) {
var ( var (
n = 10 n = 10
balances = make(map[util.Uint256][]UnspentBalance) balances = make(map[util.Uint256][]UnspentBalance)
votes = make([]*keys.PublicKey, n)
) )
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
asset := random.Uint256() asset := random.Uint256()
@ -25,16 +23,12 @@ func TestDecodeEncodeAccountState(t *testing.T) {
Value: util.Fixed8(int64(random.Int(1, 10000))), Value: util.Fixed8(int64(random.Int(1, 10000))),
}) })
} }
k, err := keys.NewPrivateKey()
assert.Nil(t, err)
votes[i] = k.PublicKey()
} }
a := &Account{ a := &Account{
Version: 0, Version: 0,
ScriptHash: random.Uint160(), ScriptHash: random.Uint160(),
IsFrozen: true, IsFrozen: true,
Votes: votes,
Balances: balances, Balances: balances,
Unclaimed: UnclaimedBalances{Raw: []byte{}}, Unclaimed: UnclaimedBalances{Raw: []byte{}},
} }

View file

@ -3,6 +3,7 @@ package state
import ( import (
"math/big" "math/big"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/emit"
) )
@ -16,6 +17,7 @@ type NEP5BalanceState struct {
type NEOBalanceState struct { type NEOBalanceState struct {
NEP5BalanceState NEP5BalanceState
BalanceHeight uint32 BalanceHeight uint32
Votes keys.PublicKeys
} }
// NEP5BalanceStateFromBytes converts serialized NEP5BalanceState to structure. // NEP5BalanceStateFromBytes converts serialized NEP5BalanceState to structure.
@ -85,10 +87,12 @@ func (s *NEOBalanceState) Bytes() []byte {
func (s *NEOBalanceState) EncodeBinary(w *io.BinWriter) { func (s *NEOBalanceState) EncodeBinary(w *io.BinWriter) {
s.NEP5BalanceState.EncodeBinary(w) s.NEP5BalanceState.EncodeBinary(w)
w.WriteU32LE(s.BalanceHeight) w.WriteU32LE(s.BalanceHeight)
w.WriteArray(s.Votes)
} }
// DecodeBinary implements io.Serializable interface. // DecodeBinary implements io.Serializable interface.
func (s *NEOBalanceState) DecodeBinary(r *io.BinReader) { func (s *NEOBalanceState) DecodeBinary(r *io.BinReader) {
s.NEP5BalanceState.DecodeBinary(r) s.NEP5BalanceState.DecodeBinary(r)
s.BalanceHeight = r.ReadU32LE() s.BalanceHeight = r.ReadU32LE()
r.ReadArray(&s.Votes)
} }

View file

@ -11,7 +11,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/core"
"github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/rpc/request" "github.com/nspcc-dev/neo-go/pkg/rpc/request"
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result" "github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
@ -49,7 +48,6 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
Version: 0, Version: 0,
ScriptHash: scriptHash, ScriptHash: scriptHash,
IsFrozen: false, IsFrozen: false,
Votes: []*keys.PublicKey{},
Balances: result.Balances{ Balances: result.Balances{
result.Balance{ result.Balance{
Asset: core.GoverningTokenID(), Asset: core.GoverningTokenID(),

View file

@ -5,18 +5,16 @@ import (
"sort" "sort"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
) )
// AccountState wrapper used for the representation of // AccountState wrapper used for the representation of
// state.Account on the RPC Server. // state.Account on the RPC Server.
type AccountState struct { type AccountState struct {
Version uint8 `json:"version"` Version uint8 `json:"version"`
ScriptHash util.Uint160 `json:"script_hash"` ScriptHash util.Uint160 `json:"script_hash"`
IsFrozen bool `json:"frozen"` IsFrozen bool `json:"frozen"`
Votes []*keys.PublicKey `json:"votes"` Balances []Balance `json:"balances"`
Balances []Balance `json:"balances"`
} }
// Balances type for sorting balances in rpc response. // Balances type for sorting balances in rpc response.
@ -48,7 +46,6 @@ func NewAccountState(a *state.Account) AccountState {
Version: a.Version, Version: a.Version,
ScriptHash: a.ScriptHash, ScriptHash: a.ScriptHash,
IsFrozen: a.IsFrozen, IsFrozen: a.IsFrozen,
Votes: a.Votes,
Balances: balances, Balances: balances,
} }
} }