core: add PickN flags to Storage.Find
Allow to pick items by index from serialized struct or array.
This commit is contained in:
parent
44af99fd07
commit
9b1a7021ba
5 changed files with 65 additions and 3 deletions
|
@ -31,6 +31,8 @@ func TestFindFlags(t *testing.T) {
|
||||||
require.EqualValues(t, storage.RemovePrefix, istorage.FindRemovePrefix)
|
require.EqualValues(t, storage.RemovePrefix, istorage.FindRemovePrefix)
|
||||||
require.EqualValues(t, storage.ValuesOnly, istorage.FindValuesOnly)
|
require.EqualValues(t, storage.ValuesOnly, istorage.FindValuesOnly)
|
||||||
require.EqualValues(t, storage.DeserializeValues, istorage.FindDeserialize)
|
require.EqualValues(t, storage.DeserializeValues, istorage.FindDeserialize)
|
||||||
|
require.EqualValues(t, storage.PickField0, istorage.FindPick0)
|
||||||
|
require.EqualValues(t, storage.PickField1, istorage.FindPick1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStoragePutGet(t *testing.T) {
|
func TestStoragePutGet(t *testing.T) {
|
||||||
|
|
|
@ -9,9 +9,11 @@ const (
|
||||||
FindRemovePrefix = 1 << 1
|
FindRemovePrefix = 1 << 1
|
||||||
FindValuesOnly = 1 << 2
|
FindValuesOnly = 1 << 2
|
||||||
FindDeserialize = 1 << 3
|
FindDeserialize = 1 << 3
|
||||||
|
FindPick0 = 1 << 4
|
||||||
|
FindPick1 = 1 << 5
|
||||||
|
|
||||||
FindAll = FindDefault | FindKeysOnly | FindRemovePrefix | FindValuesOnly |
|
FindAll = FindDefault | FindKeysOnly | FindRemovePrefix | FindValuesOnly |
|
||||||
FindDeserialize
|
FindDeserialize | FindPick0 | FindPick1
|
||||||
)
|
)
|
||||||
|
|
||||||
type Iterator struct {
|
type Iterator struct {
|
||||||
|
@ -52,6 +54,11 @@ func (s *Iterator) Value() stackitem.Item {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if s.opts&FindPick0 != 0 {
|
||||||
|
value = value.Value().([]stackitem.Item)[0]
|
||||||
|
} else if s.opts&FindPick1 != 0 {
|
||||||
|
value = value.Value().([]stackitem.Item)[1]
|
||||||
|
}
|
||||||
if s.opts&FindValuesOnly != 0 {
|
if s.opts&FindValuesOnly != 0 {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,13 +28,20 @@ func storageFind(ic *interop.Context) error {
|
||||||
if opts&^storage.FindAll != 0 {
|
if opts&^storage.FindAll != 0 {
|
||||||
return fmt.Errorf("%w: unknown flag", errFindInvalidOptions)
|
return fmt.Errorf("%w: unknown flag", errFindInvalidOptions)
|
||||||
}
|
}
|
||||||
if opts&storage.FindKeysOnly != 0 && opts&storage.FindDeserialize != 0 {
|
if opts&storage.FindKeysOnly != 0 &&
|
||||||
|
opts&(storage.FindDeserialize|storage.FindPick0|storage.FindPick1) != 0 {
|
||||||
return fmt.Errorf("%w KeysOnly conflicts with other options", errFindInvalidOptions)
|
return fmt.Errorf("%w KeysOnly conflicts with other options", errFindInvalidOptions)
|
||||||
}
|
}
|
||||||
if opts&storage.FindValuesOnly != 0 &&
|
if opts&storage.FindValuesOnly != 0 &&
|
||||||
opts&(storage.FindKeysOnly|storage.FindRemovePrefix) != 0 {
|
opts&(storage.FindKeysOnly|storage.FindRemovePrefix) != 0 {
|
||||||
return fmt.Errorf("%w: KeysOnly conflicts with ValuesOnly", errFindInvalidOptions)
|
return fmt.Errorf("%w: KeysOnly conflicts with ValuesOnly", errFindInvalidOptions)
|
||||||
}
|
}
|
||||||
|
if opts&storage.FindPick0 != 0 && opts&storage.FindPick1 != 0 {
|
||||||
|
return fmt.Errorf("%w: Pick0 conflicts with Pick1", errFindInvalidOptions)
|
||||||
|
}
|
||||||
|
if opts&storage.FindDeserialize == 0 && (opts&storage.FindPick0 != 0 || opts&storage.FindPick1 != 0) {
|
||||||
|
return fmt.Errorf("%w: PickN is specified without Deserialize", errFindInvalidOptions)
|
||||||
|
}
|
||||||
siMap, err := ic.DAO.GetStorageItemsWithPrefix(stc.ID, prefix)
|
siMap, err := ic.DAO.GetStorageItemsWithPrefix(stc.ID, prefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||||
|
@ -37,8 +38,20 @@ func TestStorageFind(t *testing.T) {
|
||||||
v, contractState, context, chain := createVMAndContractState(t)
|
v, contractState, context, chain := createVMAndContractState(t)
|
||||||
defer chain.Close()
|
defer chain.Close()
|
||||||
|
|
||||||
|
arr := []stackitem.Item{
|
||||||
|
stackitem.NewBigInteger(big.NewInt(42)),
|
||||||
|
stackitem.NewByteArray([]byte("second")),
|
||||||
|
stackitem.Null{},
|
||||||
|
}
|
||||||
|
rawArr, err := stackitem.SerializeItem(stackitem.NewArray(arr))
|
||||||
|
require.NoError(t, err)
|
||||||
|
rawArr0, err := stackitem.SerializeItem(stackitem.NewArray(arr[:0]))
|
||||||
|
require.NoError(t, err)
|
||||||
|
rawArr1, err := stackitem.SerializeItem(stackitem.NewArray(arr[:1]))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
skeys := [][]byte{{0x01, 0x02}, {0x02, 0x01}, {0x01, 0x01},
|
skeys := [][]byte{{0x01, 0x02}, {0x02, 0x01}, {0x01, 0x01},
|
||||||
{0x04, 0x00}, {0x05, 0x00}}
|
{0x04, 0x00}, {0x05, 0x00}, {0x06}, {0x07}, {0x08}}
|
||||||
items := []*state.StorageItem{
|
items := []*state.StorageItem{
|
||||||
{
|
{
|
||||||
Value: []byte{0x01, 0x02, 0x03, 0x04},
|
Value: []byte{0x01, 0x02, 0x03, 0x04},
|
||||||
|
@ -55,6 +68,15 @@ func TestStorageFind(t *testing.T) {
|
||||||
{
|
{
|
||||||
Value: []byte{0xFF, 0xFF},
|
Value: []byte{0xFF, 0xFF},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Value: rawArr,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: rawArr0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: rawArr1,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
require.NoError(t, chain.contracts.Management.PutContractState(chain.dao, contractState))
|
require.NoError(t, chain.contracts.Management.PutContractState(chain.dao, contractState))
|
||||||
|
@ -83,6 +105,10 @@ func TestStorageFind(t *testing.T) {
|
||||||
require.True(t, v.Estack().Pop().Bool())
|
require.True(t, v.Estack().Pop().Bool())
|
||||||
|
|
||||||
v.Estack().PushVal(iter)
|
v.Estack().PushVal(iter)
|
||||||
|
if expected[i] == nil {
|
||||||
|
require.Panics(t, func() { _ = iterator.Value(context) })
|
||||||
|
return
|
||||||
|
}
|
||||||
require.NoError(t, iterator.Value(context))
|
require.NoError(t, iterator.Value(context))
|
||||||
require.Equal(t, expected[i], v.Estack().Pop().Item())
|
require.Equal(t, expected[i], v.Estack().Pop().Item())
|
||||||
}
|
}
|
||||||
|
@ -144,6 +170,19 @@ func TestStorageFind(t *testing.T) {
|
||||||
require.Panics(t, func() { _ = iterator.Value(context) })
|
require.Panics(t, func() { _ = iterator.Value(context) })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
t.Run("PickN", func(t *testing.T) {
|
||||||
|
testFind(t, 0x06, istorage.FindPick0|istorage.FindValuesOnly|istorage.FindDeserialize, arr[:1])
|
||||||
|
testFind(t, 0x06, istorage.FindPick1|istorage.FindValuesOnly|istorage.FindDeserialize, arr[1:2])
|
||||||
|
// Array with 0 elements.
|
||||||
|
testFind(t, 0x07, istorage.FindPick0|istorage.FindValuesOnly|istorage.FindDeserialize,
|
||||||
|
[]stackitem.Item{nil})
|
||||||
|
// Array with 1 element.
|
||||||
|
testFind(t, 0x08, istorage.FindPick1|istorage.FindValuesOnly|istorage.FindDeserialize,
|
||||||
|
[]stackitem.Item{nil})
|
||||||
|
// Not an array, but serialized ByteArray.
|
||||||
|
testFind(t, 0x04, istorage.FindPick1|istorage.FindValuesOnly|istorage.FindDeserialize,
|
||||||
|
[]stackitem.Item{nil})
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("normal invocation, empty result", func(t *testing.T) {
|
t.Run("normal invocation, empty result", func(t *testing.T) {
|
||||||
testFind(t, 0x03, istorage.FindDefault, nil)
|
testFind(t, 0x03, istorage.FindDefault, nil)
|
||||||
|
@ -154,6 +193,9 @@ func TestStorageFind(t *testing.T) {
|
||||||
istorage.FindKeysOnly | istorage.FindValuesOnly,
|
istorage.FindKeysOnly | istorage.FindValuesOnly,
|
||||||
^istorage.FindAll,
|
^istorage.FindAll,
|
||||||
istorage.FindKeysOnly | istorage.FindDeserialize,
|
istorage.FindKeysOnly | istorage.FindDeserialize,
|
||||||
|
istorage.FindPick0,
|
||||||
|
istorage.FindPick0 | istorage.FindPick1 | istorage.FindDeserialize,
|
||||||
|
istorage.FindPick0 | istorage.FindPick1,
|
||||||
}
|
}
|
||||||
for _, opts := range invalid {
|
for _, opts := range invalid {
|
||||||
v.Estack().PushVal(opts)
|
v.Estack().PushVal(opts)
|
||||||
|
|
|
@ -29,6 +29,10 @@ const (
|
||||||
// DeserializeValues is used for deserializing values on-the-fly.
|
// DeserializeValues is used for deserializing values on-the-fly.
|
||||||
// It can be combined with other options.
|
// It can be combined with other options.
|
||||||
DeserializeValues FindFlags = 1 << 3
|
DeserializeValues FindFlags = 1 << 3
|
||||||
|
// PickField0 is used to get first field in a serialized struct or array.
|
||||||
|
PickField0 FindFlags = 1 << 4
|
||||||
|
// PickField1 is used to get second field in a serialized struct or array.
|
||||||
|
PickField1 FindFlags = 1 << 5
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConvertContextToReadOnly returns new context from the given one, but with
|
// ConvertContextToReadOnly returns new context from the given one, but with
|
||||||
|
|
Loading…
Reference in a new issue