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:
parent
8c02c6b22c
commit
4e8ee697ee
5 changed files with 133 additions and 54 deletions
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue