mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-04 19:19:44 +00:00
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
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (cd *Cached) GetStorageItems(hash util.Uint160) ([]StorageItemWithKey, error) {
|
||||
items, err := cd.DAO.GetStorageItems(hash)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
|
@ -463,22 +462,12 @@ func (ic *interopContext) storageFind(v *vm.VM) error {
|
|||
return err
|
||||
}
|
||||
pref := v.Estack().Pop().Bytes()
|
||||
siMap, err := ic.dao.GetStorageItems(stc.ScriptHash)
|
||||
next, err := ic.dao.GetStorageItemsIterator(stc.ScriptHash, pref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filteredMap := vm.NewMapItem()
|
||||
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)
|
||||
item := newStorageIterator(next)
|
||||
v.Estack().PushVal(vm.NewInteropItem(item))
|
||||
|
||||
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