Merge pull request #581 from nspcc-dev/feat/storage_find

core: implement Neo.Storage.Find interop, closes #422.
This commit is contained in:
Roman Khimov 2019-12-26 15:46:46 +03:00 committed by GitHub
commit c306d2f07c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 95 additions and 19 deletions

View file

@ -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.

View file

@ -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)

View file

@ -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},

View file

@ -64,6 +64,7 @@ func TestUnexpectedNonInterops(t *testing.T) {
(*interopContext).outputGetValue,
(*interopContext).storageContextAsReadOnly,
(*interopContext).storageDelete,
(*interopContext).storageFind,
(*interopContext).storageGet,
(*interopContext).storagePut,
(*interopContext).storagePutEx,

View file

@ -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()