dao: drop GetStorageItems* APIs

They're just adding another useless caching layer to the Seek.
This commit is contained in:
Roman Khimov 2022-03-31 18:14:11 +03:00
parent 60375e9db3
commit c12a3b71d4
7 changed files with 70 additions and 132 deletions

View file

@ -310,29 +310,6 @@ func (dao *Simple) DeleteStorageItem(id int32, key []byte) {
dao.Store.Delete(stKey) dao.Store.Delete(stKey)
} }
// GetStorageItems returns all storage items for a given id.
func (dao *Simple) GetStorageItems(id int32) ([]state.StorageItemWithKey, error) {
return dao.GetStorageItemsWithPrefix(id, nil)
}
// GetStorageItemsWithPrefix returns all storage items with given id for a
// given scripthash.
func (dao *Simple) GetStorageItemsWithPrefix(id int32, prefix []byte) ([]state.StorageItemWithKey, error) {
var siArr []state.StorageItemWithKey
saveToArr := func(k, v []byte) bool {
// Cut prefix and hash.
// #1468, but don't need to copy here, because it is done by Store.
siArr = append(siArr, state.StorageItemWithKey{
Key: k,
Item: state.StorageItem(v),
})
return true
}
dao.Seek(id, storage.SeekRange{Prefix: prefix}, saveToArr)
return siArr, nil
}
// Seek executes f for all storage items matching a given `rng` (matching given prefix and // Seek executes f for all storage items matching a given `rng` (matching given prefix and
// starting from the point specified). If key or value is to be used outside of f, they // starting from the point specified). If key or value is to be used outside of f, they
// may not be copied. Seek continues iterating until false is returned from f. // may not be copied. Seek continues iterating until false is returned from f.

View file

@ -16,6 +16,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles" "github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/stateroot" "github.com/nspcc-dev/neo-go/pkg/core/stateroot"
"github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
@ -253,35 +254,31 @@ func (s *Designate) GetDesignatedByRole(d *dao.Simple, r noderoles.Role, index u
return val.nodes.Copy(), val.height, nil return val.nodes.Copy(), val.height, nil
} }
} }
kvs, err := d.GetStorageItemsWithPrefix(s.ID, []byte{byte(r)})
if err != nil {
return nil, 0, err
}
var ( var (
ns NodeList ns NodeList
bestIndex uint32 bestIndex uint32
resSi state.StorageItem resVal []byte
start = make([]byte, 4)
) )
// kvs are sorted by key (BE index bytes) in ascending way, so iterate backwards to get the latest designated.
for i := len(kvs) - 1; i >= 0; i-- { binary.BigEndian.PutUint32(start, index)
kv := kvs[i] d.Seek(s.ID, storage.SeekRange{
if len(kv.Key) < 4 { Prefix: []byte{byte(r)},
continue Start: start,
} Backwards: true,
siInd := binary.BigEndian.Uint32(kv.Key) }, func(k, v []byte) bool {
if siInd <= index { bestIndex = binary.BigEndian.Uint32(k) // If len(k) < 4 the DB is broken and it deserves a panic.
bestIndex = siInd resVal = v
resSi = kv.Item // Take just the latest item, it's the one we need.
break return false
} })
} if resVal != nil {
if resSi != nil { err := stackitem.DeserializeConvertible(resVal, &ns)
err = stackitem.DeserializeConvertible(resSi, &ns)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
} }
return keys.PublicKeys(ns), bestIndex, err return keys.PublicKeys(ns), bestIndex, nil
} }
func (s *Designate) designateAsRole(ic *interop.Context, args []stackitem.Item) stackitem.Item { func (s *Designate) designateAsRole(ic *interop.Context, args []stackitem.Item) stackitem.Item {

View file

@ -388,13 +388,11 @@ func (m *Management) Destroy(d *dao.Simple, hash util.Uint160) error {
key := MakeContractKey(hash) key := MakeContractKey(hash)
d.DeleteStorageItem(m.ID, key) d.DeleteStorageItem(m.ID, key)
d.DeleteContractID(contract.ID) d.DeleteContractID(contract.ID)
siArr, err := d.GetStorageItems(contract.ID)
if err != nil { d.Seek(contract.ID, storage.SeekRange{}, func(k, _ []byte) bool {
return err d.DeleteStorageItem(contract.ID, k)
} return true
for _, kv := range siArr { })
d.DeleteStorageItem(contract.ID, []byte(kv.Key))
}
m.markUpdated(hash) m.markUpdated(hash)
return nil return nil
} }

View file

@ -255,11 +255,7 @@ func (n *NEO) InitializeCache(bc interop.Ledger, d *dao.Simple) error {
return err return err
} }
gr, err := n.getSortedGASRecordFromDAO(d) n.gasPerBlock.Store(n.getSortedGASRecordFromDAO(d))
if err != nil {
return err
}
n.gasPerBlock.Store(gr)
n.gasPerBlockChanged.Store(false) n.gasPerBlockChanged.Store(false)
return nil return nil
@ -375,11 +371,7 @@ func (n *NEO) PostPersist(ic *interop.Context) error {
} }
} }
if n.gasPerBlockChanged.Load().(bool) { if n.gasPerBlockChanged.Load().(bool) {
gr, err := n.getSortedGASRecordFromDAO(ic.DAO) n.gasPerBlock.Store(n.getSortedGASRecordFromDAO(ic.DAO))
if err != nil {
panic(err)
}
n.gasPerBlock.Store(gr)
n.gasPerBlockChanged.Store(false) n.gasPerBlockChanged.Store(false)
} }
@ -495,35 +487,23 @@ func (n *NEO) getGASPerBlock(ic *interop.Context, _ []stackitem.Item) stackitem.
return stackitem.NewBigInteger(gas) return stackitem.NewBigInteger(gas)
} }
func (n *NEO) getSortedGASRecordFromDAO(d *dao.Simple) (gasRecord, error) { func (n *NEO) getSortedGASRecordFromDAO(d *dao.Simple) gasRecord {
grArr, err := d.GetStorageItemsWithPrefix(n.ID, []byte{prefixGASPerBlock}) var gr = make(gasRecord, 0)
if err != nil { d.Seek(n.ID, storage.SeekRange{Prefix: []byte{prefixGASPerBlock}}, func(k, v []byte) bool {
return gasRecord{}, fmt.Errorf("failed to get gas records from storage: %w", err) gr = append(gr, gasIndexPair{
} Index: binary.BigEndian.Uint32(k),
var gr = make(gasRecord, len(grArr)) GASPerBlock: *bigint.FromBytes(v),
for i, kv := range grArr { })
indexBytes, gasValue := kv.Key, kv.Item return true
gr[i] = gasIndexPair{ })
Index: binary.BigEndian.Uint32([]byte(indexBytes)), return gr
GASPerBlock: *bigint.FromBytes(gasValue),
}
}
// GAS records should be sorted by index, but GetStorageItemsWithPrefix returns
// values sorted by BE bytes of index, so we're OK with that.
return gr, nil
} }
// GetGASPerBlock returns gas generated for block with provided index. // GetGASPerBlock returns gas generated for block with provided index.
func (n *NEO) GetGASPerBlock(d *dao.Simple, index uint32) *big.Int { func (n *NEO) GetGASPerBlock(d *dao.Simple, index uint32) *big.Int {
var ( var gr gasRecord
gr gasRecord
err error
)
if n.gasPerBlockChanged.Load().(bool) { if n.gasPerBlockChanged.Load().(bool) {
gr, err = n.getSortedGASRecordFromDAO(d) gr = n.getSortedGASRecordFromDAO(d)
if err != nil {
panic(err)
}
} else { } else {
gr = n.gasPerBlock.Load().(gasRecord) gr = n.gasPerBlock.Load().(gasRecord)
} }
@ -665,17 +645,11 @@ func (n *NEO) CalculateNEOHolderReward(d *dao.Simple, value *big.Int, start, end
} else if value.Sign() < 0 { } else if value.Sign() < 0 {
return nil, errors.New("negative value") return nil, errors.New("negative value")
} }
var ( var gr gasRecord
gr gasRecord
err error
)
if !n.gasPerBlockChanged.Load().(bool) { if !n.gasPerBlockChanged.Load().(bool) {
gr = n.gasPerBlock.Load().(gasRecord) gr = n.gasPerBlock.Load().(gasRecord)
} else { } else {
gr, err = n.getSortedGASRecordFromDAO(d) gr = n.getSortedGASRecordFromDAO(d)
if err != nil {
return nil, err
}
} }
var sum, tmp big.Int var sum, tmp big.Int
for i := len(gr) - 1; i >= 0; i-- { for i := len(gr) - 1; i >= 0; i-- {
@ -847,17 +821,15 @@ func (n *NEO) ModifyAccountVotes(acc *state.NEOBalance, d *dao.Simple, value *bi
} }
func (n *NEO) getCandidates(d *dao.Simple, sortByKey bool) ([]keyWithVotes, error) { func (n *NEO) getCandidates(d *dao.Simple, sortByKey bool) ([]keyWithVotes, error) {
siArr, err := d.GetStorageItemsWithPrefix(n.ID, []byte{prefixCandidate}) arr := make([]keyWithVotes, 0)
if err != nil { d.Seek(n.ID, storage.SeekRange{Prefix: []byte{prefixCandidate}}, func(k, v []byte) bool {
return nil, err c := new(candidate).FromBytes(v)
}
arr := make([]keyWithVotes, 0, len(siArr))
for _, kv := range siArr {
c := new(candidate).FromBytes(kv.Item)
if c.Registered { if c.Registered {
arr = append(arr, keyWithVotes{Key: string(kv.Key), Votes: &c.Votes}) arr = append(arr, keyWithVotes{Key: string(k), Votes: &c.Votes})
} }
} return true
})
if !sortByKey { if !sortByKey {
// sortByKey assumes to sort by serialized key bytes (that's the way keys // sortByKey assumes to sort by serialized key bytes (that's the way keys
// are stored and retrieved from the storage by default). Otherwise, need // are stored and retrieved from the storage by default). Otherwise, need

View file

@ -473,22 +473,24 @@ func (o *Oracle) getOriginalTxID(d *dao.Simple, tx *transaction.Transaction) uti
// getRequests returns all requests which have not been finished yet. // getRequests returns all requests which have not been finished yet.
func (o *Oracle) getRequests(d *dao.Simple) (map[uint64]*state.OracleRequest, error) { func (o *Oracle) getRequests(d *dao.Simple) (map[uint64]*state.OracleRequest, error) {
arr, err := d.GetStorageItemsWithPrefix(o.ID, prefixRequest) var reqs = make(map[uint64]*state.OracleRequest)
if err != nil { var err error
return nil, err d.Seek(o.ID, storage.SeekRange{Prefix: prefixRequest}, func(k, v []byte) bool {
} if len(k) != 8 {
reqs := make(map[uint64]*state.OracleRequest, len(arr)) err = errors.New("invalid request ID")
for _, kv := range arr { return false
if len(kv.Key) != 8 {
return nil, errors.New("invalid request ID")
} }
req := new(state.OracleRequest) req := new(state.OracleRequest)
err = stackitem.DeserializeConvertible(kv.Item, req) err = stackitem.DeserializeConvertible(v, req)
if err != nil { if err != nil {
return nil, err return false
} }
id := binary.BigEndian.Uint64([]byte(kv.Key)) id := binary.BigEndian.Uint64(k)
reqs[id] = req reqs[id] = req
return true
})
if err != nil {
return nil, err
} }
return reqs, nil return reqs, nil
} }

View file

@ -10,6 +10,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
@ -156,23 +157,20 @@ func (p *Policy) PostPersist(ic *interop.Context) error {
p.storagePrice = uint32(getIntWithKey(p.ID, ic.DAO, storagePriceKey)) p.storagePrice = uint32(getIntWithKey(p.ID, ic.DAO, storagePriceKey))
p.blockedAccounts = make([]util.Uint160, 0) p.blockedAccounts = make([]util.Uint160, 0)
siArr, err := ic.DAO.GetStorageItemsWithPrefix(p.ID, []byte{blockedAccountPrefix}) var fErr error
if err != nil { ic.DAO.Seek(p.ID, storage.SeekRange{Prefix: []byte{blockedAccountPrefix}}, func(k, _ []byte) bool {
return fmt.Errorf("failed to get blocked accounts from storage: %w", err) hash, err := util.Uint160DecodeBytesBE(k)
}
for _, kv := range siArr {
hash, err := util.Uint160DecodeBytesBE([]byte(kv.Key))
if err != nil { if err != nil {
return fmt.Errorf("failed to decode blocked account hash: %w", err) fErr = fmt.Errorf("failed to decode blocked account hash: %w", err)
return false
} }
p.blockedAccounts = append(p.blockedAccounts, hash) p.blockedAccounts = append(p.blockedAccounts, hash)
return true
})
if fErr == nil {
p.isValid = true
} }
// blockedAccounts should be sorted by account BE bytes, but GetStorageItemsWithPrefix return fErr
// returns values sorted by key (which is account's BE bytes), so don't need to sort
// one more time.
p.isValid = true
return nil
} }
// getFeePerByte is Policy contract method and returns required transaction's fee // getFeePerByte is Policy contract method and returns required transaction's fee

View file

@ -2,9 +2,3 @@ package state
// StorageItem is the value to be stored with read-only flag. // StorageItem is the value to be stored with read-only flag.
type StorageItem []byte type StorageItem []byte
// StorageItemWithKey is a storage item with corresponding key.
type StorageItemWithKey struct {
Key []byte
Item StorageItem
}