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 _signextend(expr, val, arg_typ): if isinstance(expr, vy_ast.Hex): assert len(expr.value[2:]) // 2 == arg_typ._bytes_info.m n_bits = arg_typ._bytes_info.m_bits else: assert len(expr.value) == arg_typ.maxlen n_bits = arg_typ.maxlen * 8 return unsigned_to_signed(val, n_bits)
def _evm_int(node: IRnode, unsigned: bool = True) -> Optional[int]: if isinstance(node.value, int): ret = node.value else: return None if unsigned and ret < 0: return signed_to_unsigned(ret, 256, strict=True) elif not unsigned and ret > 2**255 - 1: return unsigned_to_signed(ret, 256, strict=True) return ret
def _signextend(val_bytes, bits): as_uint = int.from_bytes(val_bytes, byteorder="big") as_sint = unsigned_to_signed(as_uint, bits) return (as_sint % 2**256).to_bytes(32, byteorder="big")
def _wrap256(x, unsigned=UNSIGNED): x %= 2**256 # wrap in a signed way. if not unsigned: x = unsigned_to_signed(x, 256, strict=True) return x