forked from TrueCloudLab/neoneo-go
Merge branch 'vm' into dauTT/vm-XSWAP-XTUCK-DEPTH-DROP-opcode
This commit is contained in:
commit
5167c37255
24 changed files with 1696 additions and 192 deletions
81
pkg/vm/csharp-interop-test/push/pushbytes1.json
Normal file
81
pkg/vm/csharp-interop-test/push/pushbytes1.json
Normal 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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
6
pkg/vm/csharp-interop-test/readme.md
Normal file
6
pkg/vm/csharp-interop-test/readme.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
## Package VM Interop
|
||||||
|
|
||||||
|
|
||||||
|
This package will use the tests in the neo-vm repo to test interopabilty
|
||||||
|
|
||||||
|
|
26
pkg/vm/csharp-interop-test/testStruct.go
Normal file
26
pkg/vm/csharp-interop-test/testStruct.go
Normal 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"`
|
||||||
|
}
|
|
@ -1,6 +1,9 @@
|
||||||
package stack
|
package stack
|
||||||
|
|
||||||
import "math/big"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
// Int represents an integer on the stack
|
// Int represents an integer on the stack
|
||||||
type Int struct {
|
type Int struct {
|
||||||
|
@ -100,6 +103,18 @@ func (i *Int) Value() *big.Int {
|
||||||
return i.val
|
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.
|
// Lte returns a bool value from the comparison of two integers, a and b.
|
||||||
// value is true if a <= b.
|
// value is true if a <= b.
|
||||||
// value is false if a > b.
|
// value is false if a > b.
|
||||||
|
@ -114,18 +129,6 @@ func (i *Int) Gte(s *Int) bool {
|
||||||
return i.Value().Cmp(s.Value()) != -1
|
return i.Value().Cmp(s.Value()) != -1
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lt returns a bool value from the comparison of two integers, a and b.
|
// Lt returns a bool value from the comparison of two integers, a and b.
|
||||||
// value is true if a < b.
|
// value is true if a < b.
|
||||||
// value is false if a >= b.
|
// value is false if a >= b.
|
||||||
|
@ -139,3 +142,64 @@ func (i *Int) Lt(s *Int) bool {
|
||||||
func (i *Int) Gt(s *Int) bool {
|
func (i *Int) Gt(s *Int) bool {
|
||||||
return i.Value().Cmp(s.Value()) == 1
|
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,5 +1,9 @@
|
||||||
package stack
|
package stack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
// Array represents an Array of stackItems on the stack
|
// Array represents an Array of stackItems on the stack
|
||||||
type Array struct {
|
type Array struct {
|
||||||
*abstractItem
|
*abstractItem
|
||||||
|
@ -11,3 +15,22 @@ type Array struct {
|
||||||
func (a *Array) Array() (*Array, error) {
|
func (a *Array) Array() (*Array, error) {
|
||||||
return a, nil
|
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 {
|
||||||
|
return &Array{
|
||||||
|
&abstractItem{},
|
||||||
|
val,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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,5 +1,9 @@
|
||||||
package stack
|
package stack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
// Boolean represents a boolean value on the stack
|
// Boolean represents a boolean value on the stack
|
||||||
type Boolean struct {
|
type Boolean struct {
|
||||||
*abstractItem
|
*abstractItem
|
||||||
|
@ -44,3 +48,9 @@ func (b *Boolean) Or(a *Boolean) *Boolean {
|
||||||
c := b.Value() || a.Value()
|
c := b.Value() || a.Value()
|
||||||
return NewBoolean(c)
|
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))
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package stack
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
@ -69,3 +70,14 @@ func reverse(b []byte) []byte {
|
||||||
|
|
||||||
return dest
|
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))
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package stack
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Context represent the current execution context of the VM.
|
// Context represent the current execution context of the VM.
|
||||||
|
@ -120,6 +121,11 @@ func (c *Context) ReadUint16() uint16 {
|
||||||
return val
|
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
|
// ReadByte reads one byte from the script
|
||||||
func (c *Context) ReadByte() (byte, error) {
|
func (c *Context) ReadByte() (byte, error) {
|
||||||
byt, err := c.ReadBytes(1)
|
byt, err := c.ReadBytes(1)
|
||||||
|
@ -150,3 +156,19 @@ func (c *Context) readVarBytes() ([]byte, error) {
|
||||||
}
|
}
|
||||||
return c.ReadBytes(int(n))
|
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))
|
||||||
|
}
|
||||||
|
|
166
pkg/vm/stack/map.go
Normal file
166
pkg/vm/stack/map.go
Normal 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() == true {
|
||||||
|
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() == true {
|
||||||
|
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() == true {
|
||||||
|
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() == true {
|
||||||
|
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
141
pkg/vm/stack/map_test.go
Normal 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 = NewArray([]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(NewArray([]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, NewArray([]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())
|
||||||
|
|
||||||
|
}
|
|
@ -173,3 +173,19 @@ func (ras *RandomAccess) PopBoolean() (*Boolean, error) {
|
||||||
}
|
}
|
||||||
return item.Boolean()
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ type Item interface {
|
||||||
ByteArray() (*ByteArray, error)
|
ByteArray() (*ByteArray, error)
|
||||||
Array() (*Array, error)
|
Array() (*Array, error)
|
||||||
Context() (*Context, error)
|
Context() (*Context, error)
|
||||||
|
Map() (*Map, error)
|
||||||
|
Hash() (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Represents an `abstract` stack item
|
// Represents an `abstract` stack item
|
||||||
|
@ -47,3 +49,13 @@ func (a *abstractItem) Array() (*Array, error) {
|
||||||
func (a *abstractItem) Context() (*Context, error) {
|
func (a *abstractItem) Context() (*Context, error) {
|
||||||
return nil, errors.New("This stack item is not of type context")
|
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")
|
||||||
|
}
|
||||||
|
|
|
@ -42,3 +42,15 @@ func testReadInt64(data []byte) int64 {
|
||||||
binary.Read(buf, binary.LittleEndian, &ret)
|
binary.Read(buf, binary.LittleEndian, &ret)
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testMakeStackMap(t *testing.T, m map[Item]Item) *Map {
|
||||||
|
a, err := NewMap(m)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func testArray(t *testing.T, m map[Item]Item) *Map {
|
||||||
|
a, err := NewMap(m)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
|
@ -5,38 +5,57 @@ import "github.com/CityOfZion/neo-go/pkg/vm/stack"
|
||||||
type stackInfo func(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error)
|
type stackInfo func(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error)
|
||||||
|
|
||||||
var opFunc = map[stack.Instruction]stackInfo{
|
var opFunc = map[stack.Instruction]stackInfo{
|
||||||
stack.DROP: DROP,
|
stack.DROP: DROP,
|
||||||
stack.DEPTH: DEPTH,
|
stack.DEPTH: DEPTH,
|
||||||
stack.XTUCK: XTUCK,
|
stack.XTUCK: XTUCK,
|
||||||
stack.XSWAP: XSWAP,
|
stack.XSWAP: XSWAP,
|
||||||
stack.NUMEQUAL: NumEqual,
|
stack.XDROP: XDROP,
|
||||||
stack.NUMNOTEQUAL: NumNotEqual,
|
stack.FROMALTSTACK: FROMALTSTACK,
|
||||||
stack.BOOLAND: BoolAnd,
|
stack.TOALTSTACK: TOALTSTACK,
|
||||||
stack.BOOLOR: BoolOr,
|
stack.DUPFROMALTSTACK: DUPFROMALTSTACK,
|
||||||
stack.LT: Lt,
|
stack.JMPIFNOT: JMPIFNOT,
|
||||||
stack.LTE: Lte,
|
stack.JMPIF: JMPIF,
|
||||||
stack.GT: Gt,
|
stack.JMP: JMP,
|
||||||
stack.GTE: Gte,
|
stack.NOP: NOP,
|
||||||
stack.SHR: Shr,
|
stack.HASH256: HASH256,
|
||||||
stack.SHL: Shl,
|
stack.HASH160: HASH160,
|
||||||
stack.INC: Inc,
|
stack.SHA256: SHA256,
|
||||||
stack.DEC: Dec,
|
stack.SHA1: SHA1,
|
||||||
stack.DIV: Div,
|
stack.XOR: Xor,
|
||||||
stack.MOD: Mod,
|
stack.OR: Or,
|
||||||
stack.NZ: Nz,
|
stack.AND: And,
|
||||||
stack.MUL: Mul,
|
stack.INVERT: Invert,
|
||||||
stack.ABS: Abs,
|
stack.MIN: Min,
|
||||||
stack.NOT: Not,
|
stack.MAX: Max,
|
||||||
stack.SIGN: Sign,
|
stack.WITHIN: Within,
|
||||||
stack.NEGATE: Negate,
|
stack.NUMEQUAL: NumEqual,
|
||||||
stack.ADD: Add,
|
stack.NUMNOTEQUAL: NumNotEqual,
|
||||||
stack.SUB: Sub,
|
stack.BOOLAND: BoolAnd,
|
||||||
stack.PUSHBYTES1: PushNBytes,
|
stack.BOOLOR: BoolOr,
|
||||||
stack.PUSHBYTES75: PushNBytes,
|
stack.LT: Lt,
|
||||||
stack.RET: RET,
|
stack.LTE: Lte,
|
||||||
stack.EQUAL: EQUAL,
|
stack.GT: Gt,
|
||||||
stack.THROWIFNOT: THROWIFNOT,
|
stack.GTE: Gte,
|
||||||
stack.THROW: THROW,
|
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() {
|
func init() {
|
||||||
|
|
|
@ -15,3 +15,88 @@ func EQUAL(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, r
|
||||||
ctx.Estack.Push(itemA.Equals(itemB))
|
ctx.Estack.Push(itemA.Equals(itemB))
|
||||||
return NONE, nil
|
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
|
||||||
|
}
|
||||||
|
|
137
pkg/vm/vm_ops_bitwise_test.go
Normal file
137
pkg/vm/vm_ops_bitwise_test.go
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
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)
|
||||||
|
v.executeOp(stack.INVERT, ctx)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
v.executeOp(stack.AND, ctx)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
v.executeOp(stack.OR, ctx)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
v.executeOp(stack.XOR, ctx)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
v.executeOp(stack.EQUAL, ctx)
|
||||||
|
|
||||||
|
// 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())
|
||||||
|
}
|
|
@ -25,3 +25,71 @@ func RET(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rst
|
||||||
|
|
||||||
return NONE, nil
|
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
|
||||||
|
}
|
||||||
|
|
168
pkg/vm/vm_ops_flow_test.go
Normal file
168
pkg/vm/vm_ops_flow_test.go
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
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)
|
||||||
|
|
||||||
|
v.executeOp(stack.NOP, ctx)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
v.executeOp(stack.JMP, ctx)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
v.executeOp(stack.JMPIF, ctx)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
v.executeOp(stack.JMPIF, ctx)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
v.executeOp(stack.JMPIFNOT, ctx)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
v.executeOp(stack.JMPIFNOT, ctx)
|
||||||
|
|
||||||
|
// Stack should have one item
|
||||||
|
assert.Equal(t, 0, ctx.Estack.Len())
|
||||||
|
|
||||||
|
// ctx.IP() = ctx.ip + 1
|
||||||
|
assert.Equal(t, 3, ctx.IP())
|
||||||
|
}
|
|
@ -205,6 +205,54 @@ func NumNotEqual(op stack.Instruction, ctx *stack.Context, istack *stack.Invocat
|
||||||
return NONE, nil
|
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.
|
// 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
|
// 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) {
|
func Abs(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||||
|
@ -434,6 +482,23 @@ func popTwoIntegers(ctx *stack.Context) (*stack.Int, *stack.Int, error) {
|
||||||
return operandA, operandB, nil
|
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) {
|
func popTwoByteArrays(ctx *stack.Context) (*stack.ByteArray, *stack.ByteArray, error) {
|
||||||
// Pop first stack item and cast as byte array
|
// Pop first stack item and cast as byte array
|
||||||
ba1, err := ctx.Estack.PopByteArray()
|
ba1, err := ctx.Estack.PopByteArray()
|
||||||
|
|
|
@ -13,9 +13,7 @@ func TestIncOp(t *testing.T) {
|
||||||
v := VM{}
|
v := VM{}
|
||||||
|
|
||||||
a, err := stack.NewInt(big.NewInt(20))
|
a, err := stack.NewInt(big.NewInt(20))
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := stack.NewContext([]byte{})
|
ctx := stack.NewContext([]byte{})
|
||||||
ctx.Estack.Push(a)
|
ctx.Estack.Push(a)
|
||||||
|
@ -26,9 +24,7 @@ func TestIncOp(t *testing.T) {
|
||||||
assert.Equal(t, 1, ctx.Estack.Len())
|
assert.Equal(t, 1, ctx.Estack.Len())
|
||||||
|
|
||||||
item, err := ctx.Estack.PopInt()
|
item, err := ctx.Estack.PopInt()
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, int64(21), item.Value().Int64())
|
assert.Equal(t, int64(21), item.Value().Int64())
|
||||||
}
|
}
|
||||||
|
@ -38,9 +34,7 @@ func TestDecOp(t *testing.T) {
|
||||||
v := VM{}
|
v := VM{}
|
||||||
|
|
||||||
a, err := stack.NewInt(big.NewInt(20))
|
a, err := stack.NewInt(big.NewInt(20))
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := stack.NewContext([]byte{})
|
ctx := stack.NewContext([]byte{})
|
||||||
ctx.Estack.Push(a)
|
ctx.Estack.Push(a)
|
||||||
|
@ -51,9 +45,7 @@ func TestDecOp(t *testing.T) {
|
||||||
assert.Equal(t, 1, ctx.Estack.Len())
|
assert.Equal(t, 1, ctx.Estack.Len())
|
||||||
|
|
||||||
item, err := ctx.Estack.PopInt()
|
item, err := ctx.Estack.PopInt()
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, int64(19), item.Value().Int64())
|
assert.Equal(t, int64(19), item.Value().Int64())
|
||||||
}
|
}
|
||||||
|
@ -63,13 +55,10 @@ func TestAddOp(t *testing.T) {
|
||||||
v := VM{}
|
v := VM{}
|
||||||
|
|
||||||
a, err := stack.NewInt(big.NewInt(20))
|
a, err := stack.NewInt(big.NewInt(20))
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
b, err := stack.NewInt(big.NewInt(23))
|
b, err := stack.NewInt(big.NewInt(23))
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := stack.NewContext([]byte{})
|
ctx := stack.NewContext([]byte{})
|
||||||
ctx.Estack.Push(a).Push(b)
|
ctx.Estack.Push(a).Push(b)
|
||||||
|
@ -80,9 +69,7 @@ func TestAddOp(t *testing.T) {
|
||||||
assert.Equal(t, 1, ctx.Estack.Len())
|
assert.Equal(t, 1, ctx.Estack.Len())
|
||||||
|
|
||||||
item, err := ctx.Estack.PopInt()
|
item, err := ctx.Estack.PopInt()
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, int64(43), item.Value().Int64())
|
assert.Equal(t, int64(43), item.Value().Int64())
|
||||||
|
|
||||||
|
@ -93,13 +80,10 @@ func TestSubOp(t *testing.T) {
|
||||||
v := VM{}
|
v := VM{}
|
||||||
|
|
||||||
a, err := stack.NewInt(big.NewInt(30))
|
a, err := stack.NewInt(big.NewInt(30))
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
b, err := stack.NewInt(big.NewInt(40))
|
b, err := stack.NewInt(big.NewInt(40))
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := stack.NewContext([]byte{})
|
ctx := stack.NewContext([]byte{})
|
||||||
ctx.Estack.Push(a).Push(b)
|
ctx.Estack.Push(a).Push(b)
|
||||||
|
@ -110,9 +94,7 @@ func TestSubOp(t *testing.T) {
|
||||||
assert.Equal(t, 1, ctx.Estack.Len())
|
assert.Equal(t, 1, ctx.Estack.Len())
|
||||||
|
|
||||||
item, err := ctx.Estack.PopInt()
|
item, err := ctx.Estack.PopInt()
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, int64(-10), item.Value().Int64())
|
assert.Equal(t, int64(-10), item.Value().Int64())
|
||||||
|
|
||||||
|
@ -123,13 +105,10 @@ func TestDivOp(t *testing.T) {
|
||||||
v := VM{}
|
v := VM{}
|
||||||
|
|
||||||
a, err := stack.NewInt(big.NewInt(10))
|
a, err := stack.NewInt(big.NewInt(10))
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
b, err := stack.NewInt(big.NewInt(4))
|
b, err := stack.NewInt(big.NewInt(4))
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := stack.NewContext([]byte{})
|
ctx := stack.NewContext([]byte{})
|
||||||
ctx.Estack.Push(a).Push(b)
|
ctx.Estack.Push(a).Push(b)
|
||||||
|
@ -140,9 +119,7 @@ func TestDivOp(t *testing.T) {
|
||||||
assert.Equal(t, 1, ctx.Estack.Len())
|
assert.Equal(t, 1, ctx.Estack.Len())
|
||||||
|
|
||||||
item, err := ctx.Estack.PopInt()
|
item, err := ctx.Estack.PopInt()
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, int64(2), item.Value().Int64())
|
assert.Equal(t, int64(2), item.Value().Int64())
|
||||||
}
|
}
|
||||||
|
@ -152,13 +129,10 @@ func TestModOp(t *testing.T) {
|
||||||
v := VM{}
|
v := VM{}
|
||||||
|
|
||||||
a, err := stack.NewInt(big.NewInt(15))
|
a, err := stack.NewInt(big.NewInt(15))
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
b, err := stack.NewInt(big.NewInt(4))
|
b, err := stack.NewInt(big.NewInt(4))
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := stack.NewContext([]byte{})
|
ctx := stack.NewContext([]byte{})
|
||||||
ctx.Estack.Push(a).Push(b)
|
ctx.Estack.Push(a).Push(b)
|
||||||
|
@ -169,9 +143,7 @@ func TestModOp(t *testing.T) {
|
||||||
assert.Equal(t, 1, ctx.Estack.Len())
|
assert.Equal(t, 1, ctx.Estack.Len())
|
||||||
|
|
||||||
item, err := ctx.Estack.PopInt()
|
item, err := ctx.Estack.PopInt()
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, int64(3), item.Value().Int64())
|
assert.Equal(t, int64(3), item.Value().Int64())
|
||||||
}
|
}
|
||||||
|
@ -181,9 +153,7 @@ func TestNzOp(t *testing.T) {
|
||||||
v := VM{}
|
v := VM{}
|
||||||
|
|
||||||
a, err := stack.NewInt(big.NewInt(20))
|
a, err := stack.NewInt(big.NewInt(20))
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := stack.NewContext([]byte{})
|
ctx := stack.NewContext([]byte{})
|
||||||
ctx.Estack.Push(a)
|
ctx.Estack.Push(a)
|
||||||
|
@ -194,9 +164,7 @@ func TestNzOp(t *testing.T) {
|
||||||
assert.Equal(t, 1, ctx.Estack.Len())
|
assert.Equal(t, 1, ctx.Estack.Len())
|
||||||
|
|
||||||
item, err := ctx.Estack.PopBoolean()
|
item, err := ctx.Estack.PopBoolean()
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, true, item.Value())
|
assert.Equal(t, true, item.Value())
|
||||||
}
|
}
|
||||||
|
@ -206,13 +174,10 @@ func TestMulOp(t *testing.T) {
|
||||||
v := VM{}
|
v := VM{}
|
||||||
|
|
||||||
a, err := stack.NewInt(big.NewInt(20))
|
a, err := stack.NewInt(big.NewInt(20))
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
b, err := stack.NewInt(big.NewInt(20))
|
b, err := stack.NewInt(big.NewInt(20))
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := stack.NewContext([]byte{})
|
ctx := stack.NewContext([]byte{})
|
||||||
ctx.Estack.Push(a).Push(b)
|
ctx.Estack.Push(a).Push(b)
|
||||||
|
@ -223,9 +188,7 @@ func TestMulOp(t *testing.T) {
|
||||||
assert.Equal(t, 1, ctx.Estack.Len())
|
assert.Equal(t, 1, ctx.Estack.Len())
|
||||||
|
|
||||||
item, err := ctx.Estack.PopInt()
|
item, err := ctx.Estack.PopInt()
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, int64(400), item.Value().Int64())
|
assert.Equal(t, int64(400), item.Value().Int64())
|
||||||
}
|
}
|
||||||
|
@ -235,9 +198,7 @@ func TestAbsOp(t *testing.T) {
|
||||||
v := VM{}
|
v := VM{}
|
||||||
|
|
||||||
a, err := stack.NewInt(big.NewInt(-20))
|
a, err := stack.NewInt(big.NewInt(-20))
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := stack.NewContext([]byte{})
|
ctx := stack.NewContext([]byte{})
|
||||||
ctx.Estack.Push(a)
|
ctx.Estack.Push(a)
|
||||||
|
@ -248,9 +209,7 @@ func TestAbsOp(t *testing.T) {
|
||||||
assert.Equal(t, 1, ctx.Estack.Len())
|
assert.Equal(t, 1, ctx.Estack.Len())
|
||||||
|
|
||||||
item, err := ctx.Estack.PopInt()
|
item, err := ctx.Estack.PopInt()
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, int64(20), item.Value().Int64())
|
assert.Equal(t, int64(20), item.Value().Int64())
|
||||||
}
|
}
|
||||||
|
@ -270,9 +229,7 @@ func TestNotOp(t *testing.T) {
|
||||||
assert.Equal(t, 1, ctx.Estack.Len())
|
assert.Equal(t, 1, ctx.Estack.Len())
|
||||||
|
|
||||||
item, err := ctx.Estack.PopBoolean()
|
item, err := ctx.Estack.PopBoolean()
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, true, item.Value())
|
assert.Equal(t, true, item.Value())
|
||||||
}
|
}
|
||||||
|
@ -282,13 +239,10 @@ func TestNumEqual(t *testing.T) {
|
||||||
v := VM{}
|
v := VM{}
|
||||||
|
|
||||||
a, err := stack.NewInt(big.NewInt(6))
|
a, err := stack.NewInt(big.NewInt(6))
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
b, err := stack.NewInt(big.NewInt(6))
|
b, err := stack.NewInt(big.NewInt(6))
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := stack.NewContext([]byte{})
|
ctx := stack.NewContext([]byte{})
|
||||||
ctx.Estack.Push(a).Push(b)
|
ctx.Estack.Push(a).Push(b)
|
||||||
|
@ -299,9 +253,7 @@ func TestNumEqual(t *testing.T) {
|
||||||
assert.Equal(t, 1, ctx.Estack.Len())
|
assert.Equal(t, 1, ctx.Estack.Len())
|
||||||
|
|
||||||
item, err := ctx.Estack.PopBoolean()
|
item, err := ctx.Estack.PopBoolean()
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, true, item.Value())
|
assert.Equal(t, true, item.Value())
|
||||||
}
|
}
|
||||||
|
@ -311,13 +263,10 @@ func TestNumNotEqual(t *testing.T) {
|
||||||
v := VM{}
|
v := VM{}
|
||||||
|
|
||||||
a, err := stack.NewInt(big.NewInt(5))
|
a, err := stack.NewInt(big.NewInt(5))
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
b, err := stack.NewInt(big.NewInt(6))
|
b, err := stack.NewInt(big.NewInt(6))
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := stack.NewContext([]byte{})
|
ctx := stack.NewContext([]byte{})
|
||||||
ctx.Estack.Push(a).Push(b)
|
ctx.Estack.Push(a).Push(b)
|
||||||
|
@ -328,9 +277,7 @@ func TestNumNotEqual(t *testing.T) {
|
||||||
assert.Equal(t, 1, ctx.Estack.Len())
|
assert.Equal(t, 1, ctx.Estack.Len())
|
||||||
|
|
||||||
item, err := ctx.Estack.PopBoolean()
|
item, err := ctx.Estack.PopBoolean()
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, true, item.Value())
|
assert.Equal(t, true, item.Value())
|
||||||
}
|
}
|
||||||
|
@ -340,9 +287,7 @@ func TestSignOp(t *testing.T) {
|
||||||
v := VM{}
|
v := VM{}
|
||||||
|
|
||||||
a, err := stack.NewInt(big.NewInt(-20))
|
a, err := stack.NewInt(big.NewInt(-20))
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := stack.NewContext([]byte{})
|
ctx := stack.NewContext([]byte{})
|
||||||
ctx.Estack.Push(a)
|
ctx.Estack.Push(a)
|
||||||
|
@ -353,9 +298,7 @@ func TestSignOp(t *testing.T) {
|
||||||
assert.Equal(t, 1, ctx.Estack.Len())
|
assert.Equal(t, 1, ctx.Estack.Len())
|
||||||
|
|
||||||
item, err := ctx.Estack.PopInt()
|
item, err := ctx.Estack.PopInt()
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, int64(-1), item.Value().Int64())
|
assert.Equal(t, int64(-1), item.Value().Int64())
|
||||||
}
|
}
|
||||||
|
@ -365,9 +308,7 @@ func TestNegateOp(t *testing.T) {
|
||||||
v := VM{}
|
v := VM{}
|
||||||
|
|
||||||
a, err := stack.NewInt(big.NewInt(-20))
|
a, err := stack.NewInt(big.NewInt(-20))
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := stack.NewContext([]byte{})
|
ctx := stack.NewContext([]byte{})
|
||||||
ctx.Estack.Push(a)
|
ctx.Estack.Push(a)
|
||||||
|
@ -378,9 +319,7 @@ func TestNegateOp(t *testing.T) {
|
||||||
assert.Equal(t, 1, ctx.Estack.Len())
|
assert.Equal(t, 1, ctx.Estack.Len())
|
||||||
|
|
||||||
item, err := ctx.Estack.PopInt()
|
item, err := ctx.Estack.PopInt()
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, int64(20), item.Value().Int64())
|
assert.Equal(t, int64(20), item.Value().Int64())
|
||||||
}
|
}
|
||||||
|
@ -448,13 +387,10 @@ func TestShlOp(t *testing.T) {
|
||||||
v := VM{}
|
v := VM{}
|
||||||
|
|
||||||
a, err := stack.NewInt(big.NewInt(2))
|
a, err := stack.NewInt(big.NewInt(2))
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
b, err := stack.NewInt(big.NewInt(3))
|
b, err := stack.NewInt(big.NewInt(3))
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := stack.NewContext([]byte{})
|
ctx := stack.NewContext([]byte{})
|
||||||
ctx.Estack.Push(a).Push(b)
|
ctx.Estack.Push(a).Push(b)
|
||||||
|
@ -470,9 +406,7 @@ func TestShlOp(t *testing.T) {
|
||||||
assert.Equal(t, 1, ctx.Estack.Len())
|
assert.Equal(t, 1, ctx.Estack.Len())
|
||||||
|
|
||||||
item, err := ctx.Estack.PopInt()
|
item, err := ctx.Estack.PopInt()
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, int64(16), item.Value().Int64())
|
assert.Equal(t, int64(16), item.Value().Int64())
|
||||||
}
|
}
|
||||||
|
@ -482,13 +416,10 @@ func TestShrOp(t *testing.T) {
|
||||||
v := VM{}
|
v := VM{}
|
||||||
|
|
||||||
a, err := stack.NewInt(big.NewInt(10))
|
a, err := stack.NewInt(big.NewInt(10))
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
b, err := stack.NewInt(big.NewInt(2))
|
b, err := stack.NewInt(big.NewInt(2))
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := stack.NewContext([]byte{})
|
ctx := stack.NewContext([]byte{})
|
||||||
ctx.Estack.Push(a).Push(b)
|
ctx.Estack.Push(a).Push(b)
|
||||||
|
@ -504,9 +435,7 @@ func TestShrOp(t *testing.T) {
|
||||||
assert.Equal(t, 1, ctx.Estack.Len())
|
assert.Equal(t, 1, ctx.Estack.Len())
|
||||||
|
|
||||||
item, err := ctx.Estack.PopInt()
|
item, err := ctx.Estack.PopInt()
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, int64(2), item.Value().Int64())
|
assert.Equal(t, int64(2), item.Value().Int64())
|
||||||
}
|
}
|
||||||
|
@ -527,9 +456,7 @@ func TestBoolAndOp(t *testing.T) {
|
||||||
assert.Equal(t, 1, ctx.Estack.Len())
|
assert.Equal(t, 1, ctx.Estack.Len())
|
||||||
|
|
||||||
item, err := ctx.Estack.PopBoolean()
|
item, err := ctx.Estack.PopBoolean()
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, true, item.Value())
|
assert.Equal(t, true, item.Value())
|
||||||
}
|
}
|
||||||
|
@ -550,9 +477,7 @@ func TestBoolOrOp(t *testing.T) {
|
||||||
assert.Equal(t, 1, ctx.Estack.Len())
|
assert.Equal(t, 1, ctx.Estack.Len())
|
||||||
|
|
||||||
item, err := ctx.Estack.PopBoolean()
|
item, err := ctx.Estack.PopBoolean()
|
||||||
if err != nil {
|
assert.Nil(t, err)
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, true, item.Value())
|
assert.Equal(t, true, item.Value())
|
||||||
}
|
}
|
||||||
|
@ -562,10 +487,10 @@ func TestLtOp(t *testing.T) {
|
||||||
v := VM{}
|
v := VM{}
|
||||||
|
|
||||||
a, err := stack.NewInt(big.NewInt(10))
|
a, err := stack.NewInt(big.NewInt(10))
|
||||||
assert.NoError(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
b, err := stack.NewInt(big.NewInt(2))
|
b, err := stack.NewInt(big.NewInt(2))
|
||||||
assert.NoError(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
ctx := stack.NewContext([]byte{})
|
ctx := stack.NewContext([]byte{})
|
||||||
ctx.Estack.Push(a).Push(b)
|
ctx.Estack.Push(a).Push(b)
|
||||||
|
@ -581,7 +506,7 @@ func TestLtOp(t *testing.T) {
|
||||||
assert.Equal(t, 1, ctx.Estack.Len())
|
assert.Equal(t, 1, ctx.Estack.Len())
|
||||||
|
|
||||||
item, err := ctx.Estack.PopBoolean()
|
item, err := ctx.Estack.PopBoolean()
|
||||||
assert.NoError(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, false, item.Value())
|
assert.Equal(t, false, item.Value())
|
||||||
}
|
}
|
||||||
|
@ -591,10 +516,10 @@ func TestGtOp(t *testing.T) {
|
||||||
v := VM{}
|
v := VM{}
|
||||||
|
|
||||||
a, err := stack.NewInt(big.NewInt(10))
|
a, err := stack.NewInt(big.NewInt(10))
|
||||||
assert.NoError(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
b, err := stack.NewInt(big.NewInt(2))
|
b, err := stack.NewInt(big.NewInt(2))
|
||||||
assert.NoError(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
ctx := stack.NewContext([]byte{})
|
ctx := stack.NewContext([]byte{})
|
||||||
ctx.Estack.Push(a).Push(b)
|
ctx.Estack.Push(a).Push(b)
|
||||||
|
@ -610,7 +535,89 @@ func TestGtOp(t *testing.T) {
|
||||||
assert.Equal(t, 1, ctx.Estack.Len())
|
assert.Equal(t, 1, ctx.Estack.Len())
|
||||||
|
|
||||||
item, err := ctx.Estack.PopBoolean()
|
item, err := ctx.Estack.PopBoolean()
|
||||||
assert.NoError(t, err)
|
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)
|
||||||
|
|
||||||
|
v.executeOp(stack.MIN, ctx)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
v.executeOp(stack.MAX, ctx)
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
v.executeOp(stack.WITHIN, ctx)
|
||||||
|
|
||||||
|
// 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())
|
assert.Equal(t, true, item.Value())
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,6 @@ func XSWAP(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, r
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return FAULT, err
|
return FAULT, err
|
||||||
}
|
}
|
||||||
|
|
||||||
item, err := ctx.Estack.Peek(0)
|
item, err := ctx.Estack.Peek(0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return FAULT, err
|
return FAULT, err
|
||||||
|
@ -50,6 +49,21 @@ func XSWAP(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, r
|
||||||
return NONE, nil
|
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
|
||||||
|
}
|
||||||
|
|
||||||
// XTUCK pops an integer n off of the stack and
|
// 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.
|
// 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) {
|
func XTUCK(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||||
|
@ -63,7 +77,6 @@ func XTUCK(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, r
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return FAULT, err
|
return FAULT, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ras, err := ctx.Estack.Insert(uint16(n.Value().Int64()), item)
|
ras, err := ctx.Estack.Insert(uint16(n.Value().Int64()), item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return FAULT, err
|
return FAULT, err
|
||||||
|
@ -74,6 +87,21 @@ func XTUCK(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, r
|
||||||
return NONE, nil
|
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.
|
// 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) {
|
func DEPTH(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||||
|
|
||||||
|
@ -83,7 +111,22 @@ func DEPTH(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, r
|
||||||
return FAULT, err
|
return FAULT, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Estack.Push(length)
|
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
|
return NONE, nil
|
||||||
}
|
}
|
||||||
|
@ -100,3 +143,21 @@ func DROP(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rs
|
||||||
|
|
||||||
return NONE, nil
|
return NONE, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XDROP pops an integer n off of the stack and
|
||||||
|
// removes the n-item from the stack starting from
|
||||||
|
// the top of the stack.
|
||||||
|
func XDROP(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) {
|
||||||
|
|
||||||
|
n, err := ctx.Estack.PopInt()
|
||||||
|
if err != nil {
|
||||||
|
return FAULT, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Estack.Remove(uint16(n.Value().Uint64()))
|
||||||
|
if err != nil {
|
||||||
|
return FAULT, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NONE, nil
|
||||||
|
}
|
||||||
|
|
|
@ -135,6 +135,85 @@ func TestXDepthOp(t *testing.T) {
|
||||||
assert.Equal(t, int64(3), item2.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)
|
||||||
|
|
||||||
|
v.executeOp(stack.DUPFROMALTSTACK, ctx)
|
||||||
|
|
||||||
|
assert.Equal(t, 1, ctx.Astack.Len())
|
||||||
|
assert.Equal(t, 2, ctx.Estack.Len())
|
||||||
|
|
||||||
|
itemE, err := ctx.Estack.PopInt()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
itemA, err := ctx.Astack.PopInt()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, int64(2), itemE.Value().Int64())
|
||||||
|
assert.Equal(t, int64(2), itemA.Value().Int64())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToAltStackOp(t *testing.T) {
|
||||||
|
|
||||||
|
v := VM{}
|
||||||
|
|
||||||
|
a, err := stack.NewInt(big.NewInt(10))
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
b, err := stack.NewInt(big.NewInt(2))
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
ctx := stack.NewContext([]byte{})
|
||||||
|
ctx.Estack.Push(a)
|
||||||
|
ctx.Astack.Push(b)
|
||||||
|
|
||||||
|
v.executeOp(stack.TOALTSTACK, ctx)
|
||||||
|
|
||||||
|
assert.Equal(t, 2, ctx.Astack.Len())
|
||||||
|
assert.Equal(t, 0, ctx.Estack.Len())
|
||||||
|
|
||||||
|
item, err := ctx.Astack.PopInt()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, int64(10), item.Value().Int64())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromAltStackOp(t *testing.T) {
|
||||||
|
|
||||||
|
v := VM{}
|
||||||
|
|
||||||
|
a, err := stack.NewInt(big.NewInt(10))
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
b, err := stack.NewInt(big.NewInt(2))
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
ctx := stack.NewContext([]byte{})
|
||||||
|
ctx.Estack.Push(a)
|
||||||
|
ctx.Astack.Push(b)
|
||||||
|
|
||||||
|
v.executeOp(stack.FROMALTSTACK, ctx)
|
||||||
|
|
||||||
|
assert.Equal(t, 0, ctx.Astack.Len())
|
||||||
|
assert.Equal(t, 2, ctx.Estack.Len())
|
||||||
|
|
||||||
|
item, err := ctx.Estack.PopInt()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, int64(2), item.Value().Int64())
|
||||||
|
}
|
||||||
|
|
||||||
func TestXDropOp(t *testing.T) {
|
func TestXDropOp(t *testing.T) {
|
||||||
|
|
||||||
v := VM{}
|
v := VM{}
|
||||||
|
@ -145,18 +224,35 @@ func TestXDropOp(t *testing.T) {
|
||||||
b, err := stack.NewInt(big.NewInt(6))
|
b, err := stack.NewInt(big.NewInt(6))
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
ctx := stack.NewContext([]byte{})
|
c, err := stack.NewInt(big.NewInt(9))
|
||||||
ctx.Estack.Push(a).Push(b)
|
|
||||||
|
|
||||||
// Remove the top stack item from the stack.
|
|
||||||
// The remaining stack is [a]
|
|
||||||
v.executeOp(stack.DROP, ctx)
|
|
||||||
|
|
||||||
// Stack should have 2 items
|
|
||||||
assert.Equal(t, 1, ctx.Estack.Len())
|
|
||||||
|
|
||||||
itemA, err := ctx.Estack.PopInt()
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, int64(3), itemA.Value().Int64())
|
d, err := stack.NewInt(big.NewInt(2))
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
ctx := stack.NewContext([]byte{})
|
||||||
|
ctx.Estack.Push(a)
|
||||||
|
ctx.Estack.Push(b)
|
||||||
|
ctx.Estack.Push(c)
|
||||||
|
ctx.Estack.Push(d)
|
||||||
|
|
||||||
|
// pop n (= d = 2) from the stack.
|
||||||
|
// we will remove the n-item which
|
||||||
|
// is located at position
|
||||||
|
// len(stack)-n-1 = 3-2-1 = 0.
|
||||||
|
// Therefore a is removed from the stack.
|
||||||
|
// Only b, c remain on the stack.
|
||||||
|
v.executeOp(stack.XDROP, ctx)
|
||||||
|
|
||||||
|
assert.Equal(t, 2, ctx.Estack.Len())
|
||||||
|
|
||||||
|
itemC, err := ctx.Estack.PopInt()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
itemB, err := ctx.Estack.PopInt()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, int64(6), itemB.Value().Int64())
|
||||||
|
assert.Equal(t, int64(9), itemC.Value().Int64())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
106
pkg/vm/vmopscrypto.go
Normal file
106
pkg/vm/vmopscrypto.go
Normal 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
|
||||||
|
}
|
101
pkg/vm/vmopscrypto_test.go
Normal file
101
pkg/vm/vmopscrypto_test.go
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
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)
|
||||||
|
|
||||||
|
v.executeOp(stack.SHA1, ctx)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
v.executeOp(stack.SHA256, ctx)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
v.executeOp(stack.HASH160, ctx)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
v.executeOp(stack.HASH256, ctx)
|
||||||
|
|
||||||
|
// 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