Merge pull request #287 from nspcc-dev/vm

VM improvements for dev branch, merging as outlined in #283.
This commit is contained in:
Roman Khimov 2019-08-12 19:19:03 +03:00 committed by GitHub
commit 2e7c0daa25
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 3393 additions and 51 deletions

View file

@ -26,7 +26,7 @@ func ExampleSignECDSA() {
// Hash a message.
alg := sha512.New()
alg.Write([]byte("I am a potato."))
_, _ = alg.Write([]byte("I am a potato."))
hash := alg.Sum(nil)
// Sign the message. You don't need a PRNG for this.
@ -59,7 +59,7 @@ func ExampleSignDSA() {
// Hash a message.
alg := sha1.New()
alg.Write([]byte("I am a potato."))
_, _ = alg.Write([]byte("I am a potato."))
hash := alg.Sum(nil)
// Sign the message. You don't need a PRNG for this.

View file

@ -0,0 +1,81 @@
{
"category": "Push",
"name": "PUSHBYTES1",
"tests":
[
{
"name": "Good definition",
"script": "0x0100",
"steps":
[
{
"actions":
[
"StepInto"
],
"result":
{
"state": "Break",
"invocationStack":
[
{
"scriptHash": "0xFBC22D517F38E7612798ECE8E5957CF6C41D8CAF",
"instructionPointer": 2,
"nextInstruction": "RET",
"evaluationStack":
[
{
"type": "ByteArray",
"value": "0x00"
}
]
}
]
}
},
{
"actions":
[
"StepInto"
],
"result":
{
"state": "Halt",
"resultStack":
[
{
"type": "ByteArray",
"value": "0x00"
}
]
}
}
]
},
{
"name": "Wrong definition (without enough length)",
"script": "0x01",
"steps":
[
{
"actions":
[
"StepInto"
],
"result":
{
"state": "Fault",
"invocationStack":
[
{
"scriptHash": "0xC51B66BCED5E4491001BD702669770DCCF440982",
"instructionPointer": 1,
"nextInstruction": "RET"
}
]
}
}
]
}
]
}

View file

@ -0,0 +1,6 @@
## Package VM Interop
This package will use the tests in the neo-vm repo to test interopabilty

View file

@ -0,0 +1,26 @@
package csharpinterop
// VMUnitTest is a struct for capturing the fields in the json files
type VMUnitTest struct {
Category string `json:"category"`
Name string `json:"name"`
Tests []struct {
Name string `json:"name"`
Script string `json:"script"`
Steps []struct {
Actions []string `json:"actions"`
Result struct {
State string `json:"state"`
InvocationStack []struct {
ScriptHash string `json:"scriptHash"`
InstructionPointer int `json:"instructionPointer"`
NextInstruction string `json:"nextInstruction"`
EvaluationStack []struct {
Type string `json:"type"`
Value string `json:"value"`
} `json:"evaluationStack"`
} `json:"invocationStack"`
} `json:"result"`
} `json:"steps"`
} `json:"tests"`
}

View file

@ -1,6 +1,9 @@
package stack
import "math/big"
import (
"fmt"
"math/big"
)
// Int represents an integer on the stack
type Int struct {
@ -19,10 +22,7 @@ func NewInt(val *big.Int) (*Int, error) {
// Equal will check if two integers hold equal value
func (i *Int) Equal(s *Int) bool {
if i.val.Cmp(s.val) != 0 {
return false
}
return true
return i.val.Cmp(s.val) == 0
}
// Add will add two stackIntegers together
@ -46,6 +46,13 @@ func (i *Int) Mul(s *Int) (*Int, error) {
}, 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{
@ -92,3 +99,104 @@ func (i *Int) Boolean() (*Boolean, error) {
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)
}

View file

@ -1,5 +1,9 @@
package stack
import (
"fmt"
)
// Array represents an Array of stackItems on the stack
type Array struct {
*abstractItem
@ -11,3 +15,22 @@ type 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))
}

View file

@ -0,0 +1,16 @@
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
}

View file

@ -1,5 +1,9 @@
package stack
import (
"fmt"
)
// Boolean represents a boolean value on the stack
type Boolean struct {
*abstractItem
@ -24,3 +28,29 @@ func (b *Boolean) Boolean() (*Boolean, error) {
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))
}

View file

@ -3,6 +3,7 @@ package stack
import (
"bytes"
"errors"
"fmt"
"math/big"
"strconv"
)
@ -69,3 +70,14 @@ func reverse(b []byte) []byte {
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))
}

View file

@ -3,6 +3,7 @@ package stack
import (
"encoding/binary"
"errors"
"fmt"
)
// Context represent the current execution context of the VM.
@ -120,6 +121,11 @@ func (c *Context) ReadUint16() uint16 {
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)
@ -150,3 +156,19 @@ func (c *Context) readVarBytes() ([]byte, error) {
}
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))
}

View file

@ -64,6 +64,8 @@ func TestByteArrConversion(t *testing.T) {
ba, err := a.ByteArray()
assert.Nil(t, err)
assert.Equal(t, num, testReadInt64(t, ba.val))
have, err := ba.Integer()
assert.Nil(t, err)

166
pkg/vm/stack/map.go Normal file
View file

@ -0,0 +1,166 @@
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
}

141
pkg/vm/stack/map_test.go Normal file
View file

@ -0,0 +1,141 @@
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())
}

View file

@ -23,11 +23,6 @@ func New() *RandomAccess {
}
}
// Items will return all items in the stack
func (ras *RandomAccess) items() []Item {
return ras.vals
}
//Len will return the length of the stack
func (ras *RandomAccess) Len() int {
if ras.vals == nil {
@ -127,6 +122,21 @@ func (ras *RandomAccess) CopyTo(stack *RandomAccess) error {
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
@ -148,3 +158,29 @@ func (ras *RandomAccess) PopByteArray() (*ByteArray, error) {
}
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
}

View file

@ -51,7 +51,7 @@ func TestStackPushPop(t *testing.T) {
assert.Equal(t, true, item.Equal(a))
// We should get an error as there are nomore items left to pop
stackElement, err = testStack.Pop()
_, err = testStack.Pop()
assert.NotNil(t, err)
}
@ -97,7 +97,7 @@ func TestStackPeekMutability(t *testing.T) {
testStack.Push(a).Push(b)
peekedItem := testPeakInteger(t, testStack, 0)
peekedItem := testPeekInteger(t, testStack, 0)
assert.Equal(t, true, peekedItem.Equal(b))
// Check that by modifying the peeked value,
@ -122,7 +122,7 @@ func TestStackPeek(t *testing.T) {
// 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 := testPeakInteger(t, testStack, uint16(i))
peekedItem := testPeekInteger(t, testStack, uint16(i))
a := testMakeStackInt(t, values[j])
fmt.Printf("%#v\n", peekedItem.val.Int64())

View file

@ -11,6 +11,8 @@ type Item interface {
ByteArray() (*ByteArray, error)
Array() (*Array, error)
Context() (*Context, error)
Map() (*Map, error)
Hash() (string, error)
}
// Represents an `abstract` stack item
@ -47,3 +49,13 @@ func (a *abstractItem) Array() (*Array, error) {
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")
}

View file

@ -10,7 +10,7 @@ import (
)
// helper functions
func testPeakInteger(t *testing.T, tStack *RandomAccess, n uint16) *Int {
func testPeekInteger(t *testing.T, tStack *RandomAccess, n uint16) *Int {
stackElement, err := tStack.Peek(n)
assert.Nil(t, err)
item, err := stackElement.Integer()
@ -36,9 +36,26 @@ func testMakeStackInt(t *testing.T, num int64) *Int {
return a
}
func testReadInt64(data []byte) int64 {
func testReadInt64(t *testing.T, data []byte) int64 {
var ret int64
buf := bytes.NewBuffer(data)
binary.Read(buf, binary.LittleEndian, &ret)
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
}

View file

@ -5,13 +5,65 @@ 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.ADD: Add,
stack.SUB: Sub,
stack.PUSHBYTES1: PushNBytes,
stack.PUSHBYTES75: PushNBytes,
stack.RET: RET,
stack.EQUAL: EQUAL,
stack.THROWIFNOT: THROWIFNOT,
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() {

View file

@ -15,3 +15,88 @@ func EQUAL(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, r
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
}

View file

@ -0,0 +1,142 @@
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())
}

View file

@ -31,3 +31,9 @@ func THROWIFNOT(op stack.Instruction, ctx *stack.Context, istack *stack.Invocati
}
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")
}

View file

@ -9,6 +9,7 @@ import (
// 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()
@ -25,3 +26,71 @@ func RET(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rst
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
}

174
pkg/vm/vm_ops_flow_test.go Normal file
View file

@ -0,0 +1,174 @@
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())
}

View file

@ -1,6 +1,8 @@
package vm
import (
"math/big"
"github.com/CityOfZion/neo-go/pkg/vm/stack"
)
@ -34,7 +36,7 @@ func Sub(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rst
}
res, err := operandB.Sub(operandA)
if err != nil {
return HALT, err
return FAULT, err
}
ctx.Estack.Push(res)
@ -42,6 +44,424 @@ func Sub(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rst
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 {
@ -55,6 +475,23 @@ func popTwoIntegers(ctx *stack.Context) (*stack.Int, *stack.Int, error) {
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()
@ -68,3 +505,16 @@ func popTwoByteArrays(ctx *stack.Context) (*stack.ByteArray, *stack.ByteArray, e
}
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
}

View file

@ -8,31 +8,71 @@ import (
"github.com/stretchr/testify/assert"
)
func TestAddOp(t *testing.T) {
func TestIncOp(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(20))
if err != nil {
t.Fail()
}
b, err := stack.NewInt(big.NewInt(23))
if err != nil {
t.Fail()
}
assert.Nil(t, err)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a).Push(b)
ctx.Estack.Push(a)
v.executeOp(stack.ADD, ctx)
_, 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()
if err != nil {
t.Fail()
}
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())
@ -43,27 +83,566 @@ func TestSubOp(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(30))
if err != nil {
t.Fail()
}
assert.Nil(t, err)
b, err := stack.NewInt(big.NewInt(40))
if err != nil {
t.Fail()
}
assert.Nil(t, err)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a).Push(b)
v.executeOp(stack.SUB, ctx)
_, 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()
if err != nil {
t.Fail()
}
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())
}

View file

@ -1,6 +1,8 @@
package vm
import (
"math/big"
"github.com/CityOfZion/neo-go/pkg/vm/stack"
)
@ -17,3 +19,282 @@ func PushNBytes(op stack.Instruction, ctx *stack.Context, istack *stack.Invocati
ctx.Estack.Push(ba)
return NONE, nil
}
// ROLL pops an integer n off of the stack and
// moves the n-item starting from
// the top of the stack onto the top stack item.
// Returns an error if the top stack item is not an
// integer or n-item does not exist.
func ROLL(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
n, err := ctx.Estack.PopInt()
if err != nil {
return FAULT, err
}
nItem, err := ctx.Estack.Remove(uint16(n.Value().Int64()))
if err != nil {
return FAULT, err
}
ctx.Estack.Push(nItem)
return NONE, nil
}
// ROT moves the third top stack item
// onto the top stack item.
// Returns an error if the third top stack item
// does not exist.
func ROT(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
item, err := ctx.Estack.Remove(2)
if err != nil {
return FAULT, err
}
ctx.Estack.Push(item)
return NONE, nil
}
// SWAP swaps the second top stack item with
// the top stack item.
// Returns an error if the second top stack item
// does not exist.
func SWAP(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
item, err := ctx.Estack.Remove(1)
if err != nil {
return FAULT, err
}
ctx.Estack.Push(item)
return NONE, nil
}
// TUCK copies the top stack item and
// inserts it before the second top stack item.
// Returns an error if the stack is empty or
// len(stack) is less or equal 2.
func TUCK(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
item, err := ctx.Estack.Peek(0)
if err != nil {
return FAULT, err
}
ras, err := ctx.Estack.Insert(2, item)
if err != nil {
return FAULT, err
}
ctx.Estack = *ras
return NONE, nil
}
// 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
}

View file

@ -0,0 +1,568 @@
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())
}

View file

@ -61,7 +61,7 @@ func TestPushAdd(t *testing.T) {
}
func TestSimpleRun(t *testing.T) {
func TestThrowIfNot(t *testing.T) {
// Program pushes 20 and 34 to the stack
// Adds them together
@ -89,6 +89,33 @@ func TestSimpleRun(t *testing.T) {
// 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

106
pkg/vm/vmopscrypto.go Normal file
View file

@ -0,0 +1,106 @@
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
}

105
pkg/vm/vmopscrypto_test.go Normal file
View file

@ -0,0 +1,105 @@
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()))
}