Merge branch 'vm' into dauTT/vm-NOP-JMP-JMPIF-JMPIFNOT-opcodes

This commit is contained in:
Roman Khimov 2019-08-12 12:42:21 +03:00 committed by GitHub
commit 3a7cfe26ac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 1217 additions and 145 deletions

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 {
@ -100,6 +103,18 @@ 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.
@ -114,18 +129,6 @@ func (i *Int) Gte(s *Int) bool {
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.
// value is true 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 {
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 {
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))
}

View file

@ -1,5 +1,9 @@
package stack
import (
"fmt"
)
// Boolean represents a boolean value on the stack
type Boolean struct {
*abstractItem
@ -44,3 +48,9 @@ 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.
@ -165,3 +166,9 @@ func (c *Context) SetIP(ip int) error {
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
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() == 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
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 = 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())
}

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

@ -42,3 +42,15 @@ func testReadInt64(data []byte) int64 {
binary.Read(buf, binary.LittleEndian, &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
}

View file

@ -9,6 +9,17 @@ var opFunc = map[stack.Instruction]stackInfo{
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,

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,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())
}

View file

@ -205,6 +205,54 @@ func NumNotEqual(op stack.Instruction, ctx *stack.Context, istack *stack.Invocat
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) {
@ -434,6 +482,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()

View file

@ -13,9 +13,7 @@ func TestIncOp(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(20))
if err != nil {
t.Fail()
}
assert.Nil(t, err)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a)
@ -26,9 +24,7 @@ func TestIncOp(t *testing.T) {
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())
}
@ -38,9 +34,7 @@ func TestDecOp(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(20))
if err != nil {
t.Fail()
}
assert.Nil(t, err)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a)
@ -51,9 +45,7 @@ func TestDecOp(t *testing.T) {
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(19), item.Value().Int64())
}
@ -63,13 +55,10 @@ func TestAddOp(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(20))
if err != nil {
t.Fail()
}
assert.Nil(t, err)
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)
@ -80,9 +69,7 @@ func TestAddOp(t *testing.T) {
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(43), item.Value().Int64())
@ -93,13 +80,10 @@ 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)
@ -110,9 +94,7 @@ func TestSubOp(t *testing.T) {
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())
@ -123,13 +105,10 @@ func TestDivOp(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(10))
if err != nil {
t.Fail()
}
assert.Nil(t, err)
b, err := stack.NewInt(big.NewInt(4))
if err != nil {
t.Fail()
}
assert.Nil(t, err)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a).Push(b)
@ -140,9 +119,7 @@ func TestDivOp(t *testing.T) {
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(2), item.Value().Int64())
}
@ -152,13 +129,10 @@ func TestModOp(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(15))
if err != nil {
t.Fail()
}
assert.Nil(t, err)
b, err := stack.NewInt(big.NewInt(4))
if err != nil {
t.Fail()
}
assert.Nil(t, err)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a).Push(b)
@ -169,9 +143,7 @@ func TestModOp(t *testing.T) {
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(3), item.Value().Int64())
}
@ -181,9 +153,7 @@ func TestNzOp(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(20))
if err != nil {
t.Fail()
}
assert.Nil(t, err)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a)
@ -194,9 +164,7 @@ func TestNzOp(t *testing.T) {
assert.Equal(t, 1, ctx.Estack.Len())
item, err := ctx.Estack.PopBoolean()
if err != nil {
t.Fail()
}
assert.Nil(t, err)
assert.Equal(t, true, item.Value())
}
@ -206,13 +174,10 @@ func TestMulOp(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(20))
if err != nil {
t.Fail()
}
assert.Nil(t, err)
b, err := stack.NewInt(big.NewInt(20))
if err != nil {
t.Fail()
}
assert.Nil(t, err)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a).Push(b)
@ -223,9 +188,7 @@ func TestMulOp(t *testing.T) {
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(400), item.Value().Int64())
}
@ -235,9 +198,7 @@ func TestAbsOp(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(-20))
if err != nil {
t.Fail()
}
assert.Nil(t, err)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a)
@ -248,9 +209,7 @@ func TestAbsOp(t *testing.T) {
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(20), item.Value().Int64())
}
@ -270,9 +229,7 @@ func TestNotOp(t *testing.T) {
assert.Equal(t, 1, ctx.Estack.Len())
item, err := ctx.Estack.PopBoolean()
if err != nil {
t.Fail()
}
assert.Nil(t, err)
assert.Equal(t, true, item.Value())
}
@ -282,13 +239,10 @@ func TestNumEqual(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(6))
if err != nil {
t.Fail()
}
assert.Nil(t, err)
b, err := stack.NewInt(big.NewInt(6))
if err != nil {
t.Fail()
}
assert.Nil(t, err)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a).Push(b)
@ -299,9 +253,7 @@ func TestNumEqual(t *testing.T) {
assert.Equal(t, 1, ctx.Estack.Len())
item, err := ctx.Estack.PopBoolean()
if err != nil {
t.Fail()
}
assert.Nil(t, err)
assert.Equal(t, true, item.Value())
}
@ -311,13 +263,10 @@ func TestNumNotEqual(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(5))
if err != nil {
t.Fail()
}
assert.Nil(t, err)
b, err := stack.NewInt(big.NewInt(6))
if err != nil {
t.Fail()
}
assert.Nil(t, err)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a).Push(b)
@ -328,9 +277,7 @@ func TestNumNotEqual(t *testing.T) {
assert.Equal(t, 1, ctx.Estack.Len())
item, err := ctx.Estack.PopBoolean()
if err != nil {
t.Fail()
}
assert.Nil(t, err)
assert.Equal(t, true, item.Value())
}
@ -340,9 +287,7 @@ func TestSignOp(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(-20))
if err != nil {
t.Fail()
}
assert.Nil(t, err)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a)
@ -353,9 +298,7 @@ func TestSignOp(t *testing.T) {
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(-1), item.Value().Int64())
}
@ -365,9 +308,7 @@ func TestNegateOp(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(-20))
if err != nil {
t.Fail()
}
assert.Nil(t, err)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a)
@ -378,9 +319,7 @@ func TestNegateOp(t *testing.T) {
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(20), item.Value().Int64())
}
@ -448,13 +387,10 @@ func TestShlOp(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(2))
if err != nil {
t.Fail()
}
assert.Nil(t, err)
b, err := stack.NewInt(big.NewInt(3))
if err != nil {
t.Fail()
}
assert.Nil(t, err)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a).Push(b)
@ -470,9 +406,7 @@ func TestShlOp(t *testing.T) {
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(16), item.Value().Int64())
}
@ -482,13 +416,10 @@ func TestShrOp(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(10))
if err != nil {
t.Fail()
}
assert.Nil(t, err)
b, err := stack.NewInt(big.NewInt(2))
if err != nil {
t.Fail()
}
assert.Nil(t, err)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a).Push(b)
@ -504,9 +435,7 @@ func TestShrOp(t *testing.T) {
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(2), item.Value().Int64())
}
@ -527,9 +456,7 @@ func TestBoolAndOp(t *testing.T) {
assert.Equal(t, 1, ctx.Estack.Len())
item, err := ctx.Estack.PopBoolean()
if err != nil {
t.Fail()
}
assert.Nil(t, err)
assert.Equal(t, true, item.Value())
}
@ -550,9 +477,7 @@ func TestBoolOrOp(t *testing.T) {
assert.Equal(t, 1, ctx.Estack.Len())
item, err := ctx.Estack.PopBoolean()
if err != nil {
t.Fail()
}
assert.Nil(t, err)
assert.Equal(t, true, item.Value())
}
@ -562,10 +487,10 @@ func TestLtOp(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(10))
assert.NoError(t, err)
assert.Nil(t, err)
b, err := stack.NewInt(big.NewInt(2))
assert.NoError(t, err)
assert.Nil(t, err)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a).Push(b)
@ -581,7 +506,7 @@ func TestLtOp(t *testing.T) {
assert.Equal(t, 1, ctx.Estack.Len())
item, err := ctx.Estack.PopBoolean()
assert.NoError(t, err)
assert.Nil(t, err)
assert.Equal(t, false, item.Value())
}
@ -591,10 +516,10 @@ func TestGtOp(t *testing.T) {
v := VM{}
a, err := stack.NewInt(big.NewInt(10))
assert.NoError(t, err)
assert.Nil(t, err)
b, err := stack.NewInt(big.NewInt(2))
assert.NoError(t, err)
assert.Nil(t, err)
ctx := stack.NewContext([]byte{})
ctx.Estack.Push(a).Push(b)
@ -610,7 +535,89 @@ func TestGtOp(t *testing.T) {
assert.Equal(t, 1, ctx.Estack.Len())
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())
}

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
}

101
pkg/vm/vmopscrypto_test.go Normal file
View 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()))
}