Merge pull request #581 from nspcc-dev/feat/storage_find
core: implement Neo.Storage.Find interop, closes #422.
This commit is contained in:
commit
c306d2f07c
5 changed files with 95 additions and 19 deletions
|
@ -4,6 +4,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/core/state"
|
||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||
|
@ -429,7 +430,6 @@ func (ic *interopContext) accountIsStandard(v *vm.VM) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
// storageFind finds stored key-value pair.
|
||||
func (ic *interopContext) storageFind(v *vm.VM) error {
|
||||
stcInterface := v.Estack().Pop().Value()
|
||||
|
@ -446,16 +446,20 @@ func (ic *interopContext) storageFind(v *vm.VM) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filteredMap := make(map[interface{}]vm.StackItem)
|
||||
for k, v := range siMap {
|
||||
if strings.HasPrefix(k, prefix) {
|
||||
_ = v
|
||||
panic("TODO")
|
||||
filteredMap[k] = vm.NewByteArrayItem(v.Value)
|
||||
}
|
||||
}
|
||||
|
||||
item := vm.NewMapIterator(filteredMap)
|
||||
v.Estack().PushVal(item)
|
||||
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
|
||||
// createContractStateFromVM pops all contract state elements from the VM
|
||||
// evaluation stack, does a lot of checks and returns Contract if it
|
||||
// succeeds.
|
||||
|
|
|
@ -34,6 +34,72 @@ import (
|
|||
* TestRuntimeDeserialize
|
||||
*/
|
||||
|
||||
func TestStorageFind(t *testing.T) {
|
||||
v, contractState, context := createVMAndContractState(t)
|
||||
|
||||
skeys := [][]byte{{0x01, 0x02}, {0x02, 0x01}}
|
||||
items := []*state.StorageItem{
|
||||
{
|
||||
Value: []byte{0x01, 0x02, 0x03, 0x04},
|
||||
},
|
||||
{
|
||||
Value: []byte{0x04, 0x03, 0x02, 0x01},
|
||||
},
|
||||
}
|
||||
|
||||
require.NoError(t, context.dao.PutContractState(contractState))
|
||||
|
||||
scriptHash := contractState.ScriptHash()
|
||||
|
||||
for i := range skeys {
|
||||
err := context.dao.PutStorageItem(scriptHash, skeys[i], items[i])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
t.Run("normal invocation", func(t *testing.T) {
|
||||
v.Estack().PushVal([]byte{0x01})
|
||||
v.Estack().PushVal(vm.NewInteropItem(&StorageContext{ScriptHash: scriptHash}))
|
||||
|
||||
err := context.storageFind(v)
|
||||
require.NoError(t, err)
|
||||
|
||||
var iter *vm.InteropItem
|
||||
require.NotPanics(t, func() { iter = v.Estack().Top().Interop() })
|
||||
|
||||
require.NoError(t, context.enumeratorNext(v))
|
||||
require.True(t, v.Estack().Pop().Bool())
|
||||
|
||||
v.Estack().PushVal(iter)
|
||||
require.NoError(t, context.iteratorKey(v))
|
||||
require.Equal(t, []byte{0x01, 0x02}, v.Estack().Pop().Bytes())
|
||||
|
||||
v.Estack().PushVal(iter)
|
||||
require.NoError(t, context.enumeratorValue(v))
|
||||
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, v.Estack().Pop().Bytes())
|
||||
|
||||
v.Estack().PushVal(iter)
|
||||
require.NoError(t, context.enumeratorNext(v))
|
||||
require.False(t, v.Estack().Pop().Bool())
|
||||
})
|
||||
|
||||
t.Run("invalid type for StorageContext", func(t *testing.T) {
|
||||
v.Estack().PushVal([]byte{0x01})
|
||||
v.Estack().PushVal(vm.NewInteropItem(nil))
|
||||
|
||||
require.Error(t, context.storageFind(v))
|
||||
})
|
||||
|
||||
t.Run("invalid script hash", func(t *testing.T) {
|
||||
invalidHash := scriptHash
|
||||
invalidHash[0] = ^invalidHash[0]
|
||||
|
||||
v.Estack().PushVal([]byte{0x01})
|
||||
v.Estack().PushVal(vm.NewInteropItem(&StorageContext{ScriptHash: invalidHash}))
|
||||
|
||||
require.Error(t, context.storageFind(v))
|
||||
})
|
||||
}
|
||||
|
||||
func TestHeaderGetVersion(t *testing.T) {
|
||||
v, block, context := createVMAndPushBlock(t)
|
||||
|
||||
|
|
|
@ -173,6 +173,7 @@ var neoInterops = []interopedFunction{
|
|||
{Name: "Neo.Runtime.Notify", Func: (*interopContext).runtimeNotify, Price: 1},
|
||||
{Name: "Neo.Runtime.Serialize", Func: (*interopContext).runtimeSerialize, Price: 1},
|
||||
{Name: "Neo.Storage.Delete", Func: (*interopContext).storageDelete, Price: 100},
|
||||
{Name: "Neo.Storage.Find", Func: (*interopContext).storageFind, Price: 1},
|
||||
{Name: "Neo.Storage.Get", Func: (*interopContext).storageGet, Price: 100},
|
||||
{Name: "Neo.Storage.GetContext", Func: (*interopContext).storageGetContext, Price: 1},
|
||||
{Name: "Neo.Storage.GetReadOnlyContext", Func: (*interopContext).storageGetReadOnlyContext, Price: 1},
|
||||
|
@ -187,7 +188,6 @@ var neoInterops = []interopedFunction{
|
|||
{Name: "Neo.Transaction.GetUnspentCoins", Func: (*interopContext).txGetUnspentCoins, Price: 200},
|
||||
{Name: "Neo.Transaction.GetWitnesses", Func: (*interopContext).txGetWitnesses, Price: 200},
|
||||
{Name: "Neo.Witness.GetVerificationScript", Func: (*interopContext).witnessGetVerificationScript, Price: 100},
|
||||
// {Name: "Neo.Storage.Find", Func: (*interopContext).storageFind, Price: 1},
|
||||
|
||||
// Aliases.
|
||||
{Name: "Neo.Iterator.Next", Func: (*interopContext).enumeratorNext, Price: 1},
|
||||
|
|
|
@ -64,6 +64,7 @@ func TestUnexpectedNonInterops(t *testing.T) {
|
|||
(*interopContext).outputGetValue,
|
||||
(*interopContext).storageContextAsReadOnly,
|
||||
(*interopContext).storageDelete,
|
||||
(*interopContext).storageFind,
|
||||
(*interopContext).storageGet,
|
||||
(*interopContext).storagePut,
|
||||
(*interopContext).storagePutEx,
|
||||
|
|
|
@ -177,32 +177,37 @@ func EnumeratorConcat(v *VM) error {
|
|||
// IteratorCreate handles syscall Neo.Iterator.Create.
|
||||
func IteratorCreate(v *VM) error {
|
||||
data := v.Estack().Pop()
|
||||
var item interface{}
|
||||
var item StackItem
|
||||
switch t := data.value.(type) {
|
||||
case *ArrayItem, *StructItem:
|
||||
item = &arrayWrapper{
|
||||
item = NewInteropItem(&arrayWrapper{
|
||||
index: -1,
|
||||
value: t.Value().([]StackItem),
|
||||
}
|
||||
})
|
||||
case *MapItem:
|
||||
keys := make([]interface{}, 0, len(t.value))
|
||||
for k := range t.value {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
||||
item = &mapWrapper{
|
||||
index: -1,
|
||||
keys: keys,
|
||||
m: t.value,
|
||||
}
|
||||
item = NewMapIterator(t.value)
|
||||
default:
|
||||
return errors.New("non-iterable type")
|
||||
}
|
||||
|
||||
v.Estack().Push(&Element{value: NewInteropItem(item)})
|
||||
v.Estack().Push(&Element{value: item})
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewMapIterator returns new interop item containing iterator over m.
|
||||
func NewMapIterator(m map[interface{}]StackItem) *InteropItem {
|
||||
keys := make([]interface{}, 0, len(m))
|
||||
for k := range m {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
||||
return NewInteropItem(&mapWrapper{
|
||||
index: -1,
|
||||
keys: keys,
|
||||
m: m,
|
||||
})
|
||||
}
|
||||
|
||||
// IteratorConcat handles syscall Neo.Iterator.Concat.
|
||||
func IteratorConcat(v *VM) error {
|
||||
iop1 := v.Estack().Pop().Interop()
|
||||
|
|
Loading…
Reference in a new issue