diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index d91cdf759..696e96914 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -940,13 +940,12 @@ func (c *codegen) emitReverse(num int) { case 2: emit.Opcode(c.prog.BinWriter, opcode.SWAP) case 3: - emit.Int(c.prog.BinWriter, 2) - emit.Opcode(c.prog.BinWriter, opcode.XSWAP) + emit.Opcode(c.prog.BinWriter, opcode.REVERSE3) + case 4: + emit.Opcode(c.prog.BinWriter, opcode.REVERSE4) default: - for i := 1; i < num; i++ { - emit.Int(c.prog.BinWriter, int64(i)) - emit.Opcode(c.prog.BinWriter, opcode.ROLL) - } + emit.Int(c.prog.BinWriter, int64(num)) + emit.Opcode(c.prog.BinWriter, opcode.REVERSEN) } } diff --git a/pkg/vm/opcode/opcode.go b/pkg/vm/opcode/opcode.go index fb6edb75d..10a4a2421 100644 --- a/pkg/vm/opcode/opcode.go +++ b/pkg/vm/opcode/opcode.go @@ -79,9 +79,9 @@ const ( OLDPUSH1 Opcode = 0x51 // FIXME remove #927 ROT Opcode = 0x51 ROLL Opcode = 0x52 - // REVERSE3 Opcode = 0x53 - // REVERSE4 Opcode = 0x54 - // REVERSEN Opcode = 0x55 + REVERSE3 Opcode = 0x53 + REVERSE4 Opcode = 0x54 + REVERSEN Opcode = 0x55 RET Opcode = 0x66 APPCALL Opcode = 0x67 diff --git a/pkg/vm/opcode/opcode_string.go b/pkg/vm/opcode/opcode_string.go index ef59f0cd3..5373d63c5 100644 --- a/pkg/vm/opcode/opcode_string.go +++ b/pkg/vm/opcode/opcode_string.go @@ -72,6 +72,9 @@ func _() { _ = x[OLDPUSH1-81] _ = x[ROT-81] _ = x[ROLL-82] + _ = x[REVERSE3-83] + _ = x[REVERSE4-84] + _ = x[REVERSEN-85] _ = x[RET-102] _ = x[APPCALL-103] _ = x[SYSCALL-104] @@ -141,7 +144,7 @@ func _() { _ = x[THROWIFNOT-241] } -const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMPLJMPIFJMPIFLJMPIFNOTJMPIFNOTLJMPEQJMPEQLJMPNEJMPNELJMPGTJMPGTLJMPGEJMPGELJMPLTJMPLTLJMPLEJMPLELCALLCALLLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPOLDPUSH1ROLLRETAPPCALLSYSCALLTAILCALLDUPFROMALTSTACKTOALTSTACKFROMALTSTACKXSWAPXTUCKCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLTEGTGTEMINMAXWITHINPACKUNPACKNEWARRAY0NEWARRAYNEWARRAYTNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSISNULLISTYPECONVERTTHROWTHROWIFNOT" +const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMPLJMPIFJMPIFLJMPIFNOTJMPIFNOTLJMPEQJMPEQLJMPNEJMPNELJMPGTJMPGTLJMPGEJMPGELJMPLTJMPLTLJMPLEJMPLELCALLCALLLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPOLDPUSH1ROLLREVERSE3REVERSE4REVERSENRETAPPCALLSYSCALLTAILCALLDUPFROMALTSTACKTOALTSTACKFROMALTSTACKXSWAPXTUCKCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLTEGTGTEMINMAXWITHINPACKUNPACKNEWARRAY0NEWARRAYNEWARRAYTNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSISNULLISTYPECONVERTTHROWTHROWIFNOT" var _Opcode_map = map[Opcode]string{ 0: _Opcode_name[0:8], @@ -205,73 +208,76 @@ var _Opcode_map = map[Opcode]string{ 80: _Opcode_name[338:342], 81: _Opcode_name[342:350], 82: _Opcode_name[350:354], - 102: _Opcode_name[354:357], - 103: _Opcode_name[357:364], - 104: _Opcode_name[364:371], - 105: _Opcode_name[371:379], - 106: _Opcode_name[379:394], - 107: _Opcode_name[394:404], - 108: _Opcode_name[404:416], - 114: _Opcode_name[416:421], - 115: _Opcode_name[421:426], - 126: _Opcode_name[426:429], - 127: _Opcode_name[429:435], - 128: _Opcode_name[435:439], - 129: _Opcode_name[439:444], - 144: _Opcode_name[444:450], - 145: _Opcode_name[450:453], - 146: _Opcode_name[453:455], - 147: _Opcode_name[455:458], - 151: _Opcode_name[458:463], - 152: _Opcode_name[463:471], - 153: _Opcode_name[471:475], - 154: _Opcode_name[475:478], - 155: _Opcode_name[478:484], - 156: _Opcode_name[484:487], - 157: _Opcode_name[487:490], - 158: _Opcode_name[490:493], - 159: _Opcode_name[493:496], - 160: _Opcode_name[496:499], - 161: _Opcode_name[499:502], - 162: _Opcode_name[502:505], - 168: _Opcode_name[505:508], - 169: _Opcode_name[508:511], - 170: _Opcode_name[511:514], - 171: _Opcode_name[514:521], - 172: _Opcode_name[521:527], - 177: _Opcode_name[527:529], - 179: _Opcode_name[529:537], - 180: _Opcode_name[537:548], - 181: _Opcode_name[548:550], - 182: _Opcode_name[550:553], - 183: _Opcode_name[553:555], - 184: _Opcode_name[555:558], - 185: _Opcode_name[558:561], - 186: _Opcode_name[561:564], - 187: _Opcode_name[564:570], - 192: _Opcode_name[570:574], - 193: _Opcode_name[574:580], - 194: _Opcode_name[580:589], - 195: _Opcode_name[589:597], - 196: _Opcode_name[597:606], - 197: _Opcode_name[606:616], - 198: _Opcode_name[616:625], - 200: _Opcode_name[625:631], - 202: _Opcode_name[631:635], - 203: _Opcode_name[635:641], - 204: _Opcode_name[641:645], - 205: _Opcode_name[645:651], - 206: _Opcode_name[651:659], - 207: _Opcode_name[659:665], - 208: _Opcode_name[665:672], - 209: _Opcode_name[672:684], - 210: _Opcode_name[684:690], - 211: _Opcode_name[690:700], - 216: _Opcode_name[700:706], - 217: _Opcode_name[706:712], - 219: _Opcode_name[712:719], - 240: _Opcode_name[719:724], - 241: _Opcode_name[724:734], + 83: _Opcode_name[354:362], + 84: _Opcode_name[362:370], + 85: _Opcode_name[370:378], + 102: _Opcode_name[378:381], + 103: _Opcode_name[381:388], + 104: _Opcode_name[388:395], + 105: _Opcode_name[395:403], + 106: _Opcode_name[403:418], + 107: _Opcode_name[418:428], + 108: _Opcode_name[428:440], + 114: _Opcode_name[440:445], + 115: _Opcode_name[445:450], + 126: _Opcode_name[450:453], + 127: _Opcode_name[453:459], + 128: _Opcode_name[459:463], + 129: _Opcode_name[463:468], + 144: _Opcode_name[468:474], + 145: _Opcode_name[474:477], + 146: _Opcode_name[477:479], + 147: _Opcode_name[479:482], + 151: _Opcode_name[482:487], + 152: _Opcode_name[487:495], + 153: _Opcode_name[495:499], + 154: _Opcode_name[499:502], + 155: _Opcode_name[502:508], + 156: _Opcode_name[508:511], + 157: _Opcode_name[511:514], + 158: _Opcode_name[514:517], + 159: _Opcode_name[517:520], + 160: _Opcode_name[520:523], + 161: _Opcode_name[523:526], + 162: _Opcode_name[526:529], + 168: _Opcode_name[529:532], + 169: _Opcode_name[532:535], + 170: _Opcode_name[535:538], + 171: _Opcode_name[538:545], + 172: _Opcode_name[545:551], + 177: _Opcode_name[551:553], + 179: _Opcode_name[553:561], + 180: _Opcode_name[561:572], + 181: _Opcode_name[572:574], + 182: _Opcode_name[574:577], + 183: _Opcode_name[577:579], + 184: _Opcode_name[579:582], + 185: _Opcode_name[582:585], + 186: _Opcode_name[585:588], + 187: _Opcode_name[588:594], + 192: _Opcode_name[594:598], + 193: _Opcode_name[598:604], + 194: _Opcode_name[604:613], + 195: _Opcode_name[613:621], + 196: _Opcode_name[621:630], + 197: _Opcode_name[630:640], + 198: _Opcode_name[640:649], + 200: _Opcode_name[649:655], + 202: _Opcode_name[655:659], + 203: _Opcode_name[659:665], + 204: _Opcode_name[665:669], + 205: _Opcode_name[669:675], + 206: _Opcode_name[675:683], + 207: _Opcode_name[683:689], + 208: _Opcode_name[689:696], + 209: _Opcode_name[696:708], + 210: _Opcode_name[708:714], + 211: _Opcode_name[714:724], + 216: _Opcode_name[724:730], + 217: _Opcode_name[730:736], + 219: _Opcode_name[736:743], + 240: _Opcode_name[743:748], + 241: _Opcode_name[748:758], } func (i Opcode) String() string { diff --git a/pkg/vm/stack.go b/pkg/vm/stack.go index fd779e941..e65bcada2 100644 --- a/pkg/vm/stack.go +++ b/pkg/vm/stack.go @@ -353,9 +353,31 @@ func (s *Stack) Swap(n1, n2 int) error { if n1 == n2 { return nil } + s.swap(n1, n2) + return nil +} + +func (s *Stack) swap(n1, n2 int) { a := s.Peek(n1) b := s.Peek(n2) a.value, b.value = b.value, a.value +} + +// ReverseTop reverses top n items of the stack. +func (s *Stack) ReverseTop(n int) error { + if n < 0 { + return errors.New("negative index") + } else if n > s.len { + return errors.New("too big index") + } else if n <= 1 { + return nil + } + + for i, j := 0, n-1; i < j; { + s.swap(i, j) + i++ + j-- + } return nil } diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index b01ebb000..83d3db976 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -732,6 +732,18 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro panic(err.Error()) } + case opcode.REVERSE3, opcode.REVERSE4, opcode.REVERSEN: + n := 3 + switch op { + case opcode.REVERSE4: + n = 4 + case opcode.REVERSEN: + n = int(v.estack.Pop().BigInt().Int64()) + } + if err := v.estack.ReverseTop(n); err != nil { + panic(err.Error()) + } + // Bit operations. case opcode.INVERT: // inplace diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index aeccd1117..cfe91c212 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -1049,22 +1049,32 @@ func runWithArgs(t *testing.T, prog []byte, result interface{}, args ...interfac getTestFuncForVM(prog, result, args...)(t) } -func getTestFuncForVM(prog []byte, result interface{}, args ...interface{}) func(t *testing.T) { +func getCustomTestFuncForVM(prog []byte, check func(t *testing.T, v *VM), args ...interface{}) func(t *testing.T) { return func(t *testing.T) { v := load(prog) for i := range args { v.estack.PushVal(args[i]) } - if result == nil { + if check == nil { checkVMFailed(t, v) return } runVM(t, v) - require.Equal(t, 1, v.estack.Len()) - require.Equal(t, makeStackItem(result), v.estack.Pop().value) + check(t, v) } } +func getTestFuncForVM(prog []byte, result interface{}, args ...interface{}) func(t *testing.T) { + var f func(t *testing.T, v *VM) + if result != nil { + f = func(t *testing.T, v *VM) { + require.Equal(t, 1, v.estack.Len()) + require.Equal(t, makeStackItem(result), v.estack.Pop().value) + } + } + return getCustomTestFuncForVM(prog, f, args...) +} + func TestNOTEQUALByteArray(t *testing.T) { prog := makeProgram(opcode.NOTEQUAL) t.Run("True", getTestFuncForVM(prog, true, []byte{1, 2}, []byte{0, 1, 2})) @@ -1568,6 +1578,37 @@ func TestROLLGood(t *testing.T) { assert.Equal(t, makeStackItem(1), vm.estack.Pop().value) } +func getCheckEStackFunc(items ...interface{}) func(t *testing.T, v *VM) { + return func(t *testing.T, v *VM) { + require.Equal(t, len(items), v.estack.Len()) + for i := 0; i < len(items); i++ { + assert.Equal(t, makeStackItem(items[i]), v.estack.Peek(i).Item()) + } + } +} + +func TestREVERSE3(t *testing.T) { + prog := makeProgram(opcode.REVERSE3) + t.Run("SmallStack", getTestFuncForVM(prog, nil, 1, 2)) + t.Run("Good", getCustomTestFuncForVM(prog, getCheckEStackFunc(1, 2, 3), 1, 2, 3)) +} + +func TestREVERSE4(t *testing.T) { + prog := makeProgram(opcode.REVERSE4) + t.Run("SmallStack", getTestFuncForVM(prog, nil, 1, 2, 3)) + t.Run("Good", getCustomTestFuncForVM(prog, getCheckEStackFunc(1, 2, 3, 4), 1, 2, 3, 4)) +} + +func TestREVERSEN(t *testing.T) { + prog := makeProgram(opcode.REVERSEN) + t.Run("NoArgument", getTestFuncForVM(prog, nil)) + t.Run("SmallStack", getTestFuncForVM(prog, nil, 1, 2, 3)) + t.Run("NegativeArgument", getTestFuncForVM(prog, nil, 1, 2, -1)) + t.Run("Zero", getCustomTestFuncForVM(prog, getCheckEStackFunc(3, 2, 1), 1, 2, 3, 0)) + t.Run("OneItem", getCustomTestFuncForVM(prog, getCheckEStackFunc(42), 42, 1)) + t.Run("Good", getCustomTestFuncForVM(prog, getCheckEStackFunc(1, 2, 3, 4, 5), 1, 2, 3, 4, 5, 5)) +} + func TestXTUCK(t *testing.T) { prog := makeProgram(opcode.XTUCK) t.Run("NoItem", getTestFuncForVM(prog, nil, 1))