Merge pull request #266 from dauTT/dauTT/vm-bitwise-opcodes-191
VM: Implement bitwise opcodes, closes #191 Implemented following opcodes: AND XOR OR INVERT
This commit is contained in:
commit
b50411b057
4 changed files with 259 additions and 2 deletions
|
@ -143,18 +143,49 @@ 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.
|
// Hash overrides the default abstract hash method.
|
||||||
func (i *Int) Hash() (string, error) {
|
func (i *Int) Hash() (string, error) {
|
||||||
data := fmt.Sprintf("%T %v", i, i.Value())
|
data := fmt.Sprintf("%T %v", i, i.Value())
|
||||||
return KeyGenerator([]byte(data))
|
return KeyGenerator([]byte(data))
|
||||||
|
}
|
||||||
|
|
||||||
// Min returns the mininum between two integers.
|
// Min returns the mininum between two integers.
|
||||||
func Min(a *Int, b *Int) *Int {
|
func Min(a *Int, b *Int) *Int {
|
||||||
if a.Lte(b) {
|
if a.Lte(b) {
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
return b
|
return b
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Max returns the maximun between two integers.
|
// Max returns the maximun between two integers.
|
||||||
|
|
|
@ -5,6 +5,10 @@ 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.XOR: Xor,
|
||||||
|
stack.OR: Or,
|
||||||
|
stack.AND: And,
|
||||||
|
stack.INVERT: Invert,
|
||||||
stack.MIN: Min,
|
stack.MIN: Min,
|
||||||
stack.MAX: Max,
|
stack.MAX: Max,
|
||||||
stack.WITHIN: Within,
|
stack.WITHIN: Within,
|
||||||
|
|
|
@ -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())
|
||||||
|
}
|
Loading…
Reference in a new issue