diff --git a/pkg/vm/vm_ops.go b/pkg/vm/vm_ops.go index 07a20cf43..0800058fe 100644 --- a/pkg/vm/vm_ops.go +++ b/pkg/vm/vm_ops.go @@ -5,6 +5,10 @@ import "github.com/CityOfZion/neo-go/pkg/vm/stack" type stackInfo func(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) var opFunc = map[stack.Instruction]stackInfo{ + stack.TUCK: TUCK, + stack.SWAP: SWAP, + stack.ROT: ROT, + stack.ROLL: ROLL, stack.PICK: PICK, stack.OVER: OVER, stack.NIP: NIP, diff --git a/pkg/vm/vm_ops_stackmani.go b/pkg/vm/vm_ops_stackmani.go index 663d9da6c..1b1670a1c 100644 --- a/pkg/vm/vm_ops_stackmani.go +++ b/pkg/vm/vm_ops_stackmani.go @@ -20,6 +20,80 @@ func PushNBytes(op stack.Instruction, ctx *stack.Context, istack *stack.Invocati return NONE, nil } +// ROLL pops an integer n off of the stack and +// moves the n-item starting from +// the top of the stack onto the top stack item. +// Returns an error if the top stack item is not an +// integer or n-item does not exist. +func ROLL(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) { + + n, err := ctx.Estack.PopInt() + if err != nil { + return FAULT, err + } + + nItem, err := ctx.Estack.Remove(uint16(n.Value().Int64())) + if err != nil { + return FAULT, err + } + + ctx.Estack.Push(nItem) + + return NONE, nil +} + +// ROT moves the third top stack item +// onto the top stack item. +// Returns an error if the third top stack item +// does not exist. +func ROT(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) { + + item, err := ctx.Estack.Remove(2) + if err != nil { + return FAULT, err + } + + ctx.Estack.Push(item) + + return NONE, nil +} + +// SWAP swaps the second top stack item with +// the top stack item. +// Returns an error if the second top stack item +// does not exist. +func SWAP(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) { + + item, err := ctx.Estack.Remove(1) + if err != nil { + return FAULT, err + } + + ctx.Estack.Push(item) + + return NONE, nil +} + +// TUCK copies the top stack item and +// inserts it before the second top stack item. +// Returns an error if the stack is empty or +// len(stack) is less or equal 2. +func TUCK(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) { + + item, err := ctx.Estack.Peek(0) + if err != nil { + return FAULT, err + } + + ras, err := ctx.Estack.Insert(2, item) + if err != nil { + return FAULT, err + } + ctx.Estack = *ras + + return NONE, nil +} + // DUP duplicates the top stack item. // Returns an error if stack is empty. func DUP(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) { diff --git a/pkg/vm/vm_ops_stackmani_test.go b/pkg/vm/vm_ops_stackmani_test.go index 3d7a487c3..efe554351 100644 --- a/pkg/vm/vm_ops_stackmani_test.go +++ b/pkg/vm/vm_ops_stackmani_test.go @@ -8,6 +8,160 @@ import ( "github.com/stretchr/testify/assert" ) +func TestRollOp(t *testing.T) { + + v := VM{} + + a, err := stack.NewInt(big.NewInt(3)) + assert.Nil(t, err) + + b, err := stack.NewInt(big.NewInt(6)) + assert.Nil(t, err) + + c, err := stack.NewInt(big.NewInt(9)) + assert.Nil(t, err) + + d, err := stack.NewInt(big.NewInt(2)) + assert.Nil(t, err) + + ctx := stack.NewContext([]byte{}) + ctx.Estack.Push(a).Push(b).Push(c).Push(d) + + // pop n (= d = 2) from the stack + // and move the n-item which + // has index len(stack)-n-1 (= 3-2-1= 0) + // onto the top stack item. + // The final stack will be [b,c,a] + v.executeOp(stack.ROLL, ctx) + + // Stack should have three items + assert.Equal(t, 3, ctx.Estack.Len()) + + itemA, err := ctx.Estack.PopInt() + assert.Nil(t, err) + + itemC, err := ctx.Estack.PopInt() + assert.Nil(t, err) + + itemB, err := ctx.Estack.PopInt() + assert.Nil(t, err) + + assert.Equal(t, int64(3), itemA.Value().Int64()) + assert.Equal(t, int64(9), itemC.Value().Int64()) + assert.Equal(t, int64(6), itemB.Value().Int64()) +} + +func TestRotOp(t *testing.T) { + + v := VM{} + + a, err := stack.NewInt(big.NewInt(3)) + assert.Nil(t, err) + + b, err := stack.NewInt(big.NewInt(6)) + assert.Nil(t, err) + + c, err := stack.NewInt(big.NewInt(9)) + assert.Nil(t, err) + ctx := stack.NewContext([]byte{}) + ctx.Estack.Push(a).Push(b).Push(c) + + // move the third top stack a item onto + // the top stack item c. + // The final stack will be [b,c,a] + v.executeOp(stack.ROT, ctx) + + // Stack should have three items + assert.Equal(t, 3, ctx.Estack.Len()) + + itemA, err := ctx.Estack.PopInt() + assert.Nil(t, err) + + itemC, err := ctx.Estack.PopInt() + assert.Nil(t, err) + + itemB, err := ctx.Estack.PopInt() + assert.Nil(t, err) + + assert.Equal(t, int64(3), itemA.Value().Int64()) + assert.Equal(t, int64(9), itemC.Value().Int64()) + assert.Equal(t, int64(6), itemB.Value().Int64()) +} + +func TestSwapOp(t *testing.T) { + + v := VM{} + + a, err := stack.NewInt(big.NewInt(3)) + assert.Nil(t, err) + + b, err := stack.NewInt(big.NewInt(6)) + assert.Nil(t, err) + + ctx := stack.NewContext([]byte{}) + ctx.Estack.Push(a).Push(b) + + // Swaps the top two stack items. + // The final stack will be [b,a] + v.executeOp(stack.SWAP, ctx) + + // Stack should have two items + assert.Equal(t, 2, ctx.Estack.Len()) + + itemA, err := ctx.Estack.PopInt() + assert.Nil(t, err) + + itemB, err := ctx.Estack.PopInt() + assert.Nil(t, err) + + assert.Equal(t, int64(3), itemA.Value().Int64()) + assert.Equal(t, int64(6), itemB.Value().Int64()) + +} + +func TestTuckOp(t *testing.T) { + + v := VM{} + + a, err := stack.NewInt(big.NewInt(3)) + assert.Nil(t, err) + + b, err := stack.NewInt(big.NewInt(6)) + assert.Nil(t, err) + + c, err := stack.NewInt(big.NewInt(9)) + assert.Nil(t, err) + + ctx := stack.NewContext([]byte{}) + ctx.Estack.Push(a).Push(b).Push(c) + + // copy the top stack item c and + // inserts it before the second top stack item. + // The final stack will be [a,c,b,c] + v.executeOp(stack.TUCK, ctx) + + // Stack should have four items + assert.Equal(t, 4, ctx.Estack.Len()) + + itemC, err := ctx.Estack.PopInt() + assert.Nil(t, err) + + itemB, err := ctx.Estack.PopInt() + assert.Nil(t, err) + + itemC2, err := ctx.Estack.PopInt() + assert.Nil(t, err) + + itemA, err := ctx.Estack.PopInt() + assert.Nil(t, err) + + assert.Equal(t, int64(9), itemC.Value().Int64()) + assert.Equal(t, int64(6), itemB.Value().Int64()) + assert.Equal(t, int64(9), itemC2.Value().Int64()) + assert.Equal(t, int64(3), itemA.Value().Int64()) + +} + func TestDupOp(t *testing.T) { v := VM{}