Merge pull request #275 from dauTT/dauTT/vm-XSWAP-XTUCK-DEPTH-DROP-opcode
VM: Implement XSWAP, XTUCK, DEPTH, DROP opcode Closes #274, merging as per #283 discussion.
This commit is contained in:
commit
37d7198877
4 changed files with 227 additions and 0 deletions
|
@ -127,6 +127,21 @@ func (ras *RandomAccess) CopyTo(stack *RandomAccess) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set sets the n-item from the stack
|
||||||
|
// starting from the top of the stack with the new item.
|
||||||
|
// the n-item to replace is located at the position "len(stack)-index-1".
|
||||||
|
func (ras *RandomAccess) Set(index uint16, item Item) error {
|
||||||
|
stackSize := uint16(len(ras.vals))
|
||||||
|
if ok := index >= stackSize; ok {
|
||||||
|
return errors.New("index out of range")
|
||||||
|
}
|
||||||
|
|
||||||
|
n := stackSize - index - 1
|
||||||
|
ras.vals[n] = item
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Convenience Functions
|
// Convenience Functions
|
||||||
|
|
||||||
// PopInt will remove the last stack item that was added
|
// PopInt will remove the last stack item that was added
|
||||||
|
|
|
@ -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.DROP: DROP,
|
||||||
|
stack.DEPTH: DEPTH,
|
||||||
|
stack.XTUCK: XTUCK,
|
||||||
|
stack.XSWAP: XSWAP,
|
||||||
stack.XDROP: XDROP,
|
stack.XDROP: XDROP,
|
||||||
stack.FROMALTSTACK: FROMALTSTACK,
|
stack.FROMALTSTACK: FROMALTSTACK,
|
||||||
stack.TOALTSTACK: TOALTSTACK,
|
stack.TOALTSTACK: TOALTSTACK,
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/stack"
|
"github.com/CityOfZion/neo-go/pkg/vm/stack"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,6 +20,35 @@ func PushNBytes(op stack.Instruction, ctx *stack.Context, istack *stack.Invocati
|
||||||
return NONE, nil
|
return NONE, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XSWAP pops an integer n off of the stack and
|
||||||
|
// swaps the n-item from the stack starting from
|
||||||
|
// the top of the stack with the top stack item.
|
||||||
|
func XSWAP(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.Peek(uint16(n.Value().Int64()))
|
||||||
|
if err != nil {
|
||||||
|
return FAULT, err
|
||||||
|
}
|
||||||
|
item, err := ctx.Estack.Peek(0)
|
||||||
|
if err != nil {
|
||||||
|
return FAULT, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ctx.Estack.Set(uint16(n.Value().Int64()), item); err != nil {
|
||||||
|
return FAULT, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ctx.Estack.Set(0, nItem); err != nil {
|
||||||
|
return FAULT, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NONE, nil
|
||||||
|
}
|
||||||
|
|
||||||
// DUPFROMALTSTACK duplicates the item on top of alternative stack and
|
// DUPFROMALTSTACK duplicates the item on top of alternative stack and
|
||||||
// puts it on top of evaluation stack.
|
// puts it on top of evaluation stack.
|
||||||
// Returns an error if the alt stack is empty.
|
// Returns an error if the alt stack is empty.
|
||||||
|
@ -33,6 +64,29 @@ func DUPFROMALTSTACK(op stack.Instruction, ctx *stack.Context, istack *stack.Inv
|
||||||
return NONE, nil
|
return NONE, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XTUCK pops an integer n off of the stack and
|
||||||
|
// inserts the top stack item to the position len(stack)-n in the evaluation stack.
|
||||||
|
func XTUCK(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||||
|
|
||||||
|
n, err := ctx.Estack.PopInt()
|
||||||
|
if err != nil || n.Value().Int64() < 0 {
|
||||||
|
return FAULT, err
|
||||||
|
}
|
||||||
|
|
||||||
|
item, err := ctx.Estack.Peek(0)
|
||||||
|
if err != nil {
|
||||||
|
return FAULT, err
|
||||||
|
}
|
||||||
|
ras, err := ctx.Estack.Insert(uint16(n.Value().Int64()), item)
|
||||||
|
if err != nil {
|
||||||
|
return FAULT, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Estack = *ras
|
||||||
|
|
||||||
|
return NONE, nil
|
||||||
|
}
|
||||||
|
|
||||||
// TOALTSTACK pops an item off of the evaluation stack and
|
// TOALTSTACK pops an item off of the evaluation stack and
|
||||||
// pushes it on top of the alternative stack.
|
// pushes it on top of the alternative stack.
|
||||||
// Returns an error if the alternative stack is empty.
|
// Returns an error if the alternative stack is empty.
|
||||||
|
@ -48,6 +102,20 @@ func TOALTSTACK(op stack.Instruction, ctx *stack.Context, istack *stack.Invocati
|
||||||
return NONE, nil
|
return NONE, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DEPTH puts the number of stack items onto the stack.
|
||||||
|
func DEPTH(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||||
|
|
||||||
|
l := ctx.Estack.Len()
|
||||||
|
length, err := stack.NewInt(big.NewInt(int64(l)))
|
||||||
|
if err != nil {
|
||||||
|
return FAULT, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Estack.Push(length)
|
||||||
|
|
||||||
|
return NONE, nil
|
||||||
|
}
|
||||||
|
|
||||||
// FROMALTSTACK pops an item off of the alternative stack and
|
// FROMALTSTACK pops an item off of the alternative stack and
|
||||||
// pushes it on top of the evaluation stack.
|
// pushes it on top of the evaluation stack.
|
||||||
// Returns an error if the evaluation stack is empty.
|
// Returns an error if the evaluation stack is empty.
|
||||||
|
@ -63,6 +131,19 @@ func FROMALTSTACK(op stack.Instruction, ctx *stack.Context, istack *stack.Invoca
|
||||||
return NONE, nil
|
return NONE, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DROP removes the the top stack item.
|
||||||
|
// Returns error if the operation Pop cannot
|
||||||
|
// be performed.
|
||||||
|
func DROP(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||||
|
|
||||||
|
_, err := ctx.Estack.Pop()
|
||||||
|
if err != nil {
|
||||||
|
return FAULT, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NONE, nil
|
||||||
|
}
|
||||||
|
|
||||||
// XDROP pops an integer n off of the stack and
|
// XDROP pops an integer n off of the stack and
|
||||||
// removes the n-item from the stack starting from
|
// removes the n-item from the stack starting from
|
||||||
// the top of the stack.
|
// the top of the stack.
|
||||||
|
|
|
@ -8,6 +8,133 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestXswapOp(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.
|
||||||
|
// we will swap the n-item which
|
||||||
|
// is located in position len(stack)-n-1 (= 3-2-1= 0)
|
||||||
|
// with the top stack item.
|
||||||
|
// The final stack will be [c,b,a]
|
||||||
|
v.executeOp(stack.XSWAP, ctx)
|
||||||
|
|
||||||
|
// Stack should have three items
|
||||||
|
assert.Equal(t, 3, ctx.Estack.Len())
|
||||||
|
|
||||||
|
itemA, err := ctx.Estack.PopInt()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
itemB, err := ctx.Estack.PopInt()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
itemC, err := ctx.Estack.PopInt()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, int64(3), itemA.Value().Int64())
|
||||||
|
assert.Equal(t, int64(6), itemB.Value().Int64())
|
||||||
|
assert.Equal(t, int64(9), itemC.Value().Int64())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestXTuckOp(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 insert the top stack item c
|
||||||
|
// to the position len(stack)-n (= 3-2 = 1)
|
||||||
|
// of the stack.The final stack will be [a,c,b,c]
|
||||||
|
v.executeOp(stack.XTUCK, ctx)
|
||||||
|
|
||||||
|
// Stack should have four items
|
||||||
|
assert.Equal(t, 4, ctx.Estack.Len())
|
||||||
|
|
||||||
|
// c
|
||||||
|
item0, err := ctx.Estack.PopInt()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
// b
|
||||||
|
item1, err := ctx.Estack.PopInt()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
// c
|
||||||
|
item2, err := ctx.Estack.PopInt()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
// a
|
||||||
|
item3, err := ctx.Estack.PopInt()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, int64(9), item0.Value().Int64())
|
||||||
|
assert.Equal(t, int64(6), item1.Value().Int64())
|
||||||
|
assert.Equal(t, int64(9), item2.Value().Int64())
|
||||||
|
assert.Equal(t, int64(3), item3.Value().Int64())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestXDepthOp(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)
|
||||||
|
|
||||||
|
// push integer whose value is len(stack) (2)
|
||||||
|
// on top of the stack
|
||||||
|
v.executeOp(stack.DEPTH, ctx)
|
||||||
|
|
||||||
|
// Stack should have three items
|
||||||
|
assert.Equal(t, 3, ctx.Estack.Len())
|
||||||
|
|
||||||
|
// len(stack)
|
||||||
|
item0, err := ctx.Estack.PopInt()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
// b
|
||||||
|
item1, err := ctx.Estack.PopInt()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
// a
|
||||||
|
item2, err := ctx.Estack.PopInt()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, int64(2), item0.Value().Int64())
|
||||||
|
assert.Equal(t, int64(6), item1.Value().Int64())
|
||||||
|
assert.Equal(t, int64(3), item2.Value().Int64())
|
||||||
|
}
|
||||||
|
|
||||||
func TestDupFromAltStackOp(t *testing.T) {
|
func TestDupFromAltStackOp(t *testing.T) {
|
||||||
|
|
||||||
v := VM{}
|
v := VM{}
|
||||||
|
|
Loading…
Reference in a new issue