vm: remove stack isolation opcodes

They are not present in NEO3 and will be creating additional difficulties
during future flow-control opcodes implementation.
This commit is contained in:
Evgenii Stratonikov 2020-04-23 11:40:06 +03:00
parent ab9d334182
commit fba185cd99
5 changed files with 89 additions and 326 deletions

View file

@ -99,14 +99,12 @@ func (c *Context) Next() (opcode.Opcode, []byte, error) {
numtoread = int(n) numtoread = int(n)
c.nextip += 4 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 numtoread = 2
case opcode.CALLI, opcode.SYSCALL: case opcode.SYSCALL:
numtoread = 4 numtoread = 4
case opcode.APPCALL, opcode.TAILCALL: case opcode.APPCALL, opcode.TAILCALL:
numtoread = 20 numtoread = 20
case opcode.CALLE, opcode.CALLET:
numtoread = 22
default: default:
if instr <= opcode.PUSHINT256 { if instr <= opcode.PUSHINT256 {
numtoread = 1 << instr numtoread = 1 << instr

View file

@ -141,13 +141,6 @@ const (
KEYS Opcode = 0xCC KEYS Opcode = 0xCC
VALUES Opcode = 0xCD VALUES Opcode = 0xCD
// Stack isolation
CALLI Opcode = 0xE0
CALLE Opcode = 0xE1
CALLED Opcode = 0xE2
CALLET Opcode = 0xE3
CALLEDT Opcode = 0xE4
// Exceptions // Exceptions
THROW Opcode = 0xF0 THROW Opcode = 0xF0
THROWIFNOT Opcode = 0xF1 THROWIFNOT Opcode = 0xF1

View file

@ -38,6 +38,7 @@ func _() {
_ = x[PUSH14-30] _ = x[PUSH14-30]
_ = x[PUSH15-31] _ = x[PUSH15-31]
_ = x[PUSH16-32] _ = x[PUSH16-32]
_ = x[OLDPUSH1-81]
_ = x[NOP-97] _ = x[NOP-97]
_ = x[JMP-98] _ = x[JMP-98]
_ = x[JMPIF-99] _ = x[JMPIF-99]
@ -120,16 +121,11 @@ func _() {
_ = x[HASKEY-203] _ = x[HASKEY-203]
_ = x[KEYS-204] _ = x[KEYS-204]
_ = x[VALUES-205] _ = x[VALUES-205]
_ = x[CALLI-224]
_ = x[CALLE-225]
_ = x[CALLED-226]
_ = x[CALLET-227]
_ = x[CALLEDT-228]
_ = x[THROW-240] _ = x[THROW-240]
_ = x[THROWIFNOT-241] _ = x[THROWIFNOT-241]
} }
const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMPIFJMPIFNOTCALLRETAPPCALLSYSCALLTAILCALLDUPFROMALTSTACKTOALTSTACKFROMALTSTACKXDROPISNULLXSWAPXTUCKDEPTHDROPDUPNIPOVERPICKROLLROTSWAPTUCKCATSUBSTRLEFTRIGHTSIZEINVERTANDORXOREQUALINCDECSIGNNEGATEABSNOTNZADDSUBMULDIVMODSHLSHRBOOLANDBOOLORNUMEQUALNUMNOTEQUALLTGTLTEGTEMINMAXWITHINSHA1SHA256HASH160HASH256CHECKSIGVERIFYCHECKMULTISIGARRAYSIZEPACKUNPACKPICKITEMSETITEMNEWARRAYNEWSTRUCTNEWMAPAPPENDREVERSEREMOVEHASKEYKEYSVALUESCALLICALLECALLEDCALLETCALLEDTTHROWTHROWIFNOT" const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16OLDPUSH1NOPJMPJMPIFJMPIFNOTCALLRETAPPCALLSYSCALLTAILCALLDUPFROMALTSTACKTOALTSTACKFROMALTSTACKXDROPISNULLXSWAPXTUCKDEPTHDROPDUPNIPOVERPICKROLLROTSWAPTUCKCATSUBSTRLEFTRIGHTSIZEINVERTANDORXOREQUALINCDECSIGNNEGATEABSNOTNZADDSUBMULDIVMODSHLSHRBOOLANDBOOLORNUMEQUALNUMNOTEQUALLTGTLTEGTEMINMAXWITHINSHA1SHA256HASH160HASH256CHECKSIGVERIFYCHECKMULTISIGARRAYSIZEPACKUNPACKPICKITEMSETITEMNEWARRAYNEWSTRUCTNEWMAPAPPENDREVERSEREMOVEHASKEYKEYSVALUESTHROWTHROWIFNOT"
var _Opcode_map = map[Opcode]string{ var _Opcode_map = map[Opcode]string{
0: _Opcode_name[0:8], 0: _Opcode_name[0:8],
@ -160,95 +156,91 @@ var _Opcode_map = map[Opcode]string{
30: _Opcode_name[170:176], 30: _Opcode_name[170:176],
31: _Opcode_name[176:182], 31: _Opcode_name[176:182],
32: _Opcode_name[182:188], 32: _Opcode_name[182:188],
97: _Opcode_name[188:191], 81: _Opcode_name[188:196],
98: _Opcode_name[191:194], 97: _Opcode_name[196:199],
99: _Opcode_name[194:199], 98: _Opcode_name[199:202],
100: _Opcode_name[199:207], 99: _Opcode_name[202:207],
101: _Opcode_name[207:211], 100: _Opcode_name[207:215],
102: _Opcode_name[211:214], 101: _Opcode_name[215:219],
103: _Opcode_name[214:221], 102: _Opcode_name[219:222],
104: _Opcode_name[221:228], 103: _Opcode_name[222:229],
105: _Opcode_name[228:236], 104: _Opcode_name[229:236],
106: _Opcode_name[236:251], 105: _Opcode_name[236:244],
107: _Opcode_name[251:261], 106: _Opcode_name[244:259],
108: _Opcode_name[261:273], 107: _Opcode_name[259:269],
109: _Opcode_name[273:278], 108: _Opcode_name[269:281],
112: _Opcode_name[278:284], 109: _Opcode_name[281:286],
114: _Opcode_name[284:289], 112: _Opcode_name[286:292],
115: _Opcode_name[289:294], 114: _Opcode_name[292:297],
116: _Opcode_name[294:299], 115: _Opcode_name[297:302],
117: _Opcode_name[299:303], 116: _Opcode_name[302:307],
118: _Opcode_name[303:306], 117: _Opcode_name[307:311],
119: _Opcode_name[306:309], 118: _Opcode_name[311:314],
120: _Opcode_name[309:313], 119: _Opcode_name[314:317],
121: _Opcode_name[313:317], 120: _Opcode_name[317:321],
122: _Opcode_name[317:321], 121: _Opcode_name[321:325],
123: _Opcode_name[321:324], 122: _Opcode_name[325:329],
124: _Opcode_name[324:328], 123: _Opcode_name[329:332],
125: _Opcode_name[328:332], 124: _Opcode_name[332:336],
126: _Opcode_name[332:335], 125: _Opcode_name[336:340],
127: _Opcode_name[335:341], 126: _Opcode_name[340:343],
128: _Opcode_name[341:345], 127: _Opcode_name[343:349],
129: _Opcode_name[345:350], 128: _Opcode_name[349:353],
130: _Opcode_name[350:354], 129: _Opcode_name[353:358],
131: _Opcode_name[354:360], 130: _Opcode_name[358:362],
132: _Opcode_name[360:363], 131: _Opcode_name[362:368],
133: _Opcode_name[363:365], 132: _Opcode_name[368:371],
134: _Opcode_name[365:368], 133: _Opcode_name[371:373],
135: _Opcode_name[368:373], 134: _Opcode_name[373:376],
139: _Opcode_name[373:376], 135: _Opcode_name[376:381],
140: _Opcode_name[376:379], 139: _Opcode_name[381:384],
141: _Opcode_name[379:383], 140: _Opcode_name[384:387],
143: _Opcode_name[383:389], 141: _Opcode_name[387:391],
144: _Opcode_name[389:392], 143: _Opcode_name[391:397],
145: _Opcode_name[392:395], 144: _Opcode_name[397:400],
146: _Opcode_name[395:397], 145: _Opcode_name[400:403],
147: _Opcode_name[397:400], 146: _Opcode_name[403:405],
148: _Opcode_name[400:403], 147: _Opcode_name[405:408],
149: _Opcode_name[403:406], 148: _Opcode_name[408:411],
150: _Opcode_name[406:409], 149: _Opcode_name[411:414],
151: _Opcode_name[409:412], 150: _Opcode_name[414:417],
152: _Opcode_name[412:415], 151: _Opcode_name[417:420],
153: _Opcode_name[415:418], 152: _Opcode_name[420:423],
154: _Opcode_name[418:425], 153: _Opcode_name[423:426],
155: _Opcode_name[425:431], 154: _Opcode_name[426:433],
156: _Opcode_name[431:439], 155: _Opcode_name[433:439],
158: _Opcode_name[439:450], 156: _Opcode_name[439:447],
159: _Opcode_name[450:452], 158: _Opcode_name[447:458],
160: _Opcode_name[452:454], 159: _Opcode_name[458:460],
161: _Opcode_name[454:457], 160: _Opcode_name[460:462],
162: _Opcode_name[457:460], 161: _Opcode_name[462:465],
163: _Opcode_name[460:463], 162: _Opcode_name[465:468],
164: _Opcode_name[463:466], 163: _Opcode_name[468:471],
165: _Opcode_name[466:472], 164: _Opcode_name[471:474],
167: _Opcode_name[472:476], 165: _Opcode_name[474:480],
168: _Opcode_name[476:482], 167: _Opcode_name[480:484],
169: _Opcode_name[482:489], 168: _Opcode_name[484:490],
170: _Opcode_name[489:496], 169: _Opcode_name[490:497],
172: _Opcode_name[496:504], 170: _Opcode_name[497:504],
173: _Opcode_name[504:510], 172: _Opcode_name[504:512],
174: _Opcode_name[510:523], 173: _Opcode_name[512:518],
192: _Opcode_name[523:532], 174: _Opcode_name[518:531],
193: _Opcode_name[532:536], 192: _Opcode_name[531:540],
194: _Opcode_name[536:542], 193: _Opcode_name[540:544],
195: _Opcode_name[542:550], 194: _Opcode_name[544:550],
196: _Opcode_name[550:557], 195: _Opcode_name[550:558],
197: _Opcode_name[557:565], 196: _Opcode_name[558:565],
198: _Opcode_name[565:574], 197: _Opcode_name[565:573],
199: _Opcode_name[574:580], 198: _Opcode_name[573:582],
200: _Opcode_name[580:586], 199: _Opcode_name[582:588],
201: _Opcode_name[586:593], 200: _Opcode_name[588:594],
202: _Opcode_name[593:599], 201: _Opcode_name[594:601],
203: _Opcode_name[599:605], 202: _Opcode_name[601:607],
204: _Opcode_name[605:609], 203: _Opcode_name[607:613],
205: _Opcode_name[609:615], 204: _Opcode_name[613:617],
224: _Opcode_name[615:620], 205: _Opcode_name[617:623],
225: _Opcode_name[620:625], 240: _Opcode_name[623:628],
226: _Opcode_name[625:631], 241: _Opcode_name[628:638],
227: _Opcode_name[631:637],
228: _Opcode_name[637:644],
240: _Opcode_name[644:649],
241: _Opcode_name[649:659],
} }
func (i Opcode) String() string { func (i Opcode) String() string {

View file

@ -529,8 +529,6 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
} }
parameter = v.estack.Pop().Bytes() parameter = v.estack.Pop().Bytes()
fallthrough
case opcode.CALLED, opcode.CALLEDT:
if !ctx.hasDynamicInvoke { if !ctx.hasDynamicInvoke {
panic("contract is not allowed to make dynamic invocations") 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: case opcode.NOP:
// unlucky ^^ // 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: case opcode.THROW:
panic("THROW") panic("THROW")

View file

@ -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 { func makeProgram(opcodes ...opcode.Opcode) []byte {
prog := make([]byte, len(opcodes)+1) // RET prog := make([]byte, len(opcodes)+1) // RET
for i := 0; i < len(opcodes); i++ { for i := 0; i < len(opcodes); i++ {