def test_unsafe_op_int(get_contract, typ, op): contract_1 = f""" @external def foo(x: {typ}, y: {typ}) -> {typ}: return unsafe_{op}(x, y) """ contract_2 = """ @external def foo(x: {typ}) -> {typ}: return unsafe_{op}(x, {literal}) """ fns = { "add": operator.add, "sub": operator.sub, "mul": operator.mul, "div": evm_div } fn = fns[op] int_info = parse_integer_typeinfo(typ) c1 = get_contract(contract_1) lo, hi = int_bounds(int_info.is_signed, int_info.bits) # (roughly 8k cases total generated) # TODO refactor to use fixtures NUM_CASES = 15 xs = [random.randrange(lo, hi) for _ in range(NUM_CASES)] ys = [random.randrange(lo, hi) for _ in range(NUM_CASES)] mod_bound = 2**int_info.bits # poor man's fuzzing - hypothesis doesn't make it easy # with the parametrized strategy if int_info.is_signed: xs += [lo, lo + 1, -1, 0, 1, hi - 1, hi] ys += [lo, lo + 1, -1, 0, 1, hi - 1, hi] for (x, y) in itertools.product(xs, ys): expected = unsigned_to_signed(fn(x, y) % mod_bound, int_info.bits) assert c1.foo(x, y) == expected c2 = get_contract(contract_2.format(typ=typ, op=op, literal=y)) assert c2.foo(x) == expected else: # 0x80 has some weird properties, like # it's a fixed point of multiplication by 0xFF fixed_pt = 2**(int_info.bits - 1) xs += [0, 1, hi - 1, hi, fixed_pt] ys += [0, 1, hi - 1, hi, fixed_pt] for (x, y) in itertools.product(xs, ys): expected = fn(x, y) % mod_bound assert c1.foo(x, y) == expected c2 = get_contract(contract_2.format(typ=typ, op=op, literal=y)) assert c2.foo(x) == expected
def test_sint_clamper_failing(w3, assert_tx_failed, get_contract, n, evm_version): bits = 8 * (n + 1) lo, hi = int_bounds(True, bits) values = [-(2 ** 255), 2 ** 255 - 1, lo - 1, hi + 1] code = f""" @external def foo(s: int{bits}) -> int{bits}: return s """ c = get_contract(code, evm_version=evm_version) for v in values: assert_tx_failed(lambda: _make_tx(w3, c.address, f"foo(int{bits})", [v]))
def test_sint_clamper_passing(w3, get_contract, n, evm_version): bits = 8 * (n + 1) lo, hi = int_bounds(True, bits) values = [-1, 0, 1, lo, hi] code = f""" @external def foo(s: int{bits}) -> int{bits}: return s """ c = get_contract(code, evm_version=evm_version) for v in values: assert c.foo(v) == v
def test_minmax_value_int(get_contract, op, typ): code = f""" @external def foo() -> {typ}: return {op}({typ}) """ c = get_contract(code) typ_info = parse_integer_typeinfo(typ) (lo, hi) = int_bounds(typ_info.is_signed, typ_info.bits) if op == "min_value": assert c.foo() == lo elif op == "max_value": assert c.foo() == hi
def test_unsafe_op_int(get_contract, typ, op): code = f""" @external def foo(x: {typ}, y: {typ}) -> {typ}: return unsafe_{op}(x, y) """ fns = { "add": operator.add, "sub": operator.sub, "mul": operator.mul, "div": evm_div } fn = fns[op] int_info = parse_integer_typeinfo(typ) c = get_contract(code) lo, hi = int_bounds(int_info.is_signed, int_info.bits) NUM_CASES = 33 # any more than this and fuzzer takes too long xs = [random.randrange(lo, hi) for _ in range(NUM_CASES)] ys = [random.randrange(lo, hi) for _ in range(NUM_CASES)] mod_bound = 2**int_info.bits # poor man's fuzzing - hypothesis doesn't make it easy # with the parametrized strategy if int_info.is_signed: xs += [lo, lo + 1, -1, 0, 1, hi - 1, hi] ys += [lo, lo + 1, -1, 0, 1, hi - 1, hi] for (x, y) in itertools.product(xs, ys): expected = _as_signed(fn(x, y) % mod_bound, int_info.bits) assert c.foo(x, y) == expected else: # 0x80 has some weird properties, like # it's a fixed point of multiplication by 0xFF fixed_pt = 2**(int_info.bits - 1) xs += [0, 1, hi - 1, hi, fixed_pt] ys += [0, 1, hi - 1, hi, fixed_pt] for (x, y) in itertools.product(xs, ys): assert c.foo(x, y) == fn(x, y) % mod_bound
def test_enum_conversion_2(get_contract_with_gas_estimation, assert_compile_failed, assert_tx_failed, val, typ): contract = f""" enum Status: STARTED PAUSED STOPPED @external def foo(a: {typ}) -> Status: return convert(a, Status) """ if typ == "uint256": c = get_contract_with_gas_estimation(contract) lo, hi = int_bounds(signed=False, bits=3) if lo <= val <= hi: assert c.foo(val) == val else: assert_tx_failed(lambda: c.foo(val)) else: assert_compile_failed( lambda: get_contract_with_gas_estimation(contract), TypeMismatch)
import itertools import operator import random import pytest from vyper.codegen.types.types import UNSIGNED_INTEGER_TYPES, parse_integer_typeinfo from vyper.exceptions import InvalidType, OverflowException, ZeroDivisionException from vyper.utils import SizeLimits, evm_div, evm_mod, int_bounds PARAMS = [] for t in sorted(UNSIGNED_INTEGER_TYPES): info = parse_integer_typeinfo(t) lo, hi = int_bounds(bits=info.bits, signed=info.is_signed) PARAMS.append((t, lo, hi, info.bits)) @pytest.mark.parametrize("typ,lo,hi,bits", PARAMS) def test_exponent_base_zero(get_contract, typ, lo, hi, bits): code = f""" @external def foo(x: {typ}) -> {typ}: return 0 ** x """ c = get_contract(code) assert c.foo(0) == 1 assert c.foo(1) == 0 assert c.foo(42) == 0 assert c.foo(hi) == 0
def bounds(self) -> Tuple[int, int]: # The bounds of this type # (note behavior for decimal: int value in IR land, # rather than Decimal value in Python land) return int_bounds(signed=self.is_signed, bits=self.bits)
def bounds(self) -> Tuple[int, int]: return int_bounds(signed=self.is_signed, bits=self.bits)
(SignedIntegerAbstractType, _SignedIntegerDefinition), {"_bits": bits}) # class Uint256Definition(UnsignedIntegerAbstractType, _UnsignedIntegerDefinition): # _bits = 256 uint_def = type(uint, (UnsignedIntegerAbstractType, _UnsignedIntegerDefinition), {"_bits": bits}) globals()[sint] = sint_def globals()[uint] = uint_def globals()[f"Int{bits}Primitive"] = type( f"Int{bits}Primitive", (_NumericPrimitive, ), { "_bounds": int_bounds(signed=True, bits=bits), "_id": f"int{bits}", "_type": sint_def, "_valid_literal": (vy_ast.Int, ), }, ) globals()[f"Uint{bits}Primitive"] = type( f"Uint{bits}Primitive", (_NumericPrimitive, ), { "_bounds": int_bounds(signed=False, bits=bits), "_id": f"uint{bits}", "_type": uint_def, "_valid_literal": (vy_ast.Int, ), },
def _comparison_helper(binop, args, prefer_strict=False): assert binop in COMPARISON_OPS if _is_int(args[0]): binop = _flip_comparison_op(binop) args = [args[1], args[0]] unsigned = not binop.startswith("s") is_strict = binop.endswith("t") is_gt = "g" in binop # local version of _evm_int which defaults to the current binop's signedness def _int(x): return _evm_int(x, unsigned=unsigned) lo, hi = int_bounds(bits=256, signed=not unsigned) # for comparison operators, we have three special boundary cases: # almost always, never and almost never. # almost_always is always true for the non-strict ("ge" and co) # comparators. for strict comparators ("gt" and co), almost_always # is true except for one case. never is never true for the strict # comparators. never is almost always false for the non-strict # comparators, except for one case. and almost_never is almost # never true (except one case) for the strict comparators. if is_gt: almost_always, never = lo, hi almost_never = hi - 1 else: almost_always, never = hi, lo almost_never = lo + 1 if is_strict and _int(args[1]) == never: # e.g. gt x MAX_UINT256, slt x MIN_INT256 return (0, []) if not is_strict and _int(args[1]) == almost_always: # e.g. ge x MIN_UINT256, sle x MAX_INT256 return (1, []) if is_strict and _int(args[1]) == almost_never: # (lt x 1), (gt x (MAX_UINT256 - 1)), (slt x (MIN_INT256 + 1)) return ("eq", [args[0], never]) # rewrites. in positions where iszero is preferred, (gt x 5) => (ge x 6) if is_strict != prefer_strict and _is_int(args[1]): rhs = _int(args[1]) if prefer_strict and rhs == never: # e.g. ge x MAX_UINT256, sle x MIN_INT256 return ("eq", args) if not prefer_strict and rhs == almost_always: # e.g. gt x 0, slt x MAX_INT256 return ("ne", args) if is_gt == is_strict: # x > 1 => x >= 2 # x <= 1 => x < 2 new_rhs = rhs + 1 else: # x >= 1 => x > 0 # x < 1 => x <= 0 new_rhs = rhs - 1 # if args[1] is OOB, it should have been handled above # in the always/never cases assert _wrap256(new_rhs, unsigned) == new_rhs, "bad optimizer step" # change the strictness of the op if prefer_strict: # e.g. "sge" => "sgt" new_op = binop.replace("e", "t") else: # e.g. "sgt" => "sge" new_op = binop.replace("t", "e") return (new_op, [args[0], new_rhs]) # special cases that are not covered by others: if binop == "gt" and _int(args[1]) == 0: # improve codesize (not gas), and maybe trigger # downstream optimizations return ("iszero", [["iszero", args[0]]])