Implemented following opcodes:

1) ROLL
2) ROT
3) SWAP
4) TUCK
This commit is contained in:
DauTT 2019-04-12 22:27:52 +02:00
parent 045db09af2
commit d36d8b456c
4 changed files with 258 additions and 0 deletions

View file

@ -127,6 +127,22 @@ func (ras *RandomAccess) CopyTo(stack *RandomAccess) error {
return nil
}
// Remove removes the n-item from the stack
// starting from the top of the stack. In other words
// the n-item to remove is located at the index "len(stack)-n-1"
func (ras *RandomAccess) Remove(n uint16) (Item, error) {
if int(n) >= len(ras.vals) {
return nil, errors.New("index out of range")
}
index := uint16(len(ras.vals)) - n - 1
item := ras.vals[index]
ras.vals = append(ras.vals[:index], ras.vals[index+1:]...)
return item, nil
}
// Convenience Functions
// PopInt will remove the last stack item that was added

View file

@ -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.NUMEQUAL: NumEqual,
stack.NUMNOTEQUAL: NumNotEqual,
stack.BOOLAND: BoolAnd,

View file

@ -17,3 +17,77 @@ func PushNBytes(op stack.Instruction, ctx *stack.Context, istack *stack.Invocati
ctx.Estack.Push(ba)
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
}

View file

@ -0,0 +1,164 @@
package vm
import (
"math/big"
"testing"
"github.com/CityOfZion/neo-go/pkg/vm/stack"
"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())
}