From 05544a151011bb33dd5ee94d31c93f64a519dd2c Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 11 Mar 2020 15:03:20 +0300 Subject: [PATCH 1/8] rpc: return empty arrays instead of null in getnep5* RPCs --- pkg/rpc/server/server.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index 05c7c2a43..43e4091b7 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -425,7 +425,10 @@ func (s *Server) getNEP5Balances(ps request.Params) (interface{}, error) { } as := s.chain.GetAccountState(u) - bs := &result.NEP5Balances{Address: address.Uint160ToString(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 { @@ -454,7 +457,11 @@ func (s *Server) getNEP5Transfers(ps request.Params) (interface{}, error) { return nil, response.ErrInvalidParams } - bs := &result.NEP5Transfers{Address: address.Uint160ToString(u)} + bs := &result.NEP5Transfers{ + Address: address.Uint160ToString(u), + Received: []result.NEP5Transfer{}, + Sent: []result.NEP5Transfer{}, + } lg := s.chain.GetNEP5TransferLog(u) cache := make(map[util.Uint160]int64) err = lg.ForEach(func(tr *state.NEP5Transfer) error { From d25dddc7803cd239b1fc14666a1c650eac6228e4 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 11 Mar 2020 15:07:14 +0300 Subject: [PATCH 2/8] rpc: refactor getDecimals Use existing functions to invoke smartcontract's method instead of constructing ad-hoc script. --- pkg/rpc/server/server.go | 45 +++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index 43e4091b7..6beddb0ea 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "math" - "math/big" "net/http" "strconv" @@ -23,9 +22,9 @@ import ( "github.com/nspcc-dev/neo-go/pkg/rpc/request" "github.com/nspcc-dev/neo-go/pkg/rpc/response" "github.com/nspcc-dev/neo-go/pkg/rpc/response/result" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/emit" - "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/pkg/errors" "go.uber.org/zap" ) @@ -515,29 +514,33 @@ func (s *Server) getDecimals(h util.Uint160, cache map[util.Uint160]int64) (int6 if d, ok := cache[h]; ok { return d, nil } - w := io.NewBufBinWriter() - emit.Int(w.BinWriter, 0) - emit.Opcode(w.BinWriter, opcode.NEWARRAY) - emit.String(w.BinWriter, "decimals") - emit.AppCall(w.BinWriter, h, true) - v, _ := s.chain.GetTestVM() - v.LoadScript(w.Bytes()) - if err := v.Run(); err != nil { + script, err := request.CreateFunctionInvocationScript(h, request.Params{ + { + Type: request.StringT, + Value: "decimals", + }, + { + Type: request.ArrayT, + Value: []request.Param{}, + }, + }) + if err != nil { return 0, err } - res := v.PopResult() - if res == nil { + res := s.runScriptInVM(script) + if res == nil || res.State != "HALT" || len(res.Stack) == 0 { + return 0, errors.New("execution error") + } + + var d int64 + switch item := res.Stack[len(res.Stack)-1]; item.Type { + case smartcontract.IntegerType: + d = item.Value.(int64) + case smartcontract.ByteArrayType: + d = emit.BytesToInt(item.Value.([]byte)).Int64() + default: return 0, errors.New("invalid result") } - bi, ok := res.(*big.Int) - if !ok { - bs, ok := res.([]byte) - if !ok { - return 0, errors.New("invalid result") - } - bi = emit.BytesToInt(bs) - } - d := bi.Int64() if d < 0 { return 0, errors.New("negative decimals") } From df2598c8dc4359bf7176745cb3074c181b0f82e1 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 11 Mar 2020 18:22:46 +0300 Subject: [PATCH 3/8] 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 From 3c6d9653b08bc97ade2ddb36d5bdaa661f3ecdcc Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 12 Mar 2020 12:32:24 +0300 Subject: [PATCH 4/8] core/state: add Size() method to NEP5TransferLog --- pkg/core/state/nep5.go | 5 +++++ pkg/core/state/nep5_test.go | 2 ++ 2 files changed, 7 insertions(+) diff --git a/pkg/core/state/nep5.go b/pkg/core/state/nep5.go index 60ed630f9..9ca8a0466 100644 --- a/pkg/core/state/nep5.go +++ b/pkg/core/state/nep5.go @@ -106,6 +106,11 @@ func (lg *NEP5TransferLog) ForEach(f func(*NEP5Transfer) error) error { return nil } +// Size returns an amount of transfer written in log. +func (lg *NEP5TransferLog) Size() int { + return len(lg.Raw) / NEP5TransferSize +} + // EncodeBinary implements io.Serializable interface. func (t *NEP5Tracker) EncodeBinary(w *io.BinWriter) { w.WriteU64LE(uint64(t.Balance)) diff --git a/pkg/core/state/nep5_test.go b/pkg/core/state/nep5_test.go index b4e70f5ea..36c20f7b5 100644 --- a/pkg/core/state/nep5_test.go +++ b/pkg/core/state/nep5_test.go @@ -25,6 +25,8 @@ func TestNEP5TransferLog_Append(t *testing.T) { require.NoError(t, lg.Append(tr)) } + require.Equal(t, len(expected), lg.Size()) + i := 0 err := lg.ForEach(func(tr *NEP5Transfer) error { require.Equal(t, expected[i], tr) From 32401a567e6a7cfec88aa3eab392010790b24373 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 12 Mar 2020 12:43:21 +0300 Subject: [PATCH 5/8] core: store NEP5Transfers in batches This is an append-only log which is read only during some RPCs. It is rather slow to get it from base every time we need to append to it. This commit stores all NEP5Transfers in batches, so that only a last batch needs to be unmarshaled during block processing. --- pkg/core/blockchain.go | 38 +++++++++++++++++++++++++++----------- pkg/core/dao.go | 29 ++++++++++++++++++++--------- pkg/core/state/nep5.go | 4 ++++ 3 files changed, 51 insertions(+), 20 deletions(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 13c4f2ca8..4a3a95aca 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -29,7 +29,7 @@ import ( // Tuning parameters. const ( headerBatchCount = 2000 - version = "0.0.7" + version = "0.0.8" // This one comes from C# code and it's different from the constant used // when creating an asset with Neo.Asset.Create interop call. It looks @@ -771,12 +771,16 @@ func (bc *Blockchain) processNEP5Transfer(cache *cachedDao, tx *transaction.Tran bs.Balance -= amount bs.LastUpdatedBlock = b.Index balances.Trackers[sc] = bs - if err := cache.PutNEP5Balances(fromAddr, balances); err != nil { - return - } transfer.Amount = -amount - if err := cache.AppendNEP5Transfer(fromAddr, transfer); err != nil { + isBig, err := cache.AppendNEP5Transfer(fromAddr, balances.NextTransferBatch, transfer) + if err != nil { + return + } + if isBig { + balances.NextTransferBatch++ + } + if err := cache.PutNEP5Balances(fromAddr, balances); err != nil { return } } @@ -789,12 +793,16 @@ func (bc *Blockchain) processNEP5Transfer(cache *cachedDao, tx *transaction.Tran bs.Balance += amount bs.LastUpdatedBlock = b.Index balances.Trackers[sc] = bs - if err := cache.PutNEP5Balances(toAddr, balances); err != nil { - return - } transfer.Amount = amount - if err := cache.AppendNEP5Transfer(toAddr, transfer); err != nil { + isBig, err := cache.AppendNEP5Transfer(toAddr, balances.NextTransferBatch, transfer) + if err != nil { + return + } + if isBig { + balances.NextTransferBatch++ + } + if err := cache.PutNEP5Balances(toAddr, balances); err != nil { return } } @@ -802,11 +810,19 @@ func (bc *Blockchain) processNEP5Transfer(cache *cachedDao, tx *transaction.Tran // GetNEP5TransferLog returns NEP5 transfer log for the acc. func (bc *Blockchain) GetNEP5TransferLog(acc util.Uint160) *state.NEP5TransferLog { - lg, err := bc.dao.GetNEP5TransferLog(acc) + balances, err := bc.dao.GetNEP5Balances(acc) if err != nil { return nil } - return lg + result := new(state.NEP5TransferLog) + for i := uint32(0); i <= balances.NextTransferBatch; i++ { + lg, err := bc.dao.GetNEP5TransferLog(acc, i) + if err != nil { + return nil + } + result.Raw = append(result.Raw, lg.Raw...) + } + return result } // GetNEP5Balances returns NEP5 balances for the acc. diff --git a/pkg/core/dao.go b/pkg/core/dao.go index abab95085..6a32b0f3e 100644 --- a/pkg/core/dao.go +++ b/pkg/core/dao.go @@ -158,9 +158,19 @@ func (dao *dao) PutNEP5Balances(acc util.Uint160, bs *state.NEP5Balances) error // -- start transfer log. +const nep5TransferBatchSize = 128 + +func getNEP5TransferLogKey(acc util.Uint160, index uint32) []byte { + key := make([]byte, 1+util.Uint160Size+4) + key[0] = byte(storage.STNEP5Transfers) + copy(key[1:], acc.BytesBE()) + binary.LittleEndian.PutUint32(key[util.Uint160Size:], index) + return key +} + // GetNEP5TransferLog retrieves transfer log from the cache. -func (dao *dao) GetNEP5TransferLog(acc util.Uint160) (*state.NEP5TransferLog, error) { - key := storage.AppendPrefix(storage.STNEP5Transfers, acc.BytesBE()) +func (dao *dao) GetNEP5TransferLog(acc util.Uint160, index uint32) (*state.NEP5TransferLog, error) { + key := getNEP5TransferLogKey(acc, index) value, err := dao.store.Get(key) if err != nil { if err == storage.ErrKeyNotFound { @@ -172,24 +182,25 @@ func (dao *dao) GetNEP5TransferLog(acc util.Uint160) (*state.NEP5TransferLog, er } // PutNEP5TransferLog saves given transfer log in the cache. -func (dao *dao) PutNEP5TransferLog(acc util.Uint160, lg *state.NEP5TransferLog) error { - key := storage.AppendPrefix(storage.STNEP5Transfers, acc.BytesBE()) +func (dao *dao) PutNEP5TransferLog(acc util.Uint160, index uint32, lg *state.NEP5TransferLog) error { + key := getNEP5TransferLogKey(acc, index) return dao.store.Put(key, lg.Raw) } // AppendNEP5Transfer appends a single NEP5 transfer to a log. -func (dao *dao) AppendNEP5Transfer(acc util.Uint160, tr *state.NEP5Transfer) error { - lg, err := dao.GetNEP5TransferLog(acc) +// First return value signalizes that log size has exceeded batch size. +func (dao *dao) AppendNEP5Transfer(acc util.Uint160, index uint32, tr *state.NEP5Transfer) (bool, error) { + lg, err := dao.GetNEP5TransferLog(acc, index) if err != nil { if err != storage.ErrKeyNotFound { - return err + return false, err } lg = new(state.NEP5TransferLog) } if err := lg.Append(tr); err != nil { - return err + return false, err } - return dao.PutNEP5TransferLog(acc, lg) + return lg.Size() >= nep5TransferBatchSize, dao.PutNEP5TransferLog(acc, index, lg) } // -- end transfer log. diff --git a/pkg/core/state/nep5.go b/pkg/core/state/nep5.go index 9ca8a0466..91fe30932 100644 --- a/pkg/core/state/nep5.go +++ b/pkg/core/state/nep5.go @@ -45,6 +45,8 @@ type NEP5Transfer struct { // to the corresponding structures. type NEP5Balances struct { Trackers map[util.Uint160]NEP5Tracker + // NextTransferBatch stores an index of the next transfer batch. + NextTransferBatch uint32 } // NewNEP5Balances returns new NEP5Balances. @@ -56,6 +58,7 @@ func NewNEP5Balances() *NEP5Balances { // DecodeBinary implements io.Serializable interface. func (bs *NEP5Balances) DecodeBinary(r *io.BinReader) { + bs.NextTransferBatch = r.ReadU32LE() lenBalances := r.ReadVarUint() m := make(map[util.Uint160]NEP5Tracker, lenBalances) for i := 0; i < int(lenBalances); i++ { @@ -70,6 +73,7 @@ func (bs *NEP5Balances) DecodeBinary(r *io.BinReader) { // EncodeBinary implements io.Serializable interface. func (bs *NEP5Balances) EncodeBinary(w *io.BinWriter) { + w.WriteU32LE(bs.NextTransferBatch) w.WriteVarUint(uint64(len(bs.Trackers))) for k, v := range bs.Trackers { w.WriteBytes(k[:]) From 6e0a57075fa17a073fb69f7ee86e885701b406fc Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 12 Mar 2020 12:47:07 +0300 Subject: [PATCH 6/8] *: gofmt --- pkg/rpc/server/server_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index b3a2a77af..77ff606d7 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -623,7 +623,7 @@ var rpcTestCases = map[string][]rpcTestCase{ require.True(t, ok) assert.Equal(t, res.Available, util.Fixed8FromInt64(8)) assert.True(t, res.Unavailable > 0) - assert.Equal(t, res.Available + res.Unavailable, res.Unclaimed) + assert.Equal(t, res.Available+res.Unavailable, res.Unclaimed) }, }, }, From 6fa2a998f42545db965db24a472a4de31d5e4419 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 12 Mar 2020 14:31:45 +0300 Subject: [PATCH 7/8] core: cache NEP5Balances in cached DAO --- pkg/core/cacheddao.go | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/pkg/core/cacheddao.go b/pkg/core/cacheddao.go index 8e8d03e7d..ce143cae1 100644 --- a/pkg/core/cacheddao.go +++ b/pkg/core/cacheddao.go @@ -14,6 +14,7 @@ type cachedDao struct { accounts map[util.Uint160]*state.Account contracts map[util.Uint160]*state.Contract unspents map[util.Uint256]*state.UnspentCoin + balances map[util.Uint160]*state.NEP5Balances } // newCachedDao returns new cachedDao wrapping around given backing store. @@ -21,7 +22,8 @@ func newCachedDao(backend storage.Store) *cachedDao { accs := make(map[util.Uint160]*state.Account) ctrs := make(map[util.Uint160]*state.Contract) unspents := make(map[util.Uint256]*state.UnspentCoin) - return &cachedDao{*newDao(backend), accs, ctrs, unspents} + balances := make(map[util.Uint160]*state.NEP5Balances) + return &cachedDao{*newDao(backend), accs, ctrs, unspents, balances} } // GetAccountStateOrNew retrieves Account from cache or underlying Store @@ -85,6 +87,20 @@ func (cd *cachedDao) PutUnspentCoinState(hash util.Uint256, ucs *state.UnspentCo return nil } +// GetNEP5Balances retrieves NEP5Balances for the acc. +func (cd *cachedDao) GetNEP5Balances(acc util.Uint160) (*state.NEP5Balances, error) { + if bs := cd.balances[acc]; bs != nil { + return bs, nil + } + return cd.dao.GetNEP5Balances(acc) +} + +// PutNEP5Balances saves NEP5Balances for the acc. +func (cd *cachedDao) PutNEP5Balances(acc util.Uint160, bs *state.NEP5Balances) error { + cd.balances[acc] = bs + return nil +} + // Persist flushes all the changes made into the (supposedly) persistent // underlying store. func (cd *cachedDao) Persist() (int, error) { @@ -100,5 +116,11 @@ func (cd *cachedDao) Persist() (int, error) { return 0, err } } + for acc, bs := range cd.balances { + err := cd.dao.PutNEP5Balances(acc, bs) + if err != nil { + return 0, err + } + } return cd.dao.Persist() } From ac475940a0a159a2fad8af83ec9d9e64fed73cb2 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 12 Mar 2020 14:49:59 +0300 Subject: [PATCH 8/8] core: cache NEP5Transfers in cached DAO --- pkg/core/cacheddao.go | 44 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/pkg/core/cacheddao.go b/pkg/core/cacheddao.go index ce143cae1..0094c31fb 100644 --- a/pkg/core/cacheddao.go +++ b/pkg/core/cacheddao.go @@ -15,6 +15,7 @@ type cachedDao struct { contracts map[util.Uint160]*state.Contract unspents map[util.Uint256]*state.UnspentCoin balances map[util.Uint160]*state.NEP5Balances + transfers map[util.Uint160]map[uint32]*state.NEP5TransferLog } // newCachedDao returns new cachedDao wrapping around given backing store. @@ -23,7 +24,8 @@ func newCachedDao(backend storage.Store) *cachedDao { ctrs := make(map[util.Uint160]*state.Contract) unspents := make(map[util.Uint256]*state.UnspentCoin) balances := make(map[util.Uint160]*state.NEP5Balances) - return &cachedDao{*newDao(backend), accs, ctrs, unspents, balances} + transfers := make(map[util.Uint160]map[uint32]*state.NEP5TransferLog) + return &cachedDao{*newDao(backend), accs, ctrs, unspents, balances, transfers} } // GetAccountStateOrNew retrieves Account from cache or underlying Store @@ -101,6 +103,38 @@ func (cd *cachedDao) PutNEP5Balances(acc util.Uint160, bs *state.NEP5Balances) e return nil } +// GetNEP5TransferLog retrieves NEP5TransferLog for the acc. +func (cd *cachedDao) GetNEP5TransferLog(acc util.Uint160, index uint32) (*state.NEP5TransferLog, error) { + ts := cd.transfers[acc] + if ts != nil && ts[index] != nil { + return ts[index], nil + } + return cd.dao.GetNEP5TransferLog(acc, index) +} + +// PutNEP5TransferLog saves NEP5TransferLog for the acc. +func (cd *cachedDao) PutNEP5TransferLog(acc util.Uint160, index uint32, bs *state.NEP5TransferLog) error { + ts := cd.transfers[acc] + if ts == nil { + ts = make(map[uint32]*state.NEP5TransferLog, 2) + cd.transfers[acc] = ts + } + ts[index] = bs + return nil +} + +// AppendNEP5Transfer appends new transfer to a transfer event log. +func (cd *cachedDao) AppendNEP5Transfer(acc util.Uint160, index uint32, tr *state.NEP5Transfer) (bool, error) { + lg, err := cd.GetNEP5TransferLog(acc, index) + if err != nil { + return false, err + } + if err := lg.Append(tr); err != nil { + return false, err + } + return lg.Size() >= nep5TransferBatchSize, cd.PutNEP5TransferLog(acc, index, lg) +} + // Persist flushes all the changes made into the (supposedly) persistent // underlying store. func (cd *cachedDao) Persist() (int, error) { @@ -122,5 +156,13 @@ func (cd *cachedDao) Persist() (int, error) { return 0, err } } + for acc, ts := range cd.transfers { + for ind, lg := range ts { + err := cd.dao.PutNEP5TransferLog(acc, ind, lg) + if err != nil { + return 0, err + } + } + } return cd.dao.Persist() }