From 1fbc0af5db60316c058ff54881daabb094883872 Mon Sep 17 00:00:00 2001 From: dauTT <30392990+dauTT@users.noreply.github.com> Date: Fri, 29 Mar 2019 22:22:45 +0100 Subject: [PATCH] VM: Implement BOOLAND, BOOLOR opcode (#251) * Implemented BOOLAND, BOOLOR opcode --- pkg/vm/stack/boolean.go | 14 ++++++++++ pkg/vm/vm_ops.go | 3 ++- pkg/vm/vm_ops_maths.go | 51 +++++++++++++++++++++++++++++++++++++ pkg/vm/vm_ops_maths_test.go | 47 ++++++++++++++++++++++++++++++++++ 4 files changed, 114 insertions(+), 1 deletion(-) diff --git a/pkg/vm/stack/boolean.go b/pkg/vm/stack/boolean.go index 93fabd84e..5a5a93207 100644 --- a/pkg/vm/stack/boolean.go +++ b/pkg/vm/stack/boolean.go @@ -30,3 +30,17 @@ func (b *Boolean) Value() bool { func (b *Boolean) Not() *Boolean { return NewBoolean(!b.Value()) } + +// And returns a Boolean whose underlying value is obtained +// by applying the && operator to two Booleans' values. +func (b *Boolean) And(a *Boolean) *Boolean { + c := b.Value() && a.Value() + return NewBoolean(c) +} + +// Or returns a Boolean whose underlying value is obtained +// by applying the || operator to two Booleans' values. +func (b *Boolean) Or(a *Boolean) *Boolean { + c := b.Value() || a.Value() + return NewBoolean(c) +} diff --git a/pkg/vm/vm_ops.go b/pkg/vm/vm_ops.go index 1854b1634..b73b6aa31 100644 --- a/pkg/vm/vm_ops.go +++ b/pkg/vm/vm_ops.go @@ -5,7 +5,8 @@ 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.BOOLAND: BoolAnd, + stack.BOOLOR: BoolOr, stack.LT: Lt, stack.GT: Gt, stack.SHR: Shr, diff --git a/pkg/vm/vm_ops_maths.go b/pkg/vm/vm_ops_maths.go index 7abd73b9f..d9a3a9bbc 100644 --- a/pkg/vm/vm_ops_maths.go +++ b/pkg/vm/vm_ops_maths.go @@ -206,6 +206,44 @@ func Not(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rst return NONE, nil } +// BoolAnd pops two booleans off of the stack and pushes a boolean to the stack +// whose value is true iff both booleans' values are true. +// Returns an error if either items cannot be casted to an boolean +func BoolAnd(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) { + + bool1, bool2, err := popTwoBooleans(ctx) + if err != nil { + return FAULT, err + } + res := bool1.And(bool2) + if err != nil { + return FAULT, err + } + + ctx.Estack.Push(res) + + return NONE, nil +} + +// BoolOr pops two booleans off of the stack and pushes a boolean to the stack +// whose value is true iff at least one of the two booleans' value is true. +// Returns an error if either items cannot be casted to an boolean +func BoolOr(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) { + + bool1, bool2, err := popTwoBooleans(ctx) + if err != nil { + return FAULT, err + } + res := bool1.Or(bool2) + if err != nil { + return FAULT, err + } + + ctx.Estack.Push(res) + + return NONE, nil +} + // Sign puts the sign of the top stack Item on top of the stack. // If value is negative, put -1; // If positive, put 1; @@ -345,3 +383,16 @@ func popTwoByteArrays(ctx *stack.Context) (*stack.ByteArray, *stack.ByteArray, e } return ba1, ba2, nil } + +func popTwoBooleans(ctx *stack.Context) (*stack.Boolean, *stack.Boolean, error) { + bool1, err := ctx.Estack.PopBoolean() + if err != nil { + return nil, nil, err + } + bool2, err := ctx.Estack.PopBoolean() + if err != nil { + return nil, nil, err + } + + return bool1, bool2, nil +} diff --git a/pkg/vm/vm_ops_maths_test.go b/pkg/vm/vm_ops_maths_test.go index 29d843999..3d0163f47 100644 --- a/pkg/vm/vm_ops_maths_test.go +++ b/pkg/vm/vm_ops_maths_test.go @@ -395,6 +395,52 @@ func TestShrOp(t *testing.T) { assert.Equal(t, int64(2), item.Value().Int64()) } +func TestBoolAndOp(t *testing.T) { + + v := VM{} + + a := stack.NewBoolean(true) + b := stack.NewBoolean(true) + + ctx := stack.NewContext([]byte{}) + ctx.Estack.Push(a).Push(b) + + v.executeOp(stack.BOOLAND, ctx) + + // Stack should have one item + assert.Equal(t, 1, ctx.Estack.Len()) + + item, err := ctx.Estack.PopBoolean() + if err != nil { + t.Fail() + } + + assert.Equal(t, true, item.Value()) +} + + func TestBoolOrOp(t *testing.T) { + + v := VM{} + + a := stack.NewBoolean(false) + b := stack.NewBoolean(true) + + ctx := stack.NewContext([]byte{}) + ctx.Estack.Push(a).Push(b) + + v.executeOp(stack.BOOLOR, ctx) + + // Stack should have one item + assert.Equal(t, 1, ctx.Estack.Len()) + + item, err := ctx.Estack.PopBoolean() + if err != nil { + t.Fail() + } + + assert.Equal(t, true, item.Value()) +} + func TestLtOp(t *testing.T) { v := VM{} @@ -452,3 +498,4 @@ func TestGtOp(t *testing.T) { assert.Equal(t, true, item.Value()) } +