From 9082c6ea1acb50acd6f2139fb020cd01e0602ee8 Mon Sep 17 00:00:00 2001 From: Ekaterina Pavlova Date: Tue, 19 Nov 2024 13:48:58 +0300 Subject: [PATCH] vm: fix PACKMAP operation Close #3613 Signed-off-by: Ekaterina Pavlova --- pkg/vm/stackitem/item.go | 3 +- pkg/vm/stackitem/serialization_test.go | 2 +- pkg/vm/vm.go | 13 +++++---- pkg/vm/vm_test.go | 39 ++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 8 deletions(-) diff --git a/pkg/vm/stackitem/item.go b/pkg/vm/stackitem/item.go index 4c480a40e..109eb7ae3 100644 --- a/pkg/vm/stackitem/item.go +++ b/pkg/vm/stackitem/item.go @@ -824,7 +824,8 @@ func NewMap() *Map { } } -// NewMapWithValue returns a new Map object filled with the specified value. +// NewMapWithValue returns a new Map object filled with the specified value +// without value validation. func NewMapWithValue(value []MapElement) *Map { if value != nil { return &Map{ diff --git a/pkg/vm/stackitem/serialization_test.go b/pkg/vm/stackitem/serialization_test.go index 358136636..005dc3999 100644 --- a/pkg/vm/stackitem/serialization_test.go +++ b/pkg/vm/stackitem/serialization_test.go @@ -219,7 +219,7 @@ func TestSerializeLimited(t *testing.T) { bigMap := make([]MapElement, customLimit/2) for i := range bigMap { bigMap[i] = MapElement{ - Key: NewByteArray([]byte("key")), + Key: NewByteArray([]byte("key" + strconv.Itoa(i))), Value: NewBool(true), } } diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 8c98192c3..8b43c39e6 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -1214,15 +1214,16 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro panic("invalid length") } - items := make([]stackitem.MapElement, n) - for i := range n { + m := stackitem.NewMap() + for range n { key := v.estack.Pop() - validateMapKey(key) val := v.estack.Pop().value - items[i].Key = key.value - items[i].Value = val + if key.Item() == nil { + panic("no key found") + } + m.Add(key.value, val) } - v.estack.PushItem(stackitem.NewMapWithValue(items)) + v.estack.PushItem(m) case opcode.PACKSTRUCT, opcode.PACK: n := toInt(v.estack.Pop().BigInt()) diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index c4dfb8ba7..7d1fb9f60 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -2083,6 +2083,45 @@ func TestUNPACKBadNotArray(t *testing.T) { runWithArgs(t, prog, nil, 1) } +func TestPACKMAPDuplicateKeys(t *testing.T) { + prog := makeProgram(opcode.PACKMAP) + vm := load(prog) + + keys := []string{"duplicateKey", "uniqueKey", "duplicateKey", "anotherUniqueKey"} + values := []string{"value1", "value2", "value3", "value4"} + + for i := range keys { + vm.estack.PushVal(values[i]) + vm.estack.PushVal(keys[i]) + } + + vm.estack.PushVal(len(keys)) + runVM(t, vm) + + require.Equal(t, 1, vm.estack.Len()) + packedItem := vm.estack.Pop().Item() + require.Equal(t, stackitem.MapT, packedItem.Type()) + + packedMap := packedItem.(*stackitem.Map) + require.Equal(t, 3, packedMap.Len()) + + expected := []stackitem.MapElement{ + { + Key: stackitem.NewByteArray([]byte("anotherUniqueKey")), + Value: stackitem.NewByteArray([]byte("value4")), + }, + { + Key: stackitem.NewByteArray([]byte("duplicateKey")), + Value: stackitem.NewByteArray([]byte("value1")), + }, + { + Key: stackitem.NewByteArray([]byte("uniqueKey")), + Value: stackitem.NewByteArray([]byte("value2")), + }, + } + require.Equal(t, expected, packedMap.Value()) +} + func TestUNPACKGood(t *testing.T) { prog := makeProgram(opcode.UNPACK) elements := []int{55, 34, 42}