diff --git a/pkg/compiler/syscall_test.go b/pkg/compiler/syscall_test.go index fc56d6d0a..466271343 100644 --- a/pkg/compiler/syscall_test.go +++ b/pkg/compiler/syscall_test.go @@ -44,6 +44,7 @@ func TestFindFlags(t *testing.T) { require.EqualValues(t, storage.DeserializeValues, istorage.FindDeserialize) require.EqualValues(t, storage.PickField0, istorage.FindPick0) require.EqualValues(t, storage.PickField1, istorage.FindPick1) + require.EqualValues(t, storage.Backwards, istorage.FindBackwards) } type syscallTestCase struct { diff --git a/pkg/core/interop/storage/find.go b/pkg/core/interop/storage/find.go index 9d2f96668..666950e34 100644 --- a/pkg/core/interop/storage/find.go +++ b/pkg/core/interop/storage/find.go @@ -19,9 +19,10 @@ const ( FindDeserialize = 1 << 3 FindPick0 = 1 << 4 FindPick1 = 1 << 5 + FindBackwards = 1 << 7 FindAll = FindDefault | FindKeysOnly | FindRemovePrefix | FindValuesOnly | - FindDeserialize | FindPick0 | FindPick1 + FindDeserialize | FindPick0 | FindPick1 | FindBackwards ) // Iterator is an iterator state representation. @@ -111,8 +112,9 @@ func Find(ic *interop.Context) error { if opts&FindDeserialize == 0 && (opts&FindPick0 != 0 || opts&FindPick1 != 0) { return fmt.Errorf("%w: PickN is specified without Deserialize", errFindInvalidOptions) } + bkwrds := opts&FindBackwards != 0 ctx, cancel := context.WithCancel(context.Background()) - seekres := ic.DAO.SeekAsync(ctx, stc.ID, storage.SeekRange{Prefix: prefix}) + seekres := ic.DAO.SeekAsync(ctx, stc.ID, storage.SeekRange{Prefix: prefix, Backwards: bkwrds}) item := NewIterator(seekres, prefix, opts) ic.VM.Estack().PushItem(stackitem.NewInterop(item)) ic.RegisterCancelFunc(func() { diff --git a/pkg/core/interop/storage/storage_test.go b/pkg/core/interop/storage/storage_test.go index fec1820b2..a4f3e5449 100644 --- a/pkg/core/interop/storage/storage_test.go +++ b/pkg/core/interop/storage/storage_test.go @@ -197,7 +197,18 @@ func TestFind(t *testing.T) { }), }) }) - + t.Run("normal invocation, backwards", func(t *testing.T) { + testFind(t, []byte{0x01}, istorage.FindBackwards, []stackitem.Item{ + stackitem.NewStruct([]stackitem.Item{ + stackitem.NewByteArray(skeys[0]), + stackitem.NewByteArray(items[0]), + }), + stackitem.NewStruct([]stackitem.Item{ + stackitem.NewByteArray(skeys[2]), + stackitem.NewByteArray(items[2]), + }), + }) + }) t.Run("keys only", func(t *testing.T) { testFind(t, []byte{0x01}, istorage.FindKeysOnly, []stackitem.Item{ stackitem.NewByteArray(skeys[2]), diff --git a/pkg/interop/storage/storage.go b/pkg/interop/storage/storage.go index 146e32104..30b649f7a 100644 --- a/pkg/interop/storage/storage.go +++ b/pkg/interop/storage/storage.go @@ -36,6 +36,8 @@ const ( PickField0 FindFlags = 1 << 4 // PickField1 is used to get second field in a serialized struct or array. PickField1 FindFlags = 1 << 5 + // Backwards is used to iterate over elements in reversed (descending) order. + Backwards FindFlags = 1 << 7 ) // ConvertContextToReadOnly returns new context from the given one, but with