forked from TrueCloudLab/neoneo-go
core: refactor callers of MemCachedStore.Seek
MemCachedStore.Seek now sorts results, so its callers may omit sorting.
This commit is contained in:
parent
7ba88e98e2
commit
72726d46d3
11 changed files with 73 additions and 68 deletions
|
@ -332,7 +332,7 @@ func (chain *FakeChain) GetTestVM(t trigger.Type, tx *transaction.Transaction, b
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStorageItems implements Blockchainer interface.
|
// GetStorageItems implements Blockchainer interface.
|
||||||
func (chain *FakeChain) GetStorageItems(id int32) (map[string]state.StorageItem, error) {
|
func (chain *FakeChain) GetStorageItems(id int32) ([]state.StorageItemWithKey, error) {
|
||||||
panic("TODO")
|
panic("TODO")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1494,7 +1494,7 @@ func (bc *Blockchain) GetStorageItem(id int32, key []byte) state.StorageItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStorageItems returns all storage items for a given contract id.
|
// GetStorageItems returns all storage items for a given contract id.
|
||||||
func (bc *Blockchain) GetStorageItems(id int32) (map[string]state.StorageItem, error) {
|
func (bc *Blockchain) GetStorageItems(id int32) ([]state.StorageItemWithKey, error) {
|
||||||
return bc.dao.GetStorageItems(id)
|
return bc.dao.GetStorageItems(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,7 @@ type Blockchainer interface {
|
||||||
GetStateModule() StateRoot
|
GetStateModule() StateRoot
|
||||||
GetStateSyncModule() StateSync
|
GetStateSyncModule() StateSync
|
||||||
GetStorageItem(id int32, key []byte) state.StorageItem
|
GetStorageItem(id int32, key []byte) state.StorageItem
|
||||||
GetStorageItems(id int32) (map[string]state.StorageItem, error)
|
GetStorageItems(id int32) ([]state.StorageItemWithKey, error)
|
||||||
GetTestVM(t trigger.Type, tx *transaction.Transaction, b *block.Block) *vm.VM
|
GetTestVM(t trigger.Type, tx *transaction.Transaction, b *block.Block) *vm.VM
|
||||||
GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error)
|
GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error)
|
||||||
SetOracle(service services.Oracle)
|
SetOracle(service services.Oracle)
|
||||||
|
|
|
@ -48,8 +48,8 @@ type DAO interface {
|
||||||
GetStateSyncPoint() (uint32, error)
|
GetStateSyncPoint() (uint32, error)
|
||||||
GetStateSyncCurrentBlockHeight() (uint32, error)
|
GetStateSyncCurrentBlockHeight() (uint32, error)
|
||||||
GetStorageItem(id int32, key []byte) state.StorageItem
|
GetStorageItem(id int32, key []byte) state.StorageItem
|
||||||
GetStorageItems(id int32) (map[string]state.StorageItem, error)
|
GetStorageItems(id int32) ([]state.StorageItemWithKey, error)
|
||||||
GetStorageItemsWithPrefix(id int32, prefix []byte) (map[string]state.StorageItem, error)
|
GetStorageItemsWithPrefix(id int32, prefix []byte) ([]state.StorageItemWithKey, error)
|
||||||
GetTransaction(hash util.Uint256) (*transaction.Transaction, uint32, error)
|
GetTransaction(hash util.Uint256) (*transaction.Transaction, uint32, error)
|
||||||
GetVersion() (string, error)
|
GetVersion() (string, error)
|
||||||
GetWrapped() DAO
|
GetWrapped() DAO
|
||||||
|
@ -313,28 +313,31 @@ func (dao *Simple) DeleteStorageItem(id int32, key []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStorageItems returns all storage items for a given id.
|
// GetStorageItems returns all storage items for a given id.
|
||||||
func (dao *Simple) GetStorageItems(id int32) (map[string]state.StorageItem, error) {
|
func (dao *Simple) GetStorageItems(id int32) ([]state.StorageItemWithKey, error) {
|
||||||
return dao.GetStorageItemsWithPrefix(id, nil)
|
return dao.GetStorageItemsWithPrefix(id, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStorageItemsWithPrefix returns all storage items with given id for a
|
// GetStorageItemsWithPrefix returns all storage items with given id for a
|
||||||
// given scripthash.
|
// given scripthash.
|
||||||
func (dao *Simple) GetStorageItemsWithPrefix(id int32, prefix []byte) (map[string]state.StorageItem, error) {
|
func (dao *Simple) GetStorageItemsWithPrefix(id int32, prefix []byte) ([]state.StorageItemWithKey, error) {
|
||||||
var siMap = make(map[string]state.StorageItem)
|
var siArr []state.StorageItemWithKey
|
||||||
|
|
||||||
saveToMap := func(k, v []byte) {
|
saveToArr := func(k, v []byte) {
|
||||||
// Cut prefix and hash.
|
// Cut prefix and hash.
|
||||||
// Must copy here, #1468.
|
// Must copy here, #1468.
|
||||||
key := slice.Copy(k)
|
key := slice.Copy(k)
|
||||||
val := slice.Copy(v)
|
val := slice.Copy(v)
|
||||||
siMap[string(key)] = state.StorageItem(val)
|
siArr = append(siArr, state.StorageItemWithKey{
|
||||||
|
Key: key,
|
||||||
|
Item: state.StorageItem(val),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
dao.Seek(id, prefix, saveToMap)
|
dao.Seek(id, prefix, saveToArr)
|
||||||
return siMap, nil
|
return siArr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seek executes f for all items with a given prefix.
|
// Seek executes f for all items with a given prefix.
|
||||||
// If key is to be used outside of f, they must be copied.
|
// If key is to be used outside of f, they may not be copied.
|
||||||
func (dao *Simple) Seek(id int32, prefix []byte, f func(k, v []byte)) {
|
func (dao *Simple) Seek(id int32, prefix []byte, f func(k, v []byte)) {
|
||||||
lookupKey := makeStorageItemKey(id, nil)
|
lookupKey := makeStorageItemKey(id, nil)
|
||||||
if prefix != nil {
|
if prefix != nil {
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
|
@ -188,26 +186,23 @@ func storageFind(ic *interop.Context) error {
|
||||||
if opts&istorage.FindDeserialize == 0 && (opts&istorage.FindPick0 != 0 || opts&istorage.FindPick1 != 0) {
|
if opts&istorage.FindDeserialize == 0 && (opts&istorage.FindPick0 != 0 || opts&istorage.FindPick1 != 0) {
|
||||||
return fmt.Errorf("%w: PickN is specified without Deserialize", errFindInvalidOptions)
|
return fmt.Errorf("%w: PickN is specified without Deserialize", errFindInvalidOptions)
|
||||||
}
|
}
|
||||||
siMap, err := ic.DAO.GetStorageItemsWithPrefix(stc.ID, prefix)
|
siArr, err := ic.DAO.GetStorageItemsWithPrefix(stc.ID, prefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
arr := make([]stackitem.MapElement, 0, len(siMap))
|
arr := make([]stackitem.MapElement, 0, len(siArr))
|
||||||
for k, v := range siMap {
|
for _, kv := range siArr {
|
||||||
keycopy := make([]byte, len(k)+len(prefix))
|
keycopy := make([]byte, len(kv.Key)+len(prefix))
|
||||||
copy(keycopy, prefix)
|
copy(keycopy, prefix)
|
||||||
copy(keycopy[len(prefix):], k)
|
copy(keycopy[len(prefix):], kv.Key)
|
||||||
arr = append(arr, stackitem.MapElement{
|
arr = append(arr, stackitem.MapElement{
|
||||||
Key: stackitem.NewByteArray(keycopy),
|
Key: stackitem.NewByteArray(keycopy),
|
||||||
Value: stackitem.NewByteArray(v),
|
Value: stackitem.NewByteArray(kv.Item),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
sort.Slice(arr, func(i, j int) bool {
|
// Items in arr should be sorted by key, but GetStorageItemsWithPrefix returns
|
||||||
k1 := arr[i].Key.Value().([]byte)
|
// sorted items, so no need to sort them one more time.
|
||||||
k2 := arr[j].Key.Value().([]byte)
|
|
||||||
return bytes.Compare(k1, k2) == -1
|
|
||||||
})
|
|
||||||
|
|
||||||
filteredMap := stackitem.NewMapWithValue(arr)
|
filteredMap := stackitem.NewMapWithValue(arr)
|
||||||
item := istorage.NewIterator(filteredMap, len(prefix), opts)
|
item := istorage.NewIterator(filteredMap, len(prefix), opts)
|
||||||
|
|
|
@ -257,17 +257,22 @@ func (s *Designate) GetDesignatedByRole(d dao.DAO, r noderoles.Role, index uint3
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
var ns NodeList
|
var (
|
||||||
var bestIndex uint32
|
ns NodeList
|
||||||
var resSi state.StorageItem
|
bestIndex uint32
|
||||||
for k, si := range kvs {
|
resSi state.StorageItem
|
||||||
if len(k) < 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-- {
|
||||||
|
kv := kvs[i]
|
||||||
|
if len(kv.Key) < 4 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
siInd := binary.BigEndian.Uint32([]byte(k))
|
siInd := binary.BigEndian.Uint32(kv.Key)
|
||||||
if (resSi == nil || siInd > bestIndex) && siInd <= index {
|
if siInd <= index {
|
||||||
bestIndex = siInd
|
bestIndex = siInd
|
||||||
resSi = si
|
resSi = kv.Item
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if resSi != nil {
|
if resSi != nil {
|
||||||
|
|
|
@ -391,12 +391,12 @@ func (m *Management) Destroy(d dao.DAO, hash util.Uint160) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
siMap, err := d.GetStorageItems(contract.ID)
|
siArr, err := d.GetStorageItems(contract.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for k := range siMap {
|
for _, kv := range siArr {
|
||||||
err := d.DeleteStorageItem(contract.ID, []byte(k))
|
err := d.DeleteStorageItem(contract.ID, []byte(kv.Key))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -471,24 +471,20 @@ func (n *NEO) getGASPerBlock(ic *interop.Context, _ []stackitem.Item) stackitem.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) getSortedGASRecordFromDAO(d dao.DAO) (gasRecord, error) {
|
func (n *NEO) getSortedGASRecordFromDAO(d dao.DAO) (gasRecord, error) {
|
||||||
grMap, err := d.GetStorageItemsWithPrefix(n.ID, []byte{prefixGASPerBlock})
|
grArr, err := d.GetStorageItemsWithPrefix(n.ID, []byte{prefixGASPerBlock})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gasRecord{}, fmt.Errorf("failed to get gas records from storage: %w", err)
|
return gasRecord{}, fmt.Errorf("failed to get gas records from storage: %w", err)
|
||||||
}
|
}
|
||||||
var (
|
var gr = make(gasRecord, len(grArr))
|
||||||
i int
|
for i, kv := range grArr {
|
||||||
gr = make(gasRecord, len(grMap))
|
indexBytes, gasValue := kv.Key, kv.Item
|
||||||
)
|
|
||||||
for indexBytes, gasValue := range grMap {
|
|
||||||
gr[i] = gasIndexPair{
|
gr[i] = gasIndexPair{
|
||||||
Index: binary.BigEndian.Uint32([]byte(indexBytes)),
|
Index: binary.BigEndian.Uint32([]byte(indexBytes)),
|
||||||
GASPerBlock: *bigint.FromBytes(gasValue),
|
GASPerBlock: *bigint.FromBytes(gasValue),
|
||||||
}
|
}
|
||||||
i++
|
|
||||||
}
|
}
|
||||||
sort.Slice(gr, func(i, j int) bool {
|
// GAS records should be sorted by index, but GetStorageItemsWithPrefix returns
|
||||||
return gr[i].Index < gr[j].Index
|
// values sorted by BE bytes of index, so we're OK with that.
|
||||||
})
|
|
||||||
return gr, nil
|
return gr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -836,21 +832,21 @@ func (n *NEO) ModifyAccountVotes(acc *state.NEOBalance, d dao.DAO, value *big.In
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) getCandidates(d dao.DAO, sortByKey bool) ([]keyWithVotes, error) {
|
func (n *NEO) getCandidates(d dao.DAO, sortByKey bool) ([]keyWithVotes, error) {
|
||||||
siMap, err := d.GetStorageItemsWithPrefix(n.ID, []byte{prefixCandidate})
|
siArr, err := d.GetStorageItemsWithPrefix(n.ID, []byte{prefixCandidate})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
arr := make([]keyWithVotes, 0, len(siMap))
|
arr := make([]keyWithVotes, 0, len(siArr))
|
||||||
for key, si := range siMap {
|
for _, kv := range siArr {
|
||||||
c := new(candidate).FromBytes(si)
|
c := new(candidate).FromBytes(kv.Item)
|
||||||
if c.Registered {
|
if c.Registered {
|
||||||
arr = append(arr, keyWithVotes{Key: key, Votes: &c.Votes})
|
arr = append(arr, keyWithVotes{Key: string(kv.Key), Votes: &c.Votes})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if sortByKey {
|
if !sortByKey {
|
||||||
// Sort by serialized key bytes (that's the way keys are stored and retrieved from the storage by default).
|
// sortByKey assumes to sort by serialized key bytes (that's the way keys
|
||||||
sort.Slice(arr, func(i, j int) bool { return strings.Compare(arr[i].Key, arr[j].Key) == -1 })
|
// are stored and retrieved from the storage by default). Otherwise, need
|
||||||
} else {
|
// to sort using big.Int comparator.
|
||||||
sort.Slice(arr, func(i, j int) bool {
|
sort.Slice(arr, func(i, j int) bool {
|
||||||
// The most-voted validators should end up in the front of the list.
|
// The most-voted validators should end up in the front of the list.
|
||||||
cmp := arr[i].Votes.Cmp(arr[j].Votes)
|
cmp := arr[i].Votes.Cmp(arr[j].Votes)
|
||||||
|
|
|
@ -483,21 +483,21 @@ func (o *Oracle) getOriginalTxID(d dao.DAO, tx *transaction.Transaction) util.Ui
|
||||||
|
|
||||||
// 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.DAO) (map[uint64]*state.OracleRequest, error) {
|
func (o *Oracle) getRequests(d dao.DAO) (map[uint64]*state.OracleRequest, error) {
|
||||||
m, err := d.GetStorageItemsWithPrefix(o.ID, prefixRequest)
|
arr, err := d.GetStorageItemsWithPrefix(o.ID, prefixRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
reqs := make(map[uint64]*state.OracleRequest, len(m))
|
reqs := make(map[uint64]*state.OracleRequest, len(arr))
|
||||||
for k, si := range m {
|
for _, kv := range arr {
|
||||||
if len(k) != 8 {
|
if len(kv.Key) != 8 {
|
||||||
return nil, errors.New("invalid request ID")
|
return nil, errors.New("invalid request ID")
|
||||||
}
|
}
|
||||||
req := new(state.OracleRequest)
|
req := new(state.OracleRequest)
|
||||||
err = stackitem.DeserializeConvertible(si, req)
|
err = stackitem.DeserializeConvertible(kv.Item, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
id := binary.BigEndian.Uint64([]byte(k))
|
id := binary.BigEndian.Uint64([]byte(kv.Key))
|
||||||
reqs[id] = req
|
reqs[id] = req
|
||||||
}
|
}
|
||||||
return reqs, nil
|
return reqs, nil
|
||||||
|
|
|
@ -162,20 +162,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)
|
||||||
siMap, err := ic.DAO.GetStorageItemsWithPrefix(p.ID, []byte{blockedAccountPrefix})
|
siArr, err := ic.DAO.GetStorageItemsWithPrefix(p.ID, []byte{blockedAccountPrefix})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get blocked accounts from storage: %w", err)
|
return fmt.Errorf("failed to get blocked accounts from storage: %w", err)
|
||||||
}
|
}
|
||||||
for key := range siMap {
|
for _, kv := range siArr {
|
||||||
hash, err := util.Uint160DecodeBytesBE([]byte(key))
|
hash, err := util.Uint160DecodeBytesBE([]byte(kv.Key))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to decode blocked account hash: %w", err)
|
return fmt.Errorf("failed to decode blocked account hash: %w", err)
|
||||||
}
|
}
|
||||||
p.blockedAccounts = append(p.blockedAccounts, hash)
|
p.blockedAccounts = append(p.blockedAccounts, hash)
|
||||||
}
|
}
|
||||||
sort.Slice(p.blockedAccounts, func(i, j int) bool {
|
// blockedAccounts should be sorted by account BE bytes, but GetStorageItemsWithPrefix
|
||||||
return p.blockedAccounts[i].Less(p.blockedAccounts[j])
|
// returns values sorted by key (which is account's BE bytes), so don't need to sort
|
||||||
})
|
// one more time.
|
||||||
|
|
||||||
p.isValid = true
|
p.isValid = true
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -2,3 +2,9 @@ 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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue