package vm import ( "math" "github.com/bytom/math/checked" ) func op1Add(vm *virtualMachine) error { err := vm.applyCost(2) if err != nil { return err } n, err := vm.popInt64(true) if err != nil { return err } res, ok := checked.AddInt64(n, 1) if !ok { return ErrRange } return vm.pushInt64(res, true) } func op1Sub(vm *virtualMachine) error { err := vm.applyCost(2) if err != nil { return err } n, err := vm.popInt64(true) if err != nil { return err } res, ok := checked.SubInt64(n, 1) if !ok { return ErrRange } return vm.pushInt64(res, true) } func op2Mul(vm *virtualMachine) error { err := vm.applyCost(2) if err != nil { return err } n, err := vm.popInt64(true) if err != nil { return err } res, ok := checked.MulInt64(n, 2) if !ok { return ErrRange } return vm.pushInt64(res, true) } func op2Div(vm *virtualMachine) error { err := vm.applyCost(2) if err != nil { return err } n, err := vm.popInt64(true) if err != nil { return err } return vm.pushInt64(n>>1, true) } func opNegate(vm *virtualMachine) error { err := vm.applyCost(2) if err != nil { return err } n, err := vm.popInt64(true) if err != nil { return err } res, ok := checked.NegateInt64(n) if !ok { return ErrRange } return vm.pushInt64(res, true) } func opAbs(vm *virtualMachine) error { err := vm.applyCost(2) if err != nil { return err } n, err := vm.popInt64(true) if err != nil { return err } if n == math.MinInt64 { return ErrRange } if n < 0 { n = -n } return vm.pushInt64(n, true) } func opNot(vm *virtualMachine) error { err := vm.applyCost(2) if err != nil { return err } n, err := vm.popInt64(true) if err != nil { return err } return vm.pushBool(n == 0, true) } func op0NotEqual(vm *virtualMachine) error { err := vm.applyCost(2) if err != nil { return err } n, err := vm.popInt64(true) if err != nil { return err } return vm.pushBool(n != 0, true) } func opAdd(vm *virtualMachine) error { err := vm.applyCost(2) if err != nil { return err } y, err := vm.popInt64(true) if err != nil { return err } x, err := vm.popInt64(true) if err != nil { return err } res, ok := checked.AddInt64(x, y) if !ok { return ErrRange } return vm.pushInt64(res, true) } func opSub(vm *virtualMachine) error { err := vm.applyCost(2) if err != nil { return err } y, err := vm.popInt64(true) if err != nil { return err } x, err := vm.popInt64(true) if err != nil { return err } res, ok := checked.SubInt64(x, y) if !ok { return ErrRange } return vm.pushInt64(res, true) } func opMul(vm *virtualMachine) error { err := vm.applyCost(8) if err != nil { return err } y, err := vm.popInt64(true) if err != nil { return err } x, err := vm.popInt64(true) if err != nil { return err } res, ok := checked.MulInt64(x, y) if !ok { return ErrRange } return vm.pushInt64(res, true) } func opDiv(vm *virtualMachine) error { err := vm.applyCost(8) if err != nil { return err } y, err := vm.popInt64(true) if err != nil { return err } x, err := vm.popInt64(true) if err != nil { return err } if y == 0 { return ErrDivZero } res, ok := checked.DivInt64(x, y) if !ok { return ErrRange } return vm.pushInt64(res, true) } func opMod(vm *virtualMachine) error { err := vm.applyCost(8) if err != nil { return err } y, err := vm.popInt64(true) if err != nil { return err } x, err := vm.popInt64(true) if err != nil { return err } if y == 0 { return ErrDivZero } res, ok := checked.ModInt64(x, y) if !ok { return ErrRange } // Go's modulus operator produces the wrong result for mixed-sign // operands if res != 0 && (x >= 0) != (y >= 0) { res += y } return vm.pushInt64(res, true) } func opLshift(vm *virtualMachine) error { err := vm.applyCost(8) if err != nil { return err } y, err := vm.popInt64(true) if err != nil { return err } if y < 0 { return ErrBadValue } x, err := vm.popInt64(true) if err != nil { return err } if x == 0 || y == 0 { return vm.pushInt64(x, true) } res, ok := checked.LshiftInt64(x, y) if !ok { return ErrRange } return vm.pushInt64(res, true) } func opRshift(vm *virtualMachine) error { err := vm.applyCost(8) if err != nil { return err } y, err := vm.popInt64(true) if err != nil { return err } x, err := vm.popInt64(true) if err != nil { return err } if y < 0 { return ErrBadValue } return vm.pushInt64(x>>uint64(y), true) } func opBoolAnd(vm *virtualMachine) error { err := vm.applyCost(2) if err != nil { return err } b, err := vm.pop(true) if err != nil { return err } a, err := vm.pop(true) if err != nil { return err } return vm.pushBool(AsBool(a) && AsBool(b), true) } func opBoolOr(vm *virtualMachine) error { err := vm.applyCost(2) if err != nil { return err } b, err := vm.pop(true) if err != nil { return err } a, err := vm.pop(true) if err != nil { return err } return vm.pushBool(AsBool(a) || AsBool(b), true) } const ( cmpLess = iota cmpLessEqual cmpGreater cmpGreaterEqual cmpEqual cmpNotEqual ) func opNumEqual(vm *virtualMachine) error { return doNumCompare(vm, cmpEqual) } func opNumEqualVerify(vm *virtualMachine) error { err := vm.applyCost(2) if err != nil { return err } y, err := vm.popInt64(true) if err != nil { return err } x, err := vm.popInt64(true) if err != nil { return err } if x == y { return nil } return ErrVerifyFailed } func opNumNotEqual(vm *virtualMachine) error { return doNumCompare(vm, cmpNotEqual) } func opLessThan(vm *virtualMachine) error { return doNumCompare(vm, cmpLess) } func opGreaterThan(vm *virtualMachine) error { return doNumCompare(vm, cmpGreater) } func opLessThanOrEqual(vm *virtualMachine) error { return doNumCompare(vm, cmpLessEqual) } func opGreaterThanOrEqual(vm *virtualMachine) error { return doNumCompare(vm, cmpGreaterEqual) } func doNumCompare(vm *virtualMachine, op int) error { err := vm.applyCost(2) if err != nil { return err } y, err := vm.popInt64(true) if err != nil { return err } x, err := vm.popInt64(true) if err != nil { return err } var res bool switch op { case cmpLess: res = x < y case cmpLessEqual: res = x <= y case cmpGreater: res = x > y case cmpGreaterEqual: res = x >= y case cmpEqual: res = x == y case cmpNotEqual: res = x != y } return vm.pushBool(res, true) } func opMin(vm *virtualMachine) error { err := vm.applyCost(2) if err != nil { return err } y, err := vm.popInt64(true) if err != nil { return err } x, err := vm.popInt64(true) if err != nil { return err } if x > y { x = y } return vm.pushInt64(x, true) } func opMax(vm *virtualMachine) error { err := vm.applyCost(2) if err != nil { return err } y, err := vm.popInt64(true) if err != nil { return err } x, err := vm.popInt64(true) if err != nil { return err } if x < y { x = y } return vm.pushInt64(x, true) } func opWithin(vm *virtualMachine) error { err := vm.applyCost(4) if err != nil { return err } max, err := vm.popInt64(true) if err != nil { return err } min, err := vm.popInt64(true) if err != nil { return err } x, err := vm.popInt64(true) if err != nil { return err } return vm.pushBool(x >= min && x < max, true) }