core: add Deserialize flag to Storage.Find

Allow to deserialize values being iterated over.
This commit is contained in:
Evgeniy Stratonikov 2021-01-12 15:09:05 +03:00 committed by Roman Khimov
parent 7fc0c04dba
commit 44af99fd07
5 changed files with 51 additions and 4 deletions

View file

@ -30,6 +30,7 @@ func TestFindFlags(t *testing.T) {
require.EqualValues(t, storage.KeysOnly, istorage.FindKeysOnly)
require.EqualValues(t, storage.RemovePrefix, istorage.FindRemovePrefix)
require.EqualValues(t, storage.ValuesOnly, istorage.FindValuesOnly)
require.EqualValues(t, storage.DeserializeValues, istorage.FindDeserialize)
}
func TestStoragePutGet(t *testing.T) {

View file

@ -8,8 +8,10 @@ const (
FindKeysOnly = 1 << 0
FindRemovePrefix = 1 << 1
FindValuesOnly = 1 << 2
FindDeserialize = 1 << 3
FindAll = FindDefault | FindKeysOnly | FindRemovePrefix | FindValuesOnly
FindAll = FindDefault | FindKeysOnly | FindRemovePrefix | FindValuesOnly |
FindDeserialize
)
type Iterator struct {
@ -41,11 +43,20 @@ func (s *Iterator) Value() stackitem.Item {
if s.opts&FindKeysOnly != 0 {
return stackitem.NewByteArray(key)
}
value := s.m[s.index].Value
if s.opts&FindDeserialize != 0 {
bs := s.m[s.index].Value.Value().([]byte)
var err error
value, err = stackitem.DeserializeItem(bs)
if err != nil {
panic(err)
}
}
if s.opts&FindValuesOnly != 0 {
return s.m[s.index].Value
return value
}
return stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray(key),
s.m[s.index].Value,
value,
})
}

View file

@ -28,6 +28,9 @@ func storageFind(ic *interop.Context) error {
if opts&^storage.FindAll != 0 {
return fmt.Errorf("%w: unknown flag", errFindInvalidOptions)
}
if opts&storage.FindKeysOnly != 0 && opts&storage.FindDeserialize != 0 {
return fmt.Errorf("%w KeysOnly conflicts with other options", errFindInvalidOptions)
}
if opts&storage.FindValuesOnly != 0 &&
opts&(storage.FindKeysOnly|storage.FindRemovePrefix) != 0 {
return fmt.Errorf("%w: KeysOnly conflicts with ValuesOnly", errFindInvalidOptions)

View file

@ -37,7 +37,8 @@ func TestStorageFind(t *testing.T) {
v, contractState, context, chain := createVMAndContractState(t)
defer chain.Close()
skeys := [][]byte{{0x01, 0x02}, {0x02, 0x01}, {0x01, 0x01}}
skeys := [][]byte{{0x01, 0x02}, {0x02, 0x01}, {0x01, 0x01},
{0x04, 0x00}, {0x05, 0x00}}
items := []*state.StorageItem{
{
Value: []byte{0x01, 0x02, 0x03, 0x04},
@ -48,6 +49,12 @@ func TestStorageFind(t *testing.T) {
{
Value: []byte{0x03, 0x04, 0x05, 0x06},
},
{
Value: []byte{byte(stackitem.ByteArrayT), 2, 0xCA, 0xFE},
},
{
Value: []byte{0xFF, 0xFF},
},
}
require.NoError(t, chain.contracts.Management.PutContractState(chain.dao, contractState))
@ -116,6 +123,27 @@ func TestStorageFind(t *testing.T) {
stackitem.NewByteArray(items[0].Value),
})
})
t.Run("deserialize values", func(t *testing.T) {
testFind(t, 0x04, istorage.FindValuesOnly|istorage.FindDeserialize, []stackitem.Item{
stackitem.NewByteArray(items[3].Value[2:]),
})
t.Run("invalid", func(t *testing.T) {
v.Estack().PushVal(istorage.FindDeserialize)
v.Estack().PushVal([]byte{0x05})
v.Estack().PushVal(stackitem.NewInterop(&StorageContext{ID: id}))
err := storageFind(context)
require.NoError(t, err)
var iter *stackitem.Interop
require.NotPanics(t, func() { iter = v.Estack().Pop().Interop() })
v.Estack().PushVal(iter)
require.NoError(t, iterator.Next(context))
v.Estack().PushVal(iter)
require.Panics(t, func() { _ = iterator.Value(context) })
})
})
t.Run("normal invocation, empty result", func(t *testing.T) {
testFind(t, 0x03, istorage.FindDefault, nil)
@ -125,6 +153,7 @@ func TestStorageFind(t *testing.T) {
invalid := []int64{
istorage.FindKeysOnly | istorage.FindValuesOnly,
^istorage.FindAll,
istorage.FindKeysOnly | istorage.FindDeserialize,
}
for _, opts := range invalid {
v.Estack().PushVal(opts)

View file

@ -26,6 +26,9 @@ const (
RemovePrefix FindFlags = 1 << 1
// ValuesOnly is used for iterating over values.
ValuesOnly FindFlags = 1 << 2
// DeserializeValues is used for deserializing values on-the-fly.
// It can be combined with other options.
DeserializeValues FindFlags = 1 << 3
)
// ConvertContextToReadOnly returns new context from the given one, but with