diff --git a/pkg/core/native/native_gas.go b/pkg/core/native/native_gas.go index b95ab54645..cdc0cd97b2 100644 --- a/pkg/core/native/native_gas.go +++ b/pkg/core/native/native_gas.go @@ -46,13 +46,18 @@ func NewGAS() *GAS { return g } -func (g *GAS) increaseBalance(_ *interop.Context, acc *state.Account, amount *big.Int) error { +func (g *GAS) increaseBalance(_ *interop.Context, _ util.Uint160, si *state.StorageItem, amount *big.Int) error { + acc, err := state.NEP5BalanceStateFromBytes(si.Value) + if err != nil { + return err + } if sign := amount.Sign(); sign == 0 { return nil - } else if sign == -1 && acc.GAS.Balance.Cmp(new(big.Int).Neg(amount)) == -1 { + } else if sign == -1 && acc.Balance.Cmp(new(big.Int).Neg(amount)) == -1 { return errors.New("insufficient funds") } - acc.GAS.Balance.Add(&acc.GAS.Balance, amount) + acc.Balance.Add(&acc.Balance, amount) + si.Value = acc.Bytes() return nil } diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index 127afe44c5..7eda3bd22f 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -111,29 +111,34 @@ func (n *NEO) OnPersist(ic *interop.Context) error { return nil } -func (n *NEO) increaseBalance(ic *interop.Context, acc *state.Account, amount *big.Int) error { +func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.StorageItem, amount *big.Int) error { + acc, err := state.NEOBalanceStateFromBytes(si.Value) + if err != nil { + return err + } if sign := amount.Sign(); sign == 0 { return nil - } else if sign == -1 && acc.NEO.Balance.Cmp(new(big.Int).Neg(amount)) == -1 { + } else if sign == -1 && acc.Balance.Cmp(new(big.Int).Neg(amount)) == -1 { return errors.New("insufficient funds") } - if err := n.distributeGas(ic, acc); err != nil { + if err := n.distributeGas(ic, h, acc); err != nil { return err } - acc.NEO.Balance.Add(&acc.NEO.Balance, amount) + acc.Balance.Add(&acc.Balance, amount) + si.Value = acc.Bytes() return nil } -func (n *NEO) distributeGas(ic *interop.Context, acc *state.Account) error { +func (n *NEO) distributeGas(ic *interop.Context, h util.Uint160, acc *state.NEOBalanceState) error { if ic.Block == nil || ic.Block.Index == 0 { return nil } - sys, net, err := ic.Chain.CalculateClaimable(util.Fixed8(acc.NEO.Balance.Int64()), acc.NEO.BalanceHeight, ic.Block.Index) + sys, net, err := ic.Chain.CalculateClaimable(util.Fixed8(acc.Balance.Int64()), acc.BalanceHeight, ic.Block.Index) if err != nil { return err } - acc.NEO.BalanceHeight = ic.Block.Index - n.GAS.mint(ic, acc.ScriptHash, big.NewInt(int64(sys+net))) + acc.BalanceHeight = ic.Block.Index + n.GAS.mint(ic, h, big.NewInt(int64(sys+net))) return nil } @@ -192,12 +197,21 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pubs keys.Public } else if !ok { return errors.New("invalid signature") } - acc, err := ic.DAO.GetAccountState(h) + key := makeAccountKey(h) + si := ic.DAO.GetStorageItem(n.Hash, key) + if si == nil { + return errors.New("invalid account") + } + acc, err := state.NEOBalanceStateFromBytes(si.Value) if err != nil { return err } - balance := util.Fixed8(acc.NEO.Balance.Int64()) - if err := ModifyAccountVotes(acc, ic.DAO, -balance); err != nil { + oldAcc, err := ic.DAO.GetAccountState(h) + if err != nil { + return err + } + balance := util.Fixed8(acc.Balance.Int64()) + if err := ModifyAccountVotes(oldAcc, ic.DAO, -balance); err != nil { return err } pubs = pubs.Unique() @@ -212,7 +226,7 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pubs keys.Public } newPubs = append(newPubs, pub) } - if lp, lv := len(newPubs), len(acc.Votes); lp != lv { + if lp, lv := len(newPubs), len(oldAcc.Votes); lp != lv { vc, err := ic.DAO.GetValidatorsCount() if err != nil { return err @@ -227,8 +241,11 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pubs keys.Public return err } } - acc.Votes = newPubs - return ModifyAccountVotes(acc, ic.DAO, balance) + oldAcc.Votes = newPubs + if err := ModifyAccountVotes(oldAcc, ic.DAO, balance); err != nil { + return err + } + return ic.DAO.PutAccountState(oldAcc) } // ModifyAccountVotes modifies votes of the specified account by value (can be negative). diff --git a/pkg/core/native/native_nep5.go b/pkg/core/native/native_nep5.go index bb4cf8e879..021eb6b9c9 100644 --- a/pkg/core/native/native_nep5.go +++ b/pkg/core/native/native_nep5.go @@ -13,6 +13,17 @@ import ( "github.com/nspcc-dev/neo-go/pkg/vm/emit" ) +// prefixAccount is the standard prefix used to store account data. +const prefixAccount = 20 + +// makeAccountKey creates a key from account script hash. +func makeAccountKey(h util.Uint160) []byte { + k := make([]byte, util.Uint160Size+1) + k[0] = prefixAccount + copy(k[1:], h.BytesBE()) + return k +} + // nep5TokenNative represents NEP-5 token contract. type nep5TokenNative struct { interop.ContractMD @@ -21,7 +32,7 @@ type nep5TokenNative struct { decimals int64 factor int64 onPersist func(*interop.Context) error - incBalance func(*interop.Context, *state.Account, *big.Int) error + incBalance func(*interop.Context, util.Uint160, *state.StorageItem, *big.Int) error } // totalSupplyKey is the key used to store totalSupply value. @@ -136,9 +147,10 @@ func (c *nep5TokenNative) transfer(ic *interop.Context, from, to util.Uint160, a return errors.New("negative amount") } - accFrom, err := ic.DAO.GetAccountStateOrNew(from) - if err != nil { - return err + keyFrom := makeAccountKey(from) + siFrom := ic.DAO.GetStorageItem(c.Hash, keyFrom) + if siFrom == nil { + return errors.New("insufficient funds") } isEmpty := from.Equals(to) || amount.Sign() == 0 @@ -146,22 +158,23 @@ func (c *nep5TokenNative) transfer(ic *interop.Context, from, to util.Uint160, a if isEmpty { inc = big.NewInt(0) } - if err := c.incBalance(ic, accFrom, inc); err != nil { + if err := c.incBalance(ic, from, siFrom, inc); err != nil { return err } - if err := ic.DAO.PutAccountState(accFrom); err != nil { + if err := ic.DAO.PutStorageItem(c.Hash, keyFrom, siFrom); err != nil { return err } if !isEmpty { - accTo, err := ic.DAO.GetAccountStateOrNew(to) - if err != nil { - return err + keyTo := makeAccountKey(to) + siTo := ic.DAO.GetStorageItem(c.Hash, keyTo) + if siTo == nil { + siTo = new(state.StorageItem) } - if err := c.incBalance(ic, accTo, amount); err != nil { + if err := c.incBalance(ic, to, siTo, amount); err != nil { return err } - if err := ic.DAO.PutAccountState(accTo); err != nil { + if err := ic.DAO.PutStorageItem(c.Hash, keyTo, siTo); err != nil { return err } } @@ -198,20 +211,21 @@ func (c *nep5TokenNative) addTokens(ic *interop.Context, h util.Uint160, amount return } - acc, err := ic.DAO.GetAccountStateOrNew(h) - if err != nil { - panic(err) + key := makeAccountKey(h) + si := ic.DAO.GetStorageItem(c.Hash, key) + if si == nil { + si = new(state.StorageItem) } - if err := c.incBalance(ic, acc, amount); err != nil { + if err := c.incBalance(ic, h, si, amount); err != nil { panic(err) } - if err := ic.DAO.PutAccountState(acc); err != nil { + if err := ic.DAO.PutStorageItem(c.Hash, key, si); err != nil { panic(err) } supply := c.getTotalSupply(ic) supply.Add(supply, amount) - err = c.saveTotalSupply(ic, supply) + err := c.saveTotalSupply(ic, supply) if err != nil { panic(err) } diff --git a/pkg/core/state/account.go b/pkg/core/state/account.go index f18a25d154..4d163b03c7 100644 --- a/pkg/core/state/account.go +++ b/pkg/core/state/account.go @@ -33,8 +33,6 @@ type Account struct { ScriptHash util.Uint160 IsFrozen bool Votes []*keys.PublicKey - GAS NEP5BalanceState - NEO NEOBalanceState Balances map[util.Uint256][]UnspentBalance Unclaimed UnclaimedBalances } @@ -57,8 +55,6 @@ func (s *Account) DecodeBinary(br *io.BinReader) { br.ReadBytes(s.ScriptHash[:]) s.IsFrozen = br.ReadBool() br.ReadArray(&s.Votes) - s.GAS.DecodeBinary(br) - s.NEO.DecodeBinary(br) s.Balances = make(map[util.Uint256][]UnspentBalance) lenBalances := br.ReadVarUint() @@ -84,8 +80,6 @@ func (s *Account) EncodeBinary(bw *io.BinWriter) { bw.WriteBytes(s.ScriptHash[:]) bw.WriteBool(s.IsFrozen) bw.WriteArray(s.Votes) - s.GAS.EncodeBinary(bw) - s.NEO.EncodeBinary(bw) bw.WriteVarUint(uint64(len(s.Balances))) for k, v := range s.Balances { diff --git a/pkg/core/state/native_state.go b/pkg/core/state/native_state.go index 9744cc754e..ad2340dbd5 100644 --- a/pkg/core/state/native_state.go +++ b/pkg/core/state/native_state.go @@ -18,6 +18,31 @@ type NEOBalanceState struct { BalanceHeight uint32 } +// NEP5BalanceStateFromBytes converts serialized NEP5BalanceState to structure. +func NEP5BalanceStateFromBytes(b []byte) (*NEP5BalanceState, error) { + balance := new(NEP5BalanceState) + if len(b) == 0 { + return balance, nil + } + r := io.NewBinReaderFromBuf(b) + balance.DecodeBinary(r) + + if r.Err != nil { + return nil, r.Err + } + return balance, nil +} + +// Bytes returns serialized NEP5BalanceState. +func (s *NEP5BalanceState) Bytes() []byte { + w := io.NewBufBinWriter() + s.EncodeBinary(w.BinWriter) + if w.Err != nil { + panic(w.Err) + } + return w.Bytes() +} + // EncodeBinary implements io.Serializable interface. func (s *NEP5BalanceState) EncodeBinary(w *io.BinWriter) { w.WriteVarBytes(emit.IntToBytes(&s.Balance)) @@ -32,6 +57,31 @@ func (s *NEP5BalanceState) DecodeBinary(r *io.BinReader) { s.Balance = *emit.BytesToInt(buf) } +// NEOBalanceStateFromBytes converts serialized NEOBalanceState to structure. +func NEOBalanceStateFromBytes(b []byte) (*NEOBalanceState, error) { + balance := new(NEOBalanceState) + if len(b) == 0 { + return balance, nil + } + r := io.NewBinReaderFromBuf(b) + balance.DecodeBinary(r) + + if r.Err != nil { + return nil, r.Err + } + return balance, nil +} + +// Bytes returns serialized NEOBalanceState. +func (s *NEOBalanceState) Bytes() []byte { + w := io.NewBufBinWriter() + s.EncodeBinary(w.BinWriter) + if w.Err != nil { + panic(w.Err) + } + return w.Bytes() +} + // EncodeBinary implements io.Serializable interface. func (s *NEOBalanceState) EncodeBinary(w *io.BinWriter) { s.NEP5BalanceState.EncodeBinary(w)