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
|
||||
}
|
||||
|
||||
// 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.
|
||||
// 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.
|
||||
|
|
|
@ -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)
|
||||
|
||||
var opFunc = map[stack.Instruction]stackInfo{
|
||||
stack.XOR: Xor,
|
||||
stack.OR: Or,
|
||||
stack.AND: And,
|
||||
stack.INVERT: Invert,
|
||||
stack.MIN: Min,
|
||||
stack.MAX: Max,
|
||||
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))
|
||||
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