forked from TrueCloudLab/neoneo-go
vm,dao: return storage iterator from DAO in Storage.Find interop
Reproduce behavior of the reference realization: - if item was Put in cache after it was encountered during Storage.Find, it must appear twice - checking if item is in cache must be performed in real-time during `Iterator.Next()`
This commit is contained in:
parent
79c87ca8a5
commit
776bd85ded
3 changed files with 93 additions and 14 deletions
|
@ -315,6 +315,52 @@ func (cd *Cached) DeleteStorageItem(scripthash util.Uint160, key []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StorageIteratorFunc is a function returning key-value pair or error.
|
||||||
|
type StorageIteratorFunc func() ([]byte, []byte, error)
|
||||||
|
|
||||||
|
// GetStorageItemsIterator returns iterator over all storage items.
|
||||||
|
// Function returned can be called until first error.
|
||||||
|
func (cd *Cached) GetStorageItemsIterator(hash util.Uint160, prefix []byte) (StorageIteratorFunc, error) {
|
||||||
|
items, err := cd.DAO.GetStorageItems(hash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(items, func(i, j int) bool { return bytes.Compare(items[i].Key, items[j].Key) == -1 })
|
||||||
|
|
||||||
|
cache := cd.storage.getItems(hash)
|
||||||
|
|
||||||
|
var getItemFromCache StorageIteratorFunc
|
||||||
|
keyIndex := -1
|
||||||
|
getItemFromCache = func() ([]byte, []byte, error) {
|
||||||
|
keyIndex++
|
||||||
|
for ; keyIndex < len(cd.storage.keys[hash]); keyIndex++ {
|
||||||
|
k := cd.storage.keys[hash][keyIndex]
|
||||||
|
v := cache[k]
|
||||||
|
if v.State != delOp && bytes.HasPrefix([]byte(k), prefix) {
|
||||||
|
val := make([]byte, len(v.StorageItem.Value))
|
||||||
|
copy(val, v.StorageItem.Value)
|
||||||
|
return []byte(k), val, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil, errors.New("no more items")
|
||||||
|
}
|
||||||
|
|
||||||
|
var f func() ([]byte, []byte, error)
|
||||||
|
index := -1
|
||||||
|
f = func() ([]byte, []byte, error) {
|
||||||
|
index++
|
||||||
|
for ; index < len(items); index++ {
|
||||||
|
_, ok := cache[string(items[index].Key)]
|
||||||
|
if !ok && bytes.HasPrefix(items[index].Key, prefix) {
|
||||||
|
return items[index].Key, items[index].Value, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getItemFromCache()
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetStorageItems returns all storage items for a given scripthash.
|
// GetStorageItems returns all storage items for a given scripthash.
|
||||||
func (cd *Cached) GetStorageItems(hash util.Uint160) ([]StorageItemWithKey, error) {
|
func (cd *Cached) GetStorageItems(hash util.Uint160) ([]StorageItemWithKey, error) {
|
||||||
items, err := cd.DAO.GetStorageItems(hash)
|
items, err := cd.DAO.GetStorageItems(hash)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
@ -463,22 +462,12 @@ func (ic *interopContext) storageFind(v *vm.VM) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
pref := v.Estack().Pop().Bytes()
|
pref := v.Estack().Pop().Bytes()
|
||||||
siMap, err := ic.dao.GetStorageItems(stc.ScriptHash)
|
next, err := ic.dao.GetStorageItemsIterator(stc.ScriptHash, pref)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
item := newStorageIterator(next)
|
||||||
filteredMap := vm.NewMapItem()
|
v.Estack().PushVal(vm.NewInteropItem(item))
|
||||||
for i := range siMap {
|
|
||||||
k := siMap[i].Key
|
|
||||||
if bytes.HasPrefix(k, pref) {
|
|
||||||
filteredMap.Add(vm.NewByteArrayItem(siMap[i].Key),
|
|
||||||
vm.NewByteArrayItem(siMap[i].Value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
item := vm.NewMapIterator(filteredMap)
|
|
||||||
v.Estack().PushVal(item)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
44
pkg/core/storage_find.go
Normal file
44
pkg/core/storage_find.go
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type storageWrapper struct {
|
||||||
|
next dao.StorageIteratorFunc
|
||||||
|
key, value vm.StackItem
|
||||||
|
finished bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// newStorageIterator returns new storage iterator from the `next()` callback.
|
||||||
|
func newStorageIterator(next dao.StorageIteratorFunc) *storageWrapper {
|
||||||
|
return &storageWrapper{
|
||||||
|
next: next,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next implements iterator interface.
|
||||||
|
func (s *storageWrapper) Next() bool {
|
||||||
|
if s.finished {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
key, value, err := s.next()
|
||||||
|
if err != nil {
|
||||||
|
s.finished = true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
s.key = vm.NewByteArrayItem(key)
|
||||||
|
s.value = vm.NewByteArrayItem(value)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements iterator interface.
|
||||||
|
func (s *storageWrapper) Value() vm.StackItem {
|
||||||
|
return s.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key implements iterator interface.
|
||||||
|
func (s *storageWrapper) Key() vm.StackItem {
|
||||||
|
return s.key
|
||||||
|
}
|
Loading…
Reference in a new issue