diff --git a/pkg/vm/stack/Int.go b/pkg/vm/stack/Int.go index 12b77aa53..903190541 100644 --- a/pkg/vm/stack/Int.go +++ b/pkg/vm/stack/Int.go @@ -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. diff --git a/pkg/vm/vm_ops.go b/pkg/vm/vm_ops.go index 50868960a..016aebd2e 100644 --- a/pkg/vm/vm_ops.go +++ b/pkg/vm/vm_ops.go @@ -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, diff --git a/pkg/vm/vm_ops_bitwise.go b/pkg/vm/vm_ops_bitwise.go index 350543fa2..4e65cafd4 100644 --- a/pkg/vm/vm_ops_bitwise.go +++ b/pkg/vm/vm_ops_bitwise.go @@ -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 +} diff --git a/pkg/vm/vm_ops_bitwise_test.go b/pkg/vm/vm_ops_bitwise_test.go new file mode 100644 index 000000000..6e92ada2f --- /dev/null +++ b/pkg/vm/vm_ops_bitwise_test.go @@ -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()) +}