vm: add tests to CALL* instructions

closes #452
This commit is contained in:
Anna Shaleva 2020-04-07 15:15:02 +03:00
parent 495c1b0565
commit 9f0bc2aaf5

View file

@ -3331,6 +3331,155 @@ 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++ {