From 9402540c3a3ca471bfd6a301d6764f872cf18cc0 Mon Sep 17 00:00:00 2001 From: dauTT <30392990+dauTT@users.noreply.github.com> Date: Fri, 29 Mar 2019 20:35:16 +0100 Subject: [PATCH 1/4] Implemented LT, GT opcode (#259) * Implemented LT, GT opcode --- pkg/vm/stack/Int.go | 14 +++++++++ pkg/vm/vm_ops.go | 3 ++ pkg/vm/vm_ops_maths.go | 32 ++++++++++++++++++++ pkg/vm/vm_ops_maths_test.go | 58 +++++++++++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+) diff --git a/pkg/vm/stack/Int.go b/pkg/vm/stack/Int.go index 52e6240d4..3226904ee 100644 --- a/pkg/vm/stack/Int.go +++ b/pkg/vm/stack/Int.go @@ -111,3 +111,17 @@ func (i *Int) Abs() (*Int, error) { 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. +func (i *Int) Lt(s *Int) bool { + return i.Value().Cmp(s.Value()) == -1 +} + +// Gt 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. +func (i *Int) Gt(s *Int) bool { + return i.Value().Cmp(s.Value()) == 1 +} diff --git a/pkg/vm/vm_ops.go b/pkg/vm/vm_ops.go index cef46a199..1854b1634 100644 --- a/pkg/vm/vm_ops.go +++ b/pkg/vm/vm_ops.go @@ -5,6 +5,9 @@ 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.LT: Lt, + stack.GT: Gt, stack.SHR: Shr, stack.SHL: Shl, stack.INC: Inc, diff --git a/pkg/vm/vm_ops_maths.go b/pkg/vm/vm_ops_maths.go index 5cbe0552a..7abd73b9f 100644 --- a/pkg/vm/vm_ops_maths.go +++ b/pkg/vm/vm_ops_maths.go @@ -300,6 +300,38 @@ func Shr(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rst return NONE, nil } +// Lt pops two integers, a and b, off of the stack and pushes a boolean the stack +// whose value is true if a's value is less than b's value. +// Returns an error if either items cannot be casted to an integer +func Lt(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 := operandB.Lt(operandA) + + ctx.Estack.Push(stack.NewBoolean(res)) + + return NONE, nil +} + +// Gt pops two integers, a and b, off of the stack and pushes a boolean the stack +// whose value is true if a's value is greated than b's value. +// Returns an error if either items cannot be casted to an integer +func Gt(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 := operandB.Gt(operandA) + + ctx.Estack.Push(stack.NewBoolean(res)) + + return NONE, 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() diff --git a/pkg/vm/vm_ops_maths_test.go b/pkg/vm/vm_ops_maths_test.go index b703751be..29d843999 100644 --- a/pkg/vm/vm_ops_maths_test.go +++ b/pkg/vm/vm_ops_maths_test.go @@ -394,3 +394,61 @@ func TestShrOp(t *testing.T) { assert.Equal(t, int64(2), item.Value().Int64()) } + +func TestLtOp(t *testing.T) { + + v := VM{} + + a, err := stack.NewInt(big.NewInt(10)) + assert.NoError(t, err) + + b, err := stack.NewInt(big.NewInt(2)) + assert.NoError(t, err) + + ctx := stack.NewContext([]byte{}) + ctx.Estack.Push(a).Push(b) + + // b is the first item pop. + // a is the second item pop. + // we perform a < b and place + // the result on top of the evaluation + // stack + v.executeOp(stack.LT, ctx) + + // Stack should have one item + assert.Equal(t, 1, ctx.Estack.Len()) + + item, err := ctx.Estack.PopBoolean() + assert.NoError(t, err) + + assert.Equal(t, false, item.Value()) +} + +func TestGtOp(t *testing.T) { + + v := VM{} + + a, err := stack.NewInt(big.NewInt(10)) + assert.NoError(t, err) + + b, err := stack.NewInt(big.NewInt(2)) + assert.NoError(t, err) + + ctx := stack.NewContext([]byte{}) + ctx.Estack.Push(a).Push(b) + + // b is the first item pop. + // a is the second item pop. + // we perform a > b and place + // the result on top of the evaluation + // stack + v.executeOp(stack.GT, ctx) + + // Stack should have one item + assert.Equal(t, 1, ctx.Estack.Len()) + + item, err := ctx.Estack.PopBoolean() + assert.NoError(t, err) + + assert.Equal(t, true, item.Value()) +} 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 2/4] 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()) } + From 7bf4d691a9f651d12708b44657a5587a8979dc5f Mon Sep 17 00:00:00 2001 From: dauTT <30392990+dauTT@users.noreply.github.com> Date: Sat, 30 Mar 2019 16:01:06 +0100 Subject: [PATCH 3/4] Implemented NUMEQUAL, NUMNOTEQUAL opcode (#256) --- pkg/vm/vm_ops.go | 2 ++ pkg/vm/vm_ops_maths.go | 32 ++++++++++++++++++++ pkg/vm/vm_ops_maths_test.go | 58 +++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+) diff --git a/pkg/vm/vm_ops.go b/pkg/vm/vm_ops.go index b73b6aa31..d50609462 100644 --- a/pkg/vm/vm_ops.go +++ b/pkg/vm/vm_ops.go @@ -5,6 +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.NUMEQUAL: NumEqual, + stack.NUMNOTEQUAL: NumNotEqual, stack.BOOLAND: BoolAnd, stack.BOOLOR: BoolOr, stack.LT: Lt, diff --git a/pkg/vm/vm_ops_maths.go b/pkg/vm/vm_ops_maths.go index d9a3a9bbc..e23a7a4a0 100644 --- a/pkg/vm/vm_ops_maths.go +++ b/pkg/vm/vm_ops_maths.go @@ -173,6 +173,38 @@ func Mul(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rst return NONE, nil } +// NumEqual pops two Items off of the stack and pushes a boolean to the stack +// whose value is true iff the the two Items are equal. +// Returns an error if either items cannot be casted to an integer. +func NumEqual(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 := operandA.Equal(operandB) + + ctx.Estack.Push(stack.NewBoolean(res)) + + return NONE, nil +} + +// NumNotEqual pops two Items off of the stack and pushes a boolean to the stack +// whose value is true iff the two Items are not equal. +// Returns an error if either items cannot be casted to an integer. +func NumNotEqual(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 := operandA.Equal(operandB) + + ctx.Estack.Push(stack.NewBoolean(!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) { diff --git a/pkg/vm/vm_ops_maths_test.go b/pkg/vm/vm_ops_maths_test.go index 3d0163f47..b8478f29b 100644 --- a/pkg/vm/vm_ops_maths_test.go +++ b/pkg/vm/vm_ops_maths_test.go @@ -277,6 +277,64 @@ func TestNotOp(t *testing.T) { assert.Equal(t, true, item.Value()) } +func TestNumEqual(t *testing.T) { + + v := VM{} + + a, err := stack.NewInt(big.NewInt(6)) + if err != nil { + t.Fail() + } + b, err := stack.NewInt(big.NewInt(6)) + if err != nil { + t.Fail() + } + + ctx := stack.NewContext([]byte{}) + ctx.Estack.Push(a).Push(b) + + v.executeOp(stack.NUMEQUAL, 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 TestNumNotEqual(t *testing.T) { + + v := VM{} + + a, err := stack.NewInt(big.NewInt(5)) + if err != nil { + t.Fail() + } + b, err := stack.NewInt(big.NewInt(6)) + if err != nil { + t.Fail() + } + + ctx := stack.NewContext([]byte{}) + ctx.Estack.Push(a).Push(b) + + v.executeOp(stack.NUMNOTEQUAL, 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 TestSignOp(t *testing.T) { v := VM{} From 045db09af24ddee70287105685f99d877dd45cf7 Mon Sep 17 00:00:00 2001 From: dauTT <30392990+dauTT@users.noreply.github.com> Date: Wed, 3 Apr 2019 00:43:52 +0200 Subject: [PATCH 4/4] Implemented LTE, GTE opcode (#260) * Implemented LTE, GTE opcode --- pkg/vm/stack/Int.go | 14 ++++++ pkg/vm/vm_ops.go | 2 + pkg/vm/vm_ops_maths.go | 52 +++++++++++++++++----- pkg/vm/vm_ops_maths_test.go | 89 ++++++++++++++++++++++++++++++------- 4 files changed, 131 insertions(+), 26 deletions(-) diff --git a/pkg/vm/stack/Int.go b/pkg/vm/stack/Int.go index 3226904ee..a10c099c7 100644 --- a/pkg/vm/stack/Int.go +++ b/pkg/vm/stack/Int.go @@ -100,6 +100,20 @@ func (i *Int) Value() *big.Int { return i.val } +// 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. +func (i *Int) Lte(s *Int) bool { + return i.Value().Cmp(s.Value()) != 1 +} + +// Gte 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. +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) { diff --git a/pkg/vm/vm_ops.go b/pkg/vm/vm_ops.go index d50609462..0a9c6f173 100644 --- a/pkg/vm/vm_ops.go +++ b/pkg/vm/vm_ops.go @@ -10,7 +10,9 @@ var opFunc = map[stack.Instruction]stackInfo{ stack.BOOLAND: BoolAnd, stack.BOOLOR: BoolOr, stack.LT: Lt, + stack.LTE: Lte, stack.GT: Gt, + stack.GTE: Gte, stack.SHR: Shr, stack.SHL: Shl, stack.INC: Inc, diff --git a/pkg/vm/vm_ops_maths.go b/pkg/vm/vm_ops_maths.go index e23a7a4a0..9bab3e48e 100644 --- a/pkg/vm/vm_ops_maths.go +++ b/pkg/vm/vm_ops_maths.go @@ -317,17 +317,36 @@ func Negate(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, return NONE, nil } -func popTwoIntegers(ctx *stack.Context) (*stack.Int, *stack.Int, error) { - operandA, err := ctx.Estack.PopInt() - if err != nil { - return nil, nil, err - } - operandB, err := ctx.Estack.PopInt() - if err != nil { - return nil, nil, err - } +// Lte pops two integers, a and b, off of the stack and pushes a boolean the stack +// whose value is true if a's value is less than or equal to b's value. +// Returns an error if either items cannot be casted to an integer +func Lte(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rstack *stack.RandomAccess) (Vmstate, error) { - return operandA, operandB, nil + operandA, operandB, err := popTwoIntegers(ctx) + if err != nil { + return FAULT, err + } + res := operandB.Lte(operandA) + + ctx.Estack.Push(stack.NewBoolean(res)) + + return NONE, nil +} + +// Gte pops two integers, a and b, off of the stack and pushes a boolean the stack +// whose value is true if a's value is greated than or equal to b's value. +// Returns an error if either items cannot be casted to an integer +func Gte(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 := operandB.Gte(operandA) + + ctx.Estack.Push(stack.NewBoolean(res)) + + return NONE, nil } // Shl pops two integers, a and b, off of the stack and pushes an integer to the stack @@ -402,6 +421,19 @@ func Gt(op stack.Instruction, ctx *stack.Context, istack *stack.Invocation, rsta return NONE, nil } +func popTwoIntegers(ctx *stack.Context) (*stack.Int, *stack.Int, error) { + operandA, err := ctx.Estack.PopInt() + if err != nil { + return nil, nil, err + } + operandB, err := ctx.Estack.PopInt() + if err != nil { + return nil, nil, err + } + + return operandA, operandB, 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() diff --git a/pkg/vm/vm_ops_maths_test.go b/pkg/vm/vm_ops_maths_test.go index b8478f29b..14f83b1ec 100644 --- a/pkg/vm/vm_ops_maths_test.go +++ b/pkg/vm/vm_ops_maths_test.go @@ -385,6 +385,64 @@ func TestNegateOp(t *testing.T) { assert.Equal(t, int64(20), item.Value().Int64()) } +func TestLteOp(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) + + // b is the first item pop. + // a is the second item pop. + // we perform a <= b and place + // the result on top of the evaluation + // stack + v.executeOp(stack.LTE, 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()) +} + +func TestGteOp(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) + + // b is the first item pop. + // a is the second item pop. + // we perform a >= b and place + // the result on top of the evaluation + // stack + v.executeOp(stack.GTE, 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()) +} + func TestShlOp(t *testing.T) { v := VM{} @@ -455,48 +513,48 @@ func TestShrOp(t *testing.T) { func TestBoolAndOp(t *testing.T) { - v := VM{} + v := VM{} - a := stack.NewBoolean(true) + a := stack.NewBoolean(true) b := stack.NewBoolean(true) - ctx := stack.NewContext([]byte{}) + ctx := stack.NewContext([]byte{}) ctx.Estack.Push(a).Push(b) - v.executeOp(stack.BOOLAND, ctx) + v.executeOp(stack.BOOLAND, ctx) - // Stack should have one item + // Stack should have one item assert.Equal(t, 1, ctx.Estack.Len()) - item, err := ctx.Estack.PopBoolean() + item, err := ctx.Estack.PopBoolean() if err != nil { t.Fail() } - assert.Equal(t, true, item.Value()) + assert.Equal(t, true, item.Value()) } - func TestBoolOrOp(t *testing.T) { +func TestBoolOrOp(t *testing.T) { - v := VM{} + v := VM{} - a := stack.NewBoolean(false) + a := stack.NewBoolean(false) b := stack.NewBoolean(true) - ctx := stack.NewContext([]byte{}) + ctx := stack.NewContext([]byte{}) ctx.Estack.Push(a).Push(b) - v.executeOp(stack.BOOLOR, ctx) + v.executeOp(stack.BOOLOR, ctx) - // Stack should have one item + // Stack should have one item assert.Equal(t, 1, ctx.Estack.Len()) - item, err := ctx.Estack.PopBoolean() + item, err := ctx.Estack.PopBoolean() if err != nil { t.Fail() } - assert.Equal(t, true, item.Value()) + assert.Equal(t, true, item.Value()) } func TestLtOp(t *testing.T) { @@ -556,4 +614,3 @@ func TestGtOp(t *testing.T) { assert.Equal(t, true, item.Value()) } -