native: store NEO and GAS state in the storage

As it should be done (although current serialization format is not quite
right).
This commit is contained in:
Roman Khimov 2020-04-23 21:28:37 +03:00
parent 8c02c6b22c
commit 4e8ee697ee
5 changed files with 133 additions and 54 deletions

View file

@ -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
}

View file

@ -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 {
if sign := amount.Sign(); sign == 0 {
return nil
} else if sign == -1 && acc.NEO.Balance.Cmp(new(big.Int).Neg(amount)) == -1 {
return errors.New("insufficient funds")
}
if err := n.distributeGas(ic, acc); err != nil {
return err
}
acc.NEO.Balance.Add(&acc.NEO.Balance, amount)
return nil
}
func (n *NEO) distributeGas(ic *interop.Context, acc *state.Account) 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)
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
}
acc.NEO.BalanceHeight = ic.Block.Index
n.GAS.mint(ic, acc.ScriptHash, big.NewInt(int64(sys+net)))
if sign := amount.Sign(); sign == 0 {
return nil
} else if sign == -1 && acc.Balance.Cmp(new(big.Int).Neg(amount)) == -1 {
return errors.New("insufficient funds")
}
if err := n.distributeGas(ic, h, acc); err != nil {
return err
}
acc.Balance.Add(&acc.Balance, amount)
si.Value = acc.Bytes()
return nil
}
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.Balance.Int64()), acc.BalanceHeight, ic.Block.Index)
if err != nil {
return err
}
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).

View file

@ -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 {
keyTo := makeAccountKey(to)
siTo := ic.DAO.GetStorageItem(c.Hash, keyTo)
if siTo == nil {
siTo = new(state.StorageItem)
}
if err := c.incBalance(ic, to, siTo, amount); err != nil {
return err
}
if err := c.incBalance(ic, accTo, 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 {
key := makeAccountKey(h)
si := ic.DAO.GetStorageItem(c.Hash, key)
if si == nil {
si = new(state.StorageItem)
}
if err := c.incBalance(ic, h, si, amount); err != nil {
panic(err)
}
if err := c.incBalance(ic, acc, 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)
}

View file

@ -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 {

View file

@ -18,6 +18,30 @@ 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 +56,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)