From d257044298f803d943bd0a4099b3c89db1e3a9ac Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 12 Sep 2019 10:39:54 +0300 Subject: [PATCH 01/14] vm: fail if XTUCK argument is 0 --- pkg/vm/vm.go | 2 +- pkg/vm/vm_test.go | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index c9bc97d2b..db03e6deb 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -375,7 +375,7 @@ func (v *VM) execute(ctx *Context, op Instruction) { case XTUCK: n := int(v.estack.Pop().BigInt().Int64()) - if n < 0 { + if n <= 0 { panic("XTUCK: invalid length") } a := v.estack.Dup(0) diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 5a7f5771a..a0682c1ab 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -658,6 +658,15 @@ func TestXTUCKbadNegative(t *testing.T) { assert.Equal(t, true, vm.state.HasFlag(faultState)) } +func TestXTUCKbadZero(t *testing.T) { + prog := makeProgram(XTUCK) + vm := load(prog) + vm.estack.PushVal(1) + vm.estack.PushVal(0) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + func TestXTUCKgood(t *testing.T) { prog := makeProgram(XTUCK) topelement := 5 From cf7fbb80ba59327a37a24ba78064aa343a508431 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 12 Sep 2019 10:50:43 +0300 Subject: [PATCH 02/14] vm: fail if EQUAL has not enough arguments --- pkg/vm/vm.go | 6 ++++++ pkg/vm/vm_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index db03e6deb..71793c0fc 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -445,7 +445,13 @@ func (v *VM) execute(ctx *Context, op Instruction) { case EQUAL: b := v.estack.Pop() + if b == nil { + panic("no top-level element found") + } a := v.estack.Pop() + if a == nil { + panic("no second-to-the-top element found") + } v.estack.PushVal(reflect.DeepEqual(a, b)) // Bit operations. diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index a0682c1ab..1628c95b7 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -335,6 +335,32 @@ func TestDepth(t *testing.T) { assert.Equal(t, int64(3), vm.estack.Pop().BigInt().Int64()) } +func TestEQUALNoArguments(t *testing.T) { + prog := makeProgram(EQUAL) + vm := load(prog) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestEQUALBad1Argument(t *testing.T) { + prog := makeProgram(EQUAL) + vm := load(prog) + vm.estack.PushVal(1) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestEQUALGoodInteger(t *testing.T) { + prog := makeProgram(EQUAL) + vm := load(prog) + vm.estack.PushVal(5) + vm.estack.PushVal(5) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 1, vm.estack.Len()) + assert.Equal(t, &BoolItem{true}, vm.estack.Pop().value) +} + func TestNumEqual(t *testing.T) { prog := makeProgram(NUMEQUAL) vm := load(prog) From 729b7a0b247bc2903b4c56accd2bbd2c87a80dd2 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 12 Sep 2019 10:57:55 +0300 Subject: [PATCH 03/14] vm: fail if ROT has not enough arguments --- pkg/vm/vm.go | 12 +++++------- pkg/vm/vm_test.go | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 71793c0fc..1a62206a5 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -388,13 +388,11 @@ func (v *VM) execute(ctx *Context, op Instruction) { v.estack.InsertAt(a, n) case ROT: - c := v.estack.Pop() - b := v.estack.Pop() - a := v.estack.Pop() - - v.estack.Push(b) - v.estack.Push(c) - v.estack.Push(a) + e := v.estack.RemoveAt(2) + if e == nil { + panic("no top-level element found") + } + v.estack.Push(e) case DEPTH: v.estack.PushVal(v.estack.Len()) diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 1628c95b7..e402887eb 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -659,6 +659,29 @@ func TestPICKgood(t *testing.T) { assert.Equal(t, int64(result), vm.estack.Pop().BigInt().Int64()) } +func TestROTBad(t *testing.T) { + prog := makeProgram(ROT) + vm := load(prog) + vm.estack.PushVal(1) + vm.estack.PushVal(2) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestROTGood(t *testing.T) { + prog := makeProgram(ROT) + vm := load(prog) + vm.estack.PushVal(1) + vm.estack.PushVal(2) + vm.estack.PushVal(3) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 3, vm.estack.Len()) + assert.Equal(t, makeStackItem(1), vm.estack.Pop().value) + assert.Equal(t, makeStackItem(3), vm.estack.Pop().value) + assert.Equal(t, makeStackItem(2), vm.estack.Pop().value) +} + func TestXTUCKbadNoitem(t *testing.T) { prog := makeProgram(XTUCK) vm := load(prog) From 6e4014547dcb8b0fb8dffbda11a8fd97a9214910 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 12 Sep 2019 11:05:10 +0300 Subject: [PATCH 04/14] vm: fail if DROP has no argument --- pkg/vm/vm.go | 3 +++ pkg/vm/vm_test.go | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 1a62206a5..218824e1f 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -439,6 +439,9 @@ func (v *VM) execute(ctx *Context, op Instruction) { } case DROP: + if v.estack.Len() < 1 { + panic("stack is too small") + } v.estack.Pop() case EQUAL: diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index e402887eb..4f2b1b231 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -803,6 +803,22 @@ func TestOVERgood(t *testing.T) { assert.Equal(t, 3, vm.estack.Len()) } +func TestDROPBadNoItem(t *testing.T) { + prog := makeProgram(DROP) + vm := load(prog) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestDROPGood(t *testing.T) { + prog := makeProgram(DROP) + vm := load(prog) + vm.estack.PushVal(1) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 0, vm.estack.Len()) +} + func TestXDROPbadNoitem(t *testing.T) { prog := makeProgram(XDROP) vm := load(prog) From 1881adabb9506b7443ee185d3609b3dc172584c9 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 12 Sep 2019 11:09:23 +0300 Subject: [PATCH 05/14] vm: fail if NIP has not enough arguments --- pkg/vm/vm.go | 7 ++++--- pkg/vm/vm_test.go | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 218824e1f..94912eda1 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -398,9 +398,10 @@ func (v *VM) execute(ctx *Context, op Instruction) { v.estack.PushVal(v.estack.Len()) case NIP: - elem := v.estack.Pop() - _ = v.estack.Pop() - v.estack.Push(elem) + elem := v.estack.RemoveAt(1) + if elem == nil { + panic("no second element found") + } case OVER: b := v.estack.Pop() diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 4f2b1b231..b85f4bc0c 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -803,6 +803,25 @@ func TestOVERgood(t *testing.T) { assert.Equal(t, 3, vm.estack.Len()) } +func TestNIPBadNoItem(t *testing.T) { + prog := makeProgram(NIP) + vm := load(prog) + vm.estack.PushVal(1) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestNIPGood(t *testing.T) { + prog := makeProgram(NIP) + vm := load(prog) + vm.estack.PushVal(1) + vm.estack.PushVal(2) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 1, vm.estack.Len()) + assert.Equal(t, makeStackItem(2), vm.estack.Pop().value) +} + func TestDROPBadNoItem(t *testing.T) { prog := makeProgram(DROP) vm := load(prog) From bafdb916a0bd9a5408e7d98b95c1a038305a8773 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 12 Sep 2019 11:19:25 +0300 Subject: [PATCH 06/14] vm: make PICKITEM accept bytearrays --- pkg/vm/vm.go | 7 ++++++- pkg/vm/vm_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 94912eda1..6bb1efe39 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -691,7 +691,12 @@ func (v *VM) execute(ctx *Context, op Instruction) { item := arr[index] v.estack.PushVal(item) default: - panic("PICKITEM: unknown type") + arr := obj.Bytes() + if index < 0 || index >= len(arr) { + panic("PICKITEM: invalid index") + } + item := arr[index] + v.estack.PushVal(int(item)) } case SETITEM: diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index b85f4bc0c..c45c2f0cb 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -514,6 +514,37 @@ func TestAPPENDWrongType(t *testing.T) { assert.Equal(t, true, vm.state.HasFlag(faultState)) } +func TestPICKITEMBadIndex(t *testing.T) { + prog := makeProgram(PICKITEM) + vm := load(prog) + vm.estack.PushVal([]StackItem{}) + vm.estack.PushVal(0) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestPICKITEMArray(t *testing.T) { + prog := makeProgram(PICKITEM) + vm := load(prog) + vm.estack.PushVal([]StackItem{makeStackItem(1), makeStackItem(2)}) + vm.estack.PushVal(1) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 1, vm.estack.Len()) + assert.Equal(t, makeStackItem(2), vm.estack.Pop().value) +} + +func TestPICKITEMByteArray(t *testing.T) { + prog := makeProgram(PICKITEM) + vm := load(prog) + vm.estack.PushVal([]byte{1, 2}) + vm.estack.PushVal(1) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 1, vm.estack.Len()) + assert.Equal(t, makeStackItem(2), vm.estack.Pop().value) +} + func TestSIGNNoArgument(t *testing.T) { prog := makeProgram(SIGN) vm := load(prog) From 71cfd14b9285359db55d3f793f9bf1bb58cba9c1 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 12 Sep 2019 11:24:10 +0300 Subject: [PATCH 07/14] vm: create an array of `false` items in NEWARRAY/NEWSTRUCT --- pkg/vm/vm.go | 12 ++++++++++-- pkg/vm/vm_test.go | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 6bb1efe39..b9c667c69 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -611,7 +611,7 @@ func (v *VM) execute(ctx *Context, op Instruction) { switch t := item.value.(type) { case *BigIntegerItem: n := t.value.Int64() - items := make([]StackItem, n) + items := makeArrayOfFalses(int(n)) v.estack.PushVal(&ArrayItem{items}) case *StructItem: v.estack.PushVal(&ArrayItem{t.value}) @@ -626,7 +626,7 @@ func (v *VM) execute(ctx *Context, op Instruction) { switch t := item.value.(type) { case *BigIntegerItem: n := t.value.Int64() - items := make([]StackItem, n) + items := makeArrayOfFalses(int(n)) v.estack.PushVal(&StructItem{items}) case *ArrayItem: v.estack.PushVal(&StructItem{t.value}) @@ -853,6 +853,14 @@ func (v *VM) execute(ctx *Context, op Instruction) { } } +func makeArrayOfFalses(n int) []StackItem { + items := make([]StackItem, n) + for i := range items { + items[i] = &BoolItem{false} + } + return items +} + func init() { log.SetPrefix("NEO-GO-VM > ") log.SetFlags(0) diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index c45c2f0cb..d813ebc89 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -397,7 +397,7 @@ func TestNEWARRAYInteger(t *testing.T) { vm.Run() assert.Equal(t, false, vm.state.HasFlag(faultState)) assert.Equal(t, 1, vm.estack.Len()) - assert.Equal(t, &ArrayItem{make([]StackItem, 1)}, vm.estack.Pop().value) + assert.Equal(t, &ArrayItem{[]StackItem{makeStackItem(false)}}, vm.estack.Pop().value) } func TestNEWARRAYStruct(t *testing.T) { @@ -437,7 +437,7 @@ func TestNEWSTRUCTInteger(t *testing.T) { vm.Run() assert.Equal(t, false, vm.state.HasFlag(faultState)) assert.Equal(t, 1, vm.estack.Len()) - assert.Equal(t, &StructItem{make([]StackItem, 1)}, vm.estack.Pop().value) + assert.Equal(t, &StructItem{[]StackItem{makeStackItem(false)}}, vm.estack.Pop().value) } func TestNEWSTRUCTArray(t *testing.T) { From 66501f9ef92648f43c18d27be34a498518c0b905 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 12 Sep 2019 11:32:09 +0300 Subject: [PATCH 08/14] vm: make REMOVE consume array from stack --- pkg/vm/vm.go | 23 +++++++++++++++++++---- pkg/vm/vm_test.go | 12 +++--------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index b9c667c69..0587a6929 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -728,10 +728,25 @@ func (v *VM) execute(ctx *Context, op Instruction) { } case REMOVE: key := int(v.estack.Pop().BigInt().Int64()) - elem := v.estack.Peek(0) - a := elem.Array() - a = append(a[:key], a[key+1:]...) - elem.value = makeStackItem(a) + elem := v.estack.Pop() + switch t := elem.value.(type) { + case *ArrayItem: + a := t.value + if key < 0 || key >= len(a) { + panic("REMOVE: invalid index") + } + a = append(a[:key], a[key+1:]...) + t.value = a + case *StructItem: + a := t.value + if key < 0 || key >= len(a) { + panic("REMOVE: invalid index") + } + a = append(a[:key], a[key+1:]...) + t.value = a + default: + panic("REMOVE: invalid type") + } case ARRAYSIZE: elem := v.estack.Pop() diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index d813ebc89..2f6857c4e 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -1314,23 +1314,17 @@ func TestREMOVEBadIndex(t *testing.T) { } func TestREMOVEGood(t *testing.T) { - prog := makeProgram(REMOVE) + prog := makeProgram(DUP, PUSH2, REMOVE) elements := []int{22, 34, 42, 55, 81} reselements := []int{22, 34, 55, 81} vm := load(prog) vm.estack.PushVal(1) vm.estack.PushVal(elements) - vm.estack.PushVal(2) vm.Run() assert.Equal(t, false, vm.state.HasFlag(faultState)) assert.Equal(t, 2, vm.estack.Len()) - a := vm.estack.Peek(0).Array() - assert.Equal(t, len(reselements), len(a)) - for k, v := range reselements { - e := a[k].Value().(*big.Int) - assert.Equal(t, int64(v), e.Int64()) - } - assert.Equal(t, int64(1), vm.estack.Peek(1).BigInt().Int64()) + assert.Equal(t, makeStackItem(reselements), vm.estack.Pop().value) + assert.Equal(t, makeStackItem(1), vm.estack.Pop().value) } func makeProgram(opcodes ...Instruction) []byte { From 09e197eaf3ec8e5e71395cd0ddc69a49f117dc43 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 12 Sep 2019 11:42:27 +0300 Subject: [PATCH 09/14] vm: make PUSH0 create empty bytearray Also make tests expect []byte{} instead of 0. --- pkg/vm/tests/binary_expr_test.go | 10 +++++----- pkg/vm/tests/for_test.go | 2 +- pkg/vm/tests/function_call_test.go | 2 +- pkg/vm/tests/if_test.go | 6 +++--- pkg/vm/tests/import_test.go | 2 +- pkg/vm/tests/struct_test.go | 4 ++-- pkg/vm/vm.go | 2 +- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pkg/vm/tests/binary_expr_test.go b/pkg/vm/tests/binary_expr_test.go index 16e7509fb..fd24c2e37 100644 --- a/pkg/vm/tests/binary_expr_test.go +++ b/pkg/vm/tests/binary_expr_test.go @@ -26,7 +26,7 @@ var binaryExprTestCases = []testCase{ return x } `, - big.NewInt(0), + []byte{}, }, { "simple div", @@ -86,7 +86,7 @@ var binaryExprTestCases = []testCase{ return 0 } `, - big.NewInt(0), + []byte{}, }, { "compare equal strings with eql", @@ -128,7 +128,7 @@ var binaryExprTestCases = []testCase{ return 0 } `, - big.NewInt(0), + []byte{}, }, { "compare equal ints with eql", @@ -156,7 +156,7 @@ var binaryExprTestCases = []testCase{ return 0 } `, - big.NewInt(0), + []byte{}, }, { "compare not equal ints with eql", @@ -170,7 +170,7 @@ var binaryExprTestCases = []testCase{ return 0 } `, - big.NewInt(0), + []byte{}, }, { "compare not equal ints with neq", diff --git a/pkg/vm/tests/for_test.go b/pkg/vm/tests/for_test.go index 70b063ffe..2e3c15288 100644 --- a/pkg/vm/tests/for_test.go +++ b/pkg/vm/tests/for_test.go @@ -275,7 +275,7 @@ func TestIfUnaryInvert(t *testing.T) { return 0 } ` - eval(t, src, big.NewInt(0)) + eval(t, src, []byte{}) } func TestAppendByte(t *testing.T) { diff --git a/pkg/vm/tests/function_call_test.go b/pkg/vm/tests/function_call_test.go index 442d703b4..1ac71b011 100644 --- a/pkg/vm/tests/function_call_test.go +++ b/pkg/vm/tests/function_call_test.go @@ -35,7 +35,7 @@ func TestNotAssignedFunctionCall(t *testing.T) { return 0 } ` - eval(t, src, big.NewInt(0)) + eval(t, src, []byte{}) } func TestMultipleFunctionCalls(t *testing.T) { diff --git a/pkg/vm/tests/if_test.go b/pkg/vm/tests/if_test.go index 6e39d0ab6..c4483309e 100644 --- a/pkg/vm/tests/if_test.go +++ b/pkg/vm/tests/if_test.go @@ -30,7 +30,7 @@ func TestGT(t *testing.T) { return 0 } ` - eval(t, src, big.NewInt(0)) + eval(t, src, []byte{}) } func TestGTE(t *testing.T) { @@ -44,7 +44,7 @@ func TestGTE(t *testing.T) { return 0 } ` - eval(t, src, big.NewInt(0)) + eval(t, src, []byte{}) } func TestLAND(t *testing.T) { @@ -89,5 +89,5 @@ func TestNestedIF(t *testing.T) { return 0 } ` - eval(t, src, big.NewInt(0)) + eval(t, src, []byte{}) } diff --git a/pkg/vm/tests/import_test.go b/pkg/vm/tests/import_test.go index c969ba71f..fb831fcef 100644 --- a/pkg/vm/tests/import_test.go +++ b/pkg/vm/tests/import_test.go @@ -32,7 +32,7 @@ func TestImportStruct(t *testing.T) { return b.Y } ` - eval(t, src, big.NewInt(0)) + eval(t, src, []byte{}) } func TestMultipleDirFileImport(t *testing.T) { diff --git a/pkg/vm/tests/struct_test.go b/pkg/vm/tests/struct_test.go index 759396889..e3de28f91 100644 --- a/pkg/vm/tests/struct_test.go +++ b/pkg/vm/tests/struct_test.go @@ -179,7 +179,7 @@ var structTestCases = []testCase{ return t.y } `, - big.NewInt(0), + []byte{}, }, { "test return struct from func", @@ -209,7 +209,7 @@ var structTestCases = []testCase{ vm.NewBigIntegerItem(1), vm.NewBigIntegerItem(2), vm.NewByteArrayItem([]byte("hello")), - vm.NewBigIntegerItem(0), + vm.NewByteArrayItem([]byte{}), }, }, { diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 0587a6929..7e9c7bd05 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -257,7 +257,7 @@ func (v *VM) execute(ctx *Context, op Instruction) { v.estack.PushVal(val) case PUSH0: - v.estack.PushVal(0) + v.estack.PushVal([]byte{}) case PUSHDATA1: n := ctx.readByte() From 978088923984ff4cc7135e014577557a968e2fc9 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 12 Sep 2019 11:48:39 +0300 Subject: [PATCH 10/14] vm: make NEWARRAY/NEWSTRUCT accept bytearray --- pkg/vm/vm.go | 16 ++++++---------- pkg/vm/vm_test.go | 16 ++++++++++------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 7e9c7bd05..3e6914686 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -609,31 +609,27 @@ func (v *VM) execute(ctx *Context, op Instruction) { case NEWARRAY: item := v.estack.Pop() switch t := item.value.(type) { - case *BigIntegerItem: - n := t.value.Int64() - items := makeArrayOfFalses(int(n)) - v.estack.PushVal(&ArrayItem{items}) case *StructItem: v.estack.PushVal(&ArrayItem{t.value}) case *ArrayItem: v.estack.PushVal(t) default: - panic("NEWARRAY: invalid operand") + n := item.BigInt() + items := makeArrayOfFalses(int(n.Int64())) + v.estack.PushVal(&ArrayItem{items}) } case NEWSTRUCT: item := v.estack.Pop() switch t := item.value.(type) { - case *BigIntegerItem: - n := t.value.Int64() - items := makeArrayOfFalses(int(n)) - v.estack.PushVal(&StructItem{items}) case *ArrayItem: v.estack.PushVal(&StructItem{t.value}) case *StructItem: v.estack.PushVal(t) default: - panic("NEWSTRUCT: invalid operand") + n := item.BigInt() + items := makeArrayOfFalses(int(n.Int64())) + v.estack.PushVal(&StructItem{items}) } case APPEND: diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 2f6857c4e..2773a4bf2 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -422,12 +422,14 @@ func TestNEWARRAYArray(t *testing.T) { assert.Equal(t, &ArrayItem{arr}, vm.estack.Pop().value) } -func TestNEWARRAYWrongType(t *testing.T) { +func TestNEWARRAYByteArray(t *testing.T) { prog := makeProgram(NEWARRAY) vm := load(prog) - vm.estack.Push(NewElement([]byte{})) + vm.estack.PushVal([]byte{}) vm.Run() - assert.Equal(t, true, vm.state.HasFlag(faultState)) + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 1, vm.estack.Len()) + assert.Equal(t, &ArrayItem{[]StackItem{}}, vm.estack.Pop().value) } func TestNEWSTRUCTInteger(t *testing.T) { @@ -462,12 +464,14 @@ func TestNEWSTRUCTStruct(t *testing.T) { assert.Equal(t, &StructItem{arr}, vm.estack.Pop().value) } -func TestNEWSTRUCTWrongType(t *testing.T) { +func TestNEWSTRUCTByteArray(t *testing.T) { prog := makeProgram(NEWSTRUCT) vm := load(prog) - vm.estack.Push(NewElement([]byte{})) + vm.estack.PushVal([]byte{}) vm.Run() - assert.Equal(t, true, vm.state.HasFlag(faultState)) + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 1, vm.estack.Len()) + assert.Equal(t, &StructItem{[]StackItem{}}, vm.estack.Pop().value) } func TestAPPENDArray(t *testing.T) { From 9eca5ee3178585eaaf2c184556e7308ffc14991b Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 12 Sep 2019 11:53:11 +0300 Subject: [PATCH 11/14] vm: convert SIZE argument to bytearray --- pkg/vm/vm.go | 5 +---- pkg/vm/vm_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 3e6914686..74a4057ae 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -759,10 +759,7 @@ func (v *VM) execute(ctx *Context, op Instruction) { case SIZE: elem := v.estack.Pop() - arr, ok := elem.value.Value().([]uint8) - if !ok { - panic("SIZE: item not of type []uint8") - } + arr := elem.Bytes() v.estack.PushVal(len(arr)) case JMP, JMPIF, JMPIFNOT: diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 2773a4bf2..f75631fb7 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -549,6 +549,33 @@ func TestPICKITEMByteArray(t *testing.T) { assert.Equal(t, makeStackItem(2), vm.estack.Pop().value) } +func TestSIZENoArgument(t *testing.T) { + prog := makeProgram(SIZE) + vm := load(prog) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestSIZEByteArray(t *testing.T) { + prog := makeProgram(SIZE) + vm := load(prog) + vm.estack.PushVal([]byte{0, 1}) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 1, vm.estack.Len()) + assert.Equal(t, makeStackItem(2), vm.estack.Pop().value) +} + +func TestSIZEBool(t *testing.T) { + prog := makeProgram(SIZE) + vm := load(prog) + vm.estack.PushVal(false) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 1, vm.estack.Len()) + assert.Equal(t, makeStackItem(1), vm.estack.Pop().value) +} + func TestSIGNNoArgument(t *testing.T) { prog := makeProgram(SIGN) vm := load(prog) From f0426ac2d52b7f84e5c35882cdf8c171523b1548 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 12 Sep 2019 12:02:38 +0300 Subject: [PATCH 12/14] vm: do nothing if SHL/SHR by 0 --- pkg/vm/vm.go | 6 ++++++ pkg/vm/vm_test.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 74a4057ae..a209a1382 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -505,11 +505,17 @@ func (v *VM) execute(ctx *Context, op Instruction) { case SHL: b := v.estack.Pop().BigInt() + if b.Int64() == 0 { + return + } a := v.estack.Pop().BigInt() v.estack.PushVal(new(big.Int).Lsh(a, uint(b.Int64()))) case SHR: b := v.estack.Pop().BigInt() + if b.Int64() == 0 { + return + } a := v.estack.Pop().BigInt() v.estack.PushVal(new(big.Int).Rsh(a, uint(b.Int64()))) diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index f75631fb7..87ac78092 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -283,6 +283,50 @@ func TestSub(t *testing.T) { assert.Equal(t, int64(2), vm.estack.Pop().BigInt().Int64()) } +func TestSHRGood(t *testing.T) { + prog := makeProgram(SHR) + vm := load(prog) + vm.estack.PushVal(4) + vm.estack.PushVal(2) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 1, vm.estack.Len()) + assert.Equal(t, makeStackItem(1), vm.estack.Pop().value) +} + +func TestSHRZero(t *testing.T) { + prog := makeProgram(SHR) + vm := load(prog) + vm.estack.PushVal([]byte{0, 1}) + vm.estack.PushVal(0) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 1, vm.estack.Len()) + assert.Equal(t, makeStackItem([]byte{0, 1}), vm.estack.Pop().value) +} + +func TestSHLGood(t *testing.T) { + prog := makeProgram(SHL) + vm := load(prog) + vm.estack.PushVal(4) + vm.estack.PushVal(2) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 1, vm.estack.Len()) + assert.Equal(t, makeStackItem(16), vm.estack.Pop().value) +} + +func TestSHLZero(t *testing.T) { + prog := makeProgram(SHL) + vm := load(prog) + vm.estack.PushVal([]byte{0, 1}) + vm.estack.PushVal(0) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 1, vm.estack.Len()) + assert.Equal(t, makeStackItem([]byte{0, 1}), vm.estack.Pop().value) +} + func TestLT(t *testing.T) { prog := makeProgram(LT) vm := load(prog) From fd264cb06f07659236e231aaea9c9661177d32a8 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 12 Sep 2019 16:54:22 +0300 Subject: [PATCH 13/14] vm: make REVERSE work with structs Also add more tests. Extend `Array` method of `Element` to work with `StructItem`. --- pkg/vm/stack.go | 2 ++ pkg/vm/vm_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/pkg/vm/stack.go b/pkg/vm/stack.go index 740aa835e..e840bccb7 100644 --- a/pkg/vm/stack.go +++ b/pkg/vm/stack.go @@ -121,6 +121,8 @@ func (e *Element) Array() []StackItem { switch t := e.value.(type) { case *ArrayItem: return t.value + case *StructItem: + return t.value default: panic("element is not an array") } diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 87ac78092..02b82b3a0 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -1331,6 +1331,35 @@ func TestREVERSEGoodOneElem(t *testing.T) { assert.Equal(t, int64(elements[0]), e.Int64()) } +func TestREVERSEGoodStruct(t *testing.T) { + eodd := []int{22, 34, 42, 55, 81} + even := []int{22, 34, 42, 55, 81, 99} + eall := [][]int{eodd, even} + + for _, elements := range eall { + prog := makeProgram(REVERSE) + vm := load(prog) + vm.estack.PushVal(1) + + arr := make([]StackItem, len(elements)) + for i := range elements { + arr[i] = makeStackItem(elements[i]) + } + vm.estack.Push(&Element{value: &StructItem{arr}}) + + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 2, vm.estack.Len()) + a := vm.estack.Peek(0).Array() + assert.Equal(t, len(elements), len(a)) + for k, v := range elements { + e := a[len(a)-1-k].Value().(*big.Int) + assert.Equal(t, int64(v), e.Int64()) + } + assert.Equal(t, int64(1), vm.estack.Peek(1).BigInt().Int64()) + } +} + func TestREVERSEGood(t *testing.T) { eodd := []int{22, 34, 42, 55, 81} even := []int{22, 34, 42, 55, 81, 99} From d2ccc3b017f2af140171ac7cc51f828e6dc84b33 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Mon, 16 Sep 2019 15:46:35 +0300 Subject: [PATCH 14/14] vm: make REVERSE consume an item from stack --- pkg/vm/vm.go | 2 +- pkg/vm/vm_test.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index a209a1382..f0647d8d6 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -722,7 +722,7 @@ func (v *VM) execute(ctx *Context, op Instruction) { } case REVERSE: - a := v.estack.Peek(0).Array() + a := v.estack.Pop().Array() if len(a) > 1 { for i, j := 0, len(a)-1; i <= j; i, j = i+1, j-1 { a[i], a[j] = a[j], a[i] diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 02b82b3a0..2193afcb9 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -1317,7 +1317,7 @@ func TestREVERSEBadNotArray(t *testing.T) { } func TestREVERSEGoodOneElem(t *testing.T) { - prog := makeProgram(REVERSE) + prog := makeProgram(DUP, REVERSE) elements := []int{22} vm := load(prog) vm.estack.PushVal(1) @@ -1337,7 +1337,7 @@ func TestREVERSEGoodStruct(t *testing.T) { eall := [][]int{eodd, even} for _, elements := range eall { - prog := makeProgram(REVERSE) + prog := makeProgram(DUP, REVERSE) vm := load(prog) vm.estack.PushVal(1) @@ -1366,7 +1366,7 @@ func TestREVERSEGood(t *testing.T) { eall := [][]int{eodd, even} for _, elements := range eall { - prog := makeProgram(REVERSE) + prog := makeProgram(DUP, REVERSE) vm := load(prog) vm.estack.PushVal(1) vm.estack.PushVal(elements)