From fcf9c1213bb144241b039b197f75e7466e51fbec Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 5 Sep 2019 15:05:44 +0300 Subject: [PATCH 1/9] vm: implement the NZ opcode --- pkg/vm/vm.go | 4 ++-- pkg/vm/vm_test.go | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index a7d9e4c3b..a8c1df58c 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -499,8 +499,8 @@ func (v *VM) execute(ctx *Context, op Instruction) { v.estack.PushVal(!x) case NZ: - panic("todo NZ") - // x := v.estack.Pop().BigInt() + x := v.estack.Pop().BigInt() + v.estack.PushVal(x.Cmp(big.NewInt(0)) != 0) // Object operations. case NEWARRAY: diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 257271056..6658fadd1 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -231,6 +231,24 @@ func TestSimpleCall(t *testing.T) { assert.Equal(t, result, int(vm.estack.Pop().BigInt().Int64())) } +func TestNZtrue(t *testing.T) { + prog := makeProgram(NZ) + vm := load(prog) + vm.estack.PushVal(1) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, true, vm.estack.Pop().Bool()) +} + +func TestNZfalse(t *testing.T) { + prog := makeProgram(NZ) + vm := load(prog) + vm.estack.PushVal(0) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, false, vm.estack.Pop().Bool()) +} + func makeProgram(opcodes ...Instruction) []byte { prog := make([]byte, len(opcodes)+1) // RET for i := 0; i < len(opcodes); i++ { From b34a1b6c2504d2678fd961fb52528a83d3d12852 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 5 Sep 2019 15:07:56 +0300 Subject: [PATCH 2/9] vm: harden tests against VM failures Add a check for failed VM state, some of tests using logic values (like NZ test) can succeed even with VM in FAULT state otherwise. --- pkg/vm/vm_test.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 6658fadd1..b9ec5ee74 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -23,6 +23,7 @@ func TestInteropHook(t *testing.T) { EmitOpcode(buf, RET) v.Load(buf.Bytes()) v.Run() + assert.Equal(t, false, v.state.HasFlag(faultState)) assert.Equal(t, 1, v.estack.Len()) assert.Equal(t, big.NewInt(1), v.estack.Pop().value.Value()) } @@ -100,6 +101,7 @@ func TestAdd(t *testing.T) { vm.estack.PushVal(4) vm.estack.PushVal(2) vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) assert.Equal(t, int64(6), vm.estack.Pop().BigInt().Int64()) } @@ -109,6 +111,7 @@ func TestMul(t *testing.T) { vm.estack.PushVal(4) vm.estack.PushVal(2) vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) assert.Equal(t, int64(8), vm.estack.Pop().BigInt().Int64()) } @@ -118,6 +121,7 @@ func TestDiv(t *testing.T) { vm.estack.PushVal(4) vm.estack.PushVal(2) vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) assert.Equal(t, int64(2), vm.estack.Pop().BigInt().Int64()) } @@ -127,6 +131,7 @@ func TestSub(t *testing.T) { vm.estack.PushVal(4) vm.estack.PushVal(2) vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) assert.Equal(t, int64(2), vm.estack.Pop().BigInt().Int64()) } @@ -136,6 +141,7 @@ func TestLT(t *testing.T) { vm.estack.PushVal(4) vm.estack.PushVal(3) vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) assert.Equal(t, false, vm.estack.Pop().Bool()) } @@ -145,6 +151,7 @@ func TestLTE(t *testing.T) { vm.estack.PushVal(2) vm.estack.PushVal(3) vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) assert.Equal(t, true, vm.estack.Pop().Bool()) } @@ -154,6 +161,7 @@ func TestGT(t *testing.T) { vm.estack.PushVal(9) vm.estack.PushVal(3) vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) assert.Equal(t, true, vm.estack.Pop().Bool()) } @@ -164,6 +172,7 @@ func TestGTE(t *testing.T) { vm.estack.PushVal(3) vm.estack.PushVal(3) vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) assert.Equal(t, true, vm.estack.Pop().Bool()) } @@ -174,6 +183,7 @@ func TestDepth(t *testing.T) { vm.estack.PushVal(2) vm.estack.PushVal(3) vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) assert.Equal(t, int64(3), vm.estack.Pop().BigInt().Int64()) } @@ -183,6 +193,7 @@ func TestNumEqual(t *testing.T) { vm.estack.PushVal(1) vm.estack.PushVal(2) vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) assert.Equal(t, false, vm.estack.Pop().Bool()) } @@ -192,6 +203,7 @@ func TestNumNotEqual(t *testing.T) { vm.estack.PushVal(2) vm.estack.PushVal(2) vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) assert.Equal(t, false, vm.estack.Pop().Bool()) } @@ -200,6 +212,7 @@ func TestINC(t *testing.T) { vm := load(prog) vm.estack.PushVal(1) vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) assert.Equal(t, big.NewInt(2), vm.estack.Pop().BigInt()) } @@ -214,6 +227,7 @@ func TestAppCall(t *testing.T) { vm.estack.PushVal(2) vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) elem := vm.estack.Pop() // depth should be 1 assert.Equal(t, int64(1), elem.BigInt().Int64()) } @@ -228,6 +242,7 @@ func TestSimpleCall(t *testing.T) { } vm := load(prog) vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) assert.Equal(t, result, int(vm.estack.Pop().BigInt().Int64())) } From 5f0f9e1638b66a75e54c87c5a2f4d60360ecc5cb Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 5 Sep 2019 15:18:04 +0300 Subject: [PATCH 3/9] vm: implement PICK instruction --- pkg/vm/vm.go | 8 ++++++++ pkg/vm/vm_test.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index a8c1df58c..11c7b3647 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -337,6 +337,14 @@ func (v *VM) execute(ctx *Context, op Instruction) { v.estack.Push(b) v.estack.Push(a) + case PICK: + n := int(v.estack.Pop().BigInt().Int64()) + if n < 0 { + panic("negative stack item returned") + } + a := v.estack.Peek(n) + v.estack.Push(a) + case ROLL: n := int(v.estack.Pop().BigInt().Int64()) if n < 0 { diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index b9ec5ee74..e80eabfb0 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -264,6 +264,39 @@ func TestNZfalse(t *testing.T) { assert.Equal(t, false, vm.estack.Pop().Bool()) } +func TestPICKbadNoitem(t *testing.T) { + prog := makeProgram(PICK) + vm := load(prog) + vm.estack.PushVal(1) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestPICKbadNegative(t *testing.T) { + prog := makeProgram(PICK) + vm := load(prog) + vm.estack.PushVal(-1) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestPICKgood(t *testing.T) { + prog := makeProgram(PICK) + result := 2 + vm := load(prog) + vm.estack.PushVal(0) + vm.estack.PushVal(1) + vm.estack.PushVal(result) + vm.estack.PushVal(3) + vm.estack.PushVal(4) + vm.estack.PushVal(5) + vm.estack.PushVal(3) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, int64(result), vm.estack.Pop().BigInt().Int64()) +} + + func makeProgram(opcodes ...Instruction) []byte { prog := make([]byte, len(opcodes)+1) // RET for i := 0; i < len(opcodes); i++ { From 85831e3e92506996360014c92aad34856398b6fc Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 5 Sep 2019 15:43:59 +0300 Subject: [PATCH 4/9] vm: fix XTUCK implementation, add tests The code that we have actually implements XTUCK and not TUCK. And it's a bit broken, so fix it and add some tests. The most interesting one (that required to touch stack code) is the one when we have 1 element on the stack and are trying to tell XTUCK to push 2 elements deep. --- pkg/vm/stack.go | 2 ++ pkg/vm/vm.go | 16 +++++++++++----- pkg/vm/vm_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/pkg/vm/stack.go b/pkg/vm/stack.go index fd1bd275f..ba425439b 100644 --- a/pkg/vm/stack.go +++ b/pkg/vm/stack.go @@ -141,6 +141,8 @@ func (s *Stack) InsertBefore(e, mark *Element) *Element { } // InsertAt will insert the given item (n) deep on the stack. +// Be very careful using it and _always_ check both e and n before invocation +// as it will silently do wrong things otherwise. func (s *Stack) InsertAt(e *Element, n int) *Element { before := s.Peek(n) if before == nil { diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 11c7b3647..c78b4f59f 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -306,13 +306,19 @@ func (v *VM) execute(ctx *Context, op Instruction) { b.value = aval } - case TUCK: + case XTUCK: n := int(v.estack.Pop().BigInt().Int64()) - if n <= 0 { - panic("OTUCK: invalid length") + if n < 0 { + panic("XTUCK: invalid length") } - - v.estack.InsertAt(v.estack.Peek(0), n) + a := v.estack.Dup(0) + if a == nil { + panic("no top-level element found") + } + if n > v.estack.Len() { + panic("can't push to the position specified") + } + v.estack.InsertAt(a, n) case ROT: c := v.estack.Pop() diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index e80eabfb0..1e365e26d 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -296,6 +296,48 @@ func TestPICKgood(t *testing.T) { assert.Equal(t, int64(result), vm.estack.Pop().BigInt().Int64()) } +func TestXTUCKbadNoitem(t *testing.T) { + prog := makeProgram(XTUCK) + vm := load(prog) + vm.estack.PushVal(1) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestXTUCKbadNoN(t *testing.T) { + prog := makeProgram(XTUCK) + vm := load(prog) + vm.estack.PushVal(1) + vm.estack.PushVal(2) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestXTUCKbadNegative(t *testing.T) { + prog := makeProgram(XTUCK) + vm := load(prog) + vm.estack.PushVal(-1) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestXTUCKgood(t *testing.T) { + prog := makeProgram(XTUCK) + topelement := 5 + xtuckdepth := 3 + vm := load(prog) + vm.estack.PushVal(0) + vm.estack.PushVal(1) + vm.estack.PushVal(2) + vm.estack.PushVal(3) + vm.estack.PushVal(4) + vm.estack.PushVal(topelement) + vm.estack.PushVal(xtuckdepth) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, int64(topelement), vm.estack.Peek(0).BigInt().Int64()) + assert.Equal(t, int64(topelement), vm.estack.Peek(xtuckdepth).BigInt().Int64()) +} func makeProgram(opcodes ...Instruction) []byte { prog := make([]byte, len(opcodes)+1) // RET From eb224aeebe962aed3438de4ba45332ae0a104090 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 5 Sep 2019 15:57:44 +0300 Subject: [PATCH 5/9] vm: add some checks for OVER and PICK, add OVER tests These actually behaved correctly because of deferred sigsegv recover, but I think it would be nice to print something more meaningful for users. --- pkg/vm/vm.go | 9 +++++++++ pkg/vm/vm_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index c78b4f59f..0ab9cbebd 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -339,7 +339,13 @@ func (v *VM) execute(ctx *Context, op Instruction) { case OVER: b := v.estack.Pop() + if b == nil { + panic("no top-level element found") + } a := v.estack.Peek(0) + if a == nil { + panic("no second element found") + } v.estack.Push(b) v.estack.Push(a) @@ -349,6 +355,9 @@ func (v *VM) execute(ctx *Context, op Instruction) { panic("negative stack item returned") } a := v.estack.Peek(n) + if a == nil { + panic("no nth element found") + } v.estack.Push(a) case ROLL: diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 1e365e26d..635a8feea 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -339,6 +339,35 @@ func TestXTUCKgood(t *testing.T) { assert.Equal(t, int64(topelement), vm.estack.Peek(xtuckdepth).BigInt().Int64()) } +func TestOVERbadNoitem(t *testing.T) { + prog := makeProgram(OVER) + vm := load(prog) + vm.estack.PushVal(1) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestOVERbadNoitems(t *testing.T) { + prog := makeProgram(OVER) + vm := load(prog) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestOVERgood(t *testing.T) { + prog := makeProgram(OVER) + vm := load(prog) + vm.estack.PushVal(42) + vm.estack.PushVal(34) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, int64(42), vm.estack.Peek(0).BigInt().Int64()) + assert.Equal(t, int64(34), vm.estack.Peek(1).BigInt().Int64()) + assert.Equal(t, int64(42), vm.estack.Peek(2).BigInt().Int64()) + assert.Equal(t, 3, vm.estack.Len()) +} + + func makeProgram(opcodes ...Instruction) []byte { prog := make([]byte, len(opcodes)+1) // RET for i := 0; i < len(opcodes); i++ { From 974f5db6bc1b7fe31b0e43608fafa86f033749e6 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 5 Sep 2019 16:34:35 +0300 Subject: [PATCH 6/9] vm: add a TUCK implementation with tests Also fix stack's InsertAt for the edge case of inserting into a position that currently is empty (the first good TUCK test illustrates this). --- pkg/vm/stack.go | 12 ++---------- pkg/vm/vm.go | 10 ++++++++++ pkg/vm/vm_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 10 deletions(-) diff --git a/pkg/vm/stack.go b/pkg/vm/stack.go index ba425439b..a08882d8d 100644 --- a/pkg/vm/stack.go +++ b/pkg/vm/stack.go @@ -132,23 +132,15 @@ func (s *Stack) insert(e, at *Element) *Element { return e } -// InsertBefore will insert the element before the mark on the stack. -func (s *Stack) InsertBefore(e, mark *Element) *Element { - if mark == nil { - return nil - } - return s.insert(e, mark.prev) -} - // InsertAt will insert the given item (n) deep on the stack. // Be very careful using it and _always_ check both e and n before invocation // as it will silently do wrong things otherwise. func (s *Stack) InsertAt(e *Element, n int) *Element { - before := s.Peek(n) + before := s.Peek(n - 1) if before == nil { return nil } - return s.InsertBefore(e, before) + return s.insert(e, before) } // Push pushes the given element on the stack. diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 0ab9cbebd..237a77d0e 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -290,6 +290,16 @@ func (v *VM) execute(ctx *Context, op Instruction) { v.estack.Push(a) v.estack.Push(b) + case TUCK: + a := v.estack.Dup(0) + if a == nil { + panic("no top-level element found") + } + if v.estack.Len() < 2 { + panic("can't TUCK with a one-element stack") + } + v.estack.InsertAt(a, 2) + case XSWAP: n := int(v.estack.Pop().BigInt().Int64()) if n < 0 { diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 635a8feea..781df0f8f 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -339,6 +339,47 @@ func TestXTUCKgood(t *testing.T) { assert.Equal(t, int64(topelement), vm.estack.Peek(xtuckdepth).BigInt().Int64()) } +func TestTUCKbadNoitems(t *testing.T) { + prog := makeProgram(TUCK) + vm := load(prog) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestTUCKbadNoitem(t *testing.T) { + prog := makeProgram(TUCK) + vm := load(prog) + vm.estack.PushVal(1) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestTUCKgood(t *testing.T) { + prog := makeProgram(TUCK) + vm := load(prog) + vm.estack.PushVal(42) + vm.estack.PushVal(34) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, int64(34), vm.estack.Peek(0).BigInt().Int64()) + assert.Equal(t, int64(42), vm.estack.Peek(1).BigInt().Int64()) + assert.Equal(t, int64(34), vm.estack.Peek(2).BigInt().Int64()) +} + +func TestTUCKgood2(t *testing.T) { + prog := makeProgram(TUCK) + vm := load(prog) + vm.estack.PushVal(11) + vm.estack.PushVal(42) + vm.estack.PushVal(34) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, int64(34), vm.estack.Peek(0).BigInt().Int64()) + assert.Equal(t, int64(42), vm.estack.Peek(1).BigInt().Int64()) + assert.Equal(t, int64(34), vm.estack.Peek(2).BigInt().Int64()) + assert.Equal(t, int64(11), vm.estack.Peek(3).BigInt().Int64()) +} + func TestOVERbadNoitem(t *testing.T) { prog := makeProgram(OVER) vm := load(prog) From b494e094c04cd91316a8028110992a088b19a4cd Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 5 Sep 2019 16:42:54 +0300 Subject: [PATCH 7/9] vm: add XDROP implementation --- pkg/vm/vm.go | 10 ++++++++++ pkg/vm/vm_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 237a77d0e..dfc172fe2 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -300,6 +300,16 @@ func (v *VM) execute(ctx *Context, op Instruction) { } v.estack.InsertAt(a, 2) + case XDROP: + n := int(v.estack.Pop().BigInt().Int64()) + if n < 0 { + panic("invalid length") + } + e := v.estack.RemoveAt(n) + if e == nil { + panic("bad index") + } + case XSWAP: n := int(v.estack.Pop().BigInt().Int64()) if n < 0 { diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 781df0f8f..8d13973ba 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -408,6 +408,44 @@ func TestOVERgood(t *testing.T) { assert.Equal(t, 3, vm.estack.Len()) } +func TestXDROPbadNoitem(t *testing.T) { + prog := makeProgram(XDROP) + vm := load(prog) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestXDROPbadNoN(t *testing.T) { + prog := makeProgram(XDROP) + vm := load(prog) + vm.estack.PushVal(1) + vm.estack.PushVal(2) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestXDROPbadNegative(t *testing.T) { + prog := makeProgram(XDROP) + vm := load(prog) + vm.estack.PushVal(1) + vm.estack.PushVal(-1) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestXDROPgood(t *testing.T) { + prog := makeProgram(XDROP) + vm := load(prog) + vm.estack.PushVal(0) + vm.estack.PushVal(1) + vm.estack.PushVal(2) + vm.estack.PushVal(2) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, 2, vm.estack.Len()) + assert.Equal(t, int64(2), vm.estack.Peek(0).BigInt().Int64()) + assert.Equal(t, int64(1), vm.estack.Peek(1).BigInt().Int64()) +} func makeProgram(opcodes ...Instruction) []byte { prog := make([]byte, len(opcodes)+1) // RET From 59de72f44626971cb441d845d9ea5929e2249bda Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 5 Sep 2019 17:20:53 +0300 Subject: [PATCH 8/9] vm: implement INVERT instruction --- pkg/vm/vm.go | 5 +++++ pkg/vm/vm_test.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index dfc172fe2..e5eeb15d8 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -402,6 +402,11 @@ func (v *VM) execute(ctx *Context, op Instruction) { v.estack.PushVal(reflect.DeepEqual(a,b)) // Bit operations. + case INVERT: + // inplace + a := v.estack.Peek(0).BigInt() + a.Not(a) + case AND: b := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt() diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 8d13973ba..7abb7bc72 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -447,6 +447,40 @@ func TestXDROPgood(t *testing.T) { assert.Equal(t, int64(1), vm.estack.Peek(1).BigInt().Int64()) } +func TestINVERTbadNoitem(t *testing.T) { + prog := makeProgram(INVERT) + vm := load(prog) + vm.Run() + assert.Equal(t, true, vm.state.HasFlag(faultState)) +} + +func TestINVERTgood1(t *testing.T) { + prog := makeProgram(INVERT) + vm := load(prog) + vm.estack.PushVal(0) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, int64(-1), vm.estack.Peek(0).BigInt().Int64()) +} + +func TestINVERTgood2(t *testing.T) { + prog := makeProgram(INVERT) + vm := load(prog) + vm.estack.PushVal(-1) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, int64(0), vm.estack.Peek(0).BigInt().Int64()) +} + +func TestINVERTgood3(t *testing.T) { + prog := makeProgram(INVERT) + vm := load(prog) + vm.estack.PushVal(0x69) + vm.Run() + assert.Equal(t, false, vm.state.HasFlag(faultState)) + assert.Equal(t, int64(-0x6A), vm.estack.Peek(0).BigInt().Int64()) +} + func makeProgram(opcodes ...Instruction) []byte { prog := make([]byte, len(opcodes)+1) // RET for i := 0; i < len(opcodes); i++ { From 3f403349799bda1f545b7e4aa475138734776d01 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 5 Sep 2019 17:32:34 +0300 Subject: [PATCH 9/9] vm: add generic stub for all unimplemented instructions --- pkg/vm/vm.go | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index e5eeb15d8..422e8e9e6 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -577,10 +577,6 @@ func (v *VM) execute(ctx *Context, op Instruction) { panic("APPEND: not of underlying type Array") } - case REVERSE: - - case REMOVE: - case PACK: n := int(v.estack.Pop().BigInt().Int64()) if n < 0 || n > v.estack.Len() { @@ -594,9 +590,6 @@ func (v *VM) execute(ctx *Context, op Instruction) { v.estack.PushVal(items) - case UNPACK: - panic("TODO") - case PICKITEM: var ( key = v.estack.Pop() @@ -719,6 +712,10 @@ func (v *VM) execute(ctx *Context, op Instruction) { v.state = haltState } + case CAT, SUBSTR, LEFT, RIGHT, CHECKSIG, CHECKMULTISIG, + UNPACK, REVERSE, REMOVE: + panic("unimplemented") + // Cryptographic operations. case SHA1: b := v.estack.Pop().Bytes() @@ -738,12 +735,6 @@ func (v *VM) execute(ctx *Context, op Instruction) { b := v.estack.Pop().Bytes() v.estack.PushVal(hash.DoubleSha256(b).Bytes()) - case CHECKSIG: - // pubkey := v.estack.Pop().Bytes() - // sig := v.estack.Pop().Bytes() - - case CHECKMULTISIG: - case NOP: // unlucky ^^