From 44af99fd079139c2b7fa9f714cbe341d7d0b1d8d Mon Sep 17 00:00:00 2001 From: Evgeniy Stratonikov Date: Tue, 12 Jan 2021 15:09:05 +0300 Subject: [PATCH] core: add Deserialize flag to `Storage.Find` Allow to deserialize values being iterated over. --- pkg/compiler/syscall_test.go | 1 + pkg/core/interop/storage/find.go | 17 ++++++++++++++--- pkg/core/interop_neo.go | 3 +++ pkg/core/interop_neo_test.go | 31 ++++++++++++++++++++++++++++++- pkg/interop/storage/storage.go | 3 +++ 5 files changed, 51 insertions(+), 4 deletions(-) diff --git a/pkg/compiler/syscall_test.go b/pkg/compiler/syscall_test.go index 9d705ca05..a9564608a 100644 --- a/pkg/compiler/syscall_test.go +++ b/pkg/compiler/syscall_test.go @@ -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) { diff --git a/pkg/core/interop/storage/find.go b/pkg/core/interop/storage/find.go index fa61d89be..290b43ff3 100644 --- a/pkg/core/interop/storage/find.go +++ b/pkg/core/interop/storage/find.go @@ -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, }) } diff --git a/pkg/core/interop_neo.go b/pkg/core/interop_neo.go index f200296c8..1a8a09eb7 100644 --- a/pkg/core/interop_neo.go +++ b/pkg/core/interop_neo.go @@ -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) diff --git a/pkg/core/interop_neo_test.go b/pkg/core/interop_neo_test.go index 82f11502e..017a23d67 100644 --- a/pkg/core/interop_neo_test.go +++ b/pkg/core/interop_neo_test.go @@ -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) diff --git a/pkg/interop/storage/storage.go b/pkg/interop/storage/storage.go index d641e352d..4bf794d54 100644 --- a/pkg/interop/storage/storage.go +++ b/pkg/interop/storage/storage.go @@ -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