Merge pull request #277 from dauTT/dauTT/vm-DUP-NIP-OVER-PICK-opcode
VM: Implement DUP, NIP, OVER, PICK opcode Closes #276, merging as per #283 discussion.
This commit is contained in:
commit
f2f9a37d3f
3 changed files with 208 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)
|
||||
|
||||
var opFunc = map[stack.Instruction]stackInfo{
|
||||
stack.PICK: PICK,
|
||||
stack.OVER: OVER,
|
||||
stack.NIP: NIP,
|
||||
stack.DUP: DUP,
|
||||
stack.DROP: DROP,
|
||||
stack.DEPTH: DEPTH,
|
||||
stack.XTUCK: XTUCK,
|
||||
|
|
|
@ -20,6 +20,19 @@ func PushNBytes(op stack.Instruction, ctx *stack.Context, istack *stack.Invocati
|
|||
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) {
|
||||
|
||||
item, err := ctx.Estack.Peek(0)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
ctx.Estack.Push(item)
|
||||
|
||||
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.
|
||||
|
@ -64,6 +77,34 @@ func DUPFROMALTSTACK(op stack.Instruction, ctx *stack.Context, istack *stack.Inv
|
|||
return NONE, nil
|
||||
}
|
||||
|
||||
// NIP removes the second top stack item.
|
||||
// Returns error if the stack item contains
|
||||
// only one element.
|
||||
func NIP(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
_, err := ctx.Estack.Remove(1)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// OVER copies the second-to-top stack item onto the top.
|
||||
// Returns an error if the stack item contains
|
||||
// only one element.
|
||||
func OVER(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
item, err := ctx.Estack.Peek(1)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
ctx.Estack.Push(item)
|
||||
|
||||
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) {
|
||||
|
@ -131,6 +172,28 @@ func FROMALTSTACK(op stack.Instruction, ctx *stack.Context, istack *stack.Invoca
|
|||
return NONE, nil
|
||||
}
|
||||
|
||||
// PICK pops an integer n off of the stack and
|
||||
// copies 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 PICK(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
|
||||
}
|
||||
|
||||
ctx.Estack.Push(nItem)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// DROP removes the the top stack item.
|
||||
// Returns error if the operation Pop cannot
|
||||
// be performed.
|
||||
|
|
|
@ -8,6 +8,147 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDupOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a, err := stack.NewInt(big.NewInt(3))
|
||||
assert.Nil(t, err)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(a)
|
||||
|
||||
v.executeOp(stack.DUP, ctx)
|
||||
|
||||
// Stack should have two items
|
||||
assert.Equal(t, 2, ctx.Estack.Len())
|
||||
|
||||
item1, err := ctx.Estack.PopInt()
|
||||
assert.Nil(t, err)
|
||||
|
||||
item2, err := ctx.Estack.PopInt()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(3), item1.Value().Int64())
|
||||
assert.Equal(t, int64(3), item2.Value().Int64())
|
||||
|
||||
}
|
||||
|
||||
func TestNipOp(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)
|
||||
|
||||
v.executeOp(stack.NIP, ctx)
|
||||
|
||||
// Stack should have two items
|
||||
assert.Equal(t, 2, ctx.Estack.Len())
|
||||
|
||||
itemC, err := ctx.Estack.PopInt()
|
||||
assert.Nil(t, err)
|
||||
|
||||
itemA, err := ctx.Estack.PopInt()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(3), itemA.Value().Int64())
|
||||
assert.Equal(t, int64(9), itemC.Value().Int64())
|
||||
|
||||
}
|
||||
|
||||
func TestOverOp(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)
|
||||
|
||||
// OVER copies the second top stack item a
|
||||
// onto the top stack item b.
|
||||
// the new stack will be [a,b,a].
|
||||
v.executeOp(stack.OVER, 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)
|
||||
|
||||
itemA2, 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(3), itemA2.Value().Int64())
|
||||
|
||||
}
|
||||
|
||||
func TestPickOp(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 copy the n-item which
|
||||
// has index len(stack)-n-1 (= 3-2-1= 0)
|
||||
// onto the top stack item.
|
||||
// The final stack will be [a,b,c,a]
|
||||
v.executeOp(stack.PICK, ctx)
|
||||
|
||||
// Stack should have four items
|
||||
assert.Equal(t, 4, 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)
|
||||
|
||||
itemA2, 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())
|
||||
assert.Equal(t, int64(3), itemA2.Value().Int64())
|
||||
|
||||
}
|
||||
func TestXswapOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
|
Loading…
Reference in a new issue