From e53055a560a6a8ce466f574d07110116790cc2fb Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 21 May 2020 12:54:50 +0300 Subject: [PATCH 01/20] opcode: use underscored names in String() --- pkg/vm/opcode/opcode.go | 22 +-- pkg/vm/opcode/opcode_string.go | 300 ++++++++++++++++----------------- 2 files changed, 161 insertions(+), 161 deletions(-) diff --git a/pkg/vm/opcode/opcode.go b/pkg/vm/opcode/opcode.go index b729c1e16..6b5a8ee5a 100644 --- a/pkg/vm/opcode/opcode.go +++ b/pkg/vm/opcode/opcode.go @@ -46,25 +46,25 @@ const ( // Flow control NOP Opcode = 0x21 JMP Opcode = 0x22 - JMPL Opcode = 0x23 + JMPL Opcode = 0x23 // JMP_L JMPIF Opcode = 0x24 - JMPIFL Opcode = 0x25 + JMPIFL Opcode = 0x25 // JMPIF_L JMPIFNOT Opcode = 0x26 - JMPIFNOTL Opcode = 0x27 + JMPIFNOTL Opcode = 0x27 // JMPIFNOT_L JMPEQ Opcode = 0x28 - JMPEQL Opcode = 0x29 + JMPEQL Opcode = 0x29 // JMPEQ_L JMPNE Opcode = 0x2A - JMPNEL Opcode = 0x2B + JMPNEL Opcode = 0x2B // JMPNE_L JMPGT Opcode = 0x2C - JMPGTL Opcode = 0x2D + JMPGTL Opcode = 0x2D // JMPGT_L JMPGE Opcode = 0x2E - JMPGEL Opcode = 0x2F + JMPGEL Opcode = 0x2F // JMPGE_L JMPLT Opcode = 0x30 - JMPLTL Opcode = 0x31 + JMPLTL Opcode = 0x31 // JMPLT_L JMPLE Opcode = 0x32 - JMPLEL Opcode = 0x33 + JMPLEL Opcode = 0x33 // JMPLE_L CALL Opcode = 0x34 - CALLL Opcode = 0x35 + CALLL Opcode = 0x35 // CALL_L CALLA Opcode = 0x36 // Exceptions @@ -193,7 +193,7 @@ const ( UNPACK Opcode = 0xC1 NEWARRAY0 Opcode = 0xC2 NEWARRAY Opcode = 0xC3 - NEWARRAYT Opcode = 0xC4 + NEWARRAYT Opcode = 0xC4 // NEWARRAY_T NEWSTRUCT0 Opcode = 0xC5 NEWSTRUCT Opcode = 0xC6 NEWMAP Opcode = 0xC8 diff --git a/pkg/vm/opcode/opcode_string.go b/pkg/vm/opcode/opcode_string.go index 8545c39f4..e8e5cf92d 100644 --- a/pkg/vm/opcode/opcode_string.go +++ b/pkg/vm/opcode/opcode_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type Opcode"; DO NOT EDIT. +// Code generated by "stringer -type Opcode -linecomment"; DO NOT EDIT. package opcode @@ -192,7 +192,7 @@ func _() { _ = x[CONVERT-219] } -const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHAPUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMPLJMPIFJMPIFLJMPIFNOTJMPIFNOTLJMPEQJMPEQLJMPNEJMPNELJMPGTJMPGTLJMPGEJMPGELJMPLTJMPLTLJMPLEJMPLELCALLCALLLCALLAABORTASSERTTHROWRETSYSCALLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPOLDPUSH1ROLLREVERSE3REVERSE4REVERSENINITSSLOTINITSLOTLDSFLD0LDSFLD1LDSFLD2LDSFLD3LDSFLD4LDSFLD5LDSFLD6LDSFLDSTSFLD0STSFLD1STSFLD2STSFLD3STSFLD4STSFLD5STSFLD6STSFLDLDLOC0LDLOC1LDLOC2LDLOC3LDLOC4LDLOC5LDLOC6LDLOCSTLOC0STLOC1STLOC2STLOC3STLOC4STLOC5STLOC6STLOCLDARG0LDARG1LDARG2LDARG3LDARG4LDARG5LDARG6LDARGSTARG0STARG1STARG2STARG3STARG4STARG5STARG6STARGNEWBUFFERMEMCPYCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLTEGTGTEMINMAXWITHINPACKUNPACKNEWARRAY0NEWARRAYNEWARRAYTNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSISNULLISTYPECONVERT" +const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHAPUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMP_LJMPIFJMPIF_LJMPIFNOTJMPIFNOT_LJMPEQJMPEQ_LJMPNEJMPNE_LJMPGTJMPGT_LJMPGEJMPGE_LJMPLTJMPLT_LJMPLEJMPLE_LCALLCALL_LCALLAABORTASSERTTHROWRETSYSCALLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPFIXME remove #927ROLLREVERSE3REVERSE4REVERSENINITSSLOTINITSLOTLDSFLD0LDSFLD1LDSFLD2LDSFLD3LDSFLD4LDSFLD5LDSFLD6LDSFLDSTSFLD0STSFLD1STSFLD2STSFLD3STSFLD4STSFLD5STSFLD6STSFLDLDLOC0LDLOC1LDLOC2LDLOC3LDLOC4LDLOC5LDLOC6LDLOCSTLOC0STLOC1STLOC2STLOC3STLOC4STLOC5STLOC6STLOCLDARG0LDARG1LDARG2LDARG3LDARG4LDARG5LDARG6LDARGSTARG0STARG1STARG2STARG3STARG4STARG5STARG6STARGNEWBUFFERMEMCPYCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLTEGTGTEMINMAXWITHINPACKUNPACKNEWARRAY0NEWARRAYNEWARRAY_TNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSISNULLISTYPECONVERT" var _Opcode_map = map[Opcode]string{ 0: _Opcode_name[0:8], @@ -226,154 +226,154 @@ var _Opcode_map = map[Opcode]string{ 32: _Opcode_name[187:193], 33: _Opcode_name[193:196], 34: _Opcode_name[196:199], - 35: _Opcode_name[199:203], - 36: _Opcode_name[203:208], - 37: _Opcode_name[208:214], - 38: _Opcode_name[214:222], - 39: _Opcode_name[222:231], - 40: _Opcode_name[231:236], - 41: _Opcode_name[236:242], - 42: _Opcode_name[242:247], - 43: _Opcode_name[247:253], - 44: _Opcode_name[253:258], - 45: _Opcode_name[258:264], - 46: _Opcode_name[264:269], - 47: _Opcode_name[269:275], - 48: _Opcode_name[275:280], - 49: _Opcode_name[280:286], - 50: _Opcode_name[286:291], - 51: _Opcode_name[291:297], - 52: _Opcode_name[297:301], - 53: _Opcode_name[301:306], - 54: _Opcode_name[306:311], - 55: _Opcode_name[311:316], - 56: _Opcode_name[316:322], - 58: _Opcode_name[322:327], - 64: _Opcode_name[327:330], - 65: _Opcode_name[330:337], - 67: _Opcode_name[337:342], - 69: _Opcode_name[342:346], - 70: _Opcode_name[346:349], - 72: _Opcode_name[349:354], - 73: _Opcode_name[354:359], - 74: _Opcode_name[359:362], - 75: _Opcode_name[362:366], - 77: _Opcode_name[366:370], - 78: _Opcode_name[370:374], - 80: _Opcode_name[374:378], - 81: _Opcode_name[378:386], - 82: _Opcode_name[386:390], - 83: _Opcode_name[390:398], - 84: _Opcode_name[398:406], - 85: _Opcode_name[406:414], - 86: _Opcode_name[414:423], - 87: _Opcode_name[423:431], - 88: _Opcode_name[431:438], - 89: _Opcode_name[438:445], - 90: _Opcode_name[445:452], - 91: _Opcode_name[452:459], - 92: _Opcode_name[459:466], - 93: _Opcode_name[466:473], - 94: _Opcode_name[473:480], - 95: _Opcode_name[480:486], - 96: _Opcode_name[486:493], - 97: _Opcode_name[493:500], - 98: _Opcode_name[500:507], - 99: _Opcode_name[507:514], - 100: _Opcode_name[514:521], - 101: _Opcode_name[521:528], - 102: _Opcode_name[528:535], - 103: _Opcode_name[535:541], - 104: _Opcode_name[541:547], - 105: _Opcode_name[547:553], - 106: _Opcode_name[553:559], - 107: _Opcode_name[559:565], - 108: _Opcode_name[565:571], - 109: _Opcode_name[571:577], - 110: _Opcode_name[577:583], - 111: _Opcode_name[583:588], - 112: _Opcode_name[588:594], - 113: _Opcode_name[594:600], - 114: _Opcode_name[600:606], - 115: _Opcode_name[606:612], - 116: _Opcode_name[612:618], - 117: _Opcode_name[618:624], - 118: _Opcode_name[624:630], - 119: _Opcode_name[630:635], - 120: _Opcode_name[635:641], - 121: _Opcode_name[641:647], - 122: _Opcode_name[647:653], - 123: _Opcode_name[653:659], - 124: _Opcode_name[659:665], - 125: _Opcode_name[665:671], - 126: _Opcode_name[671:677], - 127: _Opcode_name[677:682], - 128: _Opcode_name[682:688], - 129: _Opcode_name[688:694], - 130: _Opcode_name[694:700], - 131: _Opcode_name[700:706], - 132: _Opcode_name[706:712], - 133: _Opcode_name[712:718], - 134: _Opcode_name[718:724], - 135: _Opcode_name[724:729], - 136: _Opcode_name[729:738], - 137: _Opcode_name[738:744], - 139: _Opcode_name[744:747], - 140: _Opcode_name[747:753], - 141: _Opcode_name[753:757], - 142: _Opcode_name[757:762], - 144: _Opcode_name[762:768], - 145: _Opcode_name[768:771], - 146: _Opcode_name[771:773], - 147: _Opcode_name[773:776], - 151: _Opcode_name[776:781], - 152: _Opcode_name[781:789], - 153: _Opcode_name[789:793], - 154: _Opcode_name[793:796], - 155: _Opcode_name[796:802], - 156: _Opcode_name[802:805], - 157: _Opcode_name[805:808], - 158: _Opcode_name[808:811], - 159: _Opcode_name[811:814], - 160: _Opcode_name[814:817], - 161: _Opcode_name[817:820], - 162: _Opcode_name[820:823], - 168: _Opcode_name[823:826], - 169: _Opcode_name[826:829], - 170: _Opcode_name[829:832], - 171: _Opcode_name[832:839], - 172: _Opcode_name[839:845], - 177: _Opcode_name[845:847], - 179: _Opcode_name[847:855], - 180: _Opcode_name[855:866], - 181: _Opcode_name[866:868], - 182: _Opcode_name[868:871], - 183: _Opcode_name[871:873], - 184: _Opcode_name[873:876], - 185: _Opcode_name[876:879], - 186: _Opcode_name[879:882], - 187: _Opcode_name[882:888], - 192: _Opcode_name[888:892], - 193: _Opcode_name[892:898], - 194: _Opcode_name[898:907], - 195: _Opcode_name[907:915], - 196: _Opcode_name[915:924], - 197: _Opcode_name[924:934], - 198: _Opcode_name[934:943], - 200: _Opcode_name[943:949], - 202: _Opcode_name[949:953], - 203: _Opcode_name[953:959], - 204: _Opcode_name[959:963], - 205: _Opcode_name[963:969], - 206: _Opcode_name[969:977], - 207: _Opcode_name[977:983], - 208: _Opcode_name[983:990], - 209: _Opcode_name[990:1002], - 210: _Opcode_name[1002:1008], - 211: _Opcode_name[1008:1018], - 216: _Opcode_name[1018:1024], - 217: _Opcode_name[1024:1030], - 219: _Opcode_name[1030:1037], + 35: _Opcode_name[199:204], + 36: _Opcode_name[204:209], + 37: _Opcode_name[209:216], + 38: _Opcode_name[216:224], + 39: _Opcode_name[224:234], + 40: _Opcode_name[234:239], + 41: _Opcode_name[239:246], + 42: _Opcode_name[246:251], + 43: _Opcode_name[251:258], + 44: _Opcode_name[258:263], + 45: _Opcode_name[263:270], + 46: _Opcode_name[270:275], + 47: _Opcode_name[275:282], + 48: _Opcode_name[282:287], + 49: _Opcode_name[287:294], + 50: _Opcode_name[294:299], + 51: _Opcode_name[299:306], + 52: _Opcode_name[306:310], + 53: _Opcode_name[310:316], + 54: _Opcode_name[316:321], + 55: _Opcode_name[321:326], + 56: _Opcode_name[326:332], + 58: _Opcode_name[332:337], + 64: _Opcode_name[337:340], + 65: _Opcode_name[340:347], + 67: _Opcode_name[347:352], + 69: _Opcode_name[352:356], + 70: _Opcode_name[356:359], + 72: _Opcode_name[359:364], + 73: _Opcode_name[364:369], + 74: _Opcode_name[369:372], + 75: _Opcode_name[372:376], + 77: _Opcode_name[376:380], + 78: _Opcode_name[380:384], + 80: _Opcode_name[384:388], + 81: _Opcode_name[388:405], + 82: _Opcode_name[405:409], + 83: _Opcode_name[409:417], + 84: _Opcode_name[417:425], + 85: _Opcode_name[425:433], + 86: _Opcode_name[433:442], + 87: _Opcode_name[442:450], + 88: _Opcode_name[450:457], + 89: _Opcode_name[457:464], + 90: _Opcode_name[464:471], + 91: _Opcode_name[471:478], + 92: _Opcode_name[478:485], + 93: _Opcode_name[485:492], + 94: _Opcode_name[492:499], + 95: _Opcode_name[499:505], + 96: _Opcode_name[505:512], + 97: _Opcode_name[512:519], + 98: _Opcode_name[519:526], + 99: _Opcode_name[526:533], + 100: _Opcode_name[533:540], + 101: _Opcode_name[540:547], + 102: _Opcode_name[547:554], + 103: _Opcode_name[554:560], + 104: _Opcode_name[560:566], + 105: _Opcode_name[566:572], + 106: _Opcode_name[572:578], + 107: _Opcode_name[578:584], + 108: _Opcode_name[584:590], + 109: _Opcode_name[590:596], + 110: _Opcode_name[596:602], + 111: _Opcode_name[602:607], + 112: _Opcode_name[607:613], + 113: _Opcode_name[613:619], + 114: _Opcode_name[619:625], + 115: _Opcode_name[625:631], + 116: _Opcode_name[631:637], + 117: _Opcode_name[637:643], + 118: _Opcode_name[643:649], + 119: _Opcode_name[649:654], + 120: _Opcode_name[654:660], + 121: _Opcode_name[660:666], + 122: _Opcode_name[666:672], + 123: _Opcode_name[672:678], + 124: _Opcode_name[678:684], + 125: _Opcode_name[684:690], + 126: _Opcode_name[690:696], + 127: _Opcode_name[696:701], + 128: _Opcode_name[701:707], + 129: _Opcode_name[707:713], + 130: _Opcode_name[713:719], + 131: _Opcode_name[719:725], + 132: _Opcode_name[725:731], + 133: _Opcode_name[731:737], + 134: _Opcode_name[737:743], + 135: _Opcode_name[743:748], + 136: _Opcode_name[748:757], + 137: _Opcode_name[757:763], + 139: _Opcode_name[763:766], + 140: _Opcode_name[766:772], + 141: _Opcode_name[772:776], + 142: _Opcode_name[776:781], + 144: _Opcode_name[781:787], + 145: _Opcode_name[787:790], + 146: _Opcode_name[790:792], + 147: _Opcode_name[792:795], + 151: _Opcode_name[795:800], + 152: _Opcode_name[800:808], + 153: _Opcode_name[808:812], + 154: _Opcode_name[812:815], + 155: _Opcode_name[815:821], + 156: _Opcode_name[821:824], + 157: _Opcode_name[824:827], + 158: _Opcode_name[827:830], + 159: _Opcode_name[830:833], + 160: _Opcode_name[833:836], + 161: _Opcode_name[836:839], + 162: _Opcode_name[839:842], + 168: _Opcode_name[842:845], + 169: _Opcode_name[845:848], + 170: _Opcode_name[848:851], + 171: _Opcode_name[851:858], + 172: _Opcode_name[858:864], + 177: _Opcode_name[864:866], + 179: _Opcode_name[866:874], + 180: _Opcode_name[874:885], + 181: _Opcode_name[885:887], + 182: _Opcode_name[887:890], + 183: _Opcode_name[890:892], + 184: _Opcode_name[892:895], + 185: _Opcode_name[895:898], + 186: _Opcode_name[898:901], + 187: _Opcode_name[901:907], + 192: _Opcode_name[907:911], + 193: _Opcode_name[911:917], + 194: _Opcode_name[917:926], + 195: _Opcode_name[926:934], + 196: _Opcode_name[934:944], + 197: _Opcode_name[944:954], + 198: _Opcode_name[954:963], + 200: _Opcode_name[963:969], + 202: _Opcode_name[969:973], + 203: _Opcode_name[973:979], + 204: _Opcode_name[979:983], + 205: _Opcode_name[983:989], + 206: _Opcode_name[989:997], + 207: _Opcode_name[997:1003], + 208: _Opcode_name[1003:1010], + 209: _Opcode_name[1010:1022], + 210: _Opcode_name[1022:1028], + 211: _Opcode_name[1028:1038], + 216: _Opcode_name[1038:1044], + 217: _Opcode_name[1044:1050], + 219: _Opcode_name[1050:1057], } func (i Opcode) String() string { From 1100f629df05d81da9d0a378763a394c4c52e265 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 21 May 2020 13:00:11 +0300 Subject: [PATCH 02/20] opcode: implement FromString() --- pkg/vm/opcode/from_string.go | 20 ++++++++++++++++++++ pkg/vm/opcode/opcode_test.go | 10 ++++++++++ 2 files changed, 30 insertions(+) create mode 100644 pkg/vm/opcode/from_string.go diff --git a/pkg/vm/opcode/from_string.go b/pkg/vm/opcode/from_string.go new file mode 100644 index 000000000..b79d69ae9 --- /dev/null +++ b/pkg/vm/opcode/from_string.go @@ -0,0 +1,20 @@ +package opcode + +import "errors" + +var stringToOpcode = make(map[string]Opcode) + +func init() { + for i := 0; i < 255; i++ { + op := Opcode(i) + stringToOpcode[op.String()] = op + } +} + +// FromString converts string representation to and opcode itself. +func FromString(s string) (Opcode, error) { + if op, ok := stringToOpcode[s]; ok { + return op, nil + } + return 0, errors.New("invalid opcode") +} diff --git a/pkg/vm/opcode/opcode_test.go b/pkg/vm/opcode/opcode_test.go index 862093652..f0f5a0451 100644 --- a/pkg/vm/opcode/opcode_test.go +++ b/pkg/vm/opcode/opcode_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) // Nothing more to test here, really. @@ -18,3 +19,12 @@ func TestStringer(t *testing.T) { assert.Equal(t, s, o.String()) } } + +func TestFromString(t *testing.T) { + _, err := FromString("abcdef") + require.Error(t, err) + + op, err := FromString(MUL.String()) + require.NoError(t, err) + require.Equal(t, MUL, op) +} From 0c7b1632806a0a9eed5c45997630ab24096655f9 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 20 May 2020 15:01:06 +0300 Subject: [PATCH 03/20] vm/tests: unmarshal script in JSON tests properly Script is no specified via stringified opcodes and their arguments. --- pkg/vm/json_test.go | 57 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/pkg/vm/json_test.go b/pkg/vm/json_test.go index b17a1f2ef..9f00992a5 100644 --- a/pkg/vm/json_test.go +++ b/pkg/vm/json_test.go @@ -11,6 +11,8 @@ import ( "math/big" "os" "path/filepath" + "regexp" + "strconv" "strings" "testing" @@ -146,7 +148,9 @@ func testFile(t *testing.T, filename string) { ctx := vm.istack.Peek(i).Value().(*Context) if ctx.nextip < len(ctx.prog) { require.Equal(t, s.InstructionPointer, ctx.nextip) - require.Equal(t, s.Instruction, opcode.Opcode(ctx.prog[ctx.nextip]).String()) + op, err := opcode.FromString(s.Instruction) + require.NoError(t, err) + require.Equal(t, op, opcode.Opcode(ctx.prog[ctx.nextip])) } compareStacks(t, s.EStack, vm.estack) compareStacks(t, s.AStack, vm.astack) @@ -291,15 +295,48 @@ func (v *vmUTState) UnmarshalJSON(data []byte) error { } func (v *vmUTScript) UnmarshalJSON(data []byte) error { - b, err := decodeBytes(data) - if err != nil { + var ops []string + if err := json.Unmarshal(data, &ops); err != nil { return err } - *v = vmUTScript(b) + var script []byte + for i := range ops { + if b, ok := decodeSingle(ops[i]); ok { + script = append(script, b...) + } else { + const regex = `(?P(?:0x)?[0-9a-zA-Z]+)\*(?P[0-9]+)` + re := regexp.MustCompile(regex) + ss := re.FindStringSubmatch(ops[i]) + if len(ss) != 3 { + return fmt.Errorf("invalid script part: %s", ops[i]) + } + b, ok := decodeSingle(ss[1]) + if !ok { + return fmt.Errorf("invalid script part: %s", ops[i]) + } + num, err := strconv.Atoi(ss[2]) + if err != nil { + return fmt.Errorf("invalid script part: %s", ops[i]) + } + for i := 0; i < num; i++ { + script = append(script, b...) + } + } + } + + *v = script return nil } +func decodeSingle(s string) ([]byte, bool) { + if op, err := opcode.FromString(s); err == nil { + return []byte{byte(op)}, true + } + b, err := decodeHex(s) + return b, err == nil +} + func (v *vmUTActionType) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, (*string)(v)) } @@ -366,12 +403,18 @@ func decodeBytes(data []byte) ([]byte, error) { return []byte{}, nil } - hdata := data[3 : len(data)-1] - if b, err := hex.DecodeString(string(hdata)); err == nil { + data = data[1 : len(data)-1] // strip quotes + if b, err := decodeHex(string(data)); err == nil { return b, nil } - data = data[1 : len(data)-1] r := base64.NewDecoder(base64.StdEncoding, bytes.NewReader(data)) return ioutil.ReadAll(r) } + +func decodeHex(s string) ([]byte, error) { + if strings.HasPrefix(s, "0x") { + s = s[2:] + } + return hex.DecodeString(s) +} From e71cc04c70c06b25a12a7087c9b66494c74adbb5 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 20 May 2020 15:02:01 +0300 Subject: [PATCH 04/20] vm/tests: unmarshal VM state in JSON tests properly --- pkg/vm/json_test.go | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/pkg/vm/json_test.go b/pkg/vm/json_test.go index 9f00992a5..7771ce556 100644 --- a/pkg/vm/json_test.go +++ b/pkg/vm/json_test.go @@ -44,7 +44,7 @@ type ( } vmUTExecutionEngineState struct { - State vmUTState `json:"state"` + State State `json:"state"` ResultStack []vmUTStackItem `json:"resultStack"` InvocationStack []vmUTExecutionContextState `json:"invocationStack"` } @@ -61,8 +61,6 @@ type ( Result vmUTExecutionEngineState `json:"result"` } - vmUTState State - vmUTStackItemType string ) @@ -138,8 +136,8 @@ func testFile(t *testing.T, filename string) { for i := range test.Steps { execStep(t, vm, test.Steps[i]) result := test.Steps[i].Result - require.Equal(t, State(result.State), vm.state) - if result.State == vmUTState(faultState) { // do not compare stacks on fault + require.Equal(t, result.State, vm.state) + if result.State == faultState { // do not compare stacks on fault continue } @@ -280,20 +278,6 @@ func execStep(t *testing.T, v *VM, step vmUTStep) { } } -func (v *vmUTState) UnmarshalJSON(data []byte) error { - switch s := string(data); s { - case `"Break"`: - *v = vmUTState(breakState) - case `"Fault"`: - *v = vmUTState(faultState) - case `"Halt"`: - *v = vmUTState(haltState) - default: - panic(fmt.Sprintf("invalid state: %s", s)) - } - return nil -} - func (v *vmUTScript) UnmarshalJSON(data []byte) error { var ops []string if err := json.Unmarshal(data, &ops); err != nil { From 7ddb23abe3f57c2aadf0e6f8933c9ba841bb60fd Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 20 May 2020 15:48:19 +0300 Subject: [PATCH 05/20] vm/tests: unmarshal stack items in JSON tests properly New item types were added in NEO3 and they can be capitalized or not. --- pkg/vm/json_test.go | 53 +++++++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/pkg/vm/json_test.go b/pkg/vm/json_test.go index 7771ce556..bbd67207f 100644 --- a/pkg/vm/json_test.go +++ b/pkg/vm/json_test.go @@ -78,14 +78,17 @@ const ( vmStepOut vmUTActionType = "StepOut" vmStepOver vmUTActionType = "StepOver" - typeArray vmUTStackItemType = "Array" - typeBoolean vmUTStackItemType = "Boolean" - typeByteArray vmUTStackItemType = "ByteArray" - typeInteger vmUTStackItemType = "Integer" - typeInterop vmUTStackItemType = "Interop" - typeMap vmUTStackItemType = "Map" - typeString vmUTStackItemType = "String" - typeStruct vmUTStackItemType = "Struct" + typeArray vmUTStackItemType = "array" + typeBoolean vmUTStackItemType = "boolean" + typeBuffer vmUTStackItemType = "buffer" + typeByteString vmUTStackItemType = "bytestring" + typeInteger vmUTStackItemType = "integer" + typeInterop vmUTStackItemType = "interop" + typeMap vmUTStackItemType = "map" + typeNull vmUTStackItemType = "null" + typePointer vmUTStackItemType = "pointer" + typeString vmUTStackItemType = "string" + typeStruct vmUTStackItemType = "struct" testsDir = "testdata/neo-vm/tests/neo-vm.Tests/Tests/" ) @@ -182,6 +185,10 @@ func compareItems(t *testing.T, a, b StackItem) { default: require.Fail(t, "wrong type") } + case *PointerItem: + p, ok := b.(*PointerItem) + require.True(t, ok) + require.Equal(t, si.pos, p.pos) // there no script in test files default: require.Equal(t, a, b) } @@ -206,7 +213,7 @@ func compareStacks(t *testing.T, expected []vmUTStackItem, actual *Stack) { } func (v *vmUTStackItem) toStackItem() StackItem { - switch v.Type { + switch v.Type.toLower() { case typeArray: items := v.Value.([]vmUTStackItem) result := make([]StackItem, len(items)) @@ -229,10 +236,16 @@ func (v *vmUTStackItem) toStackItem() StackItem { return result case typeInterop: panic("not implemented") - case typeByteArray: + case typeByteString: return &ByteArrayItem{ v.Value.([]byte), } + case typeBuffer: + return &BufferItem{v.Value.([]byte)} + case typePointer: + return NewPointerItem(v.Value.(int), nil) + case typeNull: + return NullItem{} case typeBoolean: return &BoolItem{ v.Value.(bool), @@ -251,7 +264,7 @@ func (v *vmUTStackItem) toStackItem() StackItem { value: result, } default: - panic("invalid type") + panic(fmt.Sprintf("invalid type: %s", v.Type)) } } @@ -278,6 +291,10 @@ func execStep(t *testing.T, v *VM, step vmUTStep) { } } +func (v vmUTStackItemType) toLower() vmUTStackItemType { + return vmUTStackItemType(strings.ToLower(string(v))) +} + func (v *vmUTScript) UnmarshalJSON(data []byte) error { var ops []string if err := json.Unmarshal(data, &ops); err != nil { @@ -333,14 +350,14 @@ func (v *vmUTStackItem) UnmarshalJSON(data []byte) error { v.Type = si.Type - switch si.Type { + switch typ := si.Type.toLower(); typ { case typeArray, typeStruct: var a []vmUTStackItem if err := json.Unmarshal(si.Value, &a); err != nil { return err } v.Value = a - case typeInteger: + case typeInteger, typePointer: num := new(big.Int) var a int64 var s string @@ -351,20 +368,24 @@ func (v *vmUTStackItem) UnmarshalJSON(data []byte) error { } else { panic(fmt.Sprintf("invalid integer: %v", si.Value)) } - v.Value = num + if typ == typePointer { + v.Value = int(num.Int64()) + } else { + v.Value = num + } case typeBoolean: var b bool if err := json.Unmarshal(si.Value, &b); err != nil { return err } v.Value = b - case typeByteArray: + case typeByteString, typeBuffer: b, err := decodeBytes(si.Value) if err != nil { return err } v.Value = b - case typeInterop: + case typeInterop, typeNull: v.Value = nil case typeMap: var m map[string]vmUTStackItem From 128a99dec21dc2c7ddbdef8eef6a5bda7d29f4d0 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 20 May 2020 16:07:37 +0300 Subject: [PATCH 06/20] vm/tests: unmarshal action in JSON tests properly --- pkg/vm/json_test.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pkg/vm/json_test.go b/pkg/vm/json_test.go index bbd67207f..f1f5f9702 100644 --- a/pkg/vm/json_test.go +++ b/pkg/vm/json_test.go @@ -73,10 +73,10 @@ type stackItemAUX struct { } const ( - vmExecute vmUTActionType = "Execute" - vmStepInto vmUTActionType = "StepInto" - vmStepOut vmUTActionType = "StepOut" - vmStepOver vmUTActionType = "StepOver" + vmExecute vmUTActionType = "execute" + vmStepInto vmUTActionType = "stepinto" + vmStepOut vmUTActionType = "stepout" + vmStepOver vmUTActionType = "stepover" typeArray vmUTStackItemType = "array" typeBoolean vmUTStackItemType = "boolean" @@ -271,7 +271,7 @@ func (v *vmUTStackItem) toStackItem() StackItem { func execStep(t *testing.T, v *VM, step vmUTStep) { for i, a := range step.Actions { var err error - switch a { + switch a.toLower() { case vmExecute: err = v.Run() case vmStepInto: @@ -338,6 +338,10 @@ func decodeSingle(s string) ([]byte, bool) { return b, err == nil } +func (v vmUTActionType) toLower() vmUTActionType { + return vmUTActionType(strings.ToLower(string(v))) +} + func (v *vmUTActionType) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, (*string)(v)) } From 646c247b31b90c5e8e0828d9a104e62291d7efa5 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 20 May 2020 17:34:32 +0300 Subject: [PATCH 07/20] vm/tests: unmarshal Map keys properly in JSON tests --- pkg/vm/json_test.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/pkg/vm/json_test.go b/pkg/vm/json_test.go index f1f5f9702..34e2d2d46 100644 --- a/pkg/vm/json_test.go +++ b/pkg/vm/json_test.go @@ -229,9 +229,11 @@ func (v *vmUTStackItem) toStackItem() StackItem { items := v.Value.(map[string]vmUTStackItem) result := NewMapItem() for k, v := range items { - var item vmUTStackItem - _ = json.Unmarshal([]byte(`"`+k+`"`), &item) - result.Add(item.toStackItem(), v.toStackItem()) + item := jsonStringToInteger(k) + if item == nil { + panic(fmt.Sprintf("can't unmarshal StackItem %s", k)) + } + result.Add(item, v.toStackItem()) } return result case typeInterop: @@ -291,6 +293,14 @@ func execStep(t *testing.T, v *VM, step vmUTStep) { } } +func jsonStringToInteger(s string) StackItem { + b, err := decodeHex(s) + if err == nil { + return NewBigIntegerItem(new(big.Int).SetBytes(b)) + } + return nil +} + func (v vmUTStackItemType) toLower() vmUTStackItemType { return vmUTStackItemType(strings.ToLower(string(v))) } From 61b17227452a6463c665dc1a8c45bd5664e03395 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 20 May 2020 15:52:24 +0300 Subject: [PATCH 08/20] opcode: swap ROT and OLDPUSH1 Because of how stringer generates String() method, the first of them will be used as the result. --- pkg/vm/opcode/opcode.go | 2 +- pkg/vm/opcode/opcode_string.go | 230 ++++++++++++++++----------------- 2 files changed, 116 insertions(+), 116 deletions(-) diff --git a/pkg/vm/opcode/opcode.go b/pkg/vm/opcode/opcode.go index 6b5a8ee5a..b8d56666e 100644 --- a/pkg/vm/opcode/opcode.go +++ b/pkg/vm/opcode/opcode.go @@ -86,8 +86,8 @@ const ( PICK Opcode = 0x4D TUCK Opcode = 0x4E SWAP Opcode = 0x50 - OLDPUSH1 Opcode = 0x51 // FIXME remove #927 ROT Opcode = 0x51 + OLDPUSH1 Opcode = 0x51 // FIXME remove #927 ROLL Opcode = 0x52 REVERSE3 Opcode = 0x53 REVERSE4 Opcode = 0x54 diff --git a/pkg/vm/opcode/opcode_string.go b/pkg/vm/opcode/opcode_string.go index e8e5cf92d..d5ab10d51 100644 --- a/pkg/vm/opcode/opcode_string.go +++ b/pkg/vm/opcode/opcode_string.go @@ -76,8 +76,8 @@ func _() { _ = x[PICK-77] _ = x[TUCK-78] _ = x[SWAP-80] - _ = x[OLDPUSH1-81] _ = x[ROT-81] + _ = x[OLDPUSH1-81] _ = x[ROLL-82] _ = x[REVERSE3-83] _ = x[REVERSE4-84] @@ -192,7 +192,7 @@ func _() { _ = x[CONVERT-219] } -const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHAPUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMP_LJMPIFJMPIF_LJMPIFNOTJMPIFNOT_LJMPEQJMPEQ_LJMPNEJMPNE_LJMPGTJMPGT_LJMPGEJMPGE_LJMPLTJMPLT_LJMPLEJMPLE_LCALLCALL_LCALLAABORTASSERTTHROWRETSYSCALLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPFIXME remove #927ROLLREVERSE3REVERSE4REVERSENINITSSLOTINITSLOTLDSFLD0LDSFLD1LDSFLD2LDSFLD3LDSFLD4LDSFLD5LDSFLD6LDSFLDSTSFLD0STSFLD1STSFLD2STSFLD3STSFLD4STSFLD5STSFLD6STSFLDLDLOC0LDLOC1LDLOC2LDLOC3LDLOC4LDLOC5LDLOC6LDLOCSTLOC0STLOC1STLOC2STLOC3STLOC4STLOC5STLOC6STLOCLDARG0LDARG1LDARG2LDARG3LDARG4LDARG5LDARG6LDARGSTARG0STARG1STARG2STARG3STARG4STARG5STARG6STARGNEWBUFFERMEMCPYCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLTEGTGTEMINMAXWITHINPACKUNPACKNEWARRAY0NEWARRAYNEWARRAY_TNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSISNULLISTYPECONVERT" +const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHAPUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMP_LJMPIFJMPIF_LJMPIFNOTJMPIFNOT_LJMPEQJMPEQ_LJMPNEJMPNE_LJMPGTJMPGT_LJMPGEJMPGE_LJMPLTJMPLT_LJMPLEJMPLE_LCALLCALL_LCALLAABORTASSERTTHROWRETSYSCALLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPROTROLLREVERSE3REVERSE4REVERSENINITSSLOTINITSLOTLDSFLD0LDSFLD1LDSFLD2LDSFLD3LDSFLD4LDSFLD5LDSFLD6LDSFLDSTSFLD0STSFLD1STSFLD2STSFLD3STSFLD4STSFLD5STSFLD6STSFLDLDLOC0LDLOC1LDLOC2LDLOC3LDLOC4LDLOC5LDLOC6LDLOCSTLOC0STLOC1STLOC2STLOC3STLOC4STLOC5STLOC6STLOCLDARG0LDARG1LDARG2LDARG3LDARG4LDARG5LDARG6LDARGSTARG0STARG1STARG2STARG3STARG4STARG5STARG6STARGNEWBUFFERMEMCPYCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLTEGTGTEMINMAXWITHINPACKUNPACKNEWARRAY0NEWARRAYNEWARRAY_TNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSISNULLISTYPECONVERT" var _Opcode_map = map[Opcode]string{ 0: _Opcode_name[0:8], @@ -261,119 +261,119 @@ var _Opcode_map = map[Opcode]string{ 77: _Opcode_name[376:380], 78: _Opcode_name[380:384], 80: _Opcode_name[384:388], - 81: _Opcode_name[388:405], - 82: _Opcode_name[405:409], - 83: _Opcode_name[409:417], - 84: _Opcode_name[417:425], - 85: _Opcode_name[425:433], - 86: _Opcode_name[433:442], - 87: _Opcode_name[442:450], - 88: _Opcode_name[450:457], - 89: _Opcode_name[457:464], - 90: _Opcode_name[464:471], - 91: _Opcode_name[471:478], - 92: _Opcode_name[478:485], - 93: _Opcode_name[485:492], - 94: _Opcode_name[492:499], - 95: _Opcode_name[499:505], - 96: _Opcode_name[505:512], - 97: _Opcode_name[512:519], - 98: _Opcode_name[519:526], - 99: _Opcode_name[526:533], - 100: _Opcode_name[533:540], - 101: _Opcode_name[540:547], - 102: _Opcode_name[547:554], - 103: _Opcode_name[554:560], - 104: _Opcode_name[560:566], - 105: _Opcode_name[566:572], - 106: _Opcode_name[572:578], - 107: _Opcode_name[578:584], - 108: _Opcode_name[584:590], - 109: _Opcode_name[590:596], - 110: _Opcode_name[596:602], - 111: _Opcode_name[602:607], - 112: _Opcode_name[607:613], - 113: _Opcode_name[613:619], - 114: _Opcode_name[619:625], - 115: _Opcode_name[625:631], - 116: _Opcode_name[631:637], - 117: _Opcode_name[637:643], - 118: _Opcode_name[643:649], - 119: _Opcode_name[649:654], - 120: _Opcode_name[654:660], - 121: _Opcode_name[660:666], - 122: _Opcode_name[666:672], - 123: _Opcode_name[672:678], - 124: _Opcode_name[678:684], - 125: _Opcode_name[684:690], - 126: _Opcode_name[690:696], - 127: _Opcode_name[696:701], - 128: _Opcode_name[701:707], - 129: _Opcode_name[707:713], - 130: _Opcode_name[713:719], - 131: _Opcode_name[719:725], - 132: _Opcode_name[725:731], - 133: _Opcode_name[731:737], - 134: _Opcode_name[737:743], - 135: _Opcode_name[743:748], - 136: _Opcode_name[748:757], - 137: _Opcode_name[757:763], - 139: _Opcode_name[763:766], - 140: _Opcode_name[766:772], - 141: _Opcode_name[772:776], - 142: _Opcode_name[776:781], - 144: _Opcode_name[781:787], - 145: _Opcode_name[787:790], - 146: _Opcode_name[790:792], - 147: _Opcode_name[792:795], - 151: _Opcode_name[795:800], - 152: _Opcode_name[800:808], - 153: _Opcode_name[808:812], - 154: _Opcode_name[812:815], - 155: _Opcode_name[815:821], - 156: _Opcode_name[821:824], - 157: _Opcode_name[824:827], - 158: _Opcode_name[827:830], - 159: _Opcode_name[830:833], - 160: _Opcode_name[833:836], - 161: _Opcode_name[836:839], - 162: _Opcode_name[839:842], - 168: _Opcode_name[842:845], - 169: _Opcode_name[845:848], - 170: _Opcode_name[848:851], - 171: _Opcode_name[851:858], - 172: _Opcode_name[858:864], - 177: _Opcode_name[864:866], - 179: _Opcode_name[866:874], - 180: _Opcode_name[874:885], - 181: _Opcode_name[885:887], - 182: _Opcode_name[887:890], - 183: _Opcode_name[890:892], - 184: _Opcode_name[892:895], - 185: _Opcode_name[895:898], - 186: _Opcode_name[898:901], - 187: _Opcode_name[901:907], - 192: _Opcode_name[907:911], - 193: _Opcode_name[911:917], - 194: _Opcode_name[917:926], - 195: _Opcode_name[926:934], - 196: _Opcode_name[934:944], - 197: _Opcode_name[944:954], - 198: _Opcode_name[954:963], - 200: _Opcode_name[963:969], - 202: _Opcode_name[969:973], - 203: _Opcode_name[973:979], - 204: _Opcode_name[979:983], - 205: _Opcode_name[983:989], - 206: _Opcode_name[989:997], - 207: _Opcode_name[997:1003], - 208: _Opcode_name[1003:1010], - 209: _Opcode_name[1010:1022], - 210: _Opcode_name[1022:1028], - 211: _Opcode_name[1028:1038], - 216: _Opcode_name[1038:1044], - 217: _Opcode_name[1044:1050], - 219: _Opcode_name[1050:1057], + 81: _Opcode_name[388:391], + 82: _Opcode_name[391:395], + 83: _Opcode_name[395:403], + 84: _Opcode_name[403:411], + 85: _Opcode_name[411:419], + 86: _Opcode_name[419:428], + 87: _Opcode_name[428:436], + 88: _Opcode_name[436:443], + 89: _Opcode_name[443:450], + 90: _Opcode_name[450:457], + 91: _Opcode_name[457:464], + 92: _Opcode_name[464:471], + 93: _Opcode_name[471:478], + 94: _Opcode_name[478:485], + 95: _Opcode_name[485:491], + 96: _Opcode_name[491:498], + 97: _Opcode_name[498:505], + 98: _Opcode_name[505:512], + 99: _Opcode_name[512:519], + 100: _Opcode_name[519:526], + 101: _Opcode_name[526:533], + 102: _Opcode_name[533:540], + 103: _Opcode_name[540:546], + 104: _Opcode_name[546:552], + 105: _Opcode_name[552:558], + 106: _Opcode_name[558:564], + 107: _Opcode_name[564:570], + 108: _Opcode_name[570:576], + 109: _Opcode_name[576:582], + 110: _Opcode_name[582:588], + 111: _Opcode_name[588:593], + 112: _Opcode_name[593:599], + 113: _Opcode_name[599:605], + 114: _Opcode_name[605:611], + 115: _Opcode_name[611:617], + 116: _Opcode_name[617:623], + 117: _Opcode_name[623:629], + 118: _Opcode_name[629:635], + 119: _Opcode_name[635:640], + 120: _Opcode_name[640:646], + 121: _Opcode_name[646:652], + 122: _Opcode_name[652:658], + 123: _Opcode_name[658:664], + 124: _Opcode_name[664:670], + 125: _Opcode_name[670:676], + 126: _Opcode_name[676:682], + 127: _Opcode_name[682:687], + 128: _Opcode_name[687:693], + 129: _Opcode_name[693:699], + 130: _Opcode_name[699:705], + 131: _Opcode_name[705:711], + 132: _Opcode_name[711:717], + 133: _Opcode_name[717:723], + 134: _Opcode_name[723:729], + 135: _Opcode_name[729:734], + 136: _Opcode_name[734:743], + 137: _Opcode_name[743:749], + 139: _Opcode_name[749:752], + 140: _Opcode_name[752:758], + 141: _Opcode_name[758:762], + 142: _Opcode_name[762:767], + 144: _Opcode_name[767:773], + 145: _Opcode_name[773:776], + 146: _Opcode_name[776:778], + 147: _Opcode_name[778:781], + 151: _Opcode_name[781:786], + 152: _Opcode_name[786:794], + 153: _Opcode_name[794:798], + 154: _Opcode_name[798:801], + 155: _Opcode_name[801:807], + 156: _Opcode_name[807:810], + 157: _Opcode_name[810:813], + 158: _Opcode_name[813:816], + 159: _Opcode_name[816:819], + 160: _Opcode_name[819:822], + 161: _Opcode_name[822:825], + 162: _Opcode_name[825:828], + 168: _Opcode_name[828:831], + 169: _Opcode_name[831:834], + 170: _Opcode_name[834:837], + 171: _Opcode_name[837:844], + 172: _Opcode_name[844:850], + 177: _Opcode_name[850:852], + 179: _Opcode_name[852:860], + 180: _Opcode_name[860:871], + 181: _Opcode_name[871:873], + 182: _Opcode_name[873:876], + 183: _Opcode_name[876:878], + 184: _Opcode_name[878:881], + 185: _Opcode_name[881:884], + 186: _Opcode_name[884:887], + 187: _Opcode_name[887:893], + 192: _Opcode_name[893:897], + 193: _Opcode_name[897:903], + 194: _Opcode_name[903:912], + 195: _Opcode_name[912:920], + 196: _Opcode_name[920:930], + 197: _Opcode_name[930:940], + 198: _Opcode_name[940:949], + 200: _Opcode_name[949:955], + 202: _Opcode_name[955:959], + 203: _Opcode_name[959:965], + 204: _Opcode_name[965:969], + 205: _Opcode_name[969:975], + 206: _Opcode_name[975:983], + 207: _Opcode_name[983:989], + 208: _Opcode_name[989:996], + 209: _Opcode_name[996:1008], + 210: _Opcode_name[1008:1014], + 211: _Opcode_name[1014:1024], + 216: _Opcode_name[1024:1030], + 217: _Opcode_name[1030:1036], + 219: _Opcode_name[1036:1043], } func (i Opcode) String() string { From a44acd25bb36aa87e5f6a4195759cf630e1034ff Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 20 May 2020 15:51:50 +0300 Subject: [PATCH 09/20] vm/tests: restore JSON tests parsing Make sure we support new test format. Tests itself will be restored later. --- pkg/vm/json_test.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/pkg/vm/json_test.go b/pkg/vm/json_test.go index 34e2d2d46..2bcfc72ef 100644 --- a/pkg/vm/json_test.go +++ b/pkg/vm/json_test.go @@ -94,7 +94,6 @@ const ( ) func TestUT(t *testing.T) { - t.Skip() testsRan := false err := filepath.Walk(testsDir, func(path string, info os.FileInfo, err error) error { if !strings.HasSuffix(path, ".json") { @@ -124,12 +123,25 @@ func testFile(t *testing.T, filename string) { data, err := ioutil.ReadFile(filename) require.NoError(t, err) + // get rid of possible BOM + if len(data) > 2 && data[0] == 0xef && data[1] == 0xbb && data[2] == 0xbf { + data = data[3:] + } + if strings.HasSuffix(filename, "MEMCPY.json") { + return // FIXME not a valid JSON https://github.com/neo-project/neo-vm/issues/322 + } + ut := new(vmUT) - require.NoError(t, json.Unmarshal(data, ut)) + require.NoErrorf(t, json.Unmarshal(data, ut), "file: %s", filename) + return t.Run(ut.Category+":"+ut.Name, func(t *testing.T) { + isRot := strings.HasSuffix(filename, "ROT.json") for i := range ut.Tests { test := ut.Tests[i] + if isRot && test.Name == "Without push" { + return // FIXME #927 single ROT is interpreted as PUSH1 + } t.Run(ut.Tests[i].Name, func(t *testing.T) { prog := []byte(test.Script) vm := load(prog) From 820b050b181998b1406c294cf1ffedbaea3c33b6 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 21 May 2020 08:41:48 +0300 Subject: [PATCH 10/20] vm/tests: make test interop push non-nil InteropItem --- pkg/vm/json_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/vm/json_test.go b/pkg/vm/json_test.go index 2bcfc72ef..aed485a9b 100644 --- a/pkg/vm/json_test.go +++ b/pkg/vm/json_test.go @@ -112,7 +112,7 @@ func TestUT(t *testing.T) { func getTestingInterop(id uint32) *InteropFuncPrice { if id == binary.LittleEndian.Uint32([]byte{0x77, 0x77, 0x77, 0x77}) { return &InteropFuncPrice{InteropFunc(func(v *VM) error { - v.estack.Push(&Element{value: (*InteropItem)(nil)}) + v.estack.PushVal(&InteropItem{new(int)}) return nil }), 0} } From f4fa712440f29da065cf3518c6cbdff6e6b7ffaf Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 20 May 2020 16:31:10 +0300 Subject: [PATCH 11/20] vm: make PUSH0 emit Integer --- pkg/compiler/binary_expr_test.go | 10 +++++----- pkg/compiler/for_test.go | 2 +- pkg/compiler/function_call_test.go | 2 +- pkg/compiler/if_test.go | 6 +++--- pkg/compiler/import_test.go | 2 +- pkg/compiler/struct_test.go | 2 +- pkg/vm/vm.go | 7 ++----- 7 files changed, 14 insertions(+), 17 deletions(-) diff --git a/pkg/compiler/binary_expr_test.go b/pkg/compiler/binary_expr_test.go index d3f5c0ab9..bf3f94c62 100644 --- a/pkg/compiler/binary_expr_test.go +++ b/pkg/compiler/binary_expr_test.go @@ -26,7 +26,7 @@ var binaryExprTestCases = []testCase{ return x } `, - []byte{}, + big.NewInt(0), }, { "simple div", @@ -97,7 +97,7 @@ var binaryExprTestCases = []testCase{ return 0 } `, - []byte{}, + big.NewInt(0), }, { "compare equal strings with eql", @@ -139,7 +139,7 @@ var binaryExprTestCases = []testCase{ return 0 } `, - []byte{}, + big.NewInt(0), }, { "compare equal ints with eql", @@ -167,7 +167,7 @@ var binaryExprTestCases = []testCase{ return 0 } `, - []byte{}, + big.NewInt(0), }, { "compare not equal ints with eql", @@ -181,7 +181,7 @@ var binaryExprTestCases = []testCase{ return 0 } `, - []byte{}, + big.NewInt(0), }, { "compare not equal ints with neq", diff --git a/pkg/compiler/for_test.go b/pkg/compiler/for_test.go index 71160a41f..f3ea262f3 100644 --- a/pkg/compiler/for_test.go +++ b/pkg/compiler/for_test.go @@ -276,7 +276,7 @@ func TestIfUnaryInvert(t *testing.T) { return 0 } ` - eval(t, src, []byte{}) + eval(t, src, big.NewInt(0)) } func TestAppendByte(t *testing.T) { diff --git a/pkg/compiler/function_call_test.go b/pkg/compiler/function_call_test.go index 8851078f0..c9d9ca04c 100644 --- a/pkg/compiler/function_call_test.go +++ b/pkg/compiler/function_call_test.go @@ -39,7 +39,7 @@ func TestNotAssignedFunctionCall(t *testing.T) { // disable stack checks because it is hard right now // to distinguish between simple function call traversal // and the same traversal inside an assignment. - evalWithoutStackChecks(t, src, []byte{}) + evalWithoutStackChecks(t, src, big.NewInt(0)) } func TestMultipleFunctionCalls(t *testing.T) { diff --git a/pkg/compiler/if_test.go b/pkg/compiler/if_test.go index 91731460d..23077ecf9 100644 --- a/pkg/compiler/if_test.go +++ b/pkg/compiler/if_test.go @@ -30,7 +30,7 @@ func TestGT(t *testing.T) { return 0 } ` - eval(t, src, []byte{}) + eval(t, src, big.NewInt(0)) } func TestGTE(t *testing.T) { @@ -44,7 +44,7 @@ func TestGTE(t *testing.T) { return 0 } ` - eval(t, src, []byte{}) + eval(t, src, big.NewInt(0)) } func TestLAND(t *testing.T) { @@ -89,5 +89,5 @@ func TestNestedIF(t *testing.T) { return 0 } ` - eval(t, src, []byte{}) + eval(t, src, big.NewInt(0)) } diff --git a/pkg/compiler/import_test.go b/pkg/compiler/import_test.go index 7c635be3e..def752427 100644 --- a/pkg/compiler/import_test.go +++ b/pkg/compiler/import_test.go @@ -32,7 +32,7 @@ func TestImportStruct(t *testing.T) { return b.Y } ` - eval(t, src, []byte{}) + eval(t, src, big.NewInt(0)) } func TestMultipleDirFileImport(t *testing.T) { diff --git a/pkg/compiler/struct_test.go b/pkg/compiler/struct_test.go index 6646f877d..208f6a11b 100644 --- a/pkg/compiler/struct_test.go +++ b/pkg/compiler/struct_test.go @@ -255,7 +255,7 @@ var structTestCases = []testCase{ return t.y } `, - []byte{}, + big.NewInt(0), }, { "test return struct from func", diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 3a084d55a..b860cec42 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -518,17 +518,14 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro } switch op { - case opcode.PUSHM1, opcode.PUSH1, opcode.PUSH2, opcode.PUSH3, + case opcode.PUSHM1, opcode.PUSH0, opcode.PUSH1, opcode.PUSH2, opcode.PUSH3, opcode.PUSH4, opcode.PUSH5, opcode.PUSH6, opcode.PUSH7, opcode.PUSH8, opcode.PUSH9, opcode.PUSH10, opcode.PUSH11, opcode.PUSH12, opcode.PUSH13, opcode.PUSH14, opcode.PUSH15, opcode.PUSH16: - val := int(op) - int(opcode.PUSH1) + 1 + val := int(op) - int(opcode.PUSH0) v.estack.PushVal(val) - case opcode.PUSH0: - v.estack.PushVal([]byte{}) - case opcode.PUSHDATA1, opcode.PUSHDATA2, opcode.PUSHDATA4: v.estack.PushVal(parameter) From 781def735d1199b24b99f98e4a896121f646c4b3 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 20 May 2020 16:37:00 +0300 Subject: [PATCH 12/20] vm: use Null item as a default value for Array/Struct elements --- pkg/vm/vm.go | 4 ++-- pkg/vm/vm_test.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index b860cec42..f05a8259a 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -993,7 +993,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro if n > MaxArraySize { panic("too long array") } - typ := BooleanT + typ := AnyT if op == opcode.NEWARRAYT { typ = StackItemType(parameter[0]) } @@ -1018,7 +1018,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro if n > MaxArraySize { panic("too long struct") } - items := makeArrayOfType(int(n), BooleanT) + items := makeArrayOfType(int(n), AnyT) v.estack.PushVal(&StructItem{items}) } diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index da504ae45..b00069f14 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -1188,7 +1188,7 @@ func TestNEWARRAYArray(t *testing.T) { prog := makeProgram(opcode.NEWARRAY) t.Run("ByteArray", getTestFuncForVM(prog, NewArrayItem([]StackItem{}), []byte{})) t.Run("BadSize", getTestFuncForVM(prog, nil, MaxArraySize+1)) - t.Run("Integer", getTestFuncForVM(prog, []StackItem{NewBoolItem(false)}, 1)) + t.Run("Integer", getTestFuncForVM(prog, []StackItem{NullItem{}}, 1)) arr := []StackItem{makeStackItem(42)} t.Run("Array", getTestFuncForVM(prog, arr, arr)) @@ -1206,7 +1206,7 @@ func testNEWARRAYIssue437(t *testing.T, i1, i2 opcode.Opcode, appended bool) { vm := load(prog) vm.Run() - arr := makeArrayOfType(4, BooleanT) + arr := makeArrayOfType(4, AnyT) arr[2] = makeStackItem(3) arr[3] = makeStackItem(4) if appended { @@ -1247,7 +1247,7 @@ func TestNEWSTRUCT(t *testing.T) { prog := makeProgram(opcode.NEWSTRUCT) t.Run("ByteArray", getTestFuncForVM(prog, NewStructItem([]StackItem{}), []byte{})) t.Run("BadSize", getTestFuncForVM(prog, nil, MaxArraySize+1)) - t.Run("Integer", getTestFuncForVM(prog, NewStructItem([]StackItem{NewBoolItem(false)}), 1)) + t.Run("Integer", getTestFuncForVM(prog, NewStructItem([]StackItem{NullItem{}}), 1)) arr := []StackItem{makeStackItem(42)} t.Run("Array", getTestFuncForVM(prog, NewStructItem(arr), NewArrayItem(arr))) From 1a0290edc650e01b4001f7b01f261ca75c514288 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 20 May 2020 17:28:23 +0300 Subject: [PATCH 13/20] vm: copy slice when converting between Array<->Struct Related #437. Becase in NEO3 NEWSTRUCT/NEWARRAY accept only an integer change tests too. --- pkg/vm/stack_item.go | 8 ++++++-- pkg/vm/vm_test.go | 43 ++++++++++++++++++------------------------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/pkg/vm/stack_item.go b/pkg/vm/stack_item.go index 462984c3d..b2a857fa5 100644 --- a/pkg/vm/stack_item.go +++ b/pkg/vm/stack_item.go @@ -225,7 +225,9 @@ func (i *StructItem) Convert(typ StackItemType) (StackItem, error) { case StructT: return i, nil case ArrayT: - return NewArrayItem(i.value), nil + arr := make([]StackItem, len(i.value)) + copy(arr, i.value) + return NewArrayItem(arr), nil case BooleanT: return NewBoolItem(i.Bool()), nil default: @@ -641,7 +643,9 @@ func (i *ArrayItem) Convert(typ StackItemType) (StackItem, error) { case ArrayT: return i, nil case StructT: - return NewStructItem(i.value), nil + arr := make([]StackItem, len(i.value)) + copy(arr, i.value) + return NewStructItem(arr), nil case BooleanT: return NewBoolItem(i.Bool()), nil default: diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index b00069f14..3166e4c11 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -1195,16 +1195,14 @@ func TestNEWARRAYArray(t *testing.T) { t.Run("Struct", getTestFuncForVM(prog, arr, NewStructItem(arr))) } -func testNEWARRAYIssue437(t *testing.T, i1, i2 opcode.Opcode, appended bool) { +func testNEWARRAYIssue437(t *testing.T, i1 opcode.Opcode, t2 StackItemType, appended bool) { prog := makeProgram( opcode.PUSH2, i1, opcode.DUP, opcode.PUSH3, opcode.APPEND, opcode.INITSSLOT, 1, - opcode.STSFLD0, opcode.LDSFLD0, i2, + opcode.STSFLD0, opcode.LDSFLD0, opcode.CONVERT, opcode.Opcode(t2), opcode.DUP, opcode.PUSH4, opcode.APPEND, opcode.LDSFLD0, opcode.PUSH5, opcode.APPEND) - vm := load(prog) - vm.Run() arr := makeArrayOfType(4, AnyT) arr[2] = makeStackItem(3) @@ -1213,20 +1211,18 @@ func testNEWARRAYIssue437(t *testing.T, i1, i2 opcode.Opcode, appended bool) { arr = append(arr, makeStackItem(5)) } - assert.Equal(t, false, vm.HasFailed()) - assert.Equal(t, 1, vm.estack.Len()) - if i2 == opcode.NEWARRAY { - assert.Equal(t, &ArrayItem{arr}, vm.estack.Pop().value) + if t2 == ArrayT { + runWithArgs(t, prog, &ArrayItem{arr}) } else { - assert.Equal(t, &StructItem{arr}, vm.estack.Pop().value) + runWithArgs(t, prog, &StructItem{arr}) } } func TestNEWARRAYIssue437(t *testing.T) { - t.Run("Array+Array", func(t *testing.T) { testNEWARRAYIssue437(t, opcode.NEWARRAY, opcode.NEWARRAY, true) }) - t.Run("Struct+Struct", func(t *testing.T) { testNEWARRAYIssue437(t, opcode.NEWSTRUCT, opcode.NEWSTRUCT, true) }) - t.Run("Array+Struct", func(t *testing.T) { testNEWARRAYIssue437(t, opcode.NEWARRAY, opcode.NEWSTRUCT, false) }) - t.Run("Struct+Array", func(t *testing.T) { testNEWARRAYIssue437(t, opcode.NEWSTRUCT, opcode.NEWARRAY, false) }) + t.Run("Array+Array", func(t *testing.T) { testNEWARRAYIssue437(t, opcode.NEWARRAY, ArrayT, true) }) + t.Run("Struct+Struct", func(t *testing.T) { testNEWARRAYIssue437(t, opcode.NEWSTRUCT, StructT, true) }) + t.Run("Array+Struct", func(t *testing.T) { testNEWARRAYIssue437(t, opcode.NEWARRAY, StructT, false) }) + t.Run("Struct+Array", func(t *testing.T) { testNEWARRAYIssue437(t, opcode.NEWSTRUCT, ArrayT, false) }) } func TestNEWARRAYT(t *testing.T) { @@ -1903,14 +1899,12 @@ func TestREVERSEITEMS(t *testing.T) { t.Run("Buffer", getTestFuncForVM(prog, NewBufferItem([]byte{3, 2, 1}), NewBufferItem([]byte{1, 2, 3}))) } -func testREVERSEITEMSIssue437(t *testing.T, i1, i2 opcode.Opcode, reversed bool) { +func testREVERSEITEMSIssue437(t *testing.T, i1 opcode.Opcode, t2 StackItemType, reversed bool) { prog := makeProgram( opcode.PUSH0, i1, opcode.DUP, opcode.PUSH1, opcode.APPEND, opcode.DUP, opcode.PUSH2, opcode.APPEND, - opcode.DUP, i2, opcode.REVERSEITEMS) - vm := load(prog) - vm.Run() + opcode.DUP, opcode.CONVERT, opcode.Opcode(t2), opcode.REVERSEITEMS) arr := make([]StackItem, 2) if reversed { @@ -1920,20 +1914,19 @@ func testREVERSEITEMSIssue437(t *testing.T, i1, i2 opcode.Opcode, reversed bool) arr[0] = makeStackItem(1) arr[1] = makeStackItem(2) } - assert.Equal(t, false, vm.HasFailed()) - assert.Equal(t, 1, vm.estack.Len()) + if i1 == opcode.NEWARRAY { - assert.Equal(t, &ArrayItem{arr}, vm.estack.Pop().value) + runWithArgs(t, prog, &ArrayItem{arr}) } else { - assert.Equal(t, &StructItem{arr}, vm.estack.Pop().value) + runWithArgs(t, prog, &StructItem{arr}) } } func TestREVERSEITEMSIssue437(t *testing.T) { - t.Run("Array+Array", func(t *testing.T) { testREVERSEITEMSIssue437(t, opcode.NEWARRAY, opcode.NEWARRAY, true) }) - t.Run("Struct+Struct", func(t *testing.T) { testREVERSEITEMSIssue437(t, opcode.NEWSTRUCT, opcode.NEWSTRUCT, true) }) - t.Run("Array+Struct", func(t *testing.T) { testREVERSEITEMSIssue437(t, opcode.NEWARRAY, opcode.NEWSTRUCT, false) }) - t.Run("Struct+Array", func(t *testing.T) { testREVERSEITEMSIssue437(t, opcode.NEWSTRUCT, opcode.NEWARRAY, false) }) + t.Run("Array+Array", func(t *testing.T) { testREVERSEITEMSIssue437(t, opcode.NEWARRAY, ArrayT, true) }) + t.Run("Struct+Struct", func(t *testing.T) { testREVERSEITEMSIssue437(t, opcode.NEWSTRUCT, StructT, true) }) + t.Run("Array+Struct", func(t *testing.T) { testREVERSEITEMSIssue437(t, opcode.NEWARRAY, StructT, false) }) + t.Run("Struct+Array", func(t *testing.T) { testREVERSEITEMSIssue437(t, opcode.NEWSTRUCT, ArrayT, false) }) } func TestREVERSEITEMSGoodOneElem(t *testing.T) { From 063c29636b53d9b980588819678f442b9303422a Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 20 May 2020 17:09:57 +0300 Subject: [PATCH 14/20] vm: fail if NEWSTRUCT/NEWARRAY argument is not an integer NEO3 VM does not support creating new Array from a Struct. For this purpose CONVERT opcode is used. --- pkg/vm/vm.go | 46 ++++++++++++++-------------------------------- pkg/vm/vm_test.go | 37 ++++++++++++++----------------------- 2 files changed, 28 insertions(+), 55 deletions(-) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index f05a8259a..ad4c4ebec 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -981,46 +981,28 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro case opcode.NEWARRAY, opcode.NEWARRAYT: item := v.estack.Pop() - switch t := item.value.(type) { - case *StructItem: - arr := make([]StackItem, len(t.value)) - copy(arr, t.value) - v.estack.PushVal(&ArrayItem{arr}) - case *ArrayItem: - v.estack.PushVal(t) - default: - n := item.BigInt().Int64() - if n > MaxArraySize { - panic("too long array") - } - typ := AnyT - if op == opcode.NEWARRAYT { - typ = StackItemType(parameter[0]) - } - items := makeArrayOfType(int(n), typ) - v.estack.PushVal(&ArrayItem{items}) + n := item.BigInt().Int64() + if n > MaxArraySize { + panic("too long array") } + typ := AnyT + if op == opcode.NEWARRAYT { + typ = StackItemType(parameter[0]) + } + items := makeArrayOfType(int(n), typ) + v.estack.PushVal(&ArrayItem{items}) case opcode.NEWSTRUCT0: v.estack.PushVal(&StructItem{[]StackItem{}}) case opcode.NEWSTRUCT: item := v.estack.Pop() - switch t := item.value.(type) { - case *ArrayItem: - arr := make([]StackItem, len(t.value)) - copy(arr, t.value) - v.estack.PushVal(&StructItem{arr}) - case *StructItem: - v.estack.PushVal(t) - default: - n := item.BigInt().Int64() - if n > MaxArraySize { - panic("too long struct") - } - items := makeArrayOfType(int(n), AnyT) - v.estack.PushVal(&StructItem{items}) + n := item.BigInt().Int64() + if n > MaxArraySize { + panic("too long struct") } + items := makeArrayOfType(int(n), AnyT) + v.estack.PushVal(&StructItem{items}) case opcode.APPEND: itemElem := v.estack.Pop() diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 3166e4c11..dcb949e54 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -372,7 +372,7 @@ func appendBigStruct(size uint16) []opcode.Opcode { return append(prog, opcode.INITSSLOT, 1, opcode.PUSHINT16, opcode.Opcode(size), opcode.Opcode(size>>8), // LE - opcode.PACK, opcode.NEWSTRUCT, + opcode.PACK, opcode.CONVERT, opcode.Opcode(StructT), opcode.STSFLD0, opcode.LDSFLD0, opcode.DUP, opcode.PUSH0, opcode.NEWARRAY, @@ -401,20 +401,19 @@ func TestStackLimit(t *testing.T) { {opcode.NEWARRAY, 3}, // array + 2 items {opcode.STSFLD0, 3}, {opcode.LDSFLD0, 4}, - {opcode.NEWSTRUCT, 6}, // all items are copied - {opcode.NEWMAP, 7}, - {opcode.DUP, 8}, - {opcode.PUSH2, 9}, - {opcode.LDSFLD0, 10}, - {opcode.SETITEM, 8}, // -3 items and 1 new element in map - {opcode.DUP, 9}, - {opcode.PUSH2, 10}, - {opcode.LDSFLD0, 11}, - {opcode.SETITEM, 8}, // -3 items and no new elements in map - {opcode.DUP, 9}, - {opcode.PUSH2, 10}, - {opcode.REMOVE, 7}, // as we have right after NEWMAP - {opcode.DROP, 6}, // DROP map with no elements + {opcode.NEWMAP, 5}, + {opcode.DUP, 6}, + {opcode.PUSH2, 7}, + {opcode.LDSFLD0, 8}, + {opcode.SETITEM, 6}, // -3 items and 1 new element in map + {opcode.DUP, 7}, + {opcode.PUSH2, 8}, + {opcode.LDSFLD0, 9}, + {opcode.SETITEM, 6}, // -3 items and no new elements in map + {opcode.DUP, 7}, + {opcode.PUSH2, 8}, + {opcode.REMOVE, 5}, // as we have right after NEWMAP + {opcode.DROP, 4}, // DROP map with no elements } prog := make([]opcode.Opcode, len(expected)+2) @@ -1189,10 +1188,6 @@ func TestNEWARRAYArray(t *testing.T) { t.Run("ByteArray", getTestFuncForVM(prog, NewArrayItem([]StackItem{}), []byte{})) t.Run("BadSize", getTestFuncForVM(prog, nil, MaxArraySize+1)) t.Run("Integer", getTestFuncForVM(prog, []StackItem{NullItem{}}, 1)) - - arr := []StackItem{makeStackItem(42)} - t.Run("Array", getTestFuncForVM(prog, arr, arr)) - t.Run("Struct", getTestFuncForVM(prog, arr, NewStructItem(arr))) } func testNEWARRAYIssue437(t *testing.T, i1 opcode.Opcode, t2 StackItemType, appended bool) { @@ -1244,10 +1239,6 @@ func TestNEWSTRUCT(t *testing.T) { t.Run("ByteArray", getTestFuncForVM(prog, NewStructItem([]StackItem{}), []byte{})) t.Run("BadSize", getTestFuncForVM(prog, nil, MaxArraySize+1)) t.Run("Integer", getTestFuncForVM(prog, NewStructItem([]StackItem{NullItem{}}), 1)) - - arr := []StackItem{makeStackItem(42)} - t.Run("Array", getTestFuncForVM(prog, NewStructItem(arr), NewArrayItem(arr))) - t.Run("Struct", getTestFuncForVM(prog, NewStructItem(arr), NewStructItem(arr))) } func TestAPPEND(t *testing.T) { From 86c4f1abc5b595f61ba1db36ec27c5661604eafc Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 21 May 2020 09:26:31 +0300 Subject: [PATCH 15/20] vm: do not use State as a mask In NEO2 state could be used as a mask, but now it contains only a single value. --- pkg/vm/vm.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index ad4c4ebec..ba14f3700 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -338,7 +338,7 @@ func (v *VM) Run() error { // check for breakpoint before executing the next instruction ctx := v.Context() if ctx != nil && ctx.atBreakPoint() { - v.state |= breakState + v.state = breakState } switch { case v.state.HasFlag(faultState): @@ -376,7 +376,7 @@ func (v *VM) StepInto() error { ctx := v.Context() if ctx == nil { - v.state |= haltState + v.state = haltState } if v.HasStopped() { From 16bf72f9cc027b86192fd2a50a001630313f8056 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 21 May 2020 09:28:41 +0300 Subject: [PATCH 16/20] vm/tests: do not load empty script In case we load a zero-length script, VM should end in HALT state. https://github.com/neo-project/neo-vm/blob/master/tests/neo-vm.Tests/Tests/Others/OtherCases.json#L22 --- pkg/vm/vm_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index dcb949e54..cb14a0fa7 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -2455,7 +2455,9 @@ func makeProgram(opcodes ...opcode.Opcode) []byte { func load(prog []byte) *VM { vm := New() - vm.LoadScript(prog) + if len(prog) != 0 { + vm.LoadScript(prog) + } return vm } From 22791272bf08ca36cd2efadbf27feb8222749818 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 21 May 2020 09:38:12 +0300 Subject: [PATCH 17/20] vm/tests: compare static slot contents in JSON tests Make use of "staticFields" test field to compare vm static slots. Also remove altStack because it is doesn't exist. --- pkg/vm/json_test.go | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/pkg/vm/json_test.go b/pkg/vm/json_test.go index aed485a9b..4069025cf 100644 --- a/pkg/vm/json_test.go +++ b/pkg/vm/json_test.go @@ -39,8 +39,8 @@ type ( vmUTExecutionContextState struct { Instruction string `json:"nextInstruction"` InstructionPointer int `json:"instructionPointer"` - AStack []vmUTStackItem `json:"altStack"` EStack []vmUTStackItem `json:"evaluationStack"` + StaticFields []vmUTStackItem `json:"staticFields"` } vmUTExecutionEngineState struct { @@ -166,7 +166,7 @@ func testFile(t *testing.T, filename string) { require.Equal(t, op, opcode.Opcode(ctx.prog[ctx.nextip])) } compareStacks(t, s.EStack, vm.estack) - compareStacks(t, s.AStack, vm.astack) + compareSlots(t, s.StaticFields, vm.static) } } @@ -207,20 +207,32 @@ func compareItems(t *testing.T, a, b StackItem) { } func compareStacks(t *testing.T, expected []vmUTStackItem, actual *Stack) { + compareItemArrays(t, expected, actual.Len(), func(i int) StackItem { return actual.Peek(i).Item() }) +} + +func compareSlots(t *testing.T, expected []vmUTStackItem, actual *Slot) { + if actual == nil && len(expected) == 0 { + return + } + require.NotNil(t, actual) + compareItemArrays(t, expected, actual.Size(), actual.Get) +} + +func compareItemArrays(t *testing.T, expected []vmUTStackItem, n int, getItem func(i int) StackItem) { if expected == nil { return } - require.Equal(t, len(expected), actual.Len()) + require.Equal(t, len(expected), n) for i, item := range expected { - e := actual.Peek(i) - require.NotNil(t, e) + it := getItem(i) + require.NotNil(t, it) if item.Type == typeInterop { - require.IsType(t, (*InteropItem)(nil), e.value) + require.IsType(t, (*InteropItem)(nil), it) continue } - compareItems(t, item.toStackItem(), e.value) + compareItems(t, item.toStackItem(), it) } } From 9dca2288baaf403b6d813aff49936f63079ae3e0 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 21 May 2020 09:43:35 +0300 Subject: [PATCH 18/20] vm: make LEFT fail if count is too big --- pkg/vm/vm.go | 2 +- pkg/vm/vm_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index ba14f3700..1b3d93004 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -694,7 +694,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro } s := v.estack.Pop().Bytes() if t := len(s); l > t { - l = t + panic("size is too big") } v.estack.PushVal(NewBufferItem(s[:l])) diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index cb14a0fa7..d7a1666ef 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -1814,7 +1814,7 @@ func TestLEFT(t *testing.T) { t.Run("NoString", getTestFuncForVM(prog, nil, 2)) t.Run("NegativeLen", getTestFuncForVM(prog, nil, "abcdef", -1)) t.Run("Good", getTestFuncForVM(prog, NewBufferItem([]byte("ab")), "abcdef", 2)) - t.Run("GoodBigLen", getTestFuncForVM(prog, NewBufferItem([]byte("abcdef")), "abcdef", 8)) + t.Run("BadBigLen", getTestFuncForVM(prog, nil, "abcdef", 8)) } func TestRIGHT(t *testing.T) { From 673e6c84d1d50ac3923a80110513d828a60af170 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 21 May 2020 09:52:24 +0300 Subject: [PATCH 19/20] vm/tests: preserve order when decoding Map item. When using Go's `map` for decoding, order can change from time to time, while we may want to check it too. Use custom decoder for saving items in order. --- pkg/vm/json_test.go | 45 +++++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/pkg/vm/json_test.go b/pkg/vm/json_test.go index 4069025cf..229cfdd92 100644 --- a/pkg/vm/json_test.go +++ b/pkg/vm/json_test.go @@ -250,16 +250,7 @@ func (v *vmUTStackItem) toStackItem() StackItem { case typeString: panic("not implemented") case typeMap: - items := v.Value.(map[string]vmUTStackItem) - result := NewMapItem() - for k, v := range items { - item := jsonStringToInteger(k) - if item == nil { - panic(fmt.Sprintf("can't unmarshal StackItem %s", k)) - } - result.Add(item, v.toStackItem()) - } - return result + return v.Value.(*MapItem) case typeInterop: panic("not implemented") case typeByteString: @@ -426,11 +417,37 @@ func (v *vmUTStackItem) UnmarshalJSON(data []byte) error { case typeInterop, typeNull: v.Value = nil case typeMap: - var m map[string]vmUTStackItem - if err := json.Unmarshal(si.Value, &m); err != nil { - return err + // we want to have the same order as in test file, so a custom decoder is used + d := json.NewDecoder(bytes.NewReader(si.Value)) + if tok, err := d.Token(); err != nil || tok != json.Delim('{') { + return fmt.Errorf("invalid map start") } - v.Value = m + + result := NewMapItem() + for { + tok, err := d.Token() + if err != nil { + return err + } else if tok == json.Delim('}') { + break + } + key, ok := tok.(string) + if !ok { + return fmt.Errorf("string expected in map key") + } + + var it vmUTStackItem + if err := d.Decode(&it); err != nil { + return fmt.Errorf("can't decode map value: %v", err) + } + + item := jsonStringToInteger(key) + if item == nil { + return fmt.Errorf("can't unmarshal StackItem %s", key) + } + result.Add(item, it.toStackItem()) + } + v.Value = result case typeString: panic("not implemented") default: From d26758dd316ea1769d86be57098872912246535a Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 21 May 2020 11:27:53 +0300 Subject: [PATCH 20/20] vm/tests: restore NEO3 JSON tests The only skipped RN are ROT.json (see #927) and MEMCPY.json (not a valid JSON). --- pkg/vm/json_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/vm/json_test.go b/pkg/vm/json_test.go index 229cfdd92..4c35e5641 100644 --- a/pkg/vm/json_test.go +++ b/pkg/vm/json_test.go @@ -133,7 +133,6 @@ func testFile(t *testing.T, filename string) { ut := new(vmUT) require.NoErrorf(t, json.Unmarshal(data, ut), "file: %s", filename) - return t.Run(ut.Category+":"+ut.Name, func(t *testing.T) { isRot := strings.HasSuffix(filename, "ROT.json")