core: store NEP5 balances separately

There is no need to take and unmarshal an account structure only
to get it's NEP5 balances.
This commit is contained in:
Evgenii Stratonikov 2020-03-11 18:22:46 +03:00
parent d25dddc780
commit df2598c8dc
8 changed files with 81 additions and 37 deletions

View file

@ -763,18 +763,15 @@ func (bc *Blockchain) processNEP5Transfer(cache *cachedDao, tx *transaction.Tran
Tx: tx.Hash(), Tx: tx.Hash(),
} }
if !fromAddr.Equals(util.Uint160{}) { if !fromAddr.Equals(util.Uint160{}) {
acc, err := cache.GetAccountStateOrNew(fromAddr) balances, err := cache.GetNEP5Balances(fromAddr)
if err != nil { if err != nil {
return return
} }
bs := acc.NEP5Balances[sc] bs := balances.Trackers[sc]
if bs == nil {
bs = new(state.NEP5Tracker)
acc.NEP5Balances[sc] = bs
}
bs.Balance -= amount bs.Balance -= amount
bs.LastUpdatedBlock = b.Index bs.LastUpdatedBlock = b.Index
if err := cache.PutAccountState(acc); err != nil { balances.Trackers[sc] = bs
if err := cache.PutNEP5Balances(fromAddr, balances); err != nil {
return return
} }
@ -784,18 +781,15 @@ func (bc *Blockchain) processNEP5Transfer(cache *cachedDao, tx *transaction.Tran
} }
} }
if !toAddr.Equals(util.Uint160{}) { if !toAddr.Equals(util.Uint160{}) {
acc, err := cache.GetAccountStateOrNew(toAddr) balances, err := cache.GetNEP5Balances(toAddr)
if err != nil { if err != nil {
return return
} }
bs := acc.NEP5Balances[sc] bs := balances.Trackers[sc]
if bs == nil {
bs = new(state.NEP5Tracker)
acc.NEP5Balances[sc] = bs
}
bs.Balance += amount bs.Balance += amount
bs.LastUpdatedBlock = b.Index bs.LastUpdatedBlock = b.Index
if err := cache.PutAccountState(acc); err != nil { balances.Trackers[sc] = bs
if err := cache.PutNEP5Balances(toAddr, balances); err != nil {
return return
} }
@ -815,6 +809,15 @@ func (bc *Blockchain) GetNEP5TransferLog(acc util.Uint160) *state.NEP5TransferLo
return lg 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. // LastBatch returns last persisted storage batch.
func (bc *Blockchain) LastBatch() *storage.MemBatch { func (bc *Blockchain) LastBatch() *storage.MemBatch {
return bc.lastBatch return bc.lastBatch

View file

@ -36,6 +36,7 @@ type Blockchainer interface {
GetAccountState(util.Uint160) *state.Account GetAccountState(util.Uint160) *state.Account
GetAppExecResult(util.Uint256) (*state.AppExecResult, error) GetAppExecResult(util.Uint256) (*state.AppExecResult, error)
GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog
GetNEP5Balances(util.Uint160) *state.NEP5Balances
GetValidators(txes ...*transaction.Transaction) ([]*keys.PublicKey, error) GetValidators(txes ...*transaction.Transaction) ([]*keys.PublicKey, error)
GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error) GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error)
GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem

View file

@ -135,6 +135,27 @@ func (dao *dao) DeleteContractState(hash util.Uint160) error {
// -- end contracts. // -- 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. // -- start transfer log.
// GetNEP5TransferLog retrieves transfer log from the cache. // GetNEP5TransferLog retrieves transfer log from the cache.

View file

@ -35,9 +35,6 @@ type Account struct {
Votes []*keys.PublicKey Votes []*keys.PublicKey
Balances map[util.Uint256][]UnspentBalance Balances map[util.Uint256][]UnspentBalance
Unclaimed []UnclaimedBalance 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. // NewAccount returns a new Account object.
@ -49,8 +46,6 @@ func NewAccount(scriptHash util.Uint160) *Account {
Votes: []*keys.PublicKey{}, Votes: []*keys.PublicKey{},
Balances: make(map[util.Uint256][]UnspentBalance), Balances: make(map[util.Uint256][]UnspentBalance),
Unclaimed: []UnclaimedBalance{}, Unclaimed: []UnclaimedBalance{},
NEP5Balances: make(map[util.Uint160]*NEP5Tracker),
} }
} }
@ -75,16 +70,6 @@ func (s *Account) DecodeBinary(br *io.BinReader) {
} }
br.ReadArray(&s.Unclaimed) 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. // EncodeBinary encodes Account to the given BinWriter.
@ -104,12 +89,6 @@ func (s *Account) EncodeBinary(bw *io.BinWriter) {
} }
bw.WriteArray(s.Unclaimed) 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. // DecodeBinary implements io.Serializable interface.

View file

@ -41,6 +41,42 @@ type NEP5Transfer struct {
Tx util.Uint256 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. // Append appends single transfer to a log.
func (lg *NEP5TransferLog) Append(tr *NEP5Transfer) error { func (lg *NEP5TransferLog) Append(tr *NEP5Transfer) error {
w := io.NewBufBinWriter() w := io.NewBufBinWriter()

View file

@ -18,6 +18,7 @@ const (
STContract KeyPrefix = 0x50 STContract KeyPrefix = 0x50
STStorage KeyPrefix = 0x70 STStorage KeyPrefix = 0x70
STNEP5Transfers KeyPrefix = 0x72 STNEP5Transfers KeyPrefix = 0x72
STNEP5Balances KeyPrefix = 0x73
IXHeaderHashList KeyPrefix = 0x80 IXHeaderHashList KeyPrefix = 0x80
IXValidatorsCount KeyPrefix = 0x90 IXValidatorsCount KeyPrefix = 0x90
SYSCurrentBlock KeyPrefix = 0xc0 SYSCurrentBlock KeyPrefix = 0xc0

View file

@ -94,6 +94,9 @@ func (chain testChain) GetAccountState(util.Uint160) *state.Account {
func (chain testChain) GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog { func (chain testChain) GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog {
panic("TODO") panic("TODO")
} }
func (chain testChain) GetNEP5Balances(util.Uint160) *state.NEP5Balances {
panic("TODO")
}
func (chain testChain) GetValidators(...*transaction.Transaction) ([]*keys.PublicKey, error) { func (chain testChain) GetValidators(...*transaction.Transaction) ([]*keys.PublicKey, error) {
panic("TODO") panic("TODO")
} }

View file

@ -423,14 +423,14 @@ func (s *Server) getNEP5Balances(ps request.Params) (interface{}, error) {
return nil, response.ErrInvalidParams return nil, response.ErrInvalidParams
} }
as := s.chain.GetAccountState(u) as := s.chain.GetNEP5Balances(u)
bs := &result.NEP5Balances{ bs := &result.NEP5Balances{
Address: address.Uint160ToString(u), Address: address.Uint160ToString(u),
Balances: []result.NEP5Balance{}, Balances: []result.NEP5Balance{},
} }
if as != nil { if as != nil {
cache := make(map[util.Uint160]int64) cache := make(map[util.Uint160]int64)
for h, bal := range as.NEP5Balances { for h, bal := range as.Trackers {
dec, err := s.getDecimals(h, cache) dec, err := s.getDecimals(h, cache)
if err != nil { if err != nil {
continue continue