From fba185cd99111c155d8a4726fbe69670b0bfef7f Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 23 Apr 2020 11:40:06 +0300 Subject: [PATCH] vm: remove stack isolation opcodes They are not present in NEO3 and will be creating additional difficulties during future flow-control opcodes implementation. --- pkg/vm/context.go | 6 +- pkg/vm/opcode/opcode.go | 7 -- pkg/vm/opcode/opcode_string.go | 182 ++++++++++++++++----------------- pkg/vm/vm.go | 71 ------------- pkg/vm/vm_test.go | 149 --------------------------- 5 files changed, 89 insertions(+), 326 deletions(-) diff --git a/pkg/vm/context.go b/pkg/vm/context.go index 44dfcd77a..01d4c039a 100644 --- a/pkg/vm/context.go +++ b/pkg/vm/context.go @@ -99,14 +99,12 @@ func (c *Context) Next() (opcode.Opcode, []byte, error) { numtoread = int(n) c.nextip += 4 } - case opcode.JMP, opcode.JMPIF, opcode.JMPIFNOT, opcode.CALL, opcode.CALLED, opcode.CALLEDT: + case opcode.JMP, opcode.JMPIF, opcode.JMPIFNOT, opcode.CALL: numtoread = 2 - case opcode.CALLI, opcode.SYSCALL: + case opcode.SYSCALL: numtoread = 4 case opcode.APPCALL, opcode.TAILCALL: numtoread = 20 - case opcode.CALLE, opcode.CALLET: - numtoread = 22 default: if instr <= opcode.PUSHINT256 { numtoread = 1 << instr diff --git a/pkg/vm/opcode/opcode.go b/pkg/vm/opcode/opcode.go index a1b364bf6..c4f5f50c8 100644 --- a/pkg/vm/opcode/opcode.go +++ b/pkg/vm/opcode/opcode.go @@ -141,13 +141,6 @@ const ( KEYS Opcode = 0xCC VALUES Opcode = 0xCD - // Stack isolation - CALLI Opcode = 0xE0 - CALLE Opcode = 0xE1 - CALLED Opcode = 0xE2 - CALLET Opcode = 0xE3 - CALLEDT Opcode = 0xE4 - // Exceptions THROW Opcode = 0xF0 THROWIFNOT Opcode = 0xF1 diff --git a/pkg/vm/opcode/opcode_string.go b/pkg/vm/opcode/opcode_string.go index 4a1fe00ee..72e791e08 100644 --- a/pkg/vm/opcode/opcode_string.go +++ b/pkg/vm/opcode/opcode_string.go @@ -38,6 +38,7 @@ func _() { _ = x[PUSH14-30] _ = x[PUSH15-31] _ = x[PUSH16-32] + _ = x[OLDPUSH1-81] _ = x[NOP-97] _ = x[JMP-98] _ = x[JMPIF-99] @@ -120,16 +121,11 @@ func _() { _ = x[HASKEY-203] _ = x[KEYS-204] _ = x[VALUES-205] - _ = x[CALLI-224] - _ = x[CALLE-225] - _ = x[CALLED-226] - _ = x[CALLET-227] - _ = x[CALLEDT-228] _ = x[THROW-240] _ = x[THROWIFNOT-241] } -const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMPIFJMPIFNOTCALLRETAPPCALLSYSCALLTAILCALLDUPFROMALTSTACKTOALTSTACKFROMALTSTACKXDROPISNULLXSWAPXTUCKDEPTHDROPDUPNIPOVERPICKROLLROTSWAPTUCKCATSUBSTRLEFTRIGHTSIZEINVERTANDORXOREQUALINCDECSIGNNEGATEABSNOTNZADDSUBMULDIVMODSHLSHRBOOLANDBOOLORNUMEQUALNUMNOTEQUALLTGTLTEGTEMINMAXWITHINSHA1SHA256HASH160HASH256CHECKSIGVERIFYCHECKMULTISIGARRAYSIZEPACKUNPACKPICKITEMSETITEMNEWARRAYNEWSTRUCTNEWMAPAPPENDREVERSEREMOVEHASKEYKEYSVALUESCALLICALLECALLEDCALLETCALLEDTTHROWTHROWIFNOT" +const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16OLDPUSH1NOPJMPJMPIFJMPIFNOTCALLRETAPPCALLSYSCALLTAILCALLDUPFROMALTSTACKTOALTSTACKFROMALTSTACKXDROPISNULLXSWAPXTUCKDEPTHDROPDUPNIPOVERPICKROLLROTSWAPTUCKCATSUBSTRLEFTRIGHTSIZEINVERTANDORXOREQUALINCDECSIGNNEGATEABSNOTNZADDSUBMULDIVMODSHLSHRBOOLANDBOOLORNUMEQUALNUMNOTEQUALLTGTLTEGTEMINMAXWITHINSHA1SHA256HASH160HASH256CHECKSIGVERIFYCHECKMULTISIGARRAYSIZEPACKUNPACKPICKITEMSETITEMNEWARRAYNEWSTRUCTNEWMAPAPPENDREVERSEREMOVEHASKEYKEYSVALUESTHROWTHROWIFNOT" var _Opcode_map = map[Opcode]string{ 0: _Opcode_name[0:8], @@ -160,95 +156,91 @@ var _Opcode_map = map[Opcode]string{ 30: _Opcode_name[170:176], 31: _Opcode_name[176:182], 32: _Opcode_name[182:188], - 97: _Opcode_name[188:191], - 98: _Opcode_name[191:194], - 99: _Opcode_name[194:199], - 100: _Opcode_name[199:207], - 101: _Opcode_name[207:211], - 102: _Opcode_name[211:214], - 103: _Opcode_name[214:221], - 104: _Opcode_name[221:228], - 105: _Opcode_name[228:236], - 106: _Opcode_name[236:251], - 107: _Opcode_name[251:261], - 108: _Opcode_name[261:273], - 109: _Opcode_name[273:278], - 112: _Opcode_name[278:284], - 114: _Opcode_name[284:289], - 115: _Opcode_name[289:294], - 116: _Opcode_name[294:299], - 117: _Opcode_name[299:303], - 118: _Opcode_name[303:306], - 119: _Opcode_name[306:309], - 120: _Opcode_name[309:313], - 121: _Opcode_name[313:317], - 122: _Opcode_name[317:321], - 123: _Opcode_name[321:324], - 124: _Opcode_name[324:328], - 125: _Opcode_name[328:332], - 126: _Opcode_name[332:335], - 127: _Opcode_name[335:341], - 128: _Opcode_name[341:345], - 129: _Opcode_name[345:350], - 130: _Opcode_name[350:354], - 131: _Opcode_name[354:360], - 132: _Opcode_name[360:363], - 133: _Opcode_name[363:365], - 134: _Opcode_name[365:368], - 135: _Opcode_name[368:373], - 139: _Opcode_name[373:376], - 140: _Opcode_name[376:379], - 141: _Opcode_name[379:383], - 143: _Opcode_name[383:389], - 144: _Opcode_name[389:392], - 145: _Opcode_name[392:395], - 146: _Opcode_name[395:397], - 147: _Opcode_name[397:400], - 148: _Opcode_name[400:403], - 149: _Opcode_name[403:406], - 150: _Opcode_name[406:409], - 151: _Opcode_name[409:412], - 152: _Opcode_name[412:415], - 153: _Opcode_name[415:418], - 154: _Opcode_name[418:425], - 155: _Opcode_name[425:431], - 156: _Opcode_name[431:439], - 158: _Opcode_name[439:450], - 159: _Opcode_name[450:452], - 160: _Opcode_name[452:454], - 161: _Opcode_name[454:457], - 162: _Opcode_name[457:460], - 163: _Opcode_name[460:463], - 164: _Opcode_name[463:466], - 165: _Opcode_name[466:472], - 167: _Opcode_name[472:476], - 168: _Opcode_name[476:482], - 169: _Opcode_name[482:489], - 170: _Opcode_name[489:496], - 172: _Opcode_name[496:504], - 173: _Opcode_name[504:510], - 174: _Opcode_name[510:523], - 192: _Opcode_name[523:532], - 193: _Opcode_name[532:536], - 194: _Opcode_name[536:542], - 195: _Opcode_name[542:550], - 196: _Opcode_name[550:557], - 197: _Opcode_name[557:565], - 198: _Opcode_name[565:574], - 199: _Opcode_name[574:580], - 200: _Opcode_name[580:586], - 201: _Opcode_name[586:593], - 202: _Opcode_name[593:599], - 203: _Opcode_name[599:605], - 204: _Opcode_name[605:609], - 205: _Opcode_name[609:615], - 224: _Opcode_name[615:620], - 225: _Opcode_name[620:625], - 226: _Opcode_name[625:631], - 227: _Opcode_name[631:637], - 228: _Opcode_name[637:644], - 240: _Opcode_name[644:649], - 241: _Opcode_name[649:659], + 81: _Opcode_name[188:196], + 97: _Opcode_name[196:199], + 98: _Opcode_name[199:202], + 99: _Opcode_name[202:207], + 100: _Opcode_name[207:215], + 101: _Opcode_name[215:219], + 102: _Opcode_name[219:222], + 103: _Opcode_name[222:229], + 104: _Opcode_name[229:236], + 105: _Opcode_name[236:244], + 106: _Opcode_name[244:259], + 107: _Opcode_name[259:269], + 108: _Opcode_name[269:281], + 109: _Opcode_name[281:286], + 112: _Opcode_name[286:292], + 114: _Opcode_name[292:297], + 115: _Opcode_name[297:302], + 116: _Opcode_name[302:307], + 117: _Opcode_name[307:311], + 118: _Opcode_name[311:314], + 119: _Opcode_name[314:317], + 120: _Opcode_name[317:321], + 121: _Opcode_name[321:325], + 122: _Opcode_name[325:329], + 123: _Opcode_name[329:332], + 124: _Opcode_name[332:336], + 125: _Opcode_name[336:340], + 126: _Opcode_name[340:343], + 127: _Opcode_name[343:349], + 128: _Opcode_name[349:353], + 129: _Opcode_name[353:358], + 130: _Opcode_name[358:362], + 131: _Opcode_name[362:368], + 132: _Opcode_name[368:371], + 133: _Opcode_name[371:373], + 134: _Opcode_name[373:376], + 135: _Opcode_name[376:381], + 139: _Opcode_name[381:384], + 140: _Opcode_name[384:387], + 141: _Opcode_name[387:391], + 143: _Opcode_name[391:397], + 144: _Opcode_name[397:400], + 145: _Opcode_name[400:403], + 146: _Opcode_name[403:405], + 147: _Opcode_name[405:408], + 148: _Opcode_name[408:411], + 149: _Opcode_name[411:414], + 150: _Opcode_name[414:417], + 151: _Opcode_name[417:420], + 152: _Opcode_name[420:423], + 153: _Opcode_name[423:426], + 154: _Opcode_name[426:433], + 155: _Opcode_name[433:439], + 156: _Opcode_name[439:447], + 158: _Opcode_name[447:458], + 159: _Opcode_name[458:460], + 160: _Opcode_name[460:462], + 161: _Opcode_name[462:465], + 162: _Opcode_name[465:468], + 163: _Opcode_name[468:471], + 164: _Opcode_name[471:474], + 165: _Opcode_name[474:480], + 167: _Opcode_name[480:484], + 168: _Opcode_name[484:490], + 169: _Opcode_name[490:497], + 170: _Opcode_name[497:504], + 172: _Opcode_name[504:512], + 173: _Opcode_name[512:518], + 174: _Opcode_name[518:531], + 192: _Opcode_name[531:540], + 193: _Opcode_name[540:544], + 194: _Opcode_name[544:550], + 195: _Opcode_name[550:558], + 196: _Opcode_name[558:565], + 197: _Opcode_name[565:573], + 198: _Opcode_name[573:582], + 199: _Opcode_name[582:588], + 200: _Opcode_name[588:594], + 201: _Opcode_name[594:601], + 202: _Opcode_name[601:607], + 203: _Opcode_name[607:613], + 204: _Opcode_name[613:617], + 205: _Opcode_name[617:623], + 240: _Opcode_name[623:628], + 241: _Opcode_name[628:638], } func (i Opcode) String() string { diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index de2b1e1bb..ece7ef0e4 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -529,8 +529,6 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro } parameter = v.estack.Pop().Bytes() - fallthrough - case opcode.CALLED, opcode.CALLEDT: if !ctx.hasDynamicInvoke { panic("contract is not allowed to make dynamic invocations") } @@ -1281,75 +1279,6 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro case opcode.NOP: // unlucky ^^ - case opcode.CALLI, opcode.CALLE, opcode.CALLED, opcode.CALLET, opcode.CALLEDT: - var ( - tailCall = (op == opcode.CALLET || op == opcode.CALLEDT) - hashOnStack = (op == opcode.CALLED || op == opcode.CALLEDT) - addElement int - newCtx *Context - ) - - if hashOnStack { - addElement = 1 - } - - rvcount := int(parameter[0]) - pcount := int(parameter[1]) - if v.estack.Len() < pcount+addElement { - panic("missing some parameters") - } - if tailCall { - if ctx.rvcount != rvcount { - panic("context and parameter rvcount mismatch") - } - } else { - v.checkInvocationStackSize() - } - - if op == opcode.CALLI { - newCtx = ctx.Copy() - } else { - var hashBytes []byte - - if hashOnStack { - hashBytes = v.estack.Pop().Bytes() - } else { - hashBytes = parameter[2:] - } - - hash, err := util.Uint160DecodeBytesBE(hashBytes) - if err != nil { - panic(err) - } - script, hasDynamicInvoke := v.getScript(hash) - if script == nil { - panic(fmt.Sprintf("could not find script %s", hash)) - } - newCtx = NewContext(script) - newCtx.scriptHash = hash - newCtx.hasDynamicInvoke = hasDynamicInvoke - } - newCtx.rvcount = rvcount - newCtx.estack = NewStack("evaluation") - newCtx.astack = NewStack("alt") - // Going backwards to naturally push things onto the new stack. - for i := pcount; i > 0; i-- { - elem := v.estack.RemoveAt(i - 1) - newCtx.estack.Push(elem) - } - if tailCall { - _ = v.istack.Pop() - } - v.istack.PushVal(newCtx) - v.estack = newCtx.estack - v.astack = newCtx.astack - if op == opcode.CALLI { - // CALLI is a bit different from other JMPs - // https://github.com/neo-project/neo-vm/blob/master-2.x/src/neo-vm/ExecutionEngine.cs#L1175 - offset := v.getJumpOffset(newCtx, parameter[2:], 2) - v.jumpIf(newCtx, offset, true) - } - case opcode.THROW: panic("THROW") diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 6d96879dc..6f2738d24 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -3026,155 +3026,6 @@ func TestBitAndNumericOpcodes(t *testing.T) { } } -var stackIsolationTestCases = map[opcode.Opcode][]struct { - name string - params []interface{} -}{ - opcode.CALLE: { - { - name: "no_params", - }, - { - name: "with_params", - params: []interface{}{big.NewInt(5), true, []byte{1, 2, 3}}, - }, - }, - opcode.CALLED: { - { - name: "no_paranms", - }, - { - name: "with_params", - params: []interface{}{big.NewInt(5), true, []byte{1, 2, 3}}, - }, - }, - opcode.CALLET: { - { - name: "no_params", - }, - { - name: "with_params", - params: []interface{}{big.NewInt(5), true, []byte{1, 2, 3}}, - }, - }, - opcode.CALLEDT: { - { - name: "no_params", - }, - { - name: "with_params", - params: []interface{}{big.NewInt(5), true, []byte{1, 2, 3}}, - }, - }, -} - -func TestStackIsolationOpcodes(t *testing.T) { - scriptHash := util.Uint160{1, 2, 3} - for code, codeTestCases := range stackIsolationTestCases { - t.Run(code.String(), func(t *testing.T) { - for _, testCase := range codeTestCases { - t.Run(testCase.name, func(t *testing.T) { - rvcount := len(testCase.params) + 1 // parameters + DEPTH - pcount := len(testCase.params) - prog := []byte{byte(code), byte(rvcount), byte(pcount)} - if code == opcode.CALLE || code == opcode.CALLET { - prog = append(prog, scriptHash.BytesBE()...) - } - prog = append(prog, byte(opcode.RET)) - - vm := load(prog) - vm.SetScriptGetter(func(in util.Uint160) ([]byte, bool) { - if in.Equals(scriptHash) { - return makeProgram(opcode.DEPTH), true - } - return nil, false - }) - if code == opcode.CALLED || code == opcode.CALLEDT { - vm.Context().hasDynamicInvoke = true - } - if code == opcode.CALLET || code == opcode.CALLEDT { - vm.Context().rvcount = rvcount - } - // add extra parameter just to check that it won't appear on new estack - vm.estack.PushVal(7) - for _, param := range testCase.params { - vm.estack.PushVal(param) - } - if code == opcode.CALLED || code == opcode.CALLEDT { - vm.estack.PushVal(scriptHash.BytesBE()) - } - - runVM(t, vm) - elem := vm.estack.Pop() // depth should be equal to params count, as it's a separate execution context - assert.Equal(t, int64(pcount), elem.BigInt().Int64()) - for i := pcount; i > 0; i-- { - p := vm.estack.Pop() - assert.Equal(t, testCase.params[i-1], p.value.Value()) - } - }) - } - }) - } -} - -func TestCALLIAltStack(t *testing.T) { - prog := []byte{ - byte(opcode.PUSH1), - byte(opcode.DUP), - byte(opcode.TOALTSTACK), - byte(opcode.JMP), 0x5, 0, // to CALLI (+5 including JMP parameters) - byte(opcode.FROMALTSTACK), // altstack is empty, so panic here - byte(opcode.RET), - byte(opcode.CALLI), byte(0), byte(0), 0xfc, 0xff, // to FROMALTSTACK (-4) with clean stacks - byte(opcode.RET), - } - - vm := load(prog) - checkVMFailed(t, vm) -} - -func TestCALLIDEPTH(t *testing.T) { - prog := []byte{ - byte(opcode.PUSH3), - byte(opcode.PUSH2), - byte(opcode.PUSH1), - byte(opcode.JMP), 0x5, 0, // to CALLI (+5 including JMP parameters) - byte(opcode.DEPTH), // depth is 2, put it on stack - byte(opcode.RET), - byte(opcode.CALLI), byte(3), byte(2), 0xfc, 0xff, // to DEPTH (-4) with [21 on stack - byte(opcode.RET), - } - - vm := load(prog) - - runVM(t, vm) - assert.Equal(t, 4, vm.estack.Len()) - assert.Equal(t, int64(2), vm.estack.Pop().BigInt().Int64()) // depth was 2 - for i := 1; i < 4; i++ { - assert.Equal(t, int64(i), vm.estack.Pop().BigInt().Int64()) - } -} - -func TestCALLI(t *testing.T) { - prog := []byte{ - byte(opcode.PUSH3), - byte(opcode.PUSH2), - byte(opcode.PUSH1), - byte(opcode.JMP), 0x5, 0, // to CALLI (+5 including JMP parameters) - byte(opcode.SUB), // 2 - 1 - byte(opcode.RET), - byte(opcode.CALLI), byte(1), byte(2), 0xfc, 0xff, // to SUB (-4) with [21 on stack - byte(opcode.MUL), // 3 * 1 - byte(opcode.RET), - } - - vm := load(prog) - - runVM(t, vm) - assert.Equal(t, 1, vm.estack.Len()) - assert.Equal(t, int64(3), vm.estack.Pop().BigInt().Int64()) -} - func makeProgram(opcodes ...opcode.Opcode) []byte { prog := make([]byte, len(opcodes)+1) // RET for i := 0; i < len(opcodes); i++ {