forked from TrueCloudLab/neoneo-go
Merge pull request #279 from dauTT/dauTT/vm-ROLL-ROT-SWAP-TUCK-opcode
VM: Implement ROLL, ROT, SWAP, TUCK opcode Closes #278, merging as per #283 discussion.
This commit is contained in:
commit
a4b2bb268e
3 changed files with 232 additions and 0 deletions
|
@ -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)
|
type stackInfo func(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error)
|
||||||
|
|
||||||
var opFunc = map[stack.Instruction]stackInfo{
|
var opFunc = map[stack.Instruction]stackInfo{
|
||||||
|
stack.TUCK: TUCK,
|
||||||
|
stack.SWAP: SWAP,
|
||||||
|
stack.ROT: ROT,
|
||||||
|
stack.ROLL: ROLL,
|
||||||
stack.PICK: PICK,
|
stack.PICK: PICK,
|
||||||
stack.OVER: OVER,
|
stack.OVER: OVER,
|
||||||
stack.NIP: NIP,
|
stack.NIP: NIP,
|
||||||
|
|
|
@ -20,6 +20,80 @@ func PushNBytes(op stack.Instruction, ctx *stack.Context, istack *stack.Invocati
|
||||||
return NONE, nil
|
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.
|
// DUP duplicates the top stack item.
|
||||||
// Returns an error if stack is empty.
|
// Returns an error if stack is empty.
|
||||||
func DUP(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
func DUP(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||||
|
|
|
@ -8,6 +8,160 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"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) {
|
func TestDupOp(t *testing.T) {
|
||||||
|
|
||||||
v := VM{}
|
v := VM{}
|
||||||
|
|
Loading…
Reference in a new issue