From df2598c8dc4359bf7176745cb3074c181b0f82e1 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 11 Mar 2020 18:22:46 +0300 Subject: [PATCH] core: store NEP5 balances separately There is no need to take and unmarshal an account structure only to get it's NEP5 balances. --- pkg/core/blockchain.go | 31 +++++++++++++++++-------------- pkg/core/blockchainer.go | 1 + pkg/core/dao.go | 21 +++++++++++++++++++++ pkg/core/state/account.go | 21 --------------------- pkg/core/state/nep5.go | 36 ++++++++++++++++++++++++++++++++++++ pkg/core/storage/store.go | 1 + pkg/network/helper_test.go | 3 +++ pkg/rpc/server/server.go | 4 ++-- 8 files changed, 81 insertions(+), 37 deletions(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 01243fa1b..13c4f2ca8 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -763,18 +763,15 @@ func (bc *Blockchain) processNEP5Transfer(cache *cachedDao, tx *transaction.Tran Tx: tx.Hash(), } if !fromAddr.Equals(util.Uint160{}) { - acc, err := cache.GetAccountStateOrNew(fromAddr) + balances, err := cache.GetNEP5Balances(fromAddr) if err != nil { return } - bs := acc.NEP5Balances[sc] - if bs == nil { - bs = new(state.NEP5Tracker) - acc.NEP5Balances[sc] = bs - } + bs := balances.Trackers[sc] bs.Balance -= amount bs.LastUpdatedBlock = b.Index - if err := cache.PutAccountState(acc); err != nil { + balances.Trackers[sc] = bs + if err := cache.PutNEP5Balances(fromAddr, balances); err != nil { return } @@ -784,18 +781,15 @@ func (bc *Blockchain) processNEP5Transfer(cache *cachedDao, tx *transaction.Tran } } if !toAddr.Equals(util.Uint160{}) { - acc, err := cache.GetAccountStateOrNew(toAddr) + balances, err := cache.GetNEP5Balances(toAddr) if err != nil { return } - bs := acc.NEP5Balances[sc] - if bs == nil { - bs = new(state.NEP5Tracker) - acc.NEP5Balances[sc] = bs - } + bs := balances.Trackers[sc] bs.Balance += amount bs.LastUpdatedBlock = b.Index - if err := cache.PutAccountState(acc); err != nil { + balances.Trackers[sc] = bs + if err := cache.PutNEP5Balances(toAddr, balances); err != nil { return } @@ -815,6 +809,15 @@ func (bc *Blockchain) GetNEP5TransferLog(acc util.Uint160) *state.NEP5TransferLo return lg } +// GetNEP5Balances returns NEP5 balances for the acc. +func (bc *Blockchain) GetNEP5Balances(acc util.Uint160) *state.NEP5Balances { + bs, err := bc.dao.GetNEP5Balances(acc) + if err != nil { + return nil + } + return bs +} + // LastBatch returns last persisted storage batch. func (bc *Blockchain) LastBatch() *storage.MemBatch { return bc.lastBatch diff --git a/pkg/core/blockchainer.go b/pkg/core/blockchainer.go index 38de63b4e..57ee04f11 100644 --- a/pkg/core/blockchainer.go +++ b/pkg/core/blockchainer.go @@ -36,6 +36,7 @@ type Blockchainer interface { GetAccountState(util.Uint160) *state.Account GetAppExecResult(util.Uint256) (*state.AppExecResult, error) GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog + GetNEP5Balances(util.Uint160) *state.NEP5Balances GetValidators(txes ...*transaction.Transaction) ([]*keys.PublicKey, error) GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error) GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem diff --git a/pkg/core/dao.go b/pkg/core/dao.go index 4a2564797..abab95085 100644 --- a/pkg/core/dao.go +++ b/pkg/core/dao.go @@ -135,6 +135,27 @@ func (dao *dao) DeleteContractState(hash util.Uint160) error { // -- end contracts. +// -- start nep5 balances. + +// GetNEP5Balances retrieves nep5 balances from the cache. +func (dao *dao) GetNEP5Balances(acc util.Uint160) (*state.NEP5Balances, error) { + key := storage.AppendPrefix(storage.STNEP5Balances, acc.BytesBE()) + bs := state.NewNEP5Balances() + err := dao.GetAndDecode(bs, key) + if err != nil && err != storage.ErrKeyNotFound { + return nil, err + } + return bs, nil +} + +// GetNEP5Balances saves nep5 balances from the cache. +func (dao *dao) PutNEP5Balances(acc util.Uint160, bs *state.NEP5Balances) error { + key := storage.AppendPrefix(storage.STNEP5Balances, acc.BytesBE()) + return dao.Put(bs, key) +} + +// -- end nep5 balances. + // -- start transfer log. // GetNEP5TransferLog retrieves transfer log from the cache. diff --git a/pkg/core/state/account.go b/pkg/core/state/account.go index a857e0cae..828ce5bcc 100644 --- a/pkg/core/state/account.go +++ b/pkg/core/state/account.go @@ -35,9 +35,6 @@ type Account struct { Votes []*keys.PublicKey Balances map[util.Uint256][]UnspentBalance Unclaimed []UnclaimedBalance - // NEP5Balances is a map of the NEP5 contract hashes - // to the corresponding structures. - NEP5Balances map[util.Uint160]*NEP5Tracker } // NewAccount returns a new Account object. @@ -49,8 +46,6 @@ func NewAccount(scriptHash util.Uint160) *Account { Votes: []*keys.PublicKey{}, Balances: make(map[util.Uint256][]UnspentBalance), Unclaimed: []UnclaimedBalance{}, - - NEP5Balances: make(map[util.Uint160]*NEP5Tracker), } } @@ -75,16 +70,6 @@ func (s *Account) DecodeBinary(br *io.BinReader) { } br.ReadArray(&s.Unclaimed) - - lenBalances = br.ReadVarUint() - s.NEP5Balances = make(map[util.Uint160]*NEP5Tracker, lenBalances) - for i := 0; i < int(lenBalances); i++ { - var key util.Uint160 - var tr NEP5Tracker - br.ReadBytes(key[:]) - tr.DecodeBinary(br) - s.NEP5Balances[key] = &tr - } } // EncodeBinary encodes Account to the given BinWriter. @@ -104,12 +89,6 @@ func (s *Account) EncodeBinary(bw *io.BinWriter) { } bw.WriteArray(s.Unclaimed) - - bw.WriteVarUint(uint64(len(s.NEP5Balances))) - for k, v := range s.NEP5Balances { - bw.WriteBytes(k[:]) - v.EncodeBinary(bw) - } } // DecodeBinary implements io.Serializable interface. diff --git a/pkg/core/state/nep5.go b/pkg/core/state/nep5.go index 589a3d89c..60ed630f9 100644 --- a/pkg/core/state/nep5.go +++ b/pkg/core/state/nep5.go @@ -41,6 +41,42 @@ type NEP5Transfer struct { Tx util.Uint256 } +// NEP5Balances is a map of the NEP5 contract hashes +// to the corresponding structures. +type NEP5Balances struct { + Trackers map[util.Uint160]NEP5Tracker +} + +// NewNEP5Balances returns new NEP5Balances. +func NewNEP5Balances() *NEP5Balances { + return &NEP5Balances{ + Trackers: make(map[util.Uint160]NEP5Tracker), + } +} + +// DecodeBinary implements io.Serializable interface. +func (bs *NEP5Balances) DecodeBinary(r *io.BinReader) { + lenBalances := r.ReadVarUint() + m := make(map[util.Uint160]NEP5Tracker, lenBalances) + for i := 0; i < int(lenBalances); i++ { + var key util.Uint160 + var tr NEP5Tracker + r.ReadBytes(key[:]) + tr.DecodeBinary(r) + m[key] = tr + } + bs.Trackers = m +} + +// EncodeBinary implements io.Serializable interface. +func (bs *NEP5Balances) EncodeBinary(w *io.BinWriter) { + w.WriteVarUint(uint64(len(bs.Trackers))) + for k, v := range bs.Trackers { + w.WriteBytes(k[:]) + v.EncodeBinary(w) + } +} + // Append appends single transfer to a log. func (lg *NEP5TransferLog) Append(tr *NEP5Transfer) error { w := io.NewBufBinWriter() diff --git a/pkg/core/storage/store.go b/pkg/core/storage/store.go index a36523f4d..bc4669701 100644 --- a/pkg/core/storage/store.go +++ b/pkg/core/storage/store.go @@ -18,6 +18,7 @@ const ( STContract KeyPrefix = 0x50 STStorage KeyPrefix = 0x70 STNEP5Transfers KeyPrefix = 0x72 + STNEP5Balances KeyPrefix = 0x73 IXHeaderHashList KeyPrefix = 0x80 IXValidatorsCount KeyPrefix = 0x90 SYSCurrentBlock KeyPrefix = 0xc0 diff --git a/pkg/network/helper_test.go b/pkg/network/helper_test.go index 8a1cc258f..08e72adc8 100644 --- a/pkg/network/helper_test.go +++ b/pkg/network/helper_test.go @@ -94,6 +94,9 @@ func (chain testChain) GetAccountState(util.Uint160) *state.Account { func (chain testChain) GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog { panic("TODO") } +func (chain testChain) GetNEP5Balances(util.Uint160) *state.NEP5Balances { + panic("TODO") +} func (chain testChain) GetValidators(...*transaction.Transaction) ([]*keys.PublicKey, error) { panic("TODO") } diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index 6beddb0ea..4d944aab3 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -423,14 +423,14 @@ func (s *Server) getNEP5Balances(ps request.Params) (interface{}, error) { return nil, response.ErrInvalidParams } - as := s.chain.GetAccountState(u) + as := s.chain.GetNEP5Balances(u) bs := &result.NEP5Balances{ Address: address.Uint160ToString(u), Balances: []result.NEP5Balance{}, } if as != nil { cache := make(map[util.Uint160]int64) - for h, bal := range as.NEP5Balances { + for h, bal := range as.Trackers { dec, err := s.getDecimals(h, cache) if err != nil { continue