Merge pull request #273 from dauTT/dauTT/vm-DUPFROMALTSTACK-TOALTSTACK-FROMALTSTACK-XDROP-opcode

VM: Implement DUPFROMALTSTACK, TOALTSTACK, FROMALTSTACK, XDROP opcodes

Closes #272, merging as per #283 discussion.
This commit is contained in:
Roman Khimov 2019-08-12 12:49:34 +03:00 committed by GitHub
commit 6c3634cdb8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 257 additions and 43 deletions

View file

@ -158,3 +158,19 @@ func (ras *RandomAccess) PopBoolean() (*Boolean, error) {
}
return item.Boolean()
}
// 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
}

View file

@ -5,49 +5,53 @@ 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.JMPIFNOT: JMPIFNOT,
stack.JMPIF: JMPIF,
stack.JMP: JMP,
stack.NOP: NOP,
stack.HASH256: HASH256,
stack.HASH160: HASH160,
stack.SHA256: SHA256,
stack.SHA1: SHA1,
stack.XOR: Xor,
stack.OR: Or,
stack.AND: And,
stack.INVERT: Invert,
stack.MIN: Min,
stack.MAX: Max,
stack.WITHIN: Within,
stack.NUMEQUAL: NumEqual,
stack.NUMNOTEQUAL: NumNotEqual,
stack.BOOLAND: BoolAnd,
stack.BOOLOR: BoolOr,
stack.LT: Lt,
stack.LTE: Lte,
stack.GT: Gt,
stack.GTE: Gte,
stack.SHR: Shr,
stack.SHL: Shl,
stack.INC: Inc,
stack.DEC: Dec,
stack.DIV: Div,
stack.MOD: Mod,
stack.NZ: Nz,
stack.MUL: Mul,
stack.ABS: Abs,
stack.NOT: Not,
stack.SIGN: Sign,
stack.NEGATE: Negate,
stack.ADD: Add,
stack.SUB: Sub,
stack.PUSHBYTES1: PushNBytes,
stack.PUSHBYTES75: PushNBytes,
stack.RET: RET,
stack.EQUAL: EQUAL,
stack.THROWIFNOT: THROWIFNOT,
stack.THROW: THROW,
stack.XDROP: XDROP,
stack.FROMALTSTACK: FROMALTSTACK,
stack.TOALTSTACK: TOALTSTACK,
stack.DUPFROMALTSTACK: DUPFROMALTSTACK,
stack.JMPIFNOT: JMPIFNOT,
stack.JMPIF: JMPIF,
stack.JMP: JMP,
stack.NOP: NOP,
stack.HASH256: HASH256,
stack.HASH160: HASH160,
stack.SHA256: SHA256,
stack.SHA1: SHA1,
stack.XOR: Xor,
stack.OR: Or,
stack.AND: And,
stack.INVERT: Invert,
stack.MIN: Min,
stack.MAX: Max,
stack.WITHIN: Within,
stack.NUMEQUAL: NumEqual,
stack.NUMNOTEQUAL: NumNotEqual,
stack.BOOLAND: BoolAnd,
stack.BOOLOR: BoolOr,
stack.LT: Lt,
stack.LTE: Lte,
stack.GT: Gt,
stack.GTE: Gte,
stack.SHR: Shr,
stack.SHL: Shl,
stack.INC: Inc,
stack.DEC: Dec,
stack.DIV: Div,
stack.MOD: Mod,
stack.NZ: Nz,
stack.MUL: Mul,
stack.ABS: Abs,
stack.NOT: Not,
stack.SIGN: Sign,
stack.NEGATE: Negate,
stack.ADD: Add,
stack.SUB: Sub,
stack.PUSHBYTES1: PushNBytes,
stack.PUSHBYTES75: PushNBytes,
stack.RET: RET,
stack.EQUAL: EQUAL,
stack.THROWIFNOT: THROWIFNOT,
stack.THROW: THROW,
}
func init() {

View file

@ -17,3 +17,66 @@ func PushNBytes(op stack.Instruction, ctx *stack.Context, istack *stack.Invocati
ctx.Estack.Push(ba)
return NONE, nil
}
// DUPFROMALTSTACK duplicates the item on top of alternative stack and
// puts it on top of evaluation stack.
// Returns an error if the alt stack is empty.
func DUPFROMALTSTACK(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
item, err := ctx.Astack.Peek(0)
if err != nil {
return FAULT, err
}
ctx.Estack.Push(item)
return NONE, nil
}
// TOALTSTACK pops an item off of the evaluation stack and
// pushes it on top of the alternative stack.
// Returns an error if the alternative stack is empty.
func TOALTSTACK(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
item, err := ctx.Estack.Pop()
if err != nil {
return FAULT, err
}
ctx.Astack.Push(item)
return NONE, nil
}
// FROMALTSTACK pops an item off of the alternative stack and
// pushes it on top of the evaluation stack.
// Returns an error if the evaluation stack is empty.
func FROMALTSTACK(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
item, err := ctx.Astack.Pop()
if err != nil {
return FAULT, err
}
ctx.Estack.Push(item)
return NONE, nil
}
// XDROP pops an integer n off of the stack and
// removes the n-item from the stack starting from
// the top of the stack.
func XDROP(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
}
ctx.Estack.Remove(uint16(n.Value().Uint64()))
if err != nil {
return FAULT, err
}
return NONE, nil
}

View file

@ -0,0 +1,131 @@
package vm
import (
"math/big"
"testing"
"github.com/CityOfZion/neo-go/pkg/vm/stack"
"github.com/stretchr/testify/assert"
)
func TestDupFromAltStackOp(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(10))
assert.Nil(t, err)
b, err := stack.NewInt(big.NewInt(2))
assert.Nil(t, err)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a)
ctx.Astack.Push(b)
v.executeOp(stack.DUPFROMALTSTACK, ctx)
assert.Equal(t, 1, ctx.Astack.Len())
assert.Equal(t, 2, ctx.Estack.Len())
itemE, err := ctx.Estack.PopInt()
assert.Nil(t, err)
itemA, err := ctx.Astack.PopInt()
assert.Nil(t, err)
assert.Equal(t, int64(2), itemE.Value().Int64())
assert.Equal(t, int64(2), itemA.Value().Int64())
}
func TestToAltStackOp(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(10))
assert.Nil(t, err)
b, err := stack.NewInt(big.NewInt(2))
assert.Nil(t, err)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a)
ctx.Astack.Push(b)
v.executeOp(stack.TOALTSTACK, ctx)
assert.Equal(t, 2, ctx.Astack.Len())
assert.Equal(t, 0, ctx.Estack.Len())
item, err := ctx.Astack.PopInt()
assert.Nil(t, err)
assert.Equal(t, int64(10), item.Value().Int64())
}
func TestFromAltStackOp(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(10))
assert.Nil(t, err)
b, err := stack.NewInt(big.NewInt(2))
assert.Nil(t, err)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a)
ctx.Astack.Push(b)
v.executeOp(stack.FROMALTSTACK, ctx)
assert.Equal(t, 0, ctx.Astack.Len())
assert.Equal(t, 2, ctx.Estack.Len())
item, err := ctx.Estack.PopInt()
assert.Nil(t, err)
assert.Equal(t, int64(2), item.Value().Int64())
}
func TestXDropOp(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)
ctx.Estack.Push(b)
ctx.Estack.Push(c)
ctx.Estack.Push(d)
// pop n (= d = 2) from the stack.
// we will remove the n-item which
// is located at position
// len(stack)-n-1 = 3-2-1 = 0.
// Therefore a is removed from the stack.
// Only b, c remain on the stack.
v.executeOp(stack.XDROP, ctx)
assert.Equal(t, 2, ctx.Estack.Len())
itemC, err := ctx.Estack.PopInt()
assert.Nil(t, err)
itemB, err := ctx.Estack.PopInt()
assert.Nil(t, err)
assert.Equal(t, int64(6), itemB.Value().Int64())
assert.Equal(t, int64(9), itemC.Value().Int64())
}