From 9f0bc2aaf5961152598457927d22a86f75614226 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 7 Apr 2020 15:15:02 +0300 Subject: [PATCH] vm: add tests to CALL* instructions closes #452 --- pkg/vm/vm_test.go | 149 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 54946eca5..aab31a408 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -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 { prog := make([]byte, len(opcodes)+1) // RET for i := 0; i < len(opcodes); i++ {