Skip to content

Commit

Permalink
native: store NEO and GAS state in the storage
Browse files Browse the repository at this point in the history
As it should be done (although current serialization format is not quite
right).
  • Loading branch information
roman-khimov committed Apr 23, 2020
1 parent 608ba4e commit 0642931
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 40 deletions.
11 changes: 8 additions & 3 deletions pkg/core/native/native_gas.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
45 changes: 31 additions & 14 deletions pkg/core/native/native_neo.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down Expand Up @@ -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()
Expand All @@ -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
Expand All @@ -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).
Expand Down
48 changes: 31 additions & 17 deletions pkg/core/native/native_nep5.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -136,32 +147,34 @@ 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
inc := amount
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
}
}
Expand Down Expand Up @@ -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)
}
Expand Down
6 changes: 0 additions & 6 deletions pkg/core/state/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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()
Expand All @@ -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 {
Expand Down
50 changes: 50 additions & 0 deletions pkg/core/state/native_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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)
Expand Down

0 comments on commit 0642931

Please sign in to comment.