forked from TrueCloudLab/neoneo-go
_pkg.dev: drop vm
This is a nice VM, it has an interesting stack items model, better separation of elements from stack implementation, simpler stack implementation and a bit nicer opcode implementation mechanism. At the same time it lacks so many features and is so differing from our current VM (that is closely tied to the compiler) that it makes its code very hard to reuse for master's VM improvement. Also, some differences are non-obvious to judge in terms of better or worse. Stack item model seems to be more extensible, but at the same time we know very well what kind of stack items we have and this doesn't change a lot. Slice-based stack is simple but it's hard to say which one would perform better for real-world smart contracts (it has different tradeoffs in operations complexity). Based on that, I'm dropping it. Some ideas will be reused during VM refactoring, but no more than that. Refs. #307.
This commit is contained in:
parent
3f40334979
commit
d4d1c79546
33 changed files with 0 additions and 4991 deletions
|
@ -1,202 +0,0 @@
|
|||
package stack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// Int represents an integer on the stack
|
||||
type Int struct {
|
||||
*abstractItem
|
||||
val *big.Int
|
||||
}
|
||||
|
||||
// NewInt will convert a big integer into
|
||||
// a StackInteger
|
||||
func NewInt(val *big.Int) (*Int, error) {
|
||||
return &Int{
|
||||
abstractItem: &abstractItem{},
|
||||
val: val,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Equal will check if two integers hold equal value
|
||||
func (i *Int) Equal(s *Int) bool {
|
||||
return i.val.Cmp(s.val) == 0
|
||||
}
|
||||
|
||||
// Add will add two stackIntegers together
|
||||
func (i *Int) Add(s *Int) (*Int, error) {
|
||||
return &Int{
|
||||
val: new(big.Int).Add(i.val, s.val),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Sub will subtract two stackIntegers together
|
||||
func (i *Int) Sub(s *Int) (*Int, error) {
|
||||
return &Int{
|
||||
val: new(big.Int).Sub(i.val, s.val),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Mul will multiply two stackIntegers together
|
||||
func (i *Int) Mul(s *Int) (*Int, error) {
|
||||
return &Int{
|
||||
val: new(big.Int).Mul(i.val, s.val),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Div will divide one stackInteger by an other.
|
||||
func (i *Int) Div(s *Int) (*Int, error) {
|
||||
return &Int{
|
||||
val: new(big.Int).Div(i.val, s.val),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Mod will take the mod of two stackIntegers together
|
||||
func (i *Int) Mod(s *Int) (*Int, error) {
|
||||
return &Int{
|
||||
val: new(big.Int).Mod(i.val, s.val),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Rsh will shift the integer b to the right by `n` bits
|
||||
func (i *Int) Rsh(n *Int) (*Int, error) {
|
||||
return &Int{
|
||||
val: new(big.Int).Rsh(i.val, uint(n.val.Int64())),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Lsh will shift the integer b to the left by `n` bits
|
||||
func (i *Int) Lsh(n *Int) (*Int, error) {
|
||||
return &Int{
|
||||
val: new(big.Int).Lsh(i.val, uint(n.val.Int64())),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Integer will overwrite the default implementation
|
||||
// to allow go to cast this item as an integer.
|
||||
func (i *Int) Integer() (*Int, error) {
|
||||
return i, nil
|
||||
}
|
||||
|
||||
// ByteArray override the default ByteArray method
|
||||
// to convert a Integer into a byte Array
|
||||
func (i *Int) ByteArray() (*ByteArray, error) {
|
||||
b := i.val.Bytes()
|
||||
dest := reverse(b)
|
||||
return NewByteArray(dest), nil
|
||||
}
|
||||
|
||||
//Boolean override the default Boolean method
|
||||
// to convert an Integer into a Boolean StackItem
|
||||
func (i *Int) Boolean() (*Boolean, error) {
|
||||
boolean := (i.val.Int64() != 0)
|
||||
return NewBoolean(boolean), nil
|
||||
}
|
||||
|
||||
//Value returns the underlying big.Int
|
||||
func (i *Int) Value() *big.Int {
|
||||
return i.val
|
||||
}
|
||||
|
||||
// Abs returns a stack integer whose underlying value is
|
||||
// the absolute value of the original stack integer.
|
||||
func (i *Int) Abs() (*Int, error) {
|
||||
a := big.NewInt(0).Abs(i.Value())
|
||||
b, err := NewInt(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Lte returns a bool value from the comparison of two integers, a and b.
|
||||
// value is true if a <= b.
|
||||
// value is false if a > b.
|
||||
func (i *Int) Lte(s *Int) bool {
|
||||
return i.Value().Cmp(s.Value()) != 1
|
||||
}
|
||||
|
||||
// Gte returns a bool value from the comparison of two integers, a and b.
|
||||
// value is true if a >= b.
|
||||
// value is false if a < b.
|
||||
func (i *Int) Gte(s *Int) bool {
|
||||
return i.Value().Cmp(s.Value()) != -1
|
||||
}
|
||||
|
||||
// Lt returns a bool value from the comparison of two integers, a and b.
|
||||
// value is true if a < b.
|
||||
// value is false if a >= b.
|
||||
func (i *Int) Lt(s *Int) bool {
|
||||
return i.Value().Cmp(s.Value()) == -1
|
||||
}
|
||||
|
||||
// Gt returns a bool value from the comparison of two integers, a and b.
|
||||
// value is true if a > b.
|
||||
// value is false if a <= b.
|
||||
func (i *Int) Gt(s *Int) bool {
|
||||
return i.Value().Cmp(s.Value()) == 1
|
||||
}
|
||||
|
||||
// Invert returns an Integer whose underlying value is the bitwise complement
|
||||
// of the original value.
|
||||
func (i *Int) Invert() (*Int, error) {
|
||||
res := new(big.Int).Not(i.Value())
|
||||
return NewInt(res)
|
||||
}
|
||||
|
||||
// And returns an Integer whose underlying value is the result of the
|
||||
// application of the bitwise AND operator to the two original integers'
|
||||
// values.
|
||||
func (i *Int) And(s *Int) (*Int, error) {
|
||||
res := new(big.Int).And(i.Value(), s.Value())
|
||||
return NewInt(res)
|
||||
}
|
||||
|
||||
// Or returns an Integer whose underlying value is the result of the
|
||||
// application of the bitwise OR operator to the two original integers'
|
||||
// values.
|
||||
func (i *Int) Or(s *Int) (*Int, error) {
|
||||
res := new(big.Int).Or(i.Value(), s.Value())
|
||||
return NewInt(res)
|
||||
}
|
||||
|
||||
// Xor returns an Integer whose underlying value is the result of the
|
||||
// application of the bitwise XOR operator to the two original integers'
|
||||
// values.
|
||||
func (i *Int) Xor(s *Int) (*Int, error) {
|
||||
res := new(big.Int).Xor(i.Value(), s.Value())
|
||||
return NewInt(res)
|
||||
}
|
||||
|
||||
// Hash overrides the default abstract hash method.
|
||||
func (i *Int) Hash() (string, error) {
|
||||
data := fmt.Sprintf("%T %v", i, i.Value())
|
||||
return KeyGenerator([]byte(data))
|
||||
}
|
||||
|
||||
// Min returns the mininum between two integers.
|
||||
func Min(a *Int, b *Int) *Int {
|
||||
if a.Lte(b) {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Max returns the maximun between two integers.
|
||||
func Max(a *Int, b *Int) *Int {
|
||||
if a.Gte(b) {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Within returns a bool whose value is true
|
||||
// iff the value of the integer i is within the specified
|
||||
// range [a,b) (left-inclusive).
|
||||
func (i *Int) Within(a *Int, b *Int) bool {
|
||||
// i >= a && i < b
|
||||
return i.Gte(a) && i.Lt(b)
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
## VM - Stack
|
||||
|
||||
- How do i implement a new StackItem?
|
||||
|
||||
Answer: You add it's type to the Item interface, then you implement the default return method on the abstract stack item, this should be the behaviour of the stack item, if it is not the new type. Then you embed the abstract item in the new struct and override the method.
|
||||
|
||||
For example, If I wanted to add a new type called `HashMap`
|
||||
|
||||
type Item interface{
|
||||
HashMap()(*HashMap, error)
|
||||
}
|
||||
|
||||
func (a *abstractItem) HashMap() (*HashMap, error) {
|
||||
return nil, errors.New(This stack item is not a hashmap)
|
||||
}
|
||||
|
||||
type HashMap struct {
|
||||
*abstractItem
|
||||
// Variables needed for hashmap
|
||||
}
|
||||
|
||||
func (h *HashMap) HashMap()(*HashMap, error) {
|
||||
// logic to override default behaviour
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package stack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Array represents an Array of stackItems on the stack
|
||||
type Array struct {
|
||||
*abstractItem
|
||||
val []Item
|
||||
}
|
||||
|
||||
// Array overrides the default implementation
|
||||
// by the abstractItem, returning an Array struct
|
||||
func (a *Array) Array() (*Array, error) {
|
||||
return a, nil
|
||||
}
|
||||
|
||||
//Value returns the underlying Array's value
|
||||
func (a *Array) Value() []Item {
|
||||
return a.val
|
||||
}
|
||||
|
||||
// NewArray returns a new Array.
|
||||
func NewArray(val []Item) (*Array, error) {
|
||||
return &Array{
|
||||
&abstractItem{},
|
||||
val,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Hash overrides the default abstract hash method.
|
||||
func (a *Array) Hash() (string, error) {
|
||||
data := fmt.Sprintf("%T %v", a, a.Value())
|
||||
return KeyGenerator([]byte(data))
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package stack
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
// it's a stub at the moment, but will need it anyway
|
||||
// "github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestArray(t *testing.T) {
|
||||
var a Item = testMakeStackInt(t, 3)
|
||||
var b Item = testMakeStackInt(t, 6)
|
||||
var c Item = testMakeStackInt(t, 9)
|
||||
var ta = testMakeArray(t, []Item{a, b, c})
|
||||
_ = ta
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
package stack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Boolean represents a boolean value on the stack
|
||||
type Boolean struct {
|
||||
*abstractItem
|
||||
val bool
|
||||
}
|
||||
|
||||
//NewBoolean returns a new boolean stack item
|
||||
func NewBoolean(val bool) *Boolean {
|
||||
return &Boolean{
|
||||
&abstractItem{},
|
||||
val,
|
||||
}
|
||||
}
|
||||
|
||||
// Boolean overrides the default implementation
|
||||
// by the abstractItem, returning a Boolean struct
|
||||
func (b *Boolean) Boolean() (*Boolean, error) {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Value returns the underlying boolean value
|
||||
func (b *Boolean) Value() bool {
|
||||
return b.val
|
||||
}
|
||||
|
||||
// Not returns a Boolean whose underlying value is flipped.
|
||||
// If the value is True, it is flipped to False and viceversa
|
||||
func (b *Boolean) Not() *Boolean {
|
||||
return NewBoolean(!b.Value())
|
||||
}
|
||||
|
||||
// And returns a Boolean whose underlying value is obtained
|
||||
// by applying the && operator to two Booleans' values.
|
||||
func (b *Boolean) And(a *Boolean) *Boolean {
|
||||
c := b.Value() && a.Value()
|
||||
return NewBoolean(c)
|
||||
}
|
||||
|
||||
// Or returns a Boolean whose underlying value is obtained
|
||||
// by applying the || operator to two Booleans' values.
|
||||
func (b *Boolean) Or(a *Boolean) *Boolean {
|
||||
c := b.Value() || a.Value()
|
||||
return NewBoolean(c)
|
||||
}
|
||||
|
||||
// Hash overrides the default abstract hash method.
|
||||
func (b *Boolean) Hash() (string, error) {
|
||||
data := fmt.Sprintf("%T %v", b, b.Value())
|
||||
return KeyGenerator([]byte(data))
|
||||
}
|
|
@ -1,177 +0,0 @@
|
|||
package stack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/wire/util"
|
||||
)
|
||||
|
||||
// Builder follows the builder pattern and will be used to build scripts
|
||||
type Builder struct {
|
||||
w *bytes.Buffer
|
||||
err error
|
||||
}
|
||||
|
||||
// NewBuilder returns a new builder object
|
||||
func NewBuilder() *Builder {
|
||||
return &Builder{
|
||||
w: &bytes.Buffer{},
|
||||
err: nil,
|
||||
}
|
||||
}
|
||||
|
||||
// Bytes returns the byte representation of the built buffer
|
||||
func (br *Builder) Bytes() []byte {
|
||||
return br.w.Bytes()
|
||||
}
|
||||
|
||||
// Emit a VM Opcode with data to the given buffer.
|
||||
func (br *Builder) Emit(op Instruction, b []byte) *Builder {
|
||||
if br.err != nil {
|
||||
return br
|
||||
}
|
||||
br.err = br.w.WriteByte(byte(op))
|
||||
_, br.err = br.w.Write(b)
|
||||
return br
|
||||
}
|
||||
|
||||
// EmitOpcode emits a single VM Opcode the given buffer.
|
||||
func (br *Builder) EmitOpcode(op Instruction) *Builder {
|
||||
if br.err != nil {
|
||||
return br
|
||||
}
|
||||
br.err = br.w.WriteByte(byte(op))
|
||||
return br
|
||||
}
|
||||
|
||||
// EmitBool emits a bool type the given buffer.
|
||||
func (br *Builder) EmitBool(ok bool) *Builder {
|
||||
if br.err != nil {
|
||||
return br
|
||||
}
|
||||
op := PUSHT
|
||||
if !ok {
|
||||
op = PUSHF
|
||||
}
|
||||
return br.EmitOpcode(op)
|
||||
}
|
||||
|
||||
// EmitInt emits a int type to the given buffer.
|
||||
func (br *Builder) EmitInt(i int64) *Builder {
|
||||
if br.err != nil {
|
||||
return br
|
||||
}
|
||||
if i == -1 {
|
||||
return br.EmitOpcode(PUSHM1)
|
||||
}
|
||||
if i == 0 {
|
||||
return br.EmitOpcode(PUSHF)
|
||||
}
|
||||
if i > 0 && i < 16 {
|
||||
val := Instruction(int(PUSH1) - 1 + int(i))
|
||||
return br.EmitOpcode(val)
|
||||
}
|
||||
|
||||
bInt := big.NewInt(i)
|
||||
val := reverse(bInt.Bytes())
|
||||
return br.EmitBytes(val)
|
||||
}
|
||||
|
||||
// EmitString emits a string to the given buffer.
|
||||
func (br *Builder) EmitString(s string) *Builder {
|
||||
if br.err != nil {
|
||||
return br
|
||||
}
|
||||
return br.EmitBytes([]byte(s))
|
||||
}
|
||||
|
||||
// EmitBytes emits a byte array to the given buffer.
|
||||
func (br *Builder) EmitBytes(b []byte) *Builder {
|
||||
if br.err != nil {
|
||||
return br
|
||||
}
|
||||
var (
|
||||
n = len(b)
|
||||
)
|
||||
|
||||
if n <= int(PUSHBYTES75) {
|
||||
return br.Emit(Instruction(n), b)
|
||||
} else if n < 0x100 {
|
||||
br.Emit(PUSHDATA1, []byte{byte(n)})
|
||||
} else if n < 0x10000 {
|
||||
buf := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(buf, uint16(n))
|
||||
br.Emit(PUSHDATA2, buf)
|
||||
} else {
|
||||
buf := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(buf, uint32(n))
|
||||
br.Emit(PUSHDATA4, buf)
|
||||
}
|
||||
_, br.err = br.w.Write(b)
|
||||
return br
|
||||
}
|
||||
|
||||
// EmitSyscall emits the syscall API to the given buffer.
|
||||
// Syscall API string cannot be 0.
|
||||
func (br *Builder) EmitSyscall(api string) *Builder {
|
||||
if br.err != nil {
|
||||
return br
|
||||
}
|
||||
if len(api) == 0 {
|
||||
br.err = errors.New("syscall api cannot be of length 0")
|
||||
}
|
||||
buf := make([]byte, len(api)+1)
|
||||
buf[0] = byte(len(api))
|
||||
copy(buf[1:], []byte(api))
|
||||
return br.Emit(SYSCALL, buf)
|
||||
}
|
||||
|
||||
// EmitCall emits a call Opcode with label to the given buffer.
|
||||
func (br *Builder) EmitCall(op Instruction, label int16) *Builder {
|
||||
return br.EmitJmp(op, label)
|
||||
}
|
||||
|
||||
// EmitJmp emits a jump Opcode along with label to the given buffer.
|
||||
func (br *Builder) EmitJmp(op Instruction, label int16) *Builder {
|
||||
if !isOpcodeJmp(op) {
|
||||
br.err = fmt.Errorf("opcode %d is not a jump or call type", op)
|
||||
}
|
||||
buf := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(buf, uint16(label))
|
||||
return br.Emit(op, buf)
|
||||
}
|
||||
|
||||
// EmitAppCall emits an appcall, if tailCall is true, tailCall opcode will be
|
||||
// emitted instead.
|
||||
func (br *Builder) EmitAppCall(scriptHash util.Uint160, tailCall bool) *Builder {
|
||||
op := APPCALL
|
||||
if tailCall {
|
||||
op = TAILCALL
|
||||
}
|
||||
return br.Emit(op, scriptHash.Bytes())
|
||||
}
|
||||
|
||||
// EmitAppCallWithOperationAndData emits an appcall with the given operation and data.
|
||||
func (br *Builder) EmitAppCallWithOperationAndData(w *bytes.Buffer, scriptHash util.Uint160, operation string, data []byte) *Builder {
|
||||
br.EmitBytes(data)
|
||||
br.EmitString(operation)
|
||||
return br.EmitAppCall(scriptHash, false)
|
||||
}
|
||||
|
||||
// EmitAppCallWithOperation emits an appcall with the given operation.
|
||||
func (br *Builder) EmitAppCallWithOperation(scriptHash util.Uint160, operation string) *Builder {
|
||||
br.EmitBool(false)
|
||||
br.EmitString(operation)
|
||||
return br.EmitAppCall(scriptHash, false)
|
||||
}
|
||||
|
||||
func isOpcodeJmp(op Instruction) bool {
|
||||
if op == JMP || op == JMPIFNOT || op == JMPIF || op == CALL {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
package stack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// ByteArray represents a slice of bytes on the stack
|
||||
type ByteArray struct {
|
||||
*abstractItem
|
||||
val []byte
|
||||
}
|
||||
|
||||
//NewByteArray returns a ByteArray stack item
|
||||
// given a byte slice
|
||||
func NewByteArray(val []byte) *ByteArray {
|
||||
return &ByteArray{
|
||||
&abstractItem{},
|
||||
val,
|
||||
}
|
||||
}
|
||||
|
||||
//ByteArray overrides the default abstractItem Bytes array method
|
||||
func (ba *ByteArray) ByteArray() (*ByteArray, error) {
|
||||
return ba, nil
|
||||
}
|
||||
|
||||
//Equals returns true, if two bytearrays are equal
|
||||
func (ba *ByteArray) Equals(other *ByteArray) *Boolean {
|
||||
// If either are nil, return false
|
||||
if ba == nil || other == nil {
|
||||
return NewBoolean(false)
|
||||
}
|
||||
return NewBoolean(bytes.Equal(ba.val, other.val))
|
||||
}
|
||||
|
||||
//Integer overrides the default Integer method to convert an
|
||||
// ByteArray Into an integer
|
||||
func (ba *ByteArray) Integer() (*Int, error) {
|
||||
dest := reverse(ba.val)
|
||||
integerVal := new(big.Int).SetBytes(dest)
|
||||
return NewInt(integerVal)
|
||||
|
||||
}
|
||||
|
||||
// Boolean will convert a byte array into a boolean stack item
|
||||
func (ba *ByteArray) Boolean() (*Boolean, error) {
|
||||
boolean, err := strconv.ParseBool(string(ba.val))
|
||||
if err != nil {
|
||||
return nil, errors.New("cannot convert byte array to a boolean")
|
||||
}
|
||||
return NewBoolean(boolean), nil
|
||||
}
|
||||
|
||||
// XXX: move this into a pkg/util/slice folder
|
||||
// Go mod not working
|
||||
func reverse(b []byte) []byte {
|
||||
if len(b) < 2 {
|
||||
return b
|
||||
}
|
||||
|
||||
dest := make([]byte, len(b))
|
||||
|
||||
for i, j := 0, len(b)-1; i < j+1; i, j = i+1, j-1 {
|
||||
dest[i], dest[j] = b[j], b[i]
|
||||
}
|
||||
|
||||
return dest
|
||||
}
|
||||
|
||||
//Value returns the underlying ByteArray's value.
|
||||
func (ba *ByteArray) Value() []byte {
|
||||
return ba.val
|
||||
}
|
||||
|
||||
// Hash overrides the default abstract hash method.
|
||||
func (ba *ByteArray) Hash() (string, error) {
|
||||
data := fmt.Sprintf("%T %v", ba, ba.Value())
|
||||
return KeyGenerator([]byte(data))
|
||||
}
|
|
@ -1,174 +0,0 @@
|
|||
package stack
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Context represent the current execution context of the VM.
|
||||
// context will be treated as stack item and placed onto the invocation stack
|
||||
type Context struct {
|
||||
*abstractItem
|
||||
|
||||
// Instruction pointer.
|
||||
ip int
|
||||
|
||||
// The raw program script.
|
||||
prog []byte
|
||||
|
||||
// Breakpoints
|
||||
breakPoints []int
|
||||
|
||||
// Evaluation Stack
|
||||
Estack RandomAccess
|
||||
|
||||
// Alternative Stack
|
||||
Astack RandomAccess
|
||||
}
|
||||
|
||||
// NewContext return a new Context object.
|
||||
func NewContext(b []byte) *Context {
|
||||
return &Context{
|
||||
abstractItem: &abstractItem{},
|
||||
ip: -1,
|
||||
prog: b,
|
||||
breakPoints: []int{},
|
||||
}
|
||||
}
|
||||
|
||||
// Context overrides the default implementation
|
||||
// to return a context item
|
||||
func (c *Context) Context() (*Context, error) {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Next return the next instruction to execute.
|
||||
func (c *Context) Next() (Instruction, error) {
|
||||
c.ip++
|
||||
if c.ip >= len(c.prog) {
|
||||
return RET, errors.New("program pointer is more than the length of program. Returning RET OPCODE")
|
||||
}
|
||||
return Instruction(c.prog[c.ip]), nil
|
||||
}
|
||||
|
||||
// IP returns the absolute instruction without taking 0 into account.
|
||||
// If that program starts the ip = 0 but IP() will return 1, cause its
|
||||
// the first instruction.
|
||||
func (c *Context) IP() int {
|
||||
return c.ip + 1
|
||||
}
|
||||
|
||||
// LenInstr returns the number of instructions loaded.
|
||||
func (c *Context) LenInstr() int {
|
||||
return len(c.prog)
|
||||
}
|
||||
|
||||
// CurrInstr returns the current instruction and opcode.
|
||||
func (c *Context) CurrInstr() (int, Instruction) {
|
||||
if c.ip < 0 {
|
||||
return c.ip, Instruction(0x00)
|
||||
}
|
||||
return c.ip, Instruction(c.prog[c.ip])
|
||||
}
|
||||
|
||||
// Copy returns an new exact copy of c.
|
||||
func (c *Context) Copy() *Context {
|
||||
return &Context{
|
||||
ip: c.ip,
|
||||
prog: c.prog,
|
||||
breakPoints: c.breakPoints,
|
||||
}
|
||||
}
|
||||
|
||||
// Program returns the loaded program.
|
||||
func (c *Context) Program() []byte {
|
||||
return c.prog
|
||||
}
|
||||
|
||||
func (c *Context) atBreakPoint() bool {
|
||||
for _, n := range c.breakPoints {
|
||||
if n == c.ip {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Context) String() string {
|
||||
return "execution context"
|
||||
}
|
||||
|
||||
// ReadUint32 reads a uint32 from the script
|
||||
func (c *Context) ReadUint32() uint32 {
|
||||
start, end := c.IP(), c.IP()+4
|
||||
if end > len(c.prog) {
|
||||
return 0
|
||||
}
|
||||
val := binary.LittleEndian.Uint32(c.prog[start:end])
|
||||
c.ip += 4
|
||||
return val
|
||||
}
|
||||
|
||||
// ReadUint16 reads a uint16 from the script
|
||||
func (c *Context) ReadUint16() uint16 {
|
||||
start, end := c.IP(), c.IP()+2
|
||||
if end > len(c.prog) {
|
||||
return 0
|
||||
}
|
||||
val := binary.LittleEndian.Uint16(c.prog[start:end])
|
||||
c.ip += 2
|
||||
return val
|
||||
}
|
||||
|
||||
// ReadInt16 reads a int16 from the script
|
||||
func (c *Context) ReadInt16() int16 {
|
||||
return int16(c.ReadUint16())
|
||||
}
|
||||
|
||||
// ReadByte reads one byte from the script
|
||||
func (c *Context) ReadByte() (byte, error) {
|
||||
byt, err := c.ReadBytes(1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return byt[0], nil
|
||||
}
|
||||
|
||||
// ReadBytes will read n bytes from the context
|
||||
func (c *Context) ReadBytes(n int) ([]byte, error) {
|
||||
start, end := c.IP(), c.IP()+n
|
||||
if end > len(c.prog) {
|
||||
return nil, errors.New("Too many bytes to read, pointer goes past end of program")
|
||||
}
|
||||
|
||||
out := make([]byte, n)
|
||||
copy(out, c.prog[start:end])
|
||||
c.ip += n
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *Context) readVarBytes() ([]byte, error) {
|
||||
n, err := c.ReadByte()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.ReadBytes(int(n))
|
||||
}
|
||||
|
||||
// SetIP sets the instruction pointer ip to a given integer.
|
||||
// Returns an error if ip is less than -1 or greater than LenInstr.
|
||||
func (c *Context) SetIP(ip int) error {
|
||||
if ok := ip < -1 || ip > c.LenInstr(); ok {
|
||||
return errors.New("invalid instruction pointer")
|
||||
}
|
||||
c.ip = ip
|
||||
return nil
|
||||
}
|
||||
|
||||
// Hash overrides the default abstract hash method.
|
||||
func (c *Context) Hash() (string, error) {
|
||||
data := c.String() + fmt.Sprintf(" %v-%v-%v-%v-%v", c.ip, c.prog, c.breakPoints, c.Estack, c.Astack)
|
||||
return KeyGenerator([]byte(data))
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
package stack
|
||||
|
||||
// Instruction represents a operation code in the neovm
|
||||
type Instruction byte
|
||||
|
||||
// Viable list of supported instruction constants.
|
||||
const (
|
||||
// Constants
|
||||
PUSH0 Instruction = 0x00
|
||||
PUSHF Instruction = PUSH0
|
||||
PUSHBYTES1 Instruction = 0x01
|
||||
PUSHBYTES75 Instruction = 0x4B
|
||||
PUSHDATA1 Instruction = 0x4C
|
||||
PUSHDATA2 Instruction = 0x4D
|
||||
PUSHDATA4 Instruction = 0x4E
|
||||
PUSHM1 Instruction = 0x4F
|
||||
PUSH1 Instruction = 0x51
|
||||
PUSHT Instruction = PUSH1
|
||||
PUSH2 Instruction = 0x52
|
||||
PUSH3 Instruction = 0x53
|
||||
PUSH4 Instruction = 0x54
|
||||
PUSH5 Instruction = 0x55
|
||||
PUSH6 Instruction = 0x56
|
||||
PUSH7 Instruction = 0x57
|
||||
PUSH8 Instruction = 0x58
|
||||
PUSH9 Instruction = 0x59
|
||||
PUSH10 Instruction = 0x5A
|
||||
PUSH11 Instruction = 0x5B
|
||||
PUSH12 Instruction = 0x5C
|
||||
PUSH13 Instruction = 0x5D
|
||||
PUSH14 Instruction = 0x5E
|
||||
PUSH15 Instruction = 0x5F
|
||||
PUSH16 Instruction = 0x60
|
||||
|
||||
// Flow control
|
||||
NOP Instruction = 0x61
|
||||
JMP Instruction = 0x62
|
||||
JMPIF Instruction = 0x63
|
||||
JMPIFNOT Instruction = 0x64
|
||||
CALL Instruction = 0x65
|
||||
RET Instruction = 0x66
|
||||
APPCALL Instruction = 0x67
|
||||
SYSCALL Instruction = 0x68
|
||||
TAILCALL Instruction = 0x69
|
||||
|
||||
// Stack
|
||||
DUPFROMALTSTACK Instruction = 0x6A
|
||||
TOALTSTACK Instruction = 0x6B
|
||||
FROMALTSTACK Instruction = 0x6C
|
||||
XDROP Instruction = 0x6D
|
||||
XSWAP Instruction = 0x72
|
||||
XTUCK Instruction = 0x73
|
||||
DEPTH Instruction = 0x74
|
||||
DROP Instruction = 0x75
|
||||
DUP Instruction = 0x76
|
||||
NIP Instruction = 0x77
|
||||
OVER Instruction = 0x78
|
||||
PICK Instruction = 0x79
|
||||
ROLL Instruction = 0x7A
|
||||
ROT Instruction = 0x7B
|
||||
SWAP Instruction = 0x7C
|
||||
TUCK Instruction = 0x7D
|
||||
|
||||
// Splice
|
||||
CAT Instruction = 0x7E
|
||||
SUBSTR Instruction = 0x7F
|
||||
LEFT Instruction = 0x80
|
||||
RIGHT Instruction = 0x81
|
||||
SIZE Instruction = 0x82
|
||||
|
||||
// Bitwise logic
|
||||
INVERT Instruction = 0x83
|
||||
AND Instruction = 0x84
|
||||
OR Instruction = 0x85
|
||||
XOR Instruction = 0x86
|
||||
EQUAL Instruction = 0x87
|
||||
|
||||
// Arithmetic
|
||||
INC Instruction = 0x8B
|
||||
DEC Instruction = 0x8C
|
||||
SIGN Instruction = 0x8D
|
||||
NEGATE Instruction = 0x8F
|
||||
ABS Instruction = 0x90
|
||||
NOT Instruction = 0x91
|
||||
NZ Instruction = 0x92
|
||||
ADD Instruction = 0x93
|
||||
SUB Instruction = 0x94
|
||||
MUL Instruction = 0x95
|
||||
DIV Instruction = 0x96
|
||||
MOD Instruction = 0x97
|
||||
SHL Instruction = 0x98
|
||||
SHR Instruction = 0x99
|
||||
BOOLAND Instruction = 0x9A
|
||||
BOOLOR Instruction = 0x9B
|
||||
NUMEQUAL Instruction = 0x9C
|
||||
NUMNOTEQUAL Instruction = 0x9E
|
||||
LT Instruction = 0x9F
|
||||
GT Instruction = 0xA0
|
||||
LTE Instruction = 0xA1
|
||||
GTE Instruction = 0xA2
|
||||
MIN Instruction = 0xA3
|
||||
MAX Instruction = 0xA4
|
||||
WITHIN Instruction = 0xA5
|
||||
|
||||
// Crypto
|
||||
SHA1 Instruction = 0xA7
|
||||
SHA256 Instruction = 0xA8
|
||||
HASH160 Instruction = 0xA9
|
||||
HASH256 Instruction = 0xAA
|
||||
CHECKSIG Instruction = 0xAC
|
||||
CHECKMULTISIG Instruction = 0xAE
|
||||
|
||||
// Array
|
||||
ARRAYSIZE Instruction = 0xC0
|
||||
PACK Instruction = 0xC1
|
||||
UNPACK Instruction = 0xC2
|
||||
PICKITEM Instruction = 0xC3
|
||||
SETITEM Instruction = 0xC4
|
||||
NEWARRAY Instruction = 0xC5
|
||||
NEWSTRUCT Instruction = 0xC6
|
||||
APPEND Instruction = 0xC8
|
||||
REVERSE Instruction = 0xC9
|
||||
REMOVE Instruction = 0xCA
|
||||
|
||||
// Exceptions
|
||||
THROW Instruction = 0xF0
|
||||
THROWIFNOT Instruction = 0xF1
|
||||
)
|
||||
|
||||
// Value returns the byte-value of the opcode.
|
||||
func (i Instruction) Value() byte {
|
||||
return byte(i)
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
package stack
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAdd(t *testing.T) {
|
||||
a := testMakeStackInt(t, 10)
|
||||
b := testMakeStackInt(t, 20)
|
||||
expected := testMakeStackInt(t, 30)
|
||||
c, err := a.Add(b)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, true, expected.Equal(c))
|
||||
}
|
||||
func TestSub(t *testing.T) {
|
||||
a := testMakeStackInt(t, 30)
|
||||
b := testMakeStackInt(t, 200)
|
||||
expected := testMakeStackInt(t, 170)
|
||||
c, err := b.Sub(a)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, true, expected.Equal(c))
|
||||
}
|
||||
func TestMul(t *testing.T) {
|
||||
a := testMakeStackInt(t, 10)
|
||||
b := testMakeStackInt(t, 20)
|
||||
expected := testMakeStackInt(t, 200)
|
||||
c, err := a.Mul(b)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, true, expected.Equal(c))
|
||||
}
|
||||
func TestMod(t *testing.T) {
|
||||
a := testMakeStackInt(t, 10)
|
||||
b := testMakeStackInt(t, 20)
|
||||
expected := testMakeStackInt(t, 10)
|
||||
c, err := a.Mod(b)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, true, expected.Equal(c))
|
||||
}
|
||||
func TestLsh(t *testing.T) {
|
||||
a := testMakeStackInt(t, 23)
|
||||
b := testMakeStackInt(t, 8)
|
||||
expected := testMakeStackInt(t, 5888)
|
||||
c, err := a.Lsh(b)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, true, expected.Equal(c))
|
||||
}
|
||||
|
||||
func TestRsh(t *testing.T) {
|
||||
a := testMakeStackInt(t, 128)
|
||||
b := testMakeStackInt(t, 3)
|
||||
expected := testMakeStackInt(t, 16)
|
||||
c, err := a.Rsh(b)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, true, expected.Equal(c))
|
||||
}
|
||||
|
||||
func TestByteArrConversion(t *testing.T) {
|
||||
|
||||
var num int64 = 100000
|
||||
|
||||
a := testMakeStackInt(t, num)
|
||||
ba, err := a.ByteArray()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, num, testReadInt64(t, ba.val))
|
||||
|
||||
have, err := ba.Integer()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, num, have.val.Int64())
|
||||
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package stack
|
||||
|
||||
import "errors"
|
||||
|
||||
// Invocation embeds a Random Access stack
|
||||
// Providing helper methods for the context object
|
||||
type Invocation struct{ RandomAccess }
|
||||
|
||||
//NewInvocation will return a new
|
||||
// Invocation stack
|
||||
func NewInvocation() *Invocation {
|
||||
return &Invocation{
|
||||
RandomAccess{
|
||||
vals: make([]Item, 0, StackAverageSize),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Invocation) peekContext(n uint16) (*Context, error) {
|
||||
item, err := i.Peek(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return item.Context()
|
||||
}
|
||||
|
||||
// CurrentContext returns the current context on the invocation stack
|
||||
func (i *Invocation) CurrentContext() (*Context, error) {
|
||||
return i.peekContext(0)
|
||||
}
|
||||
|
||||
// PopCurrentContext Pops a context item from the top of the stack
|
||||
func (i *Invocation) PopCurrentContext() (*Context, error) {
|
||||
item, err := i.Pop()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx, err := item.Context()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ctx, err
|
||||
}
|
||||
|
||||
// CallingContext will return the cntext item
|
||||
// that will be called next.
|
||||
func (i *Invocation) CallingContext() (*Context, error) {
|
||||
if i.Len() < 1 {
|
||||
return nil, errors.New("Length of invocation stack is < 1, no calling context")
|
||||
}
|
||||
return i.peekContext(1)
|
||||
}
|
||||
|
||||
// EntryContext will return the context item that
|
||||
// started the program
|
||||
func (i *Invocation) EntryContext() (*Context, error) {
|
||||
|
||||
// firstItemIndex refers to the first item that was popped on the stack
|
||||
firstItemIndex := uint16(i.Len() - 1) // N.B. if this overflows because len is zero, then an error will be returned
|
||||
return i.peekContext(firstItemIndex)
|
||||
}
|
|
@ -1,166 +0,0 @@
|
|||
package stack
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
||||
)
|
||||
|
||||
// Map represents a map of key, value pair on the stack.
|
||||
// Both key and value are stack Items.
|
||||
type Map struct {
|
||||
*abstractItem
|
||||
val map[Item]Item
|
||||
}
|
||||
|
||||
// NewMap returns a Map stack Item given
|
||||
// a map whose keys and values are stack Items.
|
||||
func NewMap(val map[Item]Item) (*Map, error) {
|
||||
return &Map{
|
||||
abstractItem: &abstractItem{},
|
||||
val: val,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Map will overwrite the default implementation
|
||||
// to allow go to cast this item as an Map.
|
||||
func (m *Map) Map() (*Map, error) {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Boolean overrides the default Boolean method
|
||||
// to convert an Map into a Boolean StackItem
|
||||
func (m *Map) Boolean() (*Boolean, error) {
|
||||
return NewBoolean(true), nil
|
||||
}
|
||||
|
||||
// ContainsKey returns a boolean whose value is true
|
||||
// iff the underlying map value contains the Item i
|
||||
// as a key.
|
||||
func (m *Map) ContainsKey(key Item) (*Boolean, error) {
|
||||
for k := range m.Value() {
|
||||
if ok, err := CompareHash(k, key); err != nil {
|
||||
return nil, err
|
||||
} else if ok.Value() {
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
}
|
||||
return NewBoolean(false), nil
|
||||
}
|
||||
|
||||
// Value returns the underlying map's value
|
||||
func (m *Map) Value() map[Item]Item {
|
||||
return m.val
|
||||
}
|
||||
|
||||
// Remove removes the Item i from the
|
||||
// underlying map's value.
|
||||
func (m *Map) Remove(key Item) error {
|
||||
var d Item
|
||||
for k := range m.Value() {
|
||||
if ok, err := CompareHash(k, key); err != nil {
|
||||
return err
|
||||
} else if ok.Value() {
|
||||
d = k
|
||||
}
|
||||
|
||||
}
|
||||
if d != nil {
|
||||
delete(m.Value(), d)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add inserts a new key, value pair of Items into
|
||||
// the underlying map's value.
|
||||
func (m *Map) Add(key Item, value Item) error {
|
||||
for k := range m.Value() {
|
||||
if ok, err := CompareHash(k, key); err != nil {
|
||||
return err
|
||||
} else if ok.Value() {
|
||||
return errors.New("try to insert duplicate key! ")
|
||||
}
|
||||
}
|
||||
m.Value()[key] = value
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValueOfKey tries to get the value of the key Item
|
||||
// from the map's underlying value.
|
||||
func (m *Map) ValueOfKey(key Item) (Item, error) {
|
||||
for k, v := range m.Value() {
|
||||
if ok, err := CompareHash(k, key); err != nil {
|
||||
return nil, err
|
||||
} else if ok.Value() {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
}
|
||||
|
||||
// Clear empties the the underlying map's value.
|
||||
func (m *Map) Clear() {
|
||||
m.val = map[Item]Item{}
|
||||
}
|
||||
|
||||
// CompareHash compare the the Hashes of two items.
|
||||
// If they are equal it returns a true boolean. Otherwise
|
||||
// it returns false boolean. Item whose hashes are equal are
|
||||
// to be considered equal.
|
||||
func CompareHash(i1 Item, i2 Item) (*Boolean, error) {
|
||||
hash1, err := i1.Hash()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hash2, err := i2.Hash()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if hash1 == hash2 {
|
||||
return NewBoolean(true), nil
|
||||
}
|
||||
|
||||
return NewBoolean(false), nil
|
||||
}
|
||||
|
||||
// Hash overrides the default abstract hash method.
|
||||
func (m *Map) Hash() (string, error) {
|
||||
var hashSlice sort.StringSlice = []string{}
|
||||
var data = fmt.Sprintf("%T ", m)
|
||||
|
||||
for k, v := range m.Value() {
|
||||
hk, err := k.Hash()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
hv, err := v.Hash()
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
hashSlice = append(hashSlice, hk)
|
||||
hashSlice = append(hashSlice, hv)
|
||||
}
|
||||
hashSlice.Sort()
|
||||
|
||||
for _, h := range hashSlice {
|
||||
data += h
|
||||
}
|
||||
|
||||
return KeyGenerator([]byte(data))
|
||||
}
|
||||
|
||||
// KeyGenerator hashes a byte slice to obtain a unique identifier.
|
||||
func KeyGenerator(data []byte) (string, error) {
|
||||
h, err := hash.Sha256([]byte(data))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return h.String(), nil
|
||||
}
|
|
@ -1,141 +0,0 @@
|
|||
package stack
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
// define Map m for testing
|
||||
var a Item = testMakeStackInt(t, 10)
|
||||
var b Item = NewBoolean(true)
|
||||
var c Item = NewByteArray([]byte{1, 2, 34})
|
||||
var d Item = testMakeStackMap(t, map[Item]Item{
|
||||
a: c,
|
||||
b: a,
|
||||
})
|
||||
var e = NewContext([]byte{1, 2, 3, 4})
|
||||
var f = testMakeArray(t, []Item{a, b})
|
||||
|
||||
val := map[Item]Item{
|
||||
a: c,
|
||||
b: a,
|
||||
c: b,
|
||||
d: a,
|
||||
e: d,
|
||||
f: e,
|
||||
}
|
||||
m := testMakeStackMap(t, val)
|
||||
|
||||
// test ValueOfKey
|
||||
valueA, _ := m.ValueOfKey(testMakeStackInt(t, 10))
|
||||
assert.Equal(t, c, valueA)
|
||||
|
||||
valueB, _ := m.ValueOfKey(b)
|
||||
assert.Equal(t, a, valueB)
|
||||
|
||||
valueC, _ := m.ValueOfKey(NewByteArray([]byte{1, 2, 34}))
|
||||
assert.Equal(t, b, valueC)
|
||||
|
||||
valueD, _ := m.ValueOfKey(testMakeStackMap(t, map[Item]Item{
|
||||
b: a,
|
||||
a: c,
|
||||
}))
|
||||
assert.Equal(t, a, valueD)
|
||||
|
||||
valueE, _ := m.ValueOfKey(NewContext([]byte{1, 2, 3, 4}))
|
||||
assert.Equal(t, d, valueE)
|
||||
|
||||
valueF, _ := m.ValueOfKey(testMakeArray(t, []Item{a, b}))
|
||||
assert.Equal(t, e, valueF)
|
||||
|
||||
valueX, _ := m.ValueOfKey(NewByteArray([]byte{1, 2, 35}))
|
||||
assert.NotEqual(t, b, valueX)
|
||||
|
||||
checkA, err := m.ContainsKey(a)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, true, checkA.Value())
|
||||
|
||||
//test ContainsKey
|
||||
checkB, err := m.ContainsKey(b)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, true, checkB.Value())
|
||||
|
||||
checkC, err := m.ContainsKey(c)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, true, checkC.Value())
|
||||
|
||||
checkD, err := m.ContainsKey(d)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, true, checkD.Value())
|
||||
|
||||
checkE, err := m.ContainsKey(e)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, true, checkE.Value())
|
||||
|
||||
//test CompareHash
|
||||
val2 := map[Item]Item{
|
||||
f: e,
|
||||
e: d,
|
||||
d: a,
|
||||
c: b,
|
||||
b: a,
|
||||
a: c,
|
||||
}
|
||||
m2 := testMakeStackMap(t, val2)
|
||||
checkMap, err := CompareHash(m, m2)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, true, checkMap.Value())
|
||||
|
||||
checkBoolean, err := CompareHash(b, NewBoolean(true))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, true, checkBoolean.Value())
|
||||
|
||||
checkByteArray, err := CompareHash(c, NewByteArray([]byte{1, 2, 34}))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, true, checkByteArray.Value())
|
||||
|
||||
checkContext, err := CompareHash(e, NewContext([]byte{1, 2, 3, 4}))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, true, checkContext.Value())
|
||||
|
||||
checkArray, err := CompareHash(f, testMakeArray(t, []Item{a, b}))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, true, checkArray.Value())
|
||||
}
|
||||
|
||||
func TestMapAdd(t *testing.T) {
|
||||
var a Item = testMakeStackInt(t, 10)
|
||||
var b Item = NewBoolean(true)
|
||||
var m = testMakeStackMap(t, map[Item]Item{})
|
||||
|
||||
err := m.Add(a, a)
|
||||
assert.Nil(t, err)
|
||||
err = m.Add(b, a)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 2, len(m.Value()))
|
||||
|
||||
expected := testMakeStackMap(t, map[Item]Item{b: a, a: a})
|
||||
check, err := CompareHash(m, expected)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, true, check.Value())
|
||||
|
||||
}
|
||||
|
||||
func TestMapRemove(t *testing.T) {
|
||||
var a Item = testMakeStackInt(t, 10)
|
||||
var b Item = NewBoolean(true)
|
||||
var m = testMakeStackMap(t, map[Item]Item{b: a, a: a})
|
||||
|
||||
err := m.Remove(a)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, len(m.Value()))
|
||||
|
||||
expected := testMakeStackMap(t, map[Item]Item{b: a})
|
||||
check, err := CompareHash(m, expected)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, true, check.Value())
|
||||
|
||||
}
|
|
@ -1,186 +0,0 @@
|
|||
package stack
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
// StackAverageSize is used to set the capacity of the stack
|
||||
// setting this number too low, will cause extra allocations
|
||||
StackAverageSize = 20
|
||||
)
|
||||
|
||||
// RandomAccess represents a Random Access Stack
|
||||
type RandomAccess struct {
|
||||
vals []Item
|
||||
}
|
||||
|
||||
// New will return a new random access stack
|
||||
func New() *RandomAccess {
|
||||
return &RandomAccess{
|
||||
vals: make([]Item, 0, StackAverageSize),
|
||||
}
|
||||
}
|
||||
|
||||
//Len will return the length of the stack
|
||||
func (ras *RandomAccess) Len() int {
|
||||
if ras.vals == nil {
|
||||
return -1
|
||||
}
|
||||
return len(ras.vals)
|
||||
}
|
||||
|
||||
// Clear will remove all items in the stack
|
||||
func (ras *RandomAccess) Clear() {
|
||||
ras.vals = make([]Item, 0, StackAverageSize)
|
||||
}
|
||||
|
||||
// Pop will remove the last stack item that was added
|
||||
func (ras *RandomAccess) Pop() (Item, error) {
|
||||
if len(ras.vals) == 0 {
|
||||
return nil, errors.New("There are no items on the stack to pop")
|
||||
}
|
||||
if ras.vals == nil {
|
||||
return nil, errors.New("Cannot pop from a nil stack")
|
||||
}
|
||||
|
||||
l := len(ras.vals)
|
||||
item := ras.vals[l-1]
|
||||
ras.vals = ras.vals[:l-1]
|
||||
|
||||
return item, nil
|
||||
}
|
||||
|
||||
// Push will put a stack item onto the top of the stack
|
||||
func (ras *RandomAccess) Push(item Item) *RandomAccess {
|
||||
if ras.vals == nil {
|
||||
ras.vals = make([]Item, 0, StackAverageSize)
|
||||
}
|
||||
|
||||
ras.vals = append(ras.vals, item)
|
||||
|
||||
return ras
|
||||
}
|
||||
|
||||
// Insert will push a stackItem onto the stack at position `n`
|
||||
// Note; index 0 is the top of the stack, which is the end of slice
|
||||
func (ras *RandomAccess) Insert(n uint16, item Item) (*RandomAccess, error) {
|
||||
|
||||
if n == 0 {
|
||||
return ras.Push(item), nil
|
||||
}
|
||||
|
||||
if ras.vals == nil {
|
||||
ras.vals = make([]Item, 0, StackAverageSize)
|
||||
}
|
||||
|
||||
// Check that we are not inserting out of the bounds
|
||||
stackSize := uint16(len(ras.vals))
|
||||
if n > stackSize-1 {
|
||||
return nil, fmt.Errorf("Tried to insert at index %d when length of stack is %d", n, len(ras.vals))
|
||||
}
|
||||
|
||||
index := stackSize - n
|
||||
|
||||
ras.vals = append(ras.vals, item)
|
||||
copy(ras.vals[index:], ras.vals[index-1:])
|
||||
ras.vals[index] = item
|
||||
|
||||
return ras, nil
|
||||
}
|
||||
|
||||
// Peek will check an element at a given index
|
||||
// Note: 0 is the top of the stack, which is the end of the slice
|
||||
func (ras *RandomAccess) Peek(n uint16) (Item, error) {
|
||||
|
||||
stackSize := uint16(len(ras.vals))
|
||||
|
||||
if n == 0 {
|
||||
index := stackSize - 1
|
||||
return ras.vals[index], nil
|
||||
}
|
||||
|
||||
if ras.Len() < 1 {
|
||||
return nil, fmt.Errorf("cannot peak at a stack with no item, length of stack is %d", ras.Len())
|
||||
}
|
||||
|
||||
// Check that we are not peeking out of the bounds
|
||||
if n > stackSize-1 {
|
||||
return nil, fmt.Errorf("Tried to peek at index %d when length of stack is %d", n, len(ras.vals))
|
||||
}
|
||||
index := stackSize - n - 1
|
||||
|
||||
return ras.vals[index], nil
|
||||
}
|
||||
|
||||
// CopyTo will copy all of the stack items from `ras` into the stack that is passed as an argument
|
||||
// XXX: once maxstacksize is implemented, we will return error if size goes over
|
||||
// There will also be additional checks needed once stack isolation is added
|
||||
func (ras *RandomAccess) CopyTo(stack *RandomAccess) error {
|
||||
stack.vals = append(stack.vals, ras.vals...)
|
||||
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
|
||||
|
||||
// PopInt will remove the last stack item that was added
|
||||
// And cast it to an integer
|
||||
func (ras *RandomAccess) PopInt() (*Int, error) {
|
||||
item, err := ras.Pop()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return item.Integer()
|
||||
}
|
||||
|
||||
// PopByteArray will remove the last stack item that was added
|
||||
// And cast it to an ByteArray
|
||||
func (ras *RandomAccess) PopByteArray() (*ByteArray, error) {
|
||||
item, err := ras.Pop()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return item.ByteArray()
|
||||
}
|
||||
|
||||
// PopBoolean will remove the last stack item that was added
|
||||
// and cast it to a Boolean.
|
||||
func (ras *RandomAccess) PopBoolean() (*Boolean, error) {
|
||||
item, err := ras.Pop()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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
|
||||
}
|
|
@ -1,161 +0,0 @@
|
|||
package stack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStackPushPop(t *testing.T) {
|
||||
// Create two stack Integers
|
||||
a, err := NewInt(big.NewInt(10))
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
b, err := NewInt(big.NewInt(20))
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
// Create a new stack
|
||||
testStack := New()
|
||||
|
||||
// Push to stack
|
||||
testStack.Push(a).Push(b)
|
||||
|
||||
// There should only be two values on the stack
|
||||
assert.Equal(t, 2, testStack.Len())
|
||||
|
||||
// Pop first element and it should be equal to b
|
||||
stackElement, err := testStack.Pop()
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
item, err := stackElement.Integer()
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
assert.Equal(t, true, item.Equal(b))
|
||||
|
||||
// Pop second element and it should be equal to a
|
||||
stackElement, err = testStack.Pop()
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
item, err = stackElement.Integer()
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
assert.Equal(t, true, item.Equal(a))
|
||||
|
||||
// We should get an error as there are nomore items left to pop
|
||||
_, err = testStack.Pop()
|
||||
assert.NotNil(t, err)
|
||||
|
||||
}
|
||||
|
||||
// For this test to pass, we should get an error when popping from a nil stack
|
||||
// and we should initialise and push an element if pushing to an empty stack
|
||||
func TestPushPopNil(t *testing.T) {
|
||||
|
||||
// stack is nil when initialised without New constructor
|
||||
testStack := RandomAccess{}
|
||||
|
||||
// Popping from nil stack
|
||||
// - should give an error
|
||||
// - element returned should be nil
|
||||
stackElement, err := testStack.Pop()
|
||||
assert.NotNil(t, err)
|
||||
assert.Nil(t, stackElement)
|
||||
|
||||
// stack should still be nil after failing to pop
|
||||
assert.Nil(t, testStack.vals)
|
||||
|
||||
// create a random test stack item
|
||||
a, err := NewInt(big.NewInt(2))
|
||||
assert.Nil(t, err)
|
||||
|
||||
// push random item to stack
|
||||
testStack.Push(a)
|
||||
|
||||
// push should initialise the stack and put one element on the stack
|
||||
assert.Equal(t, 1, testStack.Len())
|
||||
}
|
||||
|
||||
// Test passes if we can peek and modify an item
|
||||
//without modifying the value on the stack
|
||||
func TestStackPeekMutability(t *testing.T) {
|
||||
|
||||
testStack := New()
|
||||
|
||||
a, err := NewInt(big.NewInt(2))
|
||||
assert.Nil(t, err)
|
||||
b, err := NewInt(big.NewInt(3))
|
||||
assert.Nil(t, err)
|
||||
|
||||
testStack.Push(a).Push(b)
|
||||
|
||||
peekedItem := testPeekInteger(t, testStack, 0)
|
||||
assert.Equal(t, true, peekedItem.Equal(b))
|
||||
|
||||
// Check that by modifying the peeked value,
|
||||
// we did not modify the item on the stack
|
||||
peekedItem = a
|
||||
peekedItem.val = big.NewInt(0)
|
||||
|
||||
// Pop item from stack and check it is still the same
|
||||
poppedItem := testPopInteger(t, testStack)
|
||||
assert.Equal(t, true, poppedItem.Equal(b))
|
||||
}
|
||||
func TestStackPeek(t *testing.T) {
|
||||
|
||||
testStack := New()
|
||||
|
||||
values := []int64{23, 45, 67, 89, 12, 344}
|
||||
for _, val := range values {
|
||||
a := testMakeStackInt(t, val)
|
||||
testStack.Push(a)
|
||||
}
|
||||
|
||||
// i starts at 0, j starts at len(values)-1
|
||||
for i, j := 0, len(values)-1; j >= 0; i, j = i+1, j-1 {
|
||||
|
||||
peekedItem := testPeekInteger(t, testStack, uint16(i))
|
||||
a := testMakeStackInt(t, values[j])
|
||||
|
||||
fmt.Printf("%#v\n", peekedItem.val.Int64())
|
||||
|
||||
assert.Equal(t, true, a.Equal(peekedItem))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestStackInsert(t *testing.T) {
|
||||
|
||||
testStack := New()
|
||||
|
||||
a := testMakeStackInt(t, 2)
|
||||
b := testMakeStackInt(t, 4)
|
||||
c := testMakeStackInt(t, 6)
|
||||
|
||||
// insert on an empty stack should put element on top
|
||||
_, err := testStack.Insert(0, a)
|
||||
assert.Equal(t, err, nil)
|
||||
_, err = testStack.Insert(0, b)
|
||||
assert.Equal(t, err, nil)
|
||||
_, err = testStack.Insert(1, c)
|
||||
assert.Equal(t, err, nil)
|
||||
|
||||
// Order should be [a,c,b]
|
||||
pop1 := testPopInteger(t, testStack)
|
||||
pop2 := testPopInteger(t, testStack)
|
||||
pop3 := testPopInteger(t, testStack)
|
||||
|
||||
assert.Equal(t, true, pop1.Equal(b))
|
||||
assert.Equal(t, true, pop2.Equal(c))
|
||||
assert.Equal(t, true, pop3.Equal(a))
|
||||
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package stack
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
//Item is an interface which represents object that can be placed on the stack
|
||||
type Item interface {
|
||||
Integer() (*Int, error)
|
||||
Boolean() (*Boolean, error)
|
||||
ByteArray() (*ByteArray, error)
|
||||
Array() (*Array, error)
|
||||
Context() (*Context, error)
|
||||
Map() (*Map, error)
|
||||
Hash() (string, error)
|
||||
}
|
||||
|
||||
// Represents an `abstract` stack item
|
||||
// which will hold default values for stack items
|
||||
// this is intended to be embedded into types that you will use on the stack
|
||||
type abstractItem struct{}
|
||||
|
||||
// Integer is the default implementation for a stackItem
|
||||
// Implements Item interface
|
||||
func (a *abstractItem) Integer() (*Int, error) {
|
||||
return nil, errors.New("This stack item is not an Integer")
|
||||
}
|
||||
|
||||
// Boolean is the default implementation for a stackItem
|
||||
// Implements Item interface
|
||||
func (a *abstractItem) Boolean() (*Boolean, error) {
|
||||
return nil, errors.New("This stack item is not a Boolean")
|
||||
}
|
||||
|
||||
// ByteArray is the default implementation for a stackItem
|
||||
// Implements Item interface
|
||||
func (a *abstractItem) ByteArray() (*ByteArray, error) {
|
||||
return nil, errors.New("This stack item is not a byte array")
|
||||
}
|
||||
|
||||
// Array is the default implementation for a stackItem
|
||||
// Implements Item interface
|
||||
func (a *abstractItem) Array() (*Array, error) {
|
||||
return nil, errors.New("This stack item is not an array")
|
||||
}
|
||||
|
||||
// Context is the default implementation for a stackItem
|
||||
// Implements Item interface
|
||||
func (a *abstractItem) Context() (*Context, error) {
|
||||
return nil, errors.New("This stack item is not of type context")
|
||||
}
|
||||
|
||||
// Context is the default implementation for a stackItem
|
||||
// Implements Item interface
|
||||
func (a *abstractItem) Map() (*Map, error) {
|
||||
return nil, errors.New("This stack item is not a map")
|
||||
}
|
||||
|
||||
func (a *abstractItem) Hash() (string, error) {
|
||||
return "", errors.New("This stack item need to override the Hash Method")
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
package stack
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// A simple test to ensure that by embedding the abstract interface
|
||||
// we immediately become a stack item, with the default values set to nil
|
||||
func TestInterfaceEmbedding(t *testing.T) {
|
||||
|
||||
// Create an anonymous struct that embeds the abstractItem
|
||||
a := struct {
|
||||
*abstractItem
|
||||
}{
|
||||
&abstractItem{},
|
||||
}
|
||||
|
||||
// Since interface checking can be done at compile time.
|
||||
// If he abstractItem did not implement all methods of our interface `Item`
|
||||
// Then any struct which embeds it, will also not implement the Item interface.
|
||||
// This test would then give errors, at compile time.
|
||||
var Items []Item
|
||||
Items = append(Items, a)
|
||||
|
||||
// Default methods should give errors
|
||||
// Here we just need to test against one of the methods in the interface
|
||||
for _, element := range Items {
|
||||
x, err := element.Integer()
|
||||
assert.Nil(t, x)
|
||||
assert.NotNil(t, err, nil)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TestIntCasting is a simple test to test that the Integer method is overwritten
|
||||
// from the abstractItem
|
||||
func TestIntMethodOverride(t *testing.T) {
|
||||
|
||||
testValues := []int64{0, 10, 200, 30, 90}
|
||||
var Items []Item
|
||||
|
||||
// Convert a range of int64s into Stack Integers
|
||||
// Adding them into an array of StackItems
|
||||
for _, num := range testValues {
|
||||
stackInteger, err := NewInt(big.NewInt(num))
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
Items = append(Items, stackInteger)
|
||||
}
|
||||
|
||||
// For each item, call the Integer method on the interface
|
||||
// Which should return an integer and no error
|
||||
// as the stack integer struct overrides that method
|
||||
for i, element := range Items {
|
||||
k, err := element.Integer()
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if k.val.Cmp(big.NewInt(testValues[i])) != 0 {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package stack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// helper functions
|
||||
func testPeekInteger(t *testing.T, tStack *RandomAccess, n uint16) *Int {
|
||||
stackElement, err := tStack.Peek(n)
|
||||
assert.Nil(t, err)
|
||||
item, err := stackElement.Integer()
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
func testPopInteger(t *testing.T, tStack *RandomAccess) *Int {
|
||||
stackElement, err := tStack.Pop()
|
||||
assert.Nil(t, err)
|
||||
item, err := stackElement.Integer()
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
func testMakeStackInt(t *testing.T, num int64) *Int {
|
||||
a, err := NewInt(big.NewInt(num))
|
||||
assert.Nil(t, err)
|
||||
return a
|
||||
}
|
||||
|
||||
func testReadInt64(t *testing.T, data []byte) int64 {
|
||||
var ret int64
|
||||
var arr [8]byte
|
||||
|
||||
// expands or shrinks data automatically
|
||||
copy(arr[:], data)
|
||||
buf := bytes.NewBuffer(arr[:])
|
||||
err := binary.Read(buf, binary.LittleEndian, &ret)
|
||||
assert.Nil(t, err)
|
||||
return ret
|
||||
}
|
||||
|
||||
func testMakeStackMap(t *testing.T, m map[Item]Item) *Map {
|
||||
a, err := NewMap(m)
|
||||
assert.Nil(t, err)
|
||||
return a
|
||||
}
|
||||
|
||||
func testMakeArray(t *testing.T, v []Item) *Array {
|
||||
a, err := NewArray(v)
|
||||
assert.Nil(t, err)
|
||||
return a
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
package vm
|
||||
|
||||
//Vmstate represents all possible states that the neo-vm can be in
|
||||
type Vmstate byte
|
||||
|
||||
// List of possible vm states
|
||||
const (
|
||||
// NONE is the running state of the vm
|
||||
// NONE signifies that the vm is ready to process an opcode
|
||||
NONE = 0
|
||||
// HALT is a stopped state of the vm
|
||||
// where the stop was signalled by the program completion
|
||||
HALT = 1 << 0
|
||||
// FAULT is a stopped state of the vm
|
||||
// where the stop was signalled by an error in the program
|
||||
FAULT = 1 << 1
|
||||
// BREAK is a suspended state for the VM
|
||||
// were the break was signalled by a breakpoint
|
||||
BREAK = 1 << 2
|
||||
)
|
|
@ -1,72 +0,0 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/vm/stack"
|
||||
)
|
||||
|
||||
// VM represents an instance of a Neo Virtual Machine
|
||||
type VM struct {
|
||||
// ResultStack contains the results of
|
||||
// the last evaluation stack before the program terminated
|
||||
ResultStack stack.RandomAccess
|
||||
// InvocationStack contains all of the contexts
|
||||
// loaded into the vm
|
||||
InvocationStack stack.Invocation
|
||||
state Vmstate
|
||||
}
|
||||
|
||||
// NewVM will:
|
||||
// Set the state of the VM to NONE
|
||||
// instantiate a script as a new context
|
||||
// Push the Context to the Invocation stack
|
||||
func NewVM(script []byte) *VM {
|
||||
ctx := stack.NewContext(script)
|
||||
v := &VM{
|
||||
state: NONE,
|
||||
}
|
||||
v.InvocationStack.Push(ctx)
|
||||
return v
|
||||
}
|
||||
|
||||
// Run loops over the current context by continuously stepping.
|
||||
// Run breaks; once step returns an error or any state that is not NONE
|
||||
func (v *VM) Run() (Vmstate, error) {
|
||||
for {
|
||||
state, err := v.step()
|
||||
if err != nil || state != NONE {
|
||||
return state, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// step will read `one` opcode from the script in the current context
|
||||
// Then excute that opcode
|
||||
func (v *VM) step() (Vmstate, error) {
|
||||
// Get Current Context
|
||||
ctx, err := v.InvocationStack.CurrentContext()
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
// Read Opcode from context
|
||||
op, _ := ctx.Next() // The only error that can occur from this, is if the pointer goes over the pointer
|
||||
// In the NEO-VM specs, this is ignored and we return the RET opcode
|
||||
// Execute OpCode
|
||||
state, err := v.executeOp(stack.Instruction(op), ctx)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// ExecuteOp will execute one opcode on a given context.
|
||||
// If the opcode is not registered, then an unknown opcode error will be returned
|
||||
func (v *VM) executeOp(op stack.Instruction, ctx *stack.Context) (Vmstate, error) {
|
||||
//Find function which handles that specific opcode
|
||||
handleOp, ok := opFunc[op]
|
||||
if !ok {
|
||||
return FAULT, fmt.Errorf("unknown opcode entered %v", op)
|
||||
}
|
||||
return handleOp(op, ctx, &v.InvocationStack, &v.ResultStack)
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
package vm
|
||||
|
||||
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.PICK: PICK,
|
||||
stack.OVER: OVER,
|
||||
stack.NIP: NIP,
|
||||
stack.DUP: DUP,
|
||||
stack.DROP: DROP,
|
||||
stack.DEPTH: DEPTH,
|
||||
stack.XTUCK: XTUCK,
|
||||
stack.XSWAP: XSWAP,
|
||||
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() {
|
||||
for i := int(stack.PUSHBYTES1); i <= int(stack.PUSHBYTES75); i++ {
|
||||
opFunc[stack.Instruction(i)] = PushNBytes
|
||||
}
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
package vm
|
||||
|
||||
import "github.com/CityOfZion/neo-go/pkg/vm/stack"
|
||||
|
||||
// Bitwise logic
|
||||
|
||||
// EQUAL pushes true to the stack
|
||||
// If the two top items on the stack are equal
|
||||
func EQUAL(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
itemA, itemB, err := popTwoByteArrays(ctx)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
ctx.Estack.Push(itemA.Equals(itemB))
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// Invert pops an integer x off of the stack and
|
||||
// pushes an integer on the stack whose value
|
||||
// is the bitwise complement of the value of x.
|
||||
// Returns an error if the popped value is not an integer or
|
||||
// if the bitwise complement cannot be taken.
|
||||
func Invert(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
i, err := ctx.Estack.PopInt()
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
inv, err := i.Invert()
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
ctx.Estack.Push(inv)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// And pops two integer off of the stack and
|
||||
// pushes an integer onto the stack whose value
|
||||
// is the result of the application of the bitwise AND
|
||||
// operator to the two original integers' values.
|
||||
// Returns an error if either items cannot be casted to an integer.
|
||||
func And(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
operandA, operandB, err := popTwoIntegers(ctx)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
res, err := operandA.And(operandB)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
ctx.Estack.Push(res)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// Or pops two integer off of the stack and
|
||||
// pushes an integer onto the stack whose value
|
||||
// is the result of the application of the bitwise OR
|
||||
// operator to the two original integers' values.
|
||||
// Returns an error if either items cannot be casted to an integer.
|
||||
func Or(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
operandA, operandB, err := popTwoIntegers(ctx)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
res, err := operandA.Or(operandB)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
ctx.Estack.Push(res)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// Xor pops two integer off of the stack and
|
||||
// pushes an integer onto the stack whose value
|
||||
// is the result of the application of the bitwise XOR
|
||||
// operator to the two original integers' values.
|
||||
// Returns an error if either items cannot be casted to an integer.
|
||||
func Xor(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
operandA, operandB, err := popTwoIntegers(ctx)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
res, err := operandA.Xor(operandB)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
ctx.Estack.Push(res)
|
||||
|
||||
return NONE, nil
|
||||
}
|
|
@ -1,142 +0,0 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/vm/stack"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestInvertOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
// 0000 00110 = 5
|
||||
a, err := stack.NewInt(big.NewInt(5))
|
||||
assert.Nil(t, err)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(a)
|
||||
|
||||
// 1111 11001 = -6 (two complement representation)
|
||||
_, err = v.executeOp(stack.INVERT, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopInt()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(-6), item.Value().Int64())
|
||||
}
|
||||
|
||||
func TestAndOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
// 110001 = 49
|
||||
a, err := stack.NewInt(big.NewInt(49))
|
||||
assert.Nil(t, err)
|
||||
|
||||
// 100011 = 35
|
||||
b, err := stack.NewInt(big.NewInt(35))
|
||||
assert.Nil(t, err)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(a).Push(b)
|
||||
|
||||
// 100001 = 33
|
||||
_, err = v.executeOp(stack.AND, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopInt()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(33), item.Value().Int64())
|
||||
}
|
||||
|
||||
func TestOrOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
// 110001 = 49
|
||||
a, err := stack.NewInt(big.NewInt(49))
|
||||
assert.Nil(t, err)
|
||||
|
||||
// 100011 = 35
|
||||
b, err := stack.NewInt(big.NewInt(35))
|
||||
assert.Nil(t, err)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(a).Push(b)
|
||||
|
||||
// 110011 = 51 (49 OR 35)
|
||||
_, err = v.executeOp(stack.OR, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopInt()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(51), item.Value().Int64())
|
||||
}
|
||||
|
||||
func TestXorOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
// 110001 = 49
|
||||
a, err := stack.NewInt(big.NewInt(49))
|
||||
assert.Nil(t, err)
|
||||
|
||||
// 100011 = 35
|
||||
b, err := stack.NewInt(big.NewInt(35))
|
||||
assert.Nil(t, err)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(a).Push(b)
|
||||
|
||||
// 010010 = 18 (49 XOR 35)
|
||||
_, err = v.executeOp(stack.XOR, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopInt()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(18), item.Value().Int64())
|
||||
}
|
||||
|
||||
func TestEqualOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a, err := stack.NewInt(big.NewInt(10))
|
||||
assert.Nil(t, err)
|
||||
|
||||
b, err := stack.NewInt(big.NewInt(10))
|
||||
assert.Nil(t, err)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(a).Push(b)
|
||||
|
||||
_, err = v.executeOp(stack.EQUAL, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopBoolean()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, true, item.Value())
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/vm/stack"
|
||||
)
|
||||
|
||||
// vm exceptions
|
||||
|
||||
// THROWIFNOT faults if the item on the top of the stack
|
||||
// does not evaluate to true
|
||||
// For specific logic on how a number of bytearray is evaluated can be seen
|
||||
// from the boolean conversion methods on the stack items
|
||||
func THROWIFNOT(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
// Pop item from top of stack
|
||||
item, err := ctx.Estack.Pop()
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
// Convert to a boolean
|
||||
ok, err := item.Boolean()
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
// If false, throw
|
||||
if !ok.Value() {
|
||||
return FAULT, errors.New("item on top of stack evaluates to false")
|
||||
}
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// THROW returns a FAULT VM state. This indicate that there is an error in the
|
||||
// current context loaded program.
|
||||
func THROW(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
return FAULT, errors.New("the execution of the script program end with an error")
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"github.com/CityOfZion/neo-go/pkg/vm/stack"
|
||||
)
|
||||
|
||||
// Flow control
|
||||
|
||||
// RET Returns from the current context
|
||||
// Returns HALT if there are nomore context's to run
|
||||
func RET(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
_ = ctx // fix SA4009 warning
|
||||
|
||||
// Pop current context from the Inovation stack
|
||||
ctx, err := istack.PopCurrentContext()
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
// If this was the last context, then we copy over the evaluation stack to the resultstack
|
||||
// As the program is about to terminate, once we remove the context
|
||||
if istack.Len() == 0 {
|
||||
|
||||
err = ctx.Estack.CopyTo(rstack)
|
||||
return HALT, err
|
||||
}
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// NOP Returns NONE VMState.
|
||||
func NOP(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// JMP moves the instruction pointer to an offset which is
|
||||
// calculated base on the instructionPointerOffset method.
|
||||
// Returns and error if the offset is out of range.
|
||||
func JMP(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
offset := instructionPointerOffset(ctx)
|
||||
if err := ctx.SetIP(offset); err != nil {
|
||||
return FAULT, err
|
||||
|
||||
}
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// JMPIF pops a boolean off of the stack and,
|
||||
// if the the boolean's value is true, it
|
||||
// moves the instruction pointer to an offset which is
|
||||
// calculated base on the instructionPointerOffset method.
|
||||
// Returns and error if the offset is out of range or
|
||||
// the popped item is not a boolean.
|
||||
func JMPIF(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
b, err := ctx.Estack.PopBoolean()
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
if b.Value() {
|
||||
offset := instructionPointerOffset(ctx)
|
||||
if err := ctx.SetIP(offset); err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// JMPIFNOT pops a boolean off of the stack and,
|
||||
// if the the boolean's value is false, it
|
||||
// moves the instruction pointer to an offset which is
|
||||
// calculated base on the instructionPointerOffset method.
|
||||
// Returns and error if the offset is out of range or
|
||||
// the popped item is not a boolean.
|
||||
func JMPIFNOT(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
b, err := ctx.Estack.PopBoolean()
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
if !b.Value() {
|
||||
offset := instructionPointerOffset(ctx)
|
||||
if err := ctx.SetIP(offset); err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
func instructionPointerOffset(ctx *stack.Context) int {
|
||||
return ctx.IP() + int(ctx.ReadInt16()) - 3
|
||||
}
|
|
@ -1,174 +0,0 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/vm/stack"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNopOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a, err := stack.NewInt(big.NewInt(10))
|
||||
assert.Nil(t, err)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(a)
|
||||
|
||||
_, err = v.executeOp(stack.NOP, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopInt()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(10), item.Value().Int64())
|
||||
}
|
||||
|
||||
func TestJmpOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a, err := stack.NewInt(big.NewInt(10))
|
||||
assert.Nil(t, err)
|
||||
|
||||
ctx := stack.NewContext([]byte{5, 0, 2, 3, 4})
|
||||
ctx.Estack.Push(a)
|
||||
|
||||
// ctx.ip = -1
|
||||
// ctx.IP() = ctx.ip + 1
|
||||
assert.Equal(t, 0, ctx.IP())
|
||||
|
||||
// ctx.ip will be set to offset.
|
||||
// offset = ctx.IP() + int(ctx.ReadInt16()) - 3
|
||||
// = 0 + 5 -3 = 2
|
||||
_, err = v.executeOp(stack.JMP, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
// ctx.IP() = ctx.ip + 1
|
||||
assert.Equal(t, 3, ctx.IP())
|
||||
}
|
||||
|
||||
// test JMPIF instruction with true boolean
|
||||
// on top of the stack
|
||||
func TestJmpIfOp1(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a := stack.NewBoolean(true)
|
||||
|
||||
ctx := stack.NewContext([]byte{5, 0, 2, 3, 4})
|
||||
ctx.Estack.Push(a)
|
||||
|
||||
// ctx.ip = -1
|
||||
// ctx.IP() = ctx.ip + 1
|
||||
assert.Equal(t, 0, ctx.IP())
|
||||
|
||||
// ctx.ip will be set to offset
|
||||
// because the there is a true boolean
|
||||
// on top of the stack.
|
||||
// offset = ctx.IP() + int(ctx.ReadInt16()) - 3
|
||||
// = 0 + 5 -3 = 2
|
||||
_, err := v.executeOp(stack.JMPIF, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have 0 item
|
||||
assert.Equal(t, 0, ctx.Estack.Len())
|
||||
|
||||
// ctx.IP() = ctx.ip + 1
|
||||
assert.Equal(t, 3, ctx.IP())
|
||||
}
|
||||
|
||||
// test JMPIF instruction with false boolean
|
||||
// on top of the stack
|
||||
func TestJmpIfOp2(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a := stack.NewBoolean(false)
|
||||
|
||||
ctx := stack.NewContext([]byte{5, 0, 2, 3, 4})
|
||||
ctx.Estack.Push(a)
|
||||
|
||||
// ctx.ip = -1
|
||||
// ctx.IP() = ctx.ip + 1
|
||||
assert.Equal(t, 0, ctx.IP())
|
||||
|
||||
// nothing will happen because
|
||||
// the value of the boolean on top of the stack
|
||||
// is false
|
||||
_, err := v.executeOp(stack.JMPIF, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have 0 item
|
||||
assert.Equal(t, 0, ctx.Estack.Len())
|
||||
|
||||
// ctx.IP() = ctx.ip + 1
|
||||
assert.Equal(t, 0, ctx.IP())
|
||||
}
|
||||
|
||||
// test JMPIFNOT instruction with true boolean
|
||||
// on top of the stack
|
||||
func TestJmpIfNotOp1(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a := stack.NewBoolean(true)
|
||||
|
||||
ctx := stack.NewContext([]byte{5, 0, 2, 3, 4})
|
||||
ctx.Estack.Push(a)
|
||||
|
||||
// ctx.ip = -1
|
||||
// ctx.IP() = ctx.ip + 1
|
||||
assert.Equal(t, 0, ctx.IP())
|
||||
|
||||
// nothing will happen because
|
||||
// the value of the boolean on top of the stack
|
||||
// is true
|
||||
_, err := v.executeOp(stack.JMPIFNOT, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have 0 item
|
||||
assert.Equal(t, 0, ctx.Estack.Len())
|
||||
|
||||
// ctx.IP() = ctx.ip + 1
|
||||
assert.Equal(t, 0, ctx.IP())
|
||||
}
|
||||
|
||||
// test JMPIFNOT instruction with false boolean
|
||||
// on top of the stack
|
||||
func TestJmpIfNotOp2(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a := stack.NewBoolean(false)
|
||||
|
||||
ctx := stack.NewContext([]byte{5, 0, 2, 3, 4})
|
||||
ctx.Estack.Push(a)
|
||||
|
||||
// ctx.ip = -1
|
||||
// ctx.IP() = ctx.ip + 1
|
||||
assert.Equal(t, 0, ctx.IP())
|
||||
|
||||
// ctx.ip will be set to offset
|
||||
// because the there is a false boolean
|
||||
// on top of the stack.
|
||||
// offset = ctx.IP() + int(ctx.ReadInt16()) - 3
|
||||
// = 0 + 5 -3 = 2
|
||||
_, err := v.executeOp(stack.JMPIFNOT, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 0, ctx.Estack.Len())
|
||||
|
||||
// ctx.IP() = ctx.ip + 1
|
||||
assert.Equal(t, 3, ctx.IP())
|
||||
}
|
|
@ -1,520 +0,0 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/vm/stack"
|
||||
)
|
||||
|
||||
// Add adds two stack Items together.
|
||||
// Returns an error if either items cannot be casted to an integer
|
||||
// or if integers cannot be added together
|
||||
func Add(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
operandA, operandB, err := popTwoIntegers(ctx)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
res, err := operandA.Add(operandB)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
ctx.Estack.Push(res)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// Sub subtracts two stack Items.
|
||||
// Returns an error if either items cannot be casted to an integer
|
||||
// or if integers cannot be subtracted together
|
||||
func Sub(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
operandA, operandB, err := popTwoIntegers(ctx)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
res, err := operandB.Sub(operandA)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
ctx.Estack.Push(res)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// Inc increments the stack Item's value by 1.
|
||||
// Returns an error if the item cannot be casted to an integer
|
||||
// or if 1 cannot be added to the item.
|
||||
func Inc(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
i, err := ctx.Estack.PopInt()
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
one, err := stack.NewInt(big.NewInt(1))
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
res, err := i.Add(one)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
ctx.Estack.Push(res)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// Dec decrements the stack Item's value by 1.
|
||||
// Returns an error if the item cannot be casted to an integer
|
||||
// or if 1 cannot be subtracted to the item.
|
||||
func Dec(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
i, err := ctx.Estack.PopInt()
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
one, err := stack.NewInt(big.NewInt(1))
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
res, err := i.Sub(one)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
ctx.Estack.Push(res)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// Div divides one stack Item by an other.
|
||||
// Returns an error if either items cannot be casted to an integer
|
||||
// or if the division of the integers cannot be performed.
|
||||
func Div(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
operandA, operandB, err := popTwoIntegers(ctx)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
res, err := operandB.Div(operandA)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
ctx.Estack.Push(res)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// Mod returns the mod of two stack Items.
|
||||
// Returns an error if either items cannot be casted to an integer
|
||||
// or if the mode of the integers cannot be performed.
|
||||
func Mod(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
operandA, operandB, err := popTwoIntegers(ctx)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
res, err := operandB.Mod(operandA)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
ctx.Estack.Push(res)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// Nz pops an integer from the stack.
|
||||
// Then pushes a boolean to the stack which evaluates to true
|
||||
// iff the integer was not zero.
|
||||
// Returns an error if the popped item cannot be casted to an integer
|
||||
// or if we cannot create a boolean.
|
||||
func Nz(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
i, err := ctx.Estack.PopInt()
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
b, err := i.Boolean()
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
ctx.Estack.Push(b)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// Mul multiplies two stack Items together.
|
||||
// Returns an error if either items cannot be casted to an integer
|
||||
// or if integers cannot be multiplied together.
|
||||
func Mul(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
operandA, operandB, err := popTwoIntegers(ctx)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
res, err := operandA.Mul(operandB)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
ctx.Estack.Push(res)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// NumEqual pops two Items off of the stack and pushes a boolean to the stack
|
||||
// whose value is true iff the the two Items are equal.
|
||||
// Returns an error if either items cannot be casted to an integer.
|
||||
func NumEqual(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
operandA, operandB, err := popTwoIntegers(ctx)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
res := operandA.Equal(operandB)
|
||||
|
||||
ctx.Estack.Push(stack.NewBoolean(res))
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// NumNotEqual pops two Items off of the stack and pushes a boolean to the stack
|
||||
// whose value is true iff the two Items are not equal.
|
||||
// Returns an error if either items cannot be casted to an integer.
|
||||
func NumNotEqual(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
operandA, operandB, err := popTwoIntegers(ctx)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
res := operandA.Equal(operandB)
|
||||
|
||||
ctx.Estack.Push(stack.NewBoolean(!res))
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// Min pops two integers, a and b, off of the stack and pushes an integer to the stack
|
||||
// whose value is is the minum between a and b's value.
|
||||
// Returns an error if either items cannot be casted to an integer
|
||||
func Min(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
operandA, operandB, err := popTwoIntegers(ctx)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
res := stack.Min(operandA, operandB)
|
||||
|
||||
ctx.Estack.Push(res)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// Max pops two integers, a and b, off of the stack and pushes an integer to the stack
|
||||
// whose value is is the maximum between a and b's value.
|
||||
// Returns an error if either items cannot be casted to an integer
|
||||
func Max(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
operandA, operandB, err := popTwoIntegers(ctx)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
res := stack.Max(operandA, operandB)
|
||||
|
||||
ctx.Estack.Push(res)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// Within pops three integers, a, b, and c off of the stack and pushes a boolean to the stack
|
||||
// whose value is true iff c's value is within b's value (include) and a's value.
|
||||
// Returns an error if at least one item cannot be casted to an boolean.
|
||||
func Within(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
a, b, c, err := popThreeIntegers(ctx)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
res := stack.NewBoolean(c.Within(b, a))
|
||||
|
||||
ctx.Estack.Push(res)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// Abs pops an integer off of the stack and pushes its absolute value onto the stack.
|
||||
// Returns an error if the popped value is not an integer or if the absolute value cannot be taken
|
||||
func Abs(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
i, err := ctx.Estack.PopInt()
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
a, err := i.Abs()
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
ctx.Estack.Push(a)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// Not flips the stack Item's value.
|
||||
// If the value is True, it is flipped to False and viceversa.
|
||||
func Not(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
b, err := ctx.Estack.PopBoolean()
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
ctx.Estack.Push(b.Not())
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// BoolAnd pops two booleans off of the stack and pushes a boolean to the stack
|
||||
// whose value is true iff both booleans' values are true.
|
||||
// Returns an error if either items cannot be casted to an boolean
|
||||
func BoolAnd(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
bool1, bool2, err := popTwoBooleans(ctx)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
res := bool1.And(bool2)
|
||||
ctx.Estack.Push(res)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// BoolOr pops two booleans off of the stack and pushes a boolean to the stack
|
||||
// whose value is true iff at least one of the two booleans' value is true.
|
||||
// Returns an error if either items cannot be casted to an boolean
|
||||
func BoolOr(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
bool1, bool2, err := popTwoBooleans(ctx)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
res := bool1.Or(bool2)
|
||||
|
||||
ctx.Estack.Push(res)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// Sign puts the sign of the top stack Item on top of the stack.
|
||||
// If value is negative, put -1;
|
||||
// If positive, put 1;
|
||||
// If value is zero, put 0.
|
||||
func Sign(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
i, err := ctx.Estack.PopInt()
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
s := int64(i.Value().Sign())
|
||||
sign, err := stack.NewInt(big.NewInt(s))
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
ctx.Estack.Push(sign)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// Negate flips the sign of the stack Item.
|
||||
func Negate(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
i, err := ctx.Estack.PopInt()
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
a := big.NewInt(0).Neg(i.Value())
|
||||
b, err := stack.NewInt(a)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
ctx.Estack.Push(b)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// Lte pops two integers, a and b, off of the stack and pushes a boolean the stack
|
||||
// whose value is true if a's value is less than or equal to b's value.
|
||||
// Returns an error if either items cannot be casted to an integer
|
||||
func Lte(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
operandA, operandB, err := popTwoIntegers(ctx)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
res := operandB.Lte(operandA)
|
||||
|
||||
ctx.Estack.Push(stack.NewBoolean(res))
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// Gte pops two integers, a and b, off of the stack and pushes a boolean the stack
|
||||
// whose value is true if a's value is greated than or equal to b's value.
|
||||
// Returns an error if either items cannot be casted to an integer
|
||||
func Gte(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
operandA, operandB, err := popTwoIntegers(ctx)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
res := operandB.Gte(operandA)
|
||||
|
||||
ctx.Estack.Push(stack.NewBoolean(res))
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// Shl pops two integers, a and b, off of the stack and pushes an integer to the stack
|
||||
// whose value is the b's value shift to the left by a's value bits.
|
||||
// Returns an error if either items cannot be casted to an integer
|
||||
// or if the left shift operation cannot per performed with the two integer's value.
|
||||
func Shl(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
a, b, err := popTwoIntegers(ctx)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
res, err := b.Lsh(a)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
ctx.Estack.Push(res)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// Shr pops two integers, a and b, off of the stack and pushes an integer to the stack
|
||||
// whose value is the b's value shift to the right by a's value bits.
|
||||
// Returns an error if either items cannot be casted to an integer
|
||||
// or if the right shift operation cannot per performed with the two integer's value.
|
||||
func Shr(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
a, b, err := popTwoIntegers(ctx)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
res, err := b.Rsh(a)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
ctx.Estack.Push(res)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// Lt pops two integers, a and b, off of the stack and pushes a boolean the stack
|
||||
// whose value is true if a's value is less than b's value.
|
||||
// Returns an error if either items cannot be casted to an integer
|
||||
func Lt(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
operandA, operandB, err := popTwoIntegers(ctx)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
res := operandB.Lt(operandA)
|
||||
|
||||
ctx.Estack.Push(stack.NewBoolean(res))
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// Gt pops two integers, a and b, off of the stack and pushes a boolean the stack
|
||||
// whose value is true if a's value is greated than b's value.
|
||||
// Returns an error if either items cannot be casted to an integer
|
||||
func Gt(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
operandA, operandB, err := popTwoIntegers(ctx)
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
res := operandB.Gt(operandA)
|
||||
|
||||
ctx.Estack.Push(stack.NewBoolean(res))
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
func popTwoIntegers(ctx *stack.Context) (*stack.Int, *stack.Int, error) {
|
||||
operandA, err := ctx.Estack.PopInt()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
operandB, err := ctx.Estack.PopInt()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return operandA, operandB, nil
|
||||
}
|
||||
|
||||
func popThreeIntegers(ctx *stack.Context) (*stack.Int, *stack.Int, *stack.Int, error) {
|
||||
operandA, err := ctx.Estack.PopInt()
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
operandB, err := ctx.Estack.PopInt()
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
operandC, err := ctx.Estack.PopInt()
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
return operandA, operandB, operandC, nil
|
||||
}
|
||||
|
||||
func popTwoByteArrays(ctx *stack.Context) (*stack.ByteArray, *stack.ByteArray, error) {
|
||||
// Pop first stack item and cast as byte array
|
||||
ba1, err := ctx.Estack.PopByteArray()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// Pop second stack item and cast as byte array
|
||||
ba2, err := ctx.Estack.PopByteArray()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return ba1, ba2, nil
|
||||
}
|
||||
|
||||
func popTwoBooleans(ctx *stack.Context) (*stack.Boolean, *stack.Boolean, error) {
|
||||
bool1, err := ctx.Estack.PopBoolean()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
bool2, err := ctx.Estack.PopBoolean()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return bool1, bool2, nil
|
||||
}
|
|
@ -1,648 +0,0 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/vm/stack"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIncOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a, err := stack.NewInt(big.NewInt(20))
|
||||
assert.Nil(t, err)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(a)
|
||||
|
||||
_, err = v.executeOp(stack.INC, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopInt()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(21), item.Value().Int64())
|
||||
}
|
||||
|
||||
func TestDecOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a, err := stack.NewInt(big.NewInt(20))
|
||||
assert.Nil(t, err)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(a)
|
||||
|
||||
_, err = v.executeOp(stack.DEC, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopInt()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(19), item.Value().Int64())
|
||||
}
|
||||
|
||||
func TestAddOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a, err := stack.NewInt(big.NewInt(20))
|
||||
assert.Nil(t, err)
|
||||
|
||||
b, err := stack.NewInt(big.NewInt(23))
|
||||
assert.Nil(t, err)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(a).Push(b)
|
||||
|
||||
_, err = v.executeOp(stack.ADD, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopInt()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(43), item.Value().Int64())
|
||||
|
||||
}
|
||||
|
||||
func TestSubOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a, err := stack.NewInt(big.NewInt(30))
|
||||
assert.Nil(t, err)
|
||||
|
||||
b, err := stack.NewInt(big.NewInt(40))
|
||||
assert.Nil(t, err)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(a).Push(b)
|
||||
|
||||
_, err = v.executeOp(stack.SUB, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopInt()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(-10), item.Value().Int64())
|
||||
|
||||
}
|
||||
|
||||
func TestDivOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a, err := stack.NewInt(big.NewInt(10))
|
||||
assert.Nil(t, err)
|
||||
|
||||
b, err := stack.NewInt(big.NewInt(4))
|
||||
assert.Nil(t, err)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(a).Push(b)
|
||||
|
||||
_, err = v.executeOp(stack.DIV, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopInt()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(2), item.Value().Int64())
|
||||
}
|
||||
|
||||
func TestModOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a, err := stack.NewInt(big.NewInt(15))
|
||||
assert.Nil(t, err)
|
||||
|
||||
b, err := stack.NewInt(big.NewInt(4))
|
||||
assert.Nil(t, err)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(a).Push(b)
|
||||
|
||||
_, err = v.executeOp(stack.MOD, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopInt()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(3), item.Value().Int64())
|
||||
}
|
||||
|
||||
func TestNzOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a, err := stack.NewInt(big.NewInt(20))
|
||||
assert.Nil(t, err)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(a)
|
||||
|
||||
_, err = v.executeOp(stack.NZ, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopBoolean()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, true, item.Value())
|
||||
}
|
||||
|
||||
func TestMulOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a, err := stack.NewInt(big.NewInt(20))
|
||||
assert.Nil(t, err)
|
||||
|
||||
b, err := stack.NewInt(big.NewInt(20))
|
||||
assert.Nil(t, err)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(a).Push(b)
|
||||
|
||||
_, err = v.executeOp(stack.MUL, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopInt()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(400), item.Value().Int64())
|
||||
}
|
||||
|
||||
func TestAbsOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a, err := stack.NewInt(big.NewInt(-20))
|
||||
assert.Nil(t, err)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(a)
|
||||
|
||||
_, err = v.executeOp(stack.ABS, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopInt()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(20), item.Value().Int64())
|
||||
}
|
||||
|
||||
func TestNotOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
b := stack.NewBoolean(false)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(b)
|
||||
|
||||
_, err := v.executeOp(stack.NOT, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopBoolean()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, true, item.Value())
|
||||
}
|
||||
|
||||
func TestNumEqual(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a, err := stack.NewInt(big.NewInt(6))
|
||||
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)
|
||||
|
||||
_, err = v.executeOp(stack.NUMEQUAL, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopBoolean()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, true, item.Value())
|
||||
}
|
||||
|
||||
func TestNumNotEqual(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a, err := stack.NewInt(big.NewInt(5))
|
||||
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)
|
||||
|
||||
_, err = v.executeOp(stack.NUMNOTEQUAL, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopBoolean()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, true, item.Value())
|
||||
}
|
||||
|
||||
func TestSignOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a, err := stack.NewInt(big.NewInt(-20))
|
||||
assert.Nil(t, err)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(a)
|
||||
|
||||
_, err = v.executeOp(stack.SIGN, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopInt()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(-1), item.Value().Int64())
|
||||
}
|
||||
|
||||
func TestNegateOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a, err := stack.NewInt(big.NewInt(-20))
|
||||
assert.Nil(t, err)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(a)
|
||||
|
||||
_, err = v.executeOp(stack.NEGATE, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopInt()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(20), item.Value().Int64())
|
||||
}
|
||||
|
||||
func TestLteOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a, err := stack.NewInt(big.NewInt(10))
|
||||
assert.Nil(t, err)
|
||||
|
||||
b, err := stack.NewInt(big.NewInt(10))
|
||||
assert.Nil(t, err)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(a).Push(b)
|
||||
|
||||
// b is the first item pop.
|
||||
// a is the second item pop.
|
||||
// we perform a <= b and place
|
||||
// the result on top of the evaluation
|
||||
// stack
|
||||
_, err = v.executeOp(stack.LTE, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopBoolean()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, true, item.Value())
|
||||
}
|
||||
|
||||
func TestGteOp(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).Push(b)
|
||||
|
||||
// b is the first item pop.
|
||||
// a is the second item pop.
|
||||
// we perform a >= b and place
|
||||
// the result on top of the evaluation
|
||||
// stack
|
||||
_, err = v.executeOp(stack.GTE, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopBoolean()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, true, item.Value())
|
||||
}
|
||||
|
||||
func TestShlOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a, err := stack.NewInt(big.NewInt(2))
|
||||
assert.Nil(t, err)
|
||||
|
||||
b, err := stack.NewInt(big.NewInt(3))
|
||||
assert.Nil(t, err)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(a).Push(b)
|
||||
|
||||
// b is the first item pop.
|
||||
// a is the second item pop.
|
||||
// we perform a.Lsh(b) and place
|
||||
// the result on top of the evaluation
|
||||
// stack
|
||||
_, err = v.executeOp(stack.SHL, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopInt()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(16), item.Value().Int64())
|
||||
}
|
||||
|
||||
func TestShrOp(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).Push(b)
|
||||
|
||||
// b is the first item pop.
|
||||
// a is the second item pop.
|
||||
// we perform a.Rsh(b) and place
|
||||
// the result on top of the evaluation
|
||||
// stack
|
||||
_, err = v.executeOp(stack.SHR, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopInt()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(2), item.Value().Int64())
|
||||
}
|
||||
|
||||
func TestBoolAndOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a := stack.NewBoolean(true)
|
||||
b := stack.NewBoolean(true)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(a).Push(b)
|
||||
|
||||
_, err := v.executeOp(stack.BOOLAND, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopBoolean()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, true, item.Value())
|
||||
}
|
||||
|
||||
func TestBoolOrOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a := stack.NewBoolean(false)
|
||||
b := stack.NewBoolean(true)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(a).Push(b)
|
||||
|
||||
_, err := v.executeOp(stack.BOOLOR, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopBoolean()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, true, item.Value())
|
||||
}
|
||||
|
||||
func TestLtOp(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).Push(b)
|
||||
|
||||
// b is the first item pop.
|
||||
// a is the second item pop.
|
||||
// we perform a < b and place
|
||||
// the result on top of the evaluation
|
||||
// stack
|
||||
_, err = v.executeOp(stack.LT, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopBoolean()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, false, item.Value())
|
||||
}
|
||||
|
||||
func TestGtOp(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).Push(b)
|
||||
|
||||
// b is the first item pop.
|
||||
// a is the second item pop.
|
||||
// we perform a > b and place
|
||||
// the result on top of the evaluation
|
||||
// stack
|
||||
_, err = v.executeOp(stack.GT, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopBoolean()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, true, item.Value())
|
||||
}
|
||||
|
||||
func TestMinOp(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).Push(b)
|
||||
|
||||
_, err = v.executeOp(stack.MIN, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopInt()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(2), item.Value().Int64())
|
||||
}
|
||||
|
||||
func TestMaxOp(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).Push(b)
|
||||
|
||||
_, err = v.executeOp(stack.MAX, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopInt()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, int64(10), item.Value().Int64())
|
||||
}
|
||||
|
||||
func TestWithinOp(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a, err := stack.NewInt(big.NewInt(5))
|
||||
assert.Nil(t, err)
|
||||
|
||||
b, err := stack.NewInt(big.NewInt(2))
|
||||
assert.Nil(t, err)
|
||||
|
||||
c, err := stack.NewInt(big.NewInt(10))
|
||||
assert.Nil(t, err)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(a).Push(b).Push(c)
|
||||
|
||||
// c is the first item popped.
|
||||
// b is the second item popped.
|
||||
// a is the third item popped.
|
||||
// if a is within [b, c) we place a boolean,
|
||||
// whose value is true, on top of the evaluation
|
||||
// stack. Otherwise we place a boolean with
|
||||
// false value.
|
||||
_, err = v.executeOp(stack.WITHIN, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.PopBoolean()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, true, item.Value())
|
||||
}
|
|
@ -1,300 +0,0 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/vm/stack"
|
||||
)
|
||||
|
||||
// Stack Manipulation Opcodes
|
||||
|
||||
// PushNBytes will Read N Bytes from the script and push it onto the stack
|
||||
func PushNBytes(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
val, err := ctx.ReadBytes(int(op))
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
ba := stack.NewByteArray(val)
|
||||
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
|
||||
}
|
||||
|
||||
// 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.
|
||||
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
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
||||
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
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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.
|
||||
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
|
||||
// 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
|
||||
}
|
||||
|
||||
_, err = ctx.Estack.Remove(uint16(n.Value().Uint64()))
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
return NONE, nil
|
||||
}
|
|
@ -1,568 +0,0 @@
|
|||
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]
|
||||
_, err = v.executeOp(stack.ROLL, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// 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]
|
||||
_, err = v.executeOp(stack.ROT, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// 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]
|
||||
_, err = v.executeOp(stack.SWAP, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// 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]
|
||||
_, err = v.executeOp(stack.TUCK, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// 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) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
a, err := stack.NewInt(big.NewInt(3))
|
||||
assert.Nil(t, err)
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(a)
|
||||
|
||||
_, err = v.executeOp(stack.DUP, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// 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)
|
||||
|
||||
_, err = v.executeOp(stack.NIP, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// 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].
|
||||
_, err = v.executeOp(stack.OVER, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// 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]
|
||||
_, err = v.executeOp(stack.PICK, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// 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{}
|
||||
|
||||
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]
|
||||
_, err = v.executeOp(stack.XSWAP, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// 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]
|
||||
_, err = v.executeOp(stack.XTUCK, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// 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
|
||||
_, err = v.executeOp(stack.DEPTH, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// 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) {
|
||||
|
||||
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)
|
||||
|
||||
_, err = v.executeOp(stack.DUPFROMALTSTACK, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
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)
|
||||
|
||||
_, err = v.executeOp(stack.TOALTSTACK, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
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)
|
||||
|
||||
_, err = v.executeOp(stack.FROMALTSTACK, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
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.
|
||||
_, err = v.executeOp(stack.XDROP, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
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())
|
||||
|
||||
}
|
|
@ -1,146 +0,0 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/vm/stack"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPushAdd(t *testing.T) {
|
||||
builder := stack.NewBuilder()
|
||||
|
||||
// PUSH TWO NUMBER
|
||||
// ADD THEM TOGETHER
|
||||
builder.EmitInt(20).EmitInt(34).EmitOpcode(stack.ADD)
|
||||
|
||||
// Pass program to VM
|
||||
vm := NewVM(builder.Bytes())
|
||||
|
||||
// Execute first OPCODE
|
||||
// Should be PUSH(20)
|
||||
state, err := vm.step()
|
||||
assert.Equal(t, NONE, int(state))
|
||||
assert.Nil(t, err)
|
||||
|
||||
// We should have the number 20 on stack
|
||||
ok := peekTopEStackIsValue(t, vm, 20)
|
||||
assert.True(t, ok)
|
||||
|
||||
// Excute second OPCODE
|
||||
// Should be PUSH(34)
|
||||
state, err = vm.step()
|
||||
assert.Equal(t, NONE, int(state))
|
||||
assert.Nil(t, err)
|
||||
|
||||
// We should have the number 34 at the top of the stack
|
||||
ok = peekTopEStackIsValue(t, vm, 34)
|
||||
assert.True(t, ok)
|
||||
|
||||
// Excute third OPCODE
|
||||
// Should Add both values on the stack
|
||||
state, err = vm.step()
|
||||
assert.Equal(t, NONE, int(state))
|
||||
assert.Nil(t, err)
|
||||
|
||||
// We should now have one value on the stack
|
||||
//It should be equal to 20+34 = 54
|
||||
ok = EstackLen(t, vm, 1)
|
||||
assert.True(t, ok)
|
||||
ok = peekTopEStackIsValue(t, vm, 54)
|
||||
assert.True(t, ok)
|
||||
|
||||
// If we try to step again, we should get a nil error and HALT
|
||||
// because we have gone over the instruction pointer
|
||||
// error is nil because when there are nomore instructions, the vm
|
||||
// will add a RET opcode and return
|
||||
state, err = vm.step()
|
||||
assert.Equal(t, HALT, int(state))
|
||||
assert.Nil(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestThrowIfNot(t *testing.T) {
|
||||
|
||||
// Program pushes 20 and 34 to the stack
|
||||
// Adds them together
|
||||
// pushes 54 to the stack
|
||||
// Checks if result of addition and 54 are equal
|
||||
// Faults if not
|
||||
|
||||
// Push(20)
|
||||
// Push(34)
|
||||
// Add
|
||||
// Push(54)
|
||||
// Equal
|
||||
//THROWIFNOT
|
||||
builder := stack.NewBuilder()
|
||||
builder.EmitInt(20).EmitInt(34).EmitOpcode(stack.ADD)
|
||||
builder.EmitInt(54).EmitOpcode(stack.EQUAL).EmitOpcode(stack.THROWIFNOT)
|
||||
|
||||
// Pass program to VM
|
||||
vm := NewVM(builder.Bytes())
|
||||
|
||||
// Runs vm with program
|
||||
_, err := vm.Run()
|
||||
assert.Nil(t, err)
|
||||
|
||||
// ResultStack should be nil
|
||||
assert.Equal(t, -1, vm.ResultStack.Len())
|
||||
|
||||
// InvocationStack should be empty
|
||||
assert.Equal(t, 0, vm.InvocationStack.Len())
|
||||
|
||||
}
|
||||
|
||||
func TestThrow(t *testing.T) {
|
||||
|
||||
// Program pushes 20 to the stack
|
||||
// exits with an error
|
||||
|
||||
// Push(20)
|
||||
// THROW
|
||||
|
||||
builder := stack.NewBuilder()
|
||||
builder.EmitInt(20).EmitOpcode(stack.THROW)
|
||||
|
||||
// Pass program to VM
|
||||
vm := NewVM(builder.Bytes())
|
||||
|
||||
// Runs vm with program
|
||||
_, err := vm.Run()
|
||||
assert.NotNil(t, err)
|
||||
|
||||
ctx, err := vm.InvocationStack.CurrentContext()
|
||||
assert.Equal(t, nil, err)
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
assert.Equal(t, -1, ctx.Astack.Len())
|
||||
}
|
||||
|
||||
// returns true if the value at the top of the evaluation stack is a integer
|
||||
// and equals the value passed in
|
||||
func peekTopEStackIsValue(t *testing.T, vm *VM, value int64) bool {
|
||||
item := peakTopEstack(t, vm)
|
||||
integer, err := item.Integer()
|
||||
assert.Nil(t, err)
|
||||
return value == integer.Value().Int64()
|
||||
}
|
||||
|
||||
// peaks the stack item on the top of the evaluation stack
|
||||
// if the current context and returns it
|
||||
func peakTopEstack(t *testing.T, vm *VM) stack.Item {
|
||||
ctx, err := vm.InvocationStack.CurrentContext()
|
||||
fmt.Println(err)
|
||||
assert.Nil(t, err)
|
||||
item, err := ctx.Estack.Peek(0)
|
||||
assert.Nil(t, err)
|
||||
return item
|
||||
}
|
||||
|
||||
// returns true if the total number of items on the evaluation stack is equal to value
|
||||
func EstackLen(t *testing.T, vm *VM, value int) bool {
|
||||
ctx, err := vm.InvocationStack.CurrentContext()
|
||||
assert.Nil(t, err)
|
||||
return value == ctx.Estack.Len()
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
||||
"github.com/CityOfZion/neo-go/pkg/vm/stack"
|
||||
)
|
||||
|
||||
// SHA1 pops an item off of the stack and
|
||||
// pushes a bytearray onto the stack whose value
|
||||
// is obtained by applying the sha1 algorithm to
|
||||
// the corresponding bytearray representation of the item.
|
||||
// Returns an error if the Pop method cannot be execute or
|
||||
// the popped item does not have a concrete bytearray implementation.
|
||||
func SHA1(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
ba, err := ctx.Estack.PopByteArray()
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
alg := sha1.New()
|
||||
_, _ = alg.Write(ba.Value())
|
||||
hash := alg.Sum(nil)
|
||||
res := stack.NewByteArray(hash)
|
||||
|
||||
ctx.Estack.Push(res)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// SHA256 pops an item off of the stack and
|
||||
// pushes a bytearray onto the stack whose value
|
||||
// is obtained by applying the Sha256 algorithm to
|
||||
// the corresponding bytearray representation of the item.
|
||||
// Returns an error if the Pop method cannot be execute or
|
||||
// the popped item does not have a concrete bytearray implementation.
|
||||
func SHA256(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
ba, err := ctx.Estack.PopByteArray()
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
hash, err := hash.Sha256(ba.Value())
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
res := stack.NewByteArray(hash.Bytes())
|
||||
|
||||
ctx.Estack.Push(res)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// HASH160 pops an item off of the stack and
|
||||
// pushes a bytearray onto the stack whose value
|
||||
// is obtained by applying the Hash160 algorithm to
|
||||
// the corresponding bytearray representation of the item.
|
||||
// Returns an error if the Pop method cannot be execute or
|
||||
// the popped item does not have a concrete bytearray implementation.
|
||||
func HASH160(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
ba, err := ctx.Estack.PopByteArray()
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
hash, err := hash.Hash160(ba.Value())
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
res := stack.NewByteArray(hash.Bytes())
|
||||
|
||||
ctx.Estack.Push(res)
|
||||
|
||||
return NONE, nil
|
||||
}
|
||||
|
||||
// HASH256 pops an item off of the stack and
|
||||
// pushes a bytearray onto the stack whose value
|
||||
// is obtained by applying the Hash256 algorithm to
|
||||
// the corresponding bytearray representation of the item.
|
||||
// Returns an error if the Pop method cannot be execute or
|
||||
// the popped item does not have a concrete bytearray implementation.
|
||||
func HASH256(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||
|
||||
ba, err := ctx.Estack.PopByteArray()
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
hash, err := hash.DoubleSha256(ba.Value())
|
||||
if err != nil {
|
||||
return FAULT, err
|
||||
}
|
||||
|
||||
res := stack.NewByteArray(hash.Bytes())
|
||||
|
||||
ctx.Estack.Push(res)
|
||||
|
||||
return NONE, nil
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/vm/stack"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSha1Op(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
ba1 := stack.NewByteArray([]byte("this is test string"))
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(ba1)
|
||||
|
||||
_, err := v.executeOp(stack.SHA1, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.Pop()
|
||||
assert.Nil(t, err)
|
||||
|
||||
ba2, err := item.ByteArray()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, "62d40fe74cf301cbfbe55c2679b96352449fb26d", hex.EncodeToString(ba2.Value()))
|
||||
}
|
||||
|
||||
func TestSha256Op(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
ba1 := stack.NewByteArray([]byte("this is test string"))
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(ba1)
|
||||
|
||||
_, err := v.executeOp(stack.SHA256, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.Pop()
|
||||
assert.Nil(t, err)
|
||||
|
||||
ba2, err := item.ByteArray()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, "8e76c5b9e6be2559bedccbd0ff104ebe02358ba463a44a68e96caf55f9400de5", hex.EncodeToString(ba2.Value()))
|
||||
}
|
||||
|
||||
func TestHash160Op(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
ba1 := stack.NewByteArray([]byte("this is test string"))
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(ba1)
|
||||
|
||||
_, err := v.executeOp(stack.HASH160, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.Pop()
|
||||
assert.Nil(t, err)
|
||||
|
||||
ba2, err := item.ByteArray()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, "e9c052b05a762ca9961a975db52e5417d99d958c", hex.EncodeToString(ba2.Value()))
|
||||
}
|
||||
|
||||
func TestHash256Op(t *testing.T) {
|
||||
|
||||
v := VM{}
|
||||
|
||||
ba1 := stack.NewByteArray([]byte("this is test string"))
|
||||
|
||||
ctx := stack.NewContext([]byte{})
|
||||
ctx.Estack.Push(ba1)
|
||||
|
||||
_, err := v.executeOp(stack.HASH256, ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Stack should have one item
|
||||
assert.Equal(t, 1, ctx.Estack.Len())
|
||||
|
||||
item, err := ctx.Estack.Pop()
|
||||
assert.Nil(t, err)
|
||||
|
||||
ba2, err := item.ByteArray()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, "90ef790ee2557a3f9a1ba0e6910a9ff0ea75af3767ea7380760d729ac9927a60", hex.EncodeToString(ba2.Value()))
|
||||
}
|
Loading…
Reference in a new issue