From 85831e3e92506996360014c92aad34856398b6fc Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 5 Sep 2019 15:43:59 +0300 Subject: [PATCH] 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