diff --git a/pkg/core/fee/opcode.go b/pkg/core/fee/opcode.go index 822a229ad..e3e028d28 100644 --- a/pkg/core/fee/opcode.go +++ b/pkg/core/fee/opcode.go @@ -195,6 +195,7 @@ var coefficients = map[opcode.Opcode]int64{ opcode.REVERSEITEMS: 1 << 13, opcode.REMOVE: 1 << 4, opcode.CLEARITEMS: 1 << 4, + opcode.POPITEM: 1 << 4, opcode.ISNULL: 1 << 1, opcode.ISTYPE: 1 << 1, opcode.CONVERT: 1 << 11, diff --git a/pkg/vm/opcode/opcode.go b/pkg/vm/opcode/opcode.go index 7b01823c6..f2fcca417 100644 --- a/pkg/vm/opcode/opcode.go +++ b/pkg/vm/opcode/opcode.go @@ -212,6 +212,7 @@ const ( REVERSEITEMS Opcode = 0xD1 REMOVE Opcode = 0xD2 CLEARITEMS Opcode = 0xD3 + POPITEM Opcode = 0xD4 // Types ISNULL Opcode = 0xD8 diff --git a/pkg/vm/opcode/opcode_string.go b/pkg/vm/opcode/opcode_string.go index 30260a48e..d77f8e4b6 100644 --- a/pkg/vm/opcode/opcode_string.go +++ b/pkg/vm/opcode/opcode_string.go @@ -192,12 +192,13 @@ func _() { _ = x[REVERSEITEMS-209] _ = x[REMOVE-210] _ = x[CLEARITEMS-211] + _ = x[POPITEM-212] _ = x[ISNULL-216] _ = x[ISTYPE-217] _ = x[CONVERT-219] } -const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHAPUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMP_LJMPIFJMPIF_LJMPIFNOTJMPIFNOT_LJMPEQJMPEQ_LJMPNEJMPNE_LJMPGTJMPGT_LJMPGEJMPGE_LJMPLTJMPLT_LJMPLEJMPLE_LCALLCALL_LCALLACALLTABORTASSERTTHROWTRYTRY_LENDTRYENDTRY_LENDFINALLYRETSYSCALLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPROTROLLREVERSE3REVERSE4REVERSENINITSSLOTINITSLOTLDSFLD0LDSFLD1LDSFLD2LDSFLD3LDSFLD4LDSFLD5LDSFLD6LDSFLDSTSFLD0STSFLD1STSFLD2STSFLD3STSFLD4STSFLD5STSFLD6STSFLDLDLOC0LDLOC1LDLOC2LDLOC3LDLOC4LDLOC5LDLOC6LDLOCSTLOC0STLOC1STLOC2STLOC3STLOC4STLOC5STLOC6STLOCLDARG0LDARG1LDARG2LDARG3LDARG4LDARG5LDARG6LDARGSTARG0STARG1STARG2STARG3STARG4STARG5STARG6STARGNEWBUFFERMEMCPYCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLTEGTGTEMINMAXWITHINPACKUNPACKNEWARRAY0NEWARRAYNEWARRAY_TNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSISNULLISTYPECONVERT" +const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHAPUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMP_LJMPIFJMPIF_LJMPIFNOTJMPIFNOT_LJMPEQJMPEQ_LJMPNEJMPNE_LJMPGTJMPGT_LJMPGEJMPGE_LJMPLTJMPLT_LJMPLEJMPLE_LCALLCALL_LCALLACALLTABORTASSERTTHROWTRYTRY_LENDTRYENDTRY_LENDFINALLYRETSYSCALLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPROTROLLREVERSE3REVERSE4REVERSENINITSSLOTINITSLOTLDSFLD0LDSFLD1LDSFLD2LDSFLD3LDSFLD4LDSFLD5LDSFLD6LDSFLDSTSFLD0STSFLD1STSFLD2STSFLD3STSFLD4STSFLD5STSFLD6STSFLDLDLOC0LDLOC1LDLOC2LDLOC3LDLOC4LDLOC5LDLOC6LDLOCSTLOC0STLOC1STLOC2STLOC3STLOC4STLOC5STLOC6STLOCLDARG0LDARG1LDARG2LDARG3LDARG4LDARG5LDARG6LDARGSTARG0STARG1STARG2STARG3STARG4STARG5STARG6STARGNEWBUFFERMEMCPYCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLTEGTGTEMINMAXWITHINPACKUNPACKNEWARRAY0NEWARRAYNEWARRAY_TNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSPOPITEMISNULLISTYPECONVERT" var _Opcode_map = map[Opcode]string{ 0: _Opcode_name[0:8], @@ -382,9 +383,10 @@ var _Opcode_map = map[Opcode]string{ 209: _Opcode_name[1033:1045], 210: _Opcode_name[1045:1051], 211: _Opcode_name[1051:1061], - 216: _Opcode_name[1061:1067], - 217: _Opcode_name[1067:1073], - 219: _Opcode_name[1073:1080], + 212: _Opcode_name[1061:1068], + 216: _Opcode_name[1068:1074], + 217: _Opcode_name[1074:1080], + 219: _Opcode_name[1080:1087], } func (i Opcode) String() string { diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 0b751e043..6e92c2298 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -1216,6 +1216,20 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro panic("CLEARITEMS: invalid type") } + case opcode.POPITEM: + arr := v.estack.Pop().Item() + elems := arr.Value().([]stackitem.Item) + index := len(elems) - 1 + elem := elems[index] + switch item := arr.(type) { + case *stackitem.Array: + item.Remove(index) + case *stackitem.Struct: + item.Remove(index) + } + v.refs.Remove(elem) + v.estack.PushVal(elem) + case opcode.SIZE: elem := v.estack.Pop() // Cause there is no native (byte) item type here, hence we need to check diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 4855d28ea..633fd65ce 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -2167,6 +2167,36 @@ func TestCLEARITEMS(t *testing.T) { }) } +func TestPOPITEM(t *testing.T) { + testPOPITEM := func(t *testing.T, item, elem, arr interface{}) { + prog := makeProgram(opcode.DUP, opcode.POPITEM) + v := load(prog) + v.estack.PushVal(item) + + runVM(t, v) + require.EqualValues(t, stackitem.Make(elem), v.estack.Pop().Item()) + require.EqualValues(t, stackitem.Make(arr), v.estack.Pop().Item()) + } + + elems := []stackitem.Item{stackitem.Make(11), stackitem.Make(31)} + t.Run("Array", func(t *testing.T) { + testPOPITEM(t, stackitem.NewArray(elems), 31, elems[:1]) + }) + t.Run("Struct", func(t *testing.T) { + testPOPITEM(t, stackitem.NewStruct(elems), 31, elems[:1]) + }) + t.Run("0-length array", func(t *testing.T) { + prog := makeProgram(opcode.NEWARRAY0, opcode.POPITEM) + v := load(prog) + checkVMFailed(t, v) + }) + t.Run("primitive type", func(t *testing.T) { + prog := makeProgram(opcode.PUSH4, opcode.POPITEM) + v := load(prog) + checkVMFailed(t, v) + }) +} + func TestSWAPGood(t *testing.T) { prog := makeProgram(opcode.SWAP) vm := load(prog)