def parse_BinOp(self): left = Expr.parse_value_expr(self.expr.left, self.context) right = Expr.parse_value_expr(self.expr.right, self.context) if not is_numeric_type(left.typ) or not is_numeric_type(right.typ): return arithmetic_pair = {left.typ.typ, right.typ.typ} pos = getpos(self.expr) # Special case with uint256 were int literal may be casted. if arithmetic_pair == {"uint256", "int128"}: # Check right side literal. if right.typ.is_literal and SizeLimits.in_bounds( "uint256", right.value): right = LLLnode.from_list( right.value, typ=BaseType("uint256", None, is_literal=True), pos=pos, ) # Check left side literal. elif left.typ.is_literal and SizeLimits.in_bounds( "uint256", left.value): left = LLLnode.from_list( left.value, typ=BaseType("uint256", None, is_literal=True), pos=pos, ) if left.typ.typ == "decimal" and isinstance(self.expr.op, vy_ast.Pow): return # Only allow explicit conversions to occur. if left.typ.typ != right.typ.typ: return ltyp, rtyp = left.typ.typ, right.typ.typ arith = None if isinstance(self.expr.op, (vy_ast.Add, vy_ast.Sub)): new_typ = BaseType(ltyp) op = "add" if isinstance(self.expr.op, vy_ast.Add) else "sub" if ltyp == "uint256" and isinstance(self.expr.op, vy_ast.Add): # safeadd arith = [ "seq", ["assert", ["ge", ["add", "l", "r"], "l"]], ["add", "l", "r"] ] elif ltyp == "uint256" and isinstance(self.expr.op, vy_ast.Sub): # safesub arith = [ "seq", ["assert", ["ge", "l", "r"]], ["sub", "l", "r"] ] elif ltyp == rtyp: arith = [op, "l", "r"] elif isinstance(self.expr.op, vy_ast.Mult): new_typ = BaseType(ltyp) if ltyp == rtyp == "uint256": arith = [ "with", "ans", ["mul", "l", "r"], [ "seq", [ "assert", [ "or", ["eq", ["div", "ans", "l"], "r"], ["iszero", "l"] ] ], "ans", ], ] elif ltyp == rtyp == "int128": # TODO should this be 'smul' (note edge cases in YP for smul) arith = ["mul", "l", "r"] elif ltyp == rtyp == "decimal": # TODO should this be smul arith = [ "with", "ans", ["mul", "l", "r"], [ "seq", [ "assert", [ "or", ["eq", ["sdiv", "ans", "l"], "r"], ["iszero", "l"] ] ], ["sdiv", "ans", DECIMAL_DIVISOR], ], ] elif isinstance(self.expr.op, vy_ast.Div): if right.typ.is_literal and right.value == 0: return new_typ = BaseType(ltyp) if ltyp == rtyp == "uint256": arith = ["div", "l", ["clamp_nonzero", "r"]] elif ltyp == rtyp == "int128": arith = ["sdiv", "l", ["clamp_nonzero", "r"]] elif ltyp == rtyp == "decimal": arith = [ "sdiv", # TODO check overflow cases, also should it be smul ["mul", "l", DECIMAL_DIVISOR], ["clamp_nonzero", "r"], ] elif isinstance(self.expr.op, vy_ast.Mod): if right.typ.is_literal and right.value == 0: return new_typ = BaseType(ltyp) if ltyp == rtyp == "uint256": arith = ["mod", "l", ["clamp_nonzero", "r"]] elif ltyp == rtyp: # TODO should this be regular mod arith = ["smod", "l", ["clamp_nonzero", "r"]] elif isinstance(self.expr.op, vy_ast.Pow): if ltyp != "int128" and ltyp != "uint256" and isinstance( self.expr.right, vy_ast.Name): return new_typ = BaseType(ltyp) if ltyp == rtyp == "uint256": arith = [ "seq", [ "assert", [ "or", # r == 1 | iszero(r) # could be simplified to ~(r & 1) ["or", ["eq", "r", 1], ["iszero", "r"]], ["lt", "l", ["exp", "l", "r"]], ], ], ["exp", "l", "r"], ] elif ltyp == rtyp == "int128": arith = ["exp", "l", "r"] if arith is None: return p = ["seq"] if new_typ.typ == "int128": p.append([ "clamp", ["mload", MemoryPositions.MINNUM], arith, ["mload", MemoryPositions.MAXNUM], ]) elif new_typ.typ == "decimal": p.append([ "clamp", ["mload", MemoryPositions.MINDECIMAL], arith, ["mload", MemoryPositions.MAXDECIMAL], ]) elif new_typ.typ == "uint256": p.append(arith) else: return p = ["with", "l", left, ["with", "r", right, p]] return LLLnode.from_list(p, typ=new_typ, pos=pos)
def get_length(arg): if arg.location == "memory": return LLLnode.from_list(['mload', arg], typ=BaseType('int128')) elif arg.location == "storage": return LLLnode.from_list(['sload', ['sha3_32', arg]], typ=BaseType('int128'))
def pack_arguments(signature, args, context, pos, return_placeholder=True): placeholder_typ = ByteArrayType( maxlen=sum([get_size_of_type(arg.typ) for arg in signature.args]) * 32 + 32) placeholder = context.new_placeholder(placeholder_typ) setters = [['mstore', placeholder, signature.method_id]] needpos = False staticarray_offset = 0 expected_arg_count = len(signature.args) actual_arg_count = len(args) if actual_arg_count != expected_arg_count: raise StructureException( "Wrong number of args for: %s (%s args, expected %s)" % ( signature.name, actual_arg_count, expected_arg_count, )) for i, (arg, typ) in enumerate(zip(args, [arg.typ for arg in signature.args])): if isinstance(typ, BaseType): setters.append( make_setter(LLLnode.from_list( placeholder + staticarray_offset + 32 + i * 32, typ=typ, ), arg, 'memory', pos=pos, in_function_call=True)) elif isinstance(typ, ByteArrayLike): setters.append([ 'mstore', placeholder + staticarray_offset + 32 + i * 32, '_poz' ]) arg_copy = LLLnode.from_list('_s', typ=arg.typ, location=arg.location) target = LLLnode.from_list( ['add', placeholder + 32, '_poz'], typ=typ, location='memory', ) setters.append([ 'with', '_s', arg, [ 'seq', make_byte_array_copier(target, arg_copy, pos), [ 'set', '_poz', [ 'add', 32, ['ceil32', ['add', '_poz', get_length(arg_copy)]] ] ], ], ]) needpos = True elif isinstance(typ, (StructType, ListType)): if has_dynamic_data(typ): raise TypeMismatchException("Cannot pack bytearray in struct") target = LLLnode.from_list( [placeholder + 32 + staticarray_offset + i * 32], typ=typ, location='memory', ) setters.append(make_setter(target, arg, 'memory', pos=pos)) if (isinstance(typ, ListType)): count = typ.count else: count = len(typ.tuple_items()) staticarray_offset += 32 * (count - 1) else: raise TypeMismatchException("Cannot pack argument of type %r" % typ) # For private call usage, doesn't use a returner. returner = [[placeholder + 28]] if return_placeholder else [] if needpos: return (LLLnode.from_list([ 'with', '_poz', len(args) * 32 + staticarray_offset, ['seq'] + setters + returner ], typ=placeholder_typ, location='memory'), placeholder_typ.maxlen - 28, placeholder + 32) else: return (LLLnode.from_list(['seq'] + setters + returner, typ=placeholder_typ, location='memory'), placeholder_typ.maxlen - 28, placeholder + 32)
def make_setter(left, right, location, pos, in_function_call=False): # Basic types if isinstance(left.typ, BaseType): right = unwrap_location(right) if location == "storage": return LLLnode.from_list(["sstore", left, right], typ=None) elif location == "memory": return LLLnode.from_list(["mstore", left, right], typ=None) # Byte arrays elif isinstance(left.typ, ByteArrayLike): return make_byte_array_copier(left, right, pos) # Can't copy mappings elif isinstance(left.typ, MappingType): raise TypeMismatch("Cannot copy mappings; can only copy individual elements", pos) # Arrays elif isinstance(left.typ, ListType): # Cannot do something like [a, b, c] = [1, 2, 3] if left.value == "multi": return if not isinstance(right.typ, ListType): return if right.typ.count != left.typ.count: return left_token = LLLnode.from_list("_L", typ=left.typ, location=left.location) # If the right side is a literal if right.value in ["multi", "seq_unchecked"] and right.typ.is_literal: if right.value == "seq_unchecked": # when the LLL is `seq_unchecked`, this is a literal where one or # more values must be pre-processed to avoid memory corruption subs = right.args[:-1] right = right.args[-1] else: subs = [] for i in range(left.typ.count): lhs_setter = _make_array_index_setter(left, left_token, pos, location, i) subs.append(make_setter(lhs_setter, right.args[i], location, pos=pos,)) if left.location == "memory" and isinstance(left.value, int): return LLLnode.from_list(["seq"] + subs, typ=None) else: return LLLnode.from_list(["with", "_L", left, ["seq"] + subs], typ=None) elif right.value is None: if right.typ != left.typ: return if left.location == "memory": return mzero(left, 32 * get_size_of_type(left.typ)) subs = [] for i in range(left.typ.count): subs.append( make_setter( add_variable_offset( left_token, LLLnode.from_list(i, typ="int256"), pos=pos, array_bounds_check=False, ), LLLnode.from_list(None, typ=right.typ.subtype), location, pos=pos, ) ) return LLLnode.from_list(["with", "_L", left, ["seq"] + subs], typ=None) # If the right side is a variable else: right_token = LLLnode.from_list("_R", typ=right.typ, location=right.location) subs = [] for i in range(left.typ.count): lhs_setter = _make_array_index_setter(left, left_token, pos, left.location, i) rhs_setter = _make_array_index_setter(right, right_token, pos, right.location, i) subs.append(make_setter(lhs_setter, rhs_setter, location, pos=pos,)) lll_node = ["seq"] + subs if right.location != "memory" or not isinstance(right.value, int): lll_node = ["with", "_R", right, lll_node] if left.location != "memory" or not isinstance(left.value, int): lll_node = ["with", "_L", left, lll_node] return LLLnode.from_list(lll_node, typ=None) # Structs elif isinstance(left.typ, TupleLike): if left.value == "multi" and isinstance(left.typ, StructType): return if right.value is not None: if not isinstance(right.typ, left.typ.__class__): return if isinstance(left.typ, StructType): for k in left.typ.members: if k not in right.typ.members: return for k in right.typ.members: if k not in left.typ.members: return if left.typ.name != right.typ.name: return else: if len(left.typ.members) != len(right.typ.members): return left_token = LLLnode.from_list("_L", typ=left.typ, location=left.location) keyz = left.typ.tuple_keys() # If the left side is a literal if left.value == "multi": locations = [arg.location for arg in left.args] else: locations = [location for _ in keyz] # If the right side is a literal if right.value == "multi": if len(right.args) != len(keyz): return # get the RHS arguments into a dict because # they are not guaranteed to be in the same order # the LHS keys. right_args = dict(zip(right.typ.tuple_keys(), right.args)) subs = [] for (key, loc) in zip(keyz, locations): subs.append( make_setter( add_variable_offset(left_token, key, pos=pos), right_args[key], loc, pos=pos, ) ) return LLLnode.from_list(["with", "_L", left, ["seq"] + subs], typ=None) # If the right side is a null elif right.value is None: if left.typ != right.typ: return if left.location == "memory": return mzero(left, 32 * get_size_of_type(left.typ)) subs = [] for key, loc in zip(keyz, locations): subs.append( make_setter( add_variable_offset(left_token, key, pos=pos), LLLnode.from_list(None, typ=right.typ.members[key]), loc, pos=pos, ) ) return LLLnode.from_list(["with", "_L", left, ["seq"] + subs], typ=None) # If tuple assign. elif isinstance(left.typ, TupleType) and isinstance(right.typ, TupleType): subs = [] for var_arg in left.args: if var_arg.location == "calldata": return right_token = LLLnode.from_list("_R", typ=right.typ, location=right.location) for left_arg, key, loc in zip(left.args, keyz, locations): subs.append( make_setter( left_arg, add_variable_offset(right_token, key, pos=pos), loc, pos=pos ) ) return LLLnode.from_list( ["with", "_R", right, ["seq"] + subs], typ=None, annotation="Tuple assignment", ) # If the left side is a variable i.e struct type else: subs = [] right_token = LLLnode.from_list("_R", typ=right.typ, location=right.location) for typ, loc in zip(keyz, locations): subs.append( make_setter( add_variable_offset(left_token, typ, pos=pos), add_variable_offset(right_token, typ, pos=pos), loc, pos=pos, ) ) return LLLnode.from_list( ["with", "_L", left, ["with", "_R", right, ["seq"] + subs]], typ=None, )
def gen_tuple_return(stmt, context, sub): # Is from a call expression. if sub.args and len( sub.args[0].args) > 0 and sub.args[0].args[0].value == 'call': # self-call to public. mem_pos = sub.args[0].args[-1] mem_size = get_size_of_type(sub.typ) * 32 return LLLnode.from_list(['return', mem_pos, mem_size], typ=sub.typ) elif (sub.annotation and 'Internal Call' in sub.annotation): mem_pos = sub.args[ -1].value if sub.value == 'seq_unchecked' else sub.args[0].args[-1] mem_size = get_size_of_type(sub.typ) * 32 # Add zero padder if bytes are present in output. zero_padder = ['pass'] byte_arrays = [(i, x) for i, x in enumerate(sub.typ.tuple_members()) if isinstance(x, ByteArrayLike)] if byte_arrays: i, x = byte_arrays[-1] zero_padder = zero_pad(bytez_placeholder=[ 'add', mem_pos, ['mload', mem_pos + i * 32] ], maxlen=x.maxlen, context=context) return LLLnode.from_list( ['seq'] + [sub] + [zero_padder] + [make_return_stmt(stmt, context, mem_pos, mem_size)], typ=sub.typ, pos=getpos(stmt), valency=0) subs = [] # Pre-allocate loop_memory_position if required for private function returning. loop_memory_position = (context.new_placeholder( typ=BaseType('uint256')) if context.is_private else None) # Allocate dynamic off set counter, to keep track of the total packed dynamic data size. dynamic_offset_counter_placeholder = context.new_placeholder( typ=BaseType('uint256')) dynamic_offset_counter = LLLnode( dynamic_offset_counter_placeholder, typ=None, annotation="dynamic_offset_counter" # dynamic offset position counter. ) new_sub = LLLnode.from_list( context.new_placeholder(typ=BaseType('uint256')), typ=context.return_type, location='memory', annotation='new_sub', ) left_token = LLLnode.from_list('_loc', typ=new_sub.typ, location="memory") def get_dynamic_offset_value(): # Get value of dynamic offset counter. return ['mload', dynamic_offset_counter] def increment_dynamic_offset(dynamic_spot): # Increment dyanmic offset counter in memory. return [ 'mstore', dynamic_offset_counter, [ 'add', ['add', ['ceil32', ['mload', dynamic_spot]], 32], ['mload', dynamic_offset_counter] ] ] if not isinstance(context.return_type, TupleLike): raise TypeMismatchException( 'Trying to return %r when expecting %r' % (sub.typ, context.return_type), getpos(stmt)) items = context.return_type.tuple_items() dynamic_offset_start = 32 * len(items) # The static list of args end. for i, (key, typ) in enumerate(items): variable_offset = LLLnode.from_list( ['add', 32 * i, left_token], typ=typ, annotation='variable_offset', ) # variable offset of destination if sub.typ.is_literal: arg = sub.args[i] else: arg = add_variable_offset(parent=sub, key=key, pos=getpos(stmt)) if isinstance(typ, ByteArrayLike): # Store offset pointer value. subs.append( ['mstore', variable_offset, get_dynamic_offset_value()]) # Store dynamic data, from offset pointer onwards. dynamic_spot = LLLnode.from_list( ['add', left_token, get_dynamic_offset_value()], location="memory", typ=typ, annotation='dynamic_spot', ) subs.append( make_setter(dynamic_spot, arg, location="memory", pos=getpos(stmt))) subs.append(increment_dynamic_offset(dynamic_spot)) elif isinstance(typ, BaseType): subs.append( make_setter(variable_offset, arg, "memory", pos=getpos(stmt))) elif isinstance(typ, TupleLike): subs.append(gen_tuple_return(stmt, context, arg)) else: # Maybe this should panic because the type error should be # caught at an earlier type-checking stage. raise TypeMismatchException( "Can't return type %s as part of tuple" % arg.typ, stmt) setter = LLLnode.from_list([ 'seq', ['mstore', dynamic_offset_counter, dynamic_offset_start], ['with', '_loc', new_sub, ['seq'] + subs] ], typ=None) return LLLnode.from_list([ 'seq', setter, make_return_stmt(stmt, context, new_sub, get_dynamic_offset_value(), loop_memory_position) ], typ=None, pos=getpos(stmt), valency=0)
def make_byte_slice_copier(destination, source, length, max_length, pos=None): # Special case: memory to memory if source.location == "memory" and destination.location == "memory": return LLLnode.from_list( [ "with", "_l", max_length, ["pop", ["call", ["gas"], 4, 0, source, "_l", destination, "_l"]], ], typ=None, annotation=f"copy byte slice dest: {str(destination)}", ) # special case: rhs is zero if source.value is None: if destination.location == "memory": return mzero(destination, max_length) else: loader = 0 # Copy over data elif source.location == "memory": loader = ["mload", ["add", "_pos", ["mul", 32, ["mload", MemoryPositions.FREE_LOOP_INDEX]]]] elif source.location == "storage": loader = ["sload", ["add", "_pos", ["mload", MemoryPositions.FREE_LOOP_INDEX]]] elif source.location == "calldata": loader = [ "calldataload", ["add", "_pos", ["mul", 32, ["mload", MemoryPositions.FREE_LOOP_INDEX]]], ] else: raise CompilerPanic(f"Unsupported location: {source.location}") # Where to paste it? if destination.location == "memory": setter = [ "mstore", ["add", "_opos", ["mul", 32, ["mload", MemoryPositions.FREE_LOOP_INDEX]]], loader, ] elif destination.location == "storage": setter = ["sstore", ["add", "_opos", ["mload", MemoryPositions.FREE_LOOP_INDEX]], loader] else: raise CompilerPanic(f"Unsupported location: {destination.location}") # Check to see if we hit the length checker = [ "if", ["gt", ["mul", 32, ["mload", MemoryPositions.FREE_LOOP_INDEX]], "_actual_len"], "break", ] # Make a loop to do the copying ipos = 0 if source.value is None else source o = [ "with", "_pos", ipos, [ "with", "_opos", destination, [ "with", "_actual_len", length, [ "repeat", MemoryPositions.FREE_LOOP_INDEX, 0, (max_length + 31) // 32, ["seq", checker, setter], ], ], ], ] return LLLnode.from_list( o, typ=None, annotation=f"copy byte slice src: {source} dst: {destination}", pos=pos, )
def add_variable_offset(parent, key, pos, array_bounds_check=True): typ, location = parent.typ, parent.location if isinstance(typ, TupleLike): if isinstance(typ, StructType): subtype = typ.members[key] attrs = list(typ.tuple_keys()) index = attrs.index(key) annotation = key else: attrs = list(range(len(typ.members))) index = key annotation = None if location == "storage": # for arrays and structs, calculate the storage slot by adding an offset # of [index value being accessed] * [size of each item within the sequence] offset = 0 for i in range(index): offset += get_size_of_type(typ.members[attrs[i]]) return LLLnode.from_list(["add", parent, offset], typ=subtype, location="storage",) elif location == "storage_prehashed": return LLLnode.from_list( ["add", parent, LLLnode.from_list(index, annotation=annotation)], typ=subtype, location="storage", ) elif location in ("calldata", "memory"): offset = 0 for i in range(index): offset += 32 * get_size_of_type(typ.members[attrs[i]]) return LLLnode.from_list( ["add", offset, parent], typ=typ.members[key], location=location, annotation=annotation, ) elif isinstance(typ, MappingType): sub = None if isinstance(key.typ, ByteArrayLike): if isinstance(typ.keytype, ByteArrayLike) and (typ.keytype.maxlen >= key.typ.maxlen): subtype = typ.valuetype if len(key.args[0].args) >= 3: # handle bytes literal. sub = LLLnode.from_list( [ "seq", key, [ "sha3", ["add", key.args[0].args[-1], 32], ["mload", key.args[0].args[-1]], ], ] ) else: value = key.args[0].value if value == "add": # special case, key is a bytes array within a tuple/struct value = key.args[0] sub = LLLnode.from_list(["sha3", ["add", value, 32], key]) else: subtype = typ.valuetype sub = unwrap_location(key) if sub is not None and location == "storage": return LLLnode.from_list(["sha3_64", parent, sub], typ=subtype, location="storage") elif isinstance(typ, ListType) and is_base_type(key.typ, ("int128", "int256", "uint256")): subtype = typ.subtype k = unwrap_location(key) if not array_bounds_check: sub = k elif key.typ.is_literal: # note: BaseType always has is_literal attr # perform the check at compile time and elide the runtime check. if key.value < 0 or key.value >= typ.count: return sub = k else: # this works, even for int128. for int128, since two's-complement # is used, if the index is negative, (unsigned) LT will interpret # it as a very large number, larger than any practical value for # an array index, and the clamp will throw an error. sub = ["uclamplt", k, typ.count] if location == "storage": # storage slot determined as [initial storage slot] + [index] * [size of base type] offset = get_size_of_type(subtype) return LLLnode.from_list( ["add", parent, ["mul", sub, offset]], typ=subtype, location="storage", pos=pos ) elif location == "storage_prehashed": return LLLnode.from_list(["add", parent, sub], typ=subtype, location="storage", pos=pos) elif location in ("calldata", "memory"): offset = 32 * get_size_of_type(subtype) return LLLnode.from_list( ["add", ["mul", offset, sub], parent], typ=subtype, location=location, pos=pos )
def func_init_lll(): return LLLnode.from_list(STORE_CALLDATA + LIMIT_MEMORY_SET, typ=None)
def init_func_init_lll(): return LLLnode.from_list(["seq"] + LIMIT_MEMORY_SET, typ=None)
def compare(self): left = Expr.parse_value_expr(self.expr.left, self.context) right = Expr.parse_value_expr(self.expr.comparators[0], self.context) if isinstance(right.typ, NullType): raise InvalidLiteralException( 'Comparison to None is not allowed, compare against a default value.', self.expr) if isinstance(left.typ, ByteArrayType) and isinstance( right.typ, ByteArrayType): if left.typ.maxlen != right.typ.maxlen: raise TypeMismatchException( 'Can only compare bytes of the same length', self.expr) if left.typ.maxlen > 32 or right.typ.maxlen > 32: raise ParserException( 'Can only compare bytes of length shorter than 32 bytes', self.expr) elif isinstance(self.expr.ops[0], ast.In) and \ isinstance(right.typ, ListType): if left.typ != right.typ.subtype: raise TypeMismatchException( "Can't use IN comparison with different types!", self.expr) return self.build_in_comparator() else: if not are_units_compatible( left.typ, right.typ) and not are_units_compatible( right.typ, left.typ): raise TypeMismatchException( "Can't compare values with different units!", self.expr) if len(self.expr.ops) != 1: raise StructureException( "Cannot have a comparison with more than two elements", self.expr) if isinstance(self.expr.ops[0], ast.Gt): op = 'sgt' elif isinstance(self.expr.ops[0], ast.GtE): op = 'sge' elif isinstance(self.expr.ops[0], ast.LtE): op = 'sle' elif isinstance(self.expr.ops[0], ast.Lt): op = 'slt' elif isinstance(self.expr.ops[0], ast.Eq): op = 'eq' elif isinstance(self.expr.ops[0], ast.NotEq): op = 'ne' else: raise Exception("Unsupported comparison operator") # Compare (limited to 32) byte arrays. if isinstance(left.typ, ByteArrayType) and isinstance( left.typ, ByteArrayType): left = Expr(self.expr.left, self.context).lll_node right = Expr(self.expr.comparators[0], self.context).lll_node def load_bytearray(side): if side.location == 'memory': return ['mload', ['add', 32, side]] elif side.location == 'storage': return ['sload', ['add', 1, ['sha3_32', side]]] return LLLnode.from_list( [op, load_bytearray(left), load_bytearray(right)], typ='bool', pos=getpos(self.expr)) # Compare other types. if not is_numeric_type(left.typ) or not is_numeric_type(right.typ): if op not in ('eq', 'ne'): raise TypeMismatchException("Invalid type for comparison op", self.expr) left_type, right_type = left.typ.typ, right.typ.typ # Special Case: comparison of a literal integer. If in valid range allow it to be compared. if {left_type, right_type} == {'int128', 'uint256'} and { left.typ.is_literal, right.typ.is_literal } == {True, False}: comparison_allowed = False if left.typ.is_literal and SizeLimits.in_bounds( right_type, left.value): comparison_allowed = True elif right.typ.is_literal and SizeLimits.in_bounds( left_type, right.value): comparison_allowed = True op = self._signed_to_unsigned_comparision_op(op) if comparison_allowed: return LLLnode.from_list([op, left, right], typ='bool', pos=getpos(self.expr)) elif {left_type, right_type} == {'uint256', 'uint256'}: op = self._signed_to_unsigned_comparision_op(op) elif (left_type in ('decimal', 'int128') or right_type in ('decimal', 'int128')) and left_type != right_type: raise TypeMismatchException( 'Implicit conversion from {} to {} disallowed, please convert.' .format(left_type, right_type), self.expr) if left_type == right_type: return LLLnode.from_list([op, left, right], typ='bool', pos=getpos(self.expr)) else: raise TypeMismatchException( "Unsupported types for comparison: %r %r" % (left_type, right_type), self.expr)
def parse_tree_to_lll(source_code: str, global_ctx: GlobalContext) -> Tuple[LLLnode, LLLnode]: _names_def = [_def.name for _def in global_ctx._defs] # Checks for duplicate function names if len(set(_names_def)) < len(_names_def): raise FunctionDeclarationException( "Duplicate function name: " f"{[name for name in _names_def if _names_def.count(name) > 1][0]}" ) _names_events = [_event.name for _event in global_ctx._events] # Checks for duplicate event names if len(set(_names_events)) < len(_names_events): raise EventDeclarationException(f"""Duplicate event name: {[name for name in _names_events if _names_events.count(name) > 1][0]}""" ) # Initialization function initfunc = [_def for _def in global_ctx._defs if is_initializer(_def)] # Default function defaultfunc = [_def for _def in global_ctx._defs if is_default_func(_def)] # Regular functions otherfuncs = [ _def for _def in global_ctx._defs if not is_initializer(_def) and not is_default_func(_def) ] sigs: dict = {} external_interfaces: dict = {} # Create the main statement o = ["seq"] if global_ctx._events: sigs = parse_events(sigs, global_ctx) if global_ctx._contracts or global_ctx._interfaces: external_interfaces = parse_external_interfaces( external_interfaces, global_ctx) # If there is an init func... if initfunc: o.append(init_func_init_lll()) o.append( parse_function( initfunc[0], { **{ "self": sigs }, **external_interfaces }, source_code, global_ctx, )) # If there are regular functions... if otherfuncs or defaultfunc: o, runtime = parse_other_functions(o, otherfuncs, sigs, external_interfaces, source_code, global_ctx, defaultfunc) else: runtime = o.copy() # Check if interface of contract is correct. check_valid_contract_interface(global_ctx, sigs) return LLLnode.from_list(o, typ=None), LLLnode.from_list(runtime, typ=None)
def attribute(self): # x.balance: balance of address x if self.expr.attr == 'balance': addr = Expr.parse_value_expr(self.expr.value, self.context) if not is_base_type(addr.typ, 'address'): raise TypeMismatchException( "Type mismatch: balance keyword expects an address as input", self.expr) return LLLnode.from_list(['balance', addr], typ=BaseType('uint256', {'wei': 1}), location=None, pos=getpos(self.expr)) # x.codesize: codesize of address x elif self.expr.attr == 'codesize' or self.expr.attr == 'is_contract': addr = Expr.parse_value_expr(self.expr.value, self.context) if not is_base_type(addr.typ, 'address'): raise TypeMismatchException( "Type mismatch: codesize keyword expects an address as input", self.expr) if self.expr.attr == 'codesize': eval_code = ['extcodesize', addr] output_type = 'int128' else: eval_code = ['gt', ['extcodesize', addr], 0] output_type = 'bool' return LLLnode.from_list(eval_code, typ=BaseType(output_type), location=None, pos=getpos(self.expr)) # self.x: global attribute elif isinstance(self.expr.value, ast.Name) and self.expr.value.id == "self": if self.expr.attr not in self.context.globals: raise VariableDeclarationException( "Persistent variable undeclared: " + self.expr.attr, self.expr) var = self.context.globals[self.expr.attr] return LLLnode.from_list(var.pos, typ=var.typ, location='storage', pos=getpos(self.expr), annotation='self.' + self.expr.attr) # Reserved keywords elif isinstance( self.expr.value, ast.Name) and self.expr.value.id in ("msg", "block", "tx"): key = self.expr.value.id + "." + self.expr.attr if key == "msg.sender": if self.context.is_private: raise ParserException( "msg.sender not allowed in private functions.", self.expr) return LLLnode.from_list(['caller'], typ='address', pos=getpos(self.expr)) elif key == "msg.value": if not self.context.is_payable: raise NonPayableViolationException( "Cannot use msg.value in a non-payable function", self.expr) return LLLnode.from_list(['callvalue'], typ=BaseType('uint256', {'wei': 1}), pos=getpos(self.expr)) elif key == "msg.gas": return LLLnode.from_list(['gas'], typ='uint256', pos=getpos(self.expr)) elif key == "block.difficulty": return LLLnode.from_list(['difficulty'], typ='uint256', pos=getpos(self.expr)) elif key == "block.timestamp": return LLLnode.from_list(['timestamp'], typ=BaseType('uint256', {'sec': 1}, True), pos=getpos(self.expr)) elif key == "block.coinbase": return LLLnode.from_list(['coinbase'], typ='address', pos=getpos(self.expr)) elif key == "block.number": return LLLnode.from_list(['number'], typ='uint256', pos=getpos(self.expr)) elif key == "block.prevhash": return LLLnode.from_list(['blockhash', ['sub', 'number', 1]], typ='bytes32', pos=getpos(self.expr)) elif key == "tx.origin": return LLLnode.from_list(['origin'], typ='address', pos=getpos(self.expr)) else: raise Exception("Unsupported keyword: " + key) # Other variables else: sub = Expr.parse_variable_location(self.expr.value, self.context) # contract type if isinstance(sub.typ, ContractType): return sub if not isinstance(sub.typ, StructType): raise TypeMismatchException( "Type mismatch: member variable access not expected", self.expr.value) attrs = list(sub.typ.members.keys()) if self.expr.attr not in attrs: raise TypeMismatchException( "Member %s not found. Only the following available: %s" % (self.expr.attr, " ".join(attrs)), self.expr) return add_variable_offset(sub, self.expr.attr, pos=getpos(self.expr))
def parse_Compare(self): left = Expr.parse_value_expr(self.expr.left, self.context) right = Expr.parse_value_expr(self.expr.right, self.context) if right.value is None: return if isinstance(left.typ, ByteArrayLike) and isinstance( right.typ, ByteArrayLike): # TODO: Can this if branch be removed ^ pass elif isinstance(self.expr.op, vy_ast.In) and isinstance( right.typ, ListType): if left.typ != right.typ.subtype: return return self.build_in_comparator() if isinstance(self.expr.op, vy_ast.Gt): op = "sgt" elif isinstance(self.expr.op, vy_ast.GtE): op = "sge" elif isinstance(self.expr.op, vy_ast.LtE): op = "sle" elif isinstance(self.expr.op, vy_ast.Lt): op = "slt" elif isinstance(self.expr.op, vy_ast.Eq): op = "eq" elif isinstance(self.expr.op, vy_ast.NotEq): op = "ne" else: return # Compare (limited to 32) byte arrays. if isinstance(left.typ, ByteArrayLike) and isinstance( right.typ, ByteArrayLike): left = Expr(self.expr.left, self.context).lll_node right = Expr(self.expr.right, self.context).lll_node length_mismatch = left.typ.maxlen != right.typ.maxlen left_over_32 = left.typ.maxlen > 32 right_over_32 = right.typ.maxlen > 32 if length_mismatch or left_over_32 or right_over_32: left_keccak = keccak256_helper(self.expr, [left], None, self.context) right_keccak = keccak256_helper(self.expr, [right], None, self.context) if op == "eq" or op == "ne": return LLLnode.from_list( [op, left_keccak, right_keccak], typ="bool", pos=getpos(self.expr), ) else: return else: def load_bytearray(side): if side.location == "memory": return ["mload", ["add", 32, side]] elif side.location == "storage": return ["sload", ["add", 1, ["sha3_32", side]]] return LLLnode.from_list( [op, load_bytearray(left), load_bytearray(right)], typ="bool", pos=getpos(self.expr), ) # Compare other types. if not is_numeric_type(left.typ) or not is_numeric_type(right.typ): if op not in ("eq", "ne"): return left_type, right_type = left.typ.typ, right.typ.typ # Special Case: comparison of a literal integer. If in valid range allow it to be compared. if {left_type, right_type} == {"int128", "uint256"} and { left.typ.is_literal, right.typ.is_literal, } == { True, False, }: # noqa: E501 comparison_allowed = False if left.typ.is_literal and SizeLimits.in_bounds( right_type, left.value): comparison_allowed = True elif right.typ.is_literal and SizeLimits.in_bounds( left_type, right.value): comparison_allowed = True op = self._signed_to_unsigned_comparision_op(op) if comparison_allowed: return LLLnode.from_list([op, left, right], typ="bool", pos=getpos(self.expr)) elif {left_type, right_type} == {"uint256", "uint256"}: op = self._signed_to_unsigned_comparision_op(op) elif (left_type in ("decimal", "int128") or right_type in ("decimal", "int128")) and left_type != right_type: # noqa: E501 return if left_type == right_type: return LLLnode.from_list([op, left, right], typ="bool", pos=getpos(self.expr))
def build_in_comparator(self): left = Expr(self.expr.left, self.context).lll_node right = Expr(self.expr.right, self.context).lll_node result_placeholder = self.context.new_placeholder(BaseType("bool")) setter = [] # Load nth item from list in memory. if right.value == "multi": # Copy literal to memory to be compared. tmp_list = LLLnode.from_list( obj=self.context.new_placeholder( ListType(right.typ.subtype, right.typ.count)), typ=ListType(right.typ.subtype, right.typ.count), location="memory", ) setter = make_setter(tmp_list, right, "memory", pos=getpos(self.expr)) load_i_from_list = [ "mload", [ "add", tmp_list, ["mul", 32, ["mload", MemoryPositions.FREE_LOOP_INDEX]] ], ] elif right.location == "storage": load_i_from_list = [ "sload", [ "add", ["sha3_32", right], ["mload", MemoryPositions.FREE_LOOP_INDEX] ], ] else: load_i_from_list = [ "mload", [ "add", right, ["mul", 32, ["mload", MemoryPositions.FREE_LOOP_INDEX]] ], ] # Condition repeat loop has to break on. break_loop_condition = [ "if", ["eq", unwrap_location(left), load_i_from_list], ["seq", ["mstore", "_result", 1], "break"], # store true. ] # Repeat loop to loop-compare each item in the list. for_loop_sequence = [ ["mstore", result_placeholder, 0], [ "with", "_result", result_placeholder, [ "repeat", MemoryPositions.FREE_LOOP_INDEX, 0, right.typ.count, break_loop_condition, ], ], ["mload", result_placeholder], ] # Save list to memory, so one can iterate over it, # used when literal was created with tmp_list. if setter: compare_sequence = ["seq", setter] + for_loop_sequence else: compare_sequence = ["seq"] + for_loop_sequence # Compare the result of the repeat loop to 1, to know if a match was found. lll_node = LLLnode.from_list(["eq", 1, compare_sequence], typ="bool", annotation="in comporator") return lll_node
def build_in_comparator(self): left = Expr(self.expr.left, self.context).lll_node right = Expr(self.expr.comparators[0], self.context).lll_node if left.typ != right.typ.subtype: raise TypeMismatch( f"{left.typ} cannot be in a list of {right.typ.subtype}", self.expr, ) result_placeholder = self.context.new_placeholder(BaseType('bool')) setter = [] # Load nth item from list in memory. if right.value == 'multi': # Copy literal to memory to be compared. tmp_list = LLLnode.from_list(obj=self.context.new_placeholder( ListType(right.typ.subtype, right.typ.count)), typ=ListType(right.typ.subtype, right.typ.count), location='memory') setter = make_setter(tmp_list, right, 'memory', pos=getpos(self.expr)) load_i_from_list = [ 'mload', [ 'add', tmp_list, ['mul', 32, ['mload', MemoryPositions.FREE_LOOP_INDEX]] ], ] elif right.location == "storage": load_i_from_list = [ 'sload', [ 'add', ['sha3_32', right], ['mload', MemoryPositions.FREE_LOOP_INDEX] ], ] else: load_i_from_list = [ 'mload', [ 'add', right, ['mul', 32, ['mload', MemoryPositions.FREE_LOOP_INDEX]] ], ] # Condition repeat loop has to break on. break_loop_condition = [ 'if', ['eq', unwrap_location(left), load_i_from_list], [ 'seq', ['mstore', '_result', 1], # store true. 'break' ] ] # Repeat loop to loop-compare each item in the list. for_loop_sequence = [['mstore', result_placeholder, 0], [ 'with', '_result', result_placeholder, [ 'repeat', MemoryPositions.FREE_LOOP_INDEX, 0, right.typ.count, break_loop_condition, ] ], ['mload', result_placeholder]] # Save list to memory, so one can iterate over it, # used when literal was created with tmp_list. if setter: compare_sequence = ['seq', setter] + for_loop_sequence else: compare_sequence = ['seq'] + for_loop_sequence # Compare the result of the repeat loop to 1, to know if a match was found. o = LLLnode.from_list(['eq', 1, compare_sequence], typ='bool', annotation="in comporator") return o
def add_variable_offset(parent, key, pos): typ, location = parent.typ, parent.location if isinstance(typ, (StructType, TupleType)): if isinstance(typ, StructType): if not isinstance(key, str): raise TypeMismatchException( "Expecting a member variable access; cannot access element %r" % key, pos) if key not in typ.members: raise TypeMismatchException( "Object does not have member variable %s" % key, pos) subtype = typ.members[key] attrs = list(typ.members.keys()) if key not in attrs: raise TypeMismatchException( "Member %s not found. Only the following available: %s" % (key, " ".join(attrs)), pos) index = attrs.index(key) annotation = key else: if not isinstance(key, int): raise TypeMismatchException( "Expecting a static index; cannot access element %r" % key, pos) attrs = list(range(len(typ.members))) index = key annotation = None if location == 'storage': return LLLnode.from_list( [ 'add', ['sha3_32', parent], LLLnode.from_list(index, annotation=annotation) ], typ=subtype, location='storage', ) elif location == 'storage_prehashed': return LLLnode.from_list( [ 'add', parent, LLLnode.from_list(index, annotation=annotation) ], typ=subtype, location='storage', ) elif location == 'memory': offset = 0 for i in range(index): offset += 32 * get_size_of_type(typ.members[attrs[i]]) return LLLnode.from_list(['add', offset, parent], typ=typ.members[key], location='memory', annotation=annotation) else: raise TypeMismatchException( "Not expecting a member variable access", pos) elif isinstance(typ, MappingType): if isinstance(key.typ, ByteArrayLike): if not isinstance(typ.keytype, ByteArrayLike) or ( typ.keytype.maxlen < key.typ.maxlen): raise TypeMismatchException( 'Mapping keys of bytes cannot be cast, use exact same bytes type of: %s' % (str(typ.keytype), ), pos, ) subtype = typ.valuetype if len(key.args[0].args) >= 3: # handle bytes literal. sub = LLLnode.from_list([ 'seq', key, [ 'sha3', ['add', key.args[0].args[-1], 32], ['mload', key.args[0].args[-1]] ] ]) else: sub = LLLnode.from_list([ 'sha3', ['add', key.args[0].value, 32], ['mload', key.args[0].value] ]) else: subtype = typ.valuetype sub = base_type_conversion(key, key.typ, typ.keytype, pos=pos) if location == 'storage': return LLLnode.from_list(['sha3_64', parent, sub], typ=subtype, location='storage') elif location == 'memory': raise TypeMismatchException( "Can only have fixed-side arrays in memory, not mappings", pos) elif isinstance(typ, ListType): subtype = typ.subtype sub = [ 'uclamplt', base_type_conversion(key, key.typ, BaseType('int128'), pos=pos), typ.count ] if location == 'storage': return LLLnode.from_list(['add', ['sha3_32', parent], sub], typ=subtype, location='storage') elif location == 'storage_prehashed': return LLLnode.from_list(['add', parent, sub], typ=subtype, location='storage') elif location == 'memory': offset = 32 * get_size_of_type(subtype) return LLLnode.from_list( ['add', ['mul', offset, sub], parent], typ=subtype, location='memory', ) else: raise TypeMismatchException("Not expecting an array access ", pos) else: raise TypeMismatchException( "Cannot access the child of a constant variable! %r" % typ, pos)
def compare(self): left = Expr.parse_value_expr(self.expr.left, self.context) right = Expr.parse_value_expr(self.expr.comparators[0], self.context) if isinstance(right.typ, NullType): raise InvalidLiteral( 'Comparison to None is not allowed, compare against a default value.', self.expr, ) if isinstance(left.typ, ByteArrayLike) and isinstance( right.typ, ByteArrayLike): # TODO: Can this if branch be removed ^ pass elif isinstance(self.expr.ops[0], vy_ast.In) and isinstance( right.typ, ListType): if left.typ != right.typ.subtype: raise TypeMismatch( "Can't use IN comparison with different types!", self.expr, ) return self.build_in_comparator() else: if not are_units_compatible( left.typ, right.typ) and not are_units_compatible( right.typ, left.typ): # noqa: E501 raise TypeMismatch( "Can't compare values with different units!", self.expr) if len(self.expr.ops) != 1: raise StructureException( "Cannot have a comparison with more than two elements", self.expr, ) if isinstance(self.expr.ops[0], vy_ast.Gt): op = 'sgt' elif isinstance(self.expr.ops[0], vy_ast.GtE): op = 'sge' elif isinstance(self.expr.ops[0], vy_ast.LtE): op = 'sle' elif isinstance(self.expr.ops[0], vy_ast.Lt): op = 'slt' elif isinstance(self.expr.ops[0], vy_ast.Eq): op = 'eq' elif isinstance(self.expr.ops[0], vy_ast.NotEq): op = 'ne' else: raise Exception("Unsupported comparison operator") # Compare (limited to 32) byte arrays. if isinstance(left.typ, ByteArrayLike) and isinstance( right.typ, ByteArrayLike): left = Expr(self.expr.left, self.context).lll_node right = Expr(self.expr.comparators[0], self.context).lll_node length_mismatch = (left.typ.maxlen != right.typ.maxlen) left_over_32 = left.typ.maxlen > 32 right_over_32 = right.typ.maxlen > 32 if length_mismatch or left_over_32 or right_over_32: left_keccak = keccak256_helper(self.expr, [left], None, self.context) right_keccak = keccak256_helper(self.expr, [right], None, self.context) if op == 'eq' or op == 'ne': return LLLnode.from_list( [op, left_keccak, right_keccak], typ='bool', pos=getpos(self.expr), ) else: raise StructureException( "Can only compare strings/bytes of length shorter", " than 32 bytes other than equality comparisons", self.expr, ) else: def load_bytearray(side): if side.location == 'memory': return ['mload', ['add', 32, side]] elif side.location == 'storage': return ['sload', ['add', 1, ['sha3_32', side]]] return LLLnode.from_list( [op, load_bytearray(left), load_bytearray(right)], typ='bool', pos=getpos(self.expr), ) # Compare other types. if not is_numeric_type(left.typ) or not is_numeric_type(right.typ): if op not in ('eq', 'ne'): raise TypeMismatch("Invalid type for comparison op", self.expr) left_type, right_type = left.typ.typ, right.typ.typ # Special Case: comparison of a literal integer. If in valid range allow it to be compared. if {left_type, right_type} == {'int128', 'uint256'} and { left.typ.is_literal, right.typ.is_literal } == {True, False}: # noqa: E501 comparison_allowed = False if left.typ.is_literal and SizeLimits.in_bounds( right_type, left.value): comparison_allowed = True elif right.typ.is_literal and SizeLimits.in_bounds( left_type, right.value): comparison_allowed = True op = self._signed_to_unsigned_comparision_op(op) if comparison_allowed: return LLLnode.from_list([op, left, right], typ='bool', pos=getpos(self.expr)) elif {left_type, right_type} == {'uint256', 'uint256'}: op = self._signed_to_unsigned_comparision_op(op) elif (left_type in ('decimal', 'int128') or right_type in ('decimal', 'int128')) and left_type != right_type: # noqa: E501 raise TypeMismatch( f'Implicit conversion from {left_type} to {right_type} disallowed, please convert.', self.expr, ) if left_type == right_type: return LLLnode.from_list([op, left, right], typ='bool', pos=getpos(self.expr)) else: raise TypeMismatch( f"Unsupported types for comparison: {left_type} {right_type}", self.expr, )
def make_byte_array_copier(destination, source, pos=None): if not isinstance(source.typ, (ByteArrayLike, NullType)): btype = 'byte array' if isinstance(destination.typ, ByteArrayType) else 'string' raise TypeMismatchException( "Can only set a {} to another {}".format(btype, btype), pos) if isinstance( source.typ, ByteArrayLike) and source.typ.maxlen > destination.typ.maxlen: raise TypeMismatchException( "Cannot cast from greater max-length %d to shorter max-length %d" % ( source.typ.maxlen, destination.typ.maxlen, )) # Special case: memory to memory if source.location == "memory" and destination.location == "memory": gas_calculation = GAS_IDENTITY + GAS_IDENTITYWORD * ( ceil32(source.typ.maxlen) // 32) o = LLLnode.from_list( [ 'with', '_source', source, [ 'with', '_sz', ['add', 32, ['mload', '_source']], [ 'assert', [ 'call', ['add', 18, ['div', '_sz', 10]], 4, 0, '_source', '_sz', destination, '_sz' ] ] ] ], # noqa: E501 typ=None, add_gas_estimate=gas_calculation, annotation='Memory copy') return o pos_node = LLLnode.from_list('_pos', typ=source.typ, location=source.location) # Get the length if isinstance(source.typ, NullType): length = 1 elif source.location == "memory": length = ['add', ['mload', '_pos'], 32] elif source.location == "storage": length = ['add', ['sload', '_pos'], 32] pos_node = LLLnode.from_list( ['sha3_32', pos_node], typ=source.typ, location=source.location, ) else: raise Exception("Unsupported location:" + source.location) if destination.location == "storage": destination = LLLnode.from_list( ['sha3_32', destination], typ=destination.typ, location=destination.location, ) # Maximum theoretical length max_length = 32 if isinstance(source.typ, NullType) else source.typ.maxlen + 32 return LLLnode.from_list([ 'with', '_pos', 0 if isinstance(source.typ, NullType) else source, make_byte_slice_copier( destination, pos_node, length, max_length, pos=pos) ], typ=None)
def get_length(arg): if arg.location == "memory": return LLLnode.from_list(["mload", arg], typ=BaseType("uint256")) elif arg.location == "storage": return LLLnode.from_list(["sload", arg], typ=BaseType("uint256"))
def call_self_private(stmt_expr, context, sig): # ** Private Call ** # Steps: # (x) push current local variables # (x) push arguments # (x) push jumpdest (callback ptr) # (x) jump to label # (x) pop return values # (x) pop local variables method_name, expr_args, sig = call_lookup_specs(stmt_expr, context) pre_init = [] pop_local_vars = [] push_local_vars = [] pop_return_values = [] push_args = [] # Push local variables. if context.vars: var_slots = [(v.pos, v.size) for name, v in context.vars.items()] var_slots.sort(key=lambda x: x[0]) mem_from, mem_to = var_slots[0][ 0], var_slots[-1][0] + var_slots[-1][1] * 32 push_local_vars = [['mload', pos] for pos in range(mem_from, mem_to, 32)] pop_local_vars = [['mstore', pos, 'pass'] for pos in reversed(range(mem_from, mem_to, 32))] # Push Arguments if expr_args: inargs, inargsize, arg_pos = pack_arguments(sig, expr_args, context, return_placeholder=False, pos=getpos(stmt_expr)) push_args += [ inargs ] # copy arguments first, to not mess up the push/pop sequencing. static_arg_count = len(expr_args) * 32 static_pos = arg_pos + static_arg_count total_arg_size = ceil32(inargsize - 4) if len(expr_args) * 32 != total_arg_size: # requires dynamic section. ident = 'push_args_%d_%d_%d' % (sig.method_id, stmt_expr.lineno, stmt_expr.col_offset) start_label = ident + '_start' end_label = ident + '_end' i_placeholder = context.new_placeholder(BaseType('uint256')) push_args += [ ['mstore', i_placeholder, arg_pos + total_arg_size], ['label', start_label], [ 'if', ['lt', ['mload', i_placeholder], static_pos], ['goto', end_label] ], [ 'if_unchecked', ['ne', ['mload', ['mload', i_placeholder]], 0], ['mload', ['mload', i_placeholder]] ], [ 'mstore', i_placeholder, ['sub', ['mload', i_placeholder], 32] ], # decrease i ['goto', start_label], ['label', end_label] ] # push static section push_args += [['mload', pos] for pos in reversed(range(arg_pos, static_pos, 32))] # Jump to function label. jump_to_func = [ ['add', ['pc'], 6], # set callback pointer. ['goto', 'priv_{}'.format(sig.method_id)], ['jumpdest'], ] # Pop return values. returner = [0] if sig.output_type: output_placeholder, returner, output_size = call_make_placeholder( stmt_expr, context, sig) if output_size > 0: dynamic_offsets = [] if isinstance(sig.output_type, (BaseType, ListType)): pop_return_values = [[ 'mstore', ['add', output_placeholder, pos], 'pass' ] for pos in range(0, output_size, 32)] elif isinstance(sig.output_type, ByteArrayType): dynamic_offsets = [(0, sig.output_type)] pop_return_values = [ ['pop', 'pass'], ] elif isinstance(sig.output_type, TupleLike): static_offset = 0 pop_return_values = [] for out_type in sig.output_type.members: if isinstance(out_type, ByteArrayType): pop_return_values.append([ 'mstore', ['add', output_placeholder, static_offset], 'pass' ]) dynamic_offsets.append(([ 'mload', ['add', output_placeholder, static_offset] ], out_type)) else: pop_return_values.append([ 'mstore', ['add', output_placeholder, static_offset], 'pass' ]) static_offset += 32 # append dynamic unpacker. dyn_idx = 0 for in_memory_offset, out_type in dynamic_offsets: ident = "%d_%d_arg_%d" % (stmt_expr.lineno, stmt_expr.col_offset, dyn_idx) dyn_idx += 1 start_label = 'dyn_unpack_start_' + ident end_label = 'dyn_unpack_end_' + ident i_placeholder = context.new_placeholder( typ=BaseType('uint256')) begin_pos = ['add', output_placeholder, in_memory_offset] # loop until length. o = LLLnode.from_list( [ 'seq_unchecked', ['mstore', begin_pos, 'pass'], # get len ['mstore', i_placeholder, 0], ['label', start_label], [ 'if', [ 'ge', ['mload', i_placeholder], ['ceil32', ['mload', begin_pos]] ], ['goto', end_label] ], # break [ 'mstore', [ 'add', ['add', begin_pos, 32], ['mload', i_placeholder] ], 'pass' ], # pop into correct memory slot. [ 'mstore', i_placeholder, ['add', 32, ['mload', i_placeholder]] ], # increment i ['goto', start_label], ['label', end_label] ], typ=None, annotation='dynamic unpacker', pos=getpos(stmt_expr)) pop_return_values.append(o) call_body = (['seq_unchecked'] + pre_init + push_local_vars + push_args + jump_to_func + pop_return_values + pop_local_vars + [returner]) # If we have no return, we need to pop off pop_returner_call_body = ['pop', call_body ] if sig.output_type is None else call_body o = LLLnode.from_list(pop_returner_call_body, typ=sig.output_type, location='memory', pos=getpos(stmt_expr), annotation='Internal Call: %s' % method_name, add_gas_estimate=sig.gas) o.gas += sig.gas return o
def pack_arguments(signature, args, context, stmt_expr, is_external_call): pos = getpos(stmt_expr) setters = [] staticarray_offset = 0 maxlen = sum([get_size_of_type(arg.typ) for arg in signature.args]) * 32 if is_external_call: maxlen += 32 placeholder_typ = ByteArrayType(maxlen=maxlen) placeholder = context.new_internal_variable(placeholder_typ) if is_external_call: setters.append(["mstore", placeholder, signature.method_id]) placeholder += 32 if len(signature.args) != len(args): return # check for dynamic-length types dynamic_remaining = len([i for i in signature.args if isinstance(i.typ, ByteArrayLike)]) needpos = bool(dynamic_remaining) for i, (arg, typ) in enumerate(zip(args, [arg.typ for arg in signature.args])): if isinstance(typ, BaseType): setters.append( make_setter( LLLnode.from_list(placeholder + staticarray_offset + i * 32, typ=typ,), arg, "memory", pos=pos, in_function_call=True, ) ) elif isinstance(typ, ByteArrayLike): dynamic_remaining -= 1 setters.append(["mstore", placeholder + staticarray_offset + i * 32, "_poz"]) arg_copy = LLLnode.from_list("_s", typ=arg.typ, location=arg.location) target = LLLnode.from_list(["add", placeholder, "_poz"], typ=typ, location="memory",) pos_setter = "pass" # if `arg.value` is None, this is a call to `empty()` # if `arg.typ.maxlen` is 0, this is a literal "" or b"" if arg.value is None or arg.typ.maxlen == 0: if dynamic_remaining: # only adjust the dynamic pointer if this is not the last dynamic type pos_setter = ["set", "_poz", ["add", "_poz", 64]] setters.append(["seq", mzero(target, 64), pos_setter]) else: if dynamic_remaining: pos_setter = [ "set", "_poz", ["add", 32, ["ceil32", ["add", "_poz", get_length(arg_copy)]]], ] setters.append( [ "with", "_s", arg, ["seq", make_byte_array_copier(target, arg_copy, pos), pos_setter], ] ) elif isinstance(typ, (StructType, ListType)): if has_dynamic_data(typ): return target = LLLnode.from_list( [placeholder + staticarray_offset + i * 32], typ=typ, location="memory", ) setters.append(make_setter(target, arg, "memory", pos=pos)) staticarray_offset += 32 * (get_size_of_type(typ) - 1) else: return if is_external_call: returner = [[placeholder - 4]] inargsize = placeholder_typ.maxlen - 28 else: # internal call does not use a returner or adjust max length for signature returner = [] inargsize = placeholder_typ.maxlen if needpos: return ( LLLnode.from_list( ["with", "_poz", len(args) * 32 + staticarray_offset, ["seq"] + setters + returner], typ=placeholder_typ, location="memory", ), inargsize, placeholder, ) else: return ( LLLnode.from_list(["seq"] + setters + returner, typ=placeholder_typ, location="memory"), inargsize, placeholder, )
def arithmetic(self): pre_alloc_left, left = self.arithmetic_get_reference(self.expr.left) pre_alloc_right, right = self.arithmetic_get_reference(self.expr.right) if not is_numeric_type(left.typ) or not is_numeric_type(right.typ): raise TypeMismatchException( "Unsupported types for arithmetic op: %r %r" % (left.typ, right.typ), self.expr, ) arithmetic_pair = {left.typ.typ, right.typ.typ} # Special Case: Simplify any literal to literal arithmetic at compile time. if left.typ.is_literal and right.typ.is_literal and \ isinstance(right.value, int) and isinstance(left.value, int) and \ arithmetic_pair.issubset({'uint256', 'int128'}): if isinstance(self.expr.op, ast.Add): val = left.value + right.value elif isinstance(self.expr.op, ast.Sub): val = left.value - right.value elif isinstance(self.expr.op, ast.Mult): val = left.value * right.value elif isinstance(self.expr.op, ast.Div): val = left.value // right.value elif isinstance(self.expr.op, ast.Mod): val = left.value % right.value elif isinstance(self.expr.op, ast.Pow): val = left.value**right.value else: raise ParserException( 'Unsupported literal operator: %s' % str(type(self.expr.op)), self.expr, ) num = ast.Num(n=val) num.source_code = self.expr.source_code num.lineno = self.expr.lineno num.col_offset = self.expr.col_offset return Expr.parse_value_expr(num, self.context) # Special case with uint256 were int literal may be casted. if arithmetic_pair == {'uint256', 'int128'}: # Check right side literal. if right.typ.is_literal and SizeLimits.in_bounds( 'uint256', right.value): right = LLLnode.from_list( right.value, typ=BaseType('uint256', None, is_literal=True), pos=getpos(self.expr), ) # Check left side literal. elif left.typ.is_literal and SizeLimits.in_bounds( 'uint256', left.value): left = LLLnode.from_list( left.value, typ=BaseType('uint256', None, is_literal=True), pos=getpos(self.expr), ) # Only allow explicit conversions to occur. if left.typ.typ != right.typ.typ: raise TypeMismatchException( "Cannot implicitly convert {} to {}.".format( left.typ.typ, right.typ.typ), self.expr, ) ltyp, rtyp = left.typ.typ, right.typ.typ if isinstance(self.expr.op, (ast.Add, ast.Sub)): if left.typ.unit != right.typ.unit and left.typ.unit != {} and right.typ.unit != {}: raise TypeMismatchException( "Unit mismatch: %r %r" % (left.typ.unit, right.typ.unit), self.expr, ) if left.typ.positional and right.typ.positional and isinstance( self.expr.op, ast.Add): raise TypeMismatchException( "Cannot add two positional units!", self.expr, ) new_unit = left.typ.unit or right.typ.unit # xor, as subtracting two positionals gives a delta new_positional = left.typ.positional ^ right.typ.positional op = 'add' if isinstance(self.expr.op, ast.Add) else 'sub' if ltyp == 'uint256' and isinstance(self.expr.op, ast.Add): o = LLLnode.from_list( [ 'seq', # Checks that: a + b >= a ['assert', ['ge', ['add', left, right], left]], ['add', left, right], ], typ=BaseType('uint256', new_unit, new_positional), pos=getpos(self.expr)) elif ltyp == 'uint256' and isinstance(self.expr.op, ast.Sub): o = LLLnode.from_list( [ 'seq', # Checks that: a >= b ['assert', ['ge', left, right]], ['sub', left, right] ], typ=BaseType('uint256', new_unit, new_positional), pos=getpos(self.expr)) elif ltyp == rtyp: o = LLLnode.from_list( [op, left, right], typ=BaseType(ltyp, new_unit, new_positional), pos=getpos(self.expr), ) else: raise Exception("Unsupported Operation '%r(%r, %r)'" % (op, ltyp, rtyp)) elif isinstance(self.expr.op, ast.Mult): if left.typ.positional or right.typ.positional: raise TypeMismatchException( "Cannot multiply positional values!", self.expr) new_unit = combine_units(left.typ.unit, right.typ.unit) if ltyp == rtyp == 'uint256': o = LLLnode.from_list([ 'if', ['eq', left, 0], [0], [ 'seq', [ 'assert', ['eq', ['div', ['mul', left, right], left], right] ], ['mul', left, right] ], ], typ=BaseType('uint256', new_unit), pos=getpos(self.expr)) elif ltyp == rtyp == 'int128': o = LLLnode.from_list( ['mul', left, right], typ=BaseType('int128', new_unit), pos=getpos(self.expr), ) elif ltyp == rtyp == 'decimal': o = LLLnode.from_list([ 'with', 'r', right, [ 'with', 'l', left, [ 'with', 'ans', ['mul', 'l', 'r'], [ 'seq', [ 'assert', [ 'or', [ 'eq', ['sdiv', 'ans', 'l'], 'r' ], ['iszero', 'l'] ] ], ['sdiv', 'ans', DECIMAL_DIVISOR], ], ], ], ], typ=BaseType('decimal', new_unit), pos=getpos(self.expr)) else: raise Exception("Unsupported Operation 'mul(%r, %r)'" % (ltyp, rtyp)) elif isinstance(self.expr.op, ast.Div): if left.typ.positional or right.typ.positional: raise TypeMismatchException("Cannot divide positional values!", self.expr) new_unit = combine_units(left.typ.unit, right.typ.unit, div=True) if ltyp == rtyp == 'uint256': o = LLLnode.from_list( [ 'seq', # Checks that: b != 0 ['assert', right], ['div', left, right], ], typ=BaseType('uint256', new_unit), pos=getpos(self.expr)) elif ltyp == rtyp == 'int128': o = LLLnode.from_list( ['sdiv', left, ['clamp_nonzero', right]], typ=BaseType('int128', new_unit), pos=getpos(self.expr), ) elif ltyp == rtyp == 'decimal': o = LLLnode.from_list([ 'with', 'l', left, [ 'with', 'r', ['clamp_nonzero', right], [ 'sdiv', ['mul', 'l', DECIMAL_DIVISOR], 'r', ], ] ], typ=BaseType('decimal', new_unit), pos=getpos(self.expr)) else: raise Exception("Unsupported Operation 'div(%r, %r)'" % (ltyp, rtyp)) elif isinstance(self.expr.op, ast.Mod): if left.typ.positional or right.typ.positional: raise TypeMismatchException( "Cannot use positional values as modulus arguments!", self.expr, ) if not are_units_compatible(left.typ, right.typ) and not ( left.typ.unit or right.typ.unit): # noqa: E501 raise TypeMismatchException( "Modulus arguments must have same unit", self.expr) new_unit = left.typ.unit or right.typ.unit if ltyp == rtyp == 'uint256': o = LLLnode.from_list( ['seq', ['assert', right], ['mod', left, right]], typ=BaseType('uint256', new_unit), pos=getpos(self.expr)) elif ltyp == rtyp: o = LLLnode.from_list( ['smod', left, ['clamp_nonzero', right]], typ=BaseType(ltyp, new_unit), pos=getpos(self.expr), ) else: raise Exception("Unsupported Operation 'mod(%r, %r)'" % (ltyp, rtyp)) elif isinstance(self.expr.op, ast.Pow): if left.typ.positional or right.typ.positional: raise TypeMismatchException( "Cannot use positional values as exponential arguments!", self.expr, ) if right.typ.unit: raise TypeMismatchException( "Cannot use unit values as exponents", self.expr, ) if ltyp != 'int128' and ltyp != 'uint256' and isinstance( self.expr.right, ast.Name): raise TypeMismatchException( "Cannot use dynamic values as exponents, for unit base types", self.expr, ) if ltyp == rtyp == 'uint256': o = LLLnode.from_list([ 'seq', [ 'assert', [ 'or', ['or', ['eq', right, 1], ['iszero', right]], ['lt', left, ['exp', left, right]] ], ], ['exp', left, right], ], typ=BaseType('uint256'), pos=getpos(self.expr)) elif ltyp == rtyp == 'int128': new_unit = left.typ.unit if left.typ.unit and not isinstance(self.expr.right, ast.Name): new_unit = { left.typ.unit.copy().popitem()[0]: self.expr.right.n } o = LLLnode.from_list( ['exp', left, right], typ=BaseType('int128', new_unit), pos=getpos(self.expr), ) else: raise TypeMismatchException( 'Only whole number exponents are supported', self.expr) else: raise ParserException( "Unsupported binary operator: %r" % self.expr.op, self.expr) p = ['seq'] if pre_alloc_left: p.append(pre_alloc_left) if pre_alloc_right: p.append(pre_alloc_right) if o.typ.typ == 'int128': p.append([ 'clamp', ['mload', MemoryPositions.MINNUM], o, ['mload', MemoryPositions.MAXNUM], ]) return LLLnode.from_list(p, typ=o.typ, pos=getpos(self.expr)) elif o.typ.typ == 'decimal': p.append([ 'clamp', ['mload', MemoryPositions.MINDECIMAL], o, ['mload', MemoryPositions.MAXDECIMAL], ]) return LLLnode.from_list(p, typ=o.typ, pos=getpos(self.expr)) if o.typ.typ == 'uint256': p.append(o) return LLLnode.from_list(p, typ=o.typ, pos=getpos(self.expr)) else: raise Exception("%r %r" % (o, o.typ))
def make_byte_array_copier(destination, source, pos=None): if not isinstance(source.typ, ByteArrayLike): btype = "byte array" if isinstance(destination.typ, ByteArrayType) else "string" raise TypeMismatch(f"Can only set a {btype} to another {btype}", pos) if isinstance(source.typ, ByteArrayLike) and source.typ.maxlen > destination.typ.maxlen: raise TypeMismatch( f"Cannot cast from greater max-length {source.typ.maxlen} to shorter " f"max-length {destination.typ.maxlen}" ) # stricter check for zeroing a byte array. if isinstance(source.typ, ByteArrayLike): if source.value is None and source.typ.maxlen != destination.typ.maxlen: raise TypeMismatch( f"Bad type for clearing bytes: expected {destination.typ}" f" but got {source.typ}" ) # Special case: memory to memory if source.location == "memory" and destination.location == "memory": gas_calculation = GAS_IDENTITY + GAS_IDENTITYWORD * (ceil32(source.typ.maxlen) // 32) o = LLLnode.from_list( [ "with", "_source", source, [ "with", "_sz", ["add", 32, ["mload", "_source"]], ["assert", ["call", ["gas"], 4, 0, "_source", "_sz", destination, "_sz"]], ], ], # noqa: E501 typ=None, add_gas_estimate=gas_calculation, annotation="Memory copy", ) return o if source.value is None: pos_node = source else: pos_node = LLLnode.from_list("_pos", typ=source.typ, location=source.location) # Get the length if source.value is None: length = 1 elif source.location == "memory": length = ["add", ["mload", "_pos"], 32] elif source.location == "storage": length = ["add", ["sload", "_pos"], 32] pos_node = LLLnode.from_list(pos_node, typ=source.typ, location=source.location,) else: raise CompilerPanic(f"Unsupported location: {source.location}") if destination.location == "storage": destination = LLLnode.from_list( destination, typ=destination.typ, location=destination.location, ) # Maximum theoretical length max_length = 32 if source.value is None else source.typ.maxlen + 32 return LLLnode.from_list( [ "with", "_pos", 0 if source.value is None else source, make_byte_slice_copier(destination, pos_node, length, max_length, pos=pos), ], typ=None, )
def number(self): orignum = get_original_if_0_prefixed(self.expr, self.context) if orignum is None and isinstance(self.expr.n, int): # Literal (mostly likely) becomes int128 if SizeLimits.in_bounds('int128', self.expr.n) or self.expr.n < 0: return LLLnode.from_list( self.expr.n, typ=BaseType('int128', unit={}, is_literal=True), pos=getpos(self.expr), ) # Literal is large enough (mostly likely) becomes uint256. else: return LLLnode.from_list( self.expr.n, typ=BaseType('uint256', unit={}, is_literal=True), pos=getpos(self.expr), ) elif isinstance(self.expr.n, float): numstring, num, den = get_number_as_fraction( self.expr, self.context) # if not SizeLimits.in_bounds('decimal', num // den): # if not SizeLimits.MINDECIMAL * den <= num <= SizeLimits.MAXDECIMAL * den: if not (SizeLimits.MINNUM * den < num < SizeLimits.MAXNUM * den): raise InvalidLiteralException( "Number out of range: " + numstring, self.expr) if DECIMAL_DIVISOR % den: raise InvalidLiteralException( "Too many decimal places: " + numstring, self.expr) return LLLnode.from_list( num * DECIMAL_DIVISOR // den, typ=BaseType('decimal', unit=None, is_literal=True), pos=getpos(self.expr), ) # Binary literal. elif orignum[:2] == '0b': str_val = orignum[2:] total_bits = len(orignum[2:]) total_bits = ( total_bits if total_bits % 8 == 0 else total_bits + 8 - (total_bits % 8) # ceil8 to get byte length. ) if len( orignum[2:] ) != total_bits: # Support only full formed bit definitions. raise InvalidLiteralException( ("Bit notation requires a multiple of 8 bits / 1 byte. {} " "bit(s) are missing.").format(total_bits - len(orignum[2:])), self.expr) byte_len = int(total_bits / 8) placeholder = self.context.new_placeholder(ByteArrayType(byte_len)) seq = [] seq.append(['mstore', placeholder, byte_len]) for i in range(0, total_bits, 256): section = str_val[i:i + 256] int_val = int(section, 2) << (256 - len(section) ) # bytes are right padded. seq.append(['mstore', ['add', placeholder, i + 32], int_val]) return LLLnode.from_list( ['seq'] + seq + [placeholder], typ=ByteArrayType(byte_len), location='memory', pos=getpos(self.expr), annotation='Create ByteArray (Binary literal): %s' % str_val, ) elif len(orignum) == 42: if checksum_encode(orignum) != orignum: raise InvalidLiteralException( """Address checksum mismatch. If you are sure this is the right address, the correct checksummed form is: %s""" % checksum_encode(orignum), self.expr) return LLLnode.from_list( self.expr.n, typ=BaseType('address', is_literal=True), pos=getpos(self.expr), ) elif len(orignum) == 66: return LLLnode.from_list( self.expr.n, typ=BaseType('bytes32', is_literal=True), pos=getpos(self.expr), ) else: raise InvalidLiteralException( ("Cannot read 0x value with length %d. Expecting 42 (address " "incl 0x) or 66 (bytes32 incl 0x)") % len(orignum), self.expr)
def make_byte_slice_copier(destination, source, length, max_length, pos=None): # Special case: memory to memory if source.location == "memory" and destination.location == "memory": return LLLnode.from_list([ 'with', '_l', max_length, [ 'pop', [ 'call', 18 + max_length // 10, 4, 0, source, '_l', destination, '_l' ] ] ], typ=None, annotation='copy byte slice dest: %s' % str(destination)) # Copy over data if isinstance(source.typ, NullType): loader = 0 elif source.location == "memory": loader = [ 'mload', [ 'add', '_pos', ['mul', 32, ['mload', MemoryPositions.FREE_LOOP_INDEX]] ] ] elif source.location == "storage": loader = [ 'sload', ['add', '_pos', ['mload', MemoryPositions.FREE_LOOP_INDEX]] ] else: raise Exception("Unsupported location:" + source.location) # Where to paste it? if destination.location == "memory": setter = [ 'mstore', [ 'add', '_opos', ['mul', 32, ['mload', MemoryPositions.FREE_LOOP_INDEX]] ], loader ] elif destination.location == "storage": setter = [ 'sstore', ['add', '_opos', ['mload', MemoryPositions.FREE_LOOP_INDEX]], loader ] else: raise Exception("Unsupported location:" + destination.location) # Check to see if we hit the length checker = [ 'if', [ 'gt', ['mul', 32, ['mload', MemoryPositions.FREE_LOOP_INDEX]], '_actual_len' ], 'break' ] # Make a loop to do the copying o = [ 'with', '_pos', source, [ 'with', '_opos', destination, [ 'with', '_actual_len', length, [ 'repeat', MemoryPositions.FREE_LOOP_INDEX, 0, (max_length + 31) // 32, ['seq', checker, setter] ] ] ] ] return LLLnode.from_list( o, typ=None, annotation='copy byte slice src: %s dst: %s' % (source, destination), pos=pos, )
def attribute(self): # x.balance: balance of address x if self.expr.attr == 'balance': addr = Expr.parse_value_expr(self.expr.value, self.context) if not is_base_type(addr.typ, 'address'): raise TypeMismatch( "Type mismatch: balance keyword expects an address as input", self.expr) if (isinstance(self.expr.value, vy_ast.Name) and self.expr.value.id == "self" and version_check(begin="istanbul")): seq = ['selfbalance'] else: seq = ['balance', addr] return LLLnode.from_list( seq, typ=BaseType('uint256', {'wei': 1}), location=None, pos=getpos(self.expr), ) # x.codesize: codesize of address x elif self.expr.attr == 'codesize' or self.expr.attr == 'is_contract': addr = Expr.parse_value_expr(self.expr.value, self.context) if not is_base_type(addr.typ, 'address'): raise TypeMismatch( "Type mismatch: codesize keyword expects an address as input", self.expr, ) if self.expr.attr == 'codesize': eval_code = ['extcodesize', addr] output_type = 'int128' else: eval_code = ['gt', ['extcodesize', addr], 0] output_type = 'bool' return LLLnode.from_list( eval_code, typ=BaseType(output_type), location=None, pos=getpos(self.expr), ) # x.codehash: keccak of address x elif self.expr.attr == 'codehash': addr = Expr.parse_value_expr(self.expr.value, self.context) if not is_base_type(addr.typ, 'address'): raise TypeMismatch( "codehash keyword expects an address as input", self.expr, ) if not version_check(begin="constantinople"): raise EvmVersionException( "address.codehash is unavailable prior to constantinople ruleset", self.expr) return LLLnode.from_list(['extcodehash', addr], typ=BaseType('bytes32'), location=None, pos=getpos(self.expr)) # self.x: global attribute elif isinstance(self.expr.value, vy_ast.Name) and self.expr.value.id == "self": if self.expr.attr not in self.context.globals: raise VariableDeclarationException( "Persistent variable undeclared: " + self.expr.attr, self.expr, ) var = self.context.globals[self.expr.attr] return LLLnode.from_list( var.pos, typ=var.typ, location='storage', pos=getpos(self.expr), annotation='self.' + self.expr.attr, ) # Reserved keywords elif (isinstance(self.expr.value, vy_ast.Name) and self.expr.value.id in ENVIRONMENT_VARIABLES): key = self.expr.value.id + "." + self.expr.attr if key == "msg.sender": if self.context.is_private: raise StructureException( "msg.sender not allowed in private functions.", self.expr) return LLLnode.from_list(['caller'], typ='address', pos=getpos(self.expr)) elif key == "msg.value": if not self.context.is_payable: raise NonPayableViolation( "Cannot use msg.value in a non-payable function", self.expr, ) return LLLnode.from_list( ['callvalue'], typ=BaseType('uint256', {'wei': 1}), pos=getpos(self.expr), ) elif key == "msg.gas": return LLLnode.from_list( ['gas'], typ='uint256', pos=getpos(self.expr), ) elif key == "block.difficulty": return LLLnode.from_list( ['difficulty'], typ='uint256', pos=getpos(self.expr), ) elif key == "block.timestamp": return LLLnode.from_list( ['timestamp'], typ=BaseType('uint256', {'sec': 1}, True), pos=getpos(self.expr), ) elif key == "block.coinbase": return LLLnode.from_list(['coinbase'], typ='address', pos=getpos(self.expr)) elif key == "block.number": return LLLnode.from_list(['number'], typ='uint256', pos=getpos(self.expr)) elif key == "block.prevhash": return LLLnode.from_list( ['blockhash', ['sub', 'number', 1]], typ='bytes32', pos=getpos(self.expr), ) elif key == "tx.origin": return LLLnode.from_list(['origin'], typ='address', pos=getpos(self.expr)) elif key == "chain.id": if not version_check(begin="istanbul"): raise EvmVersionException( "chain.id is unavailable prior to istanbul ruleset", self.expr) return LLLnode.from_list(['chainid'], typ='uint256', pos=getpos(self.expr)) else: raise StructureException("Unsupported keyword: " + key, self.expr) # Other variables else: sub = Expr.parse_variable_location(self.expr.value, self.context) # contract type if isinstance(sub.typ, ContractType): return sub if not isinstance(sub.typ, StructType): raise TypeMismatch( "Type mismatch: member variable access not expected", self.expr.value, ) attrs = list(sub.typ.members.keys()) if self.expr.attr not in attrs: raise TypeMismatch( f"Member {self.expr.attr} not found. Only the following available: " f"{' '.join(attrs)}", self.expr, ) return add_variable_offset(sub, self.expr.attr, pos=getpos(self.expr))
def add_variable_offset(parent, key, pos): typ, location = parent.typ, parent.location if isinstance(typ, (StructType, TupleType)): if isinstance(typ, StructType): if not isinstance(key, str): raise TypeMismatchException( "Expecting a member variable access; cannot access element %r" % key, pos) if key not in typ.members: raise TypeMismatchException( "Object does not have member variable %s" % key, pos) subtype = typ.members[key] attrs = list(typ.members.keys()) if key not in attrs: raise TypeMismatchException( "Member %s not found. Only the following available: %s" % (key, " ".join(attrs)), pos) index = attrs.index(key) annotation = key else: if not isinstance(key, int): raise TypeMismatchException( "Expecting a static index; cannot access element %r" % key, pos) attrs = list(range(len(typ.members))) index = key annotation = None if location == 'storage': return LLLnode.from_list( [ 'add', ['sha3_32', parent], LLLnode.from_list(index, annotation=annotation) ], typ=subtype, location='storage', ) elif location == 'storage_prehashed': return LLLnode.from_list( [ 'add', parent, LLLnode.from_list(index, annotation=annotation) ], typ=subtype, location='storage', ) elif location == 'memory': offset = 0 for i in range(index): offset += 32 * get_size_of_type(typ.members[attrs[i]]) return LLLnode.from_list(['add', offset, parent], typ=typ.members[key], location='memory', annotation=annotation) else: raise TypeMismatchException( "Not expecting a member variable access", pos) elif isinstance(typ, MappingType): if isinstance(key.typ, ByteArrayLike): if not isinstance(typ.keytype, ByteArrayLike) or ( typ.keytype.maxlen < key.typ.maxlen): raise TypeMismatchException( 'Mapping keys of bytes cannot be cast, use exact same bytes type of: %s' % (str(typ.keytype), ), pos, ) subtype = typ.valuetype if len(key.args[0].args) >= 3: # handle bytes literal. sub = LLLnode.from_list([ 'seq', key, [ 'sha3', ['add', key.args[0].args[-1], 32], ['mload', key.args[0].args[-1]] ] ]) else: sub = LLLnode.from_list([ 'sha3', ['add', key.args[0].value, 32], ['mload', key.args[0].value] ]) else: subtype = typ.valuetype sub = base_type_conversion(key, key.typ, typ.keytype, pos=pos) if location == 'storage': return LLLnode.from_list(['sha3_64', parent, sub], typ=subtype, location='storage') elif location == 'memory': raise TypeMismatchException( "Can only have fixed-side arrays in memory, not mappings", pos) elif isinstance(typ, ListType): subtype = typ.subtype k = unwrap_location(key) if not is_base_type(key.typ, ('int128', 'uint256')): raise TypeMismatchException( 'Invalid type for array index: %r' % key.typ, pos) if key.typ.is_literal: # note: BaseType always has is_literal attr # perform the check at compile time and elide the runtime check. if key.value < 0 or key.value >= typ.count: raise ArrayIndexException( 'Array index determined to be out of bounds. ' 'Index is %r but array size is %r' % (key.value, typ.count), pos) sub = k else: # this works, even for int128. for int128, since two's-complement # is used, if the index is negative, (unsigned) LT will interpret # it as a very large number, larger than any practical value for # an array index, and the clamp will throw an error. sub = ['uclamplt', k, typ.count] if location == 'storage': return LLLnode.from_list(['add', ['sha3_32', parent], sub], typ=subtype, location='storage') elif location == 'storage_prehashed': return LLLnode.from_list(['add', parent, sub], typ=subtype, location='storage') elif location == 'memory': offset = 32 * get_size_of_type(subtype) return LLLnode.from_list( ['add', ['mul', offset, sub], parent], typ=subtype, location='memory', ) else: raise TypeMismatchException("Not expecting an array access ", pos) else: raise TypeMismatchException( "Cannot access the child of a constant variable! %r" % typ, pos)
def arithmetic(self): left = Expr.parse_value_expr(self.expr.left, self.context) right = Expr.parse_value_expr(self.expr.right, self.context) if not is_numeric_type(left.typ) or not is_numeric_type(right.typ): raise TypeMismatch( f"Unsupported types for arithmetic op: {left.typ} {right.typ}", self.expr, ) arithmetic_pair = {left.typ.typ, right.typ.typ} # Special Case: Simplify any literal to literal arithmetic at compile time. if left.typ.is_literal and right.typ.is_literal and \ isinstance(right.value, int) and isinstance(left.value, int) and \ arithmetic_pair.issubset({'uint256', 'int128'}): if isinstance(self.expr.op, vy_ast.Add): val = left.value + right.value elif isinstance(self.expr.op, vy_ast.Sub): val = left.value - right.value elif isinstance(self.expr.op, vy_ast.Mult): val = left.value * right.value elif isinstance(self.expr.op, vy_ast.Pow): val = left.value**right.value elif isinstance(self.expr.op, (vy_ast.Div, vy_ast.Mod)): if right.value == 0: raise ZeroDivisionException( "integer division or modulo by zero", self.expr, ) if isinstance(self.expr.op, vy_ast.Div): val = left.value // right.value elif isinstance(self.expr.op, vy_ast.Mod): # modified modulo logic to remain consistent with EVM behaviour val = abs(left.value) % abs(right.value) if left.value < 0: val = -val else: raise StructureException( f'Unsupported literal operator: {type(self.expr.op)}', self.expr, ) num = vy_ast.Int(n=val) num.full_source_code = self.expr.full_source_code num.node_source_code = self.expr.node_source_code num.lineno = self.expr.lineno num.col_offset = self.expr.col_offset num.end_lineno = self.expr.end_lineno num.end_col_offset = self.expr.end_col_offset return Expr.parse_value_expr(num, self.context) pos = getpos(self.expr) # Special case with uint256 were int literal may be casted. if arithmetic_pair == {'uint256', 'int128'}: # Check right side literal. if right.typ.is_literal and SizeLimits.in_bounds( 'uint256', right.value): right = LLLnode.from_list( right.value, typ=BaseType('uint256', None, is_literal=True), pos=pos, ) # Check left side literal. elif left.typ.is_literal and SizeLimits.in_bounds( 'uint256', left.value): left = LLLnode.from_list( left.value, typ=BaseType('uint256', None, is_literal=True), pos=pos, ) if left.typ.typ == "decimal" and isinstance(self.expr.op, vy_ast.Pow): raise TypeMismatch( "Cannot perform exponentiation on decimal values.", self.expr, ) # Only allow explicit conversions to occur. if left.typ.typ != right.typ.typ: raise TypeMismatch( f"Cannot implicitly convert {left.typ.typ} to {right.typ.typ}.", self.expr, ) ltyp, rtyp = left.typ.typ, right.typ.typ if isinstance(self.expr.op, (vy_ast.Add, vy_ast.Sub)): if left.typ.unit != right.typ.unit and left.typ.unit != {} and right.typ.unit != {}: raise TypeMismatch( f"Unit mismatch: {left.typ.unit} {right.typ.unit}", self.expr, ) if (left.typ.positional and right.typ.positional and isinstance(self.expr.op, vy_ast.Add)): raise TypeMismatch( "Cannot add two positional units!", self.expr, ) new_unit = left.typ.unit or right.typ.unit # xor, as subtracting two positionals gives a delta new_positional = left.typ.positional ^ right.typ.positional new_typ = BaseType(ltyp, new_unit, new_positional) op = 'add' if isinstance(self.expr.op, vy_ast.Add) else 'sub' if ltyp == 'uint256' and isinstance(self.expr.op, vy_ast.Add): # safeadd arith = [ 'seq', ['assert', ['ge', ['add', 'l', 'r'], 'l']], ['add', 'l', 'r'] ] elif ltyp == 'uint256' and isinstance(self.expr.op, vy_ast.Sub): # safesub arith = [ 'seq', ['assert', ['ge', 'l', 'r']], ['sub', 'l', 'r'] ] elif ltyp == rtyp: arith = [op, 'l', 'r'] else: raise Exception( f"Unsupported Operation '{op}({ltyp}, {rtyp})'") elif isinstance(self.expr.op, vy_ast.Mult): if left.typ.positional or right.typ.positional: raise TypeMismatch("Cannot multiply positional values!", self.expr) new_unit = combine_units(left.typ.unit, right.typ.unit) new_typ = BaseType(ltyp, new_unit) if ltyp == rtyp == 'uint256': arith = [ 'with', 'ans', ['mul', 'l', 'r'], [ 'seq', [ 'assert', [ 'or', ['eq', ['div', 'ans', 'l'], 'r'], ['iszero', 'l'] ] ], 'ans' ] ] elif ltyp == rtyp == 'int128': # TODO should this be 'smul' (note edge cases in YP for smul) arith = ['mul', 'l', 'r'] elif ltyp == rtyp == 'decimal': # TODO should this be smul arith = [ 'with', 'ans', ['mul', 'l', 'r'], [ 'seq', [ 'assert', [ 'or', ['eq', ['sdiv', 'ans', 'l'], 'r'], ['iszero', 'l'] ] ], ['sdiv', 'ans', DECIMAL_DIVISOR] ] ] else: raise Exception(f"Unsupported Operation 'mul({ltyp}, {rtyp})'") elif isinstance(self.expr.op, vy_ast.Div): if right.typ.is_literal and right.value == 0: raise ZeroDivisionException("Cannot divide by 0.", self.expr) if left.typ.positional or right.typ.positional: raise TypeMismatch("Cannot divide positional values!", self.expr) new_unit = combine_units(left.typ.unit, right.typ.unit, div=True) new_typ = BaseType(ltyp, new_unit) if ltyp == rtyp == 'uint256': arith = ['div', 'l', ['clamp_nonzero', 'r']] elif ltyp == rtyp == 'int128': arith = ['sdiv', 'l', ['clamp_nonzero', 'r']] elif ltyp == rtyp == 'decimal': arith = [ 'sdiv', # TODO check overflow cases, also should it be smul ['mul', 'l', DECIMAL_DIVISOR], ['clamp_nonzero', 'r'] ] else: raise Exception(f"Unsupported Operation 'div({ltyp}, {rtyp})'") elif isinstance(self.expr.op, vy_ast.Mod): if right.typ.is_literal and right.value == 0: raise ZeroDivisionException("Cannot calculate modulus of 0.", self.expr) if left.typ.positional or right.typ.positional: raise TypeMismatch( "Cannot use positional values as modulus arguments!", self.expr, ) if not are_units_compatible(left.typ, right.typ) and not ( left.typ.unit or right.typ.unit): # noqa: E501 raise TypeMismatch("Modulus arguments must have same unit", self.expr) new_unit = left.typ.unit or right.typ.unit new_typ = BaseType(ltyp, new_unit) if ltyp == rtyp == 'uint256': arith = ['mod', 'l', ['clamp_nonzero', 'r']] elif ltyp == rtyp: # TODO should this be regular mod arith = ['smod', 'l', ['clamp_nonzero', 'r']] else: raise Exception(f"Unsupported Operation 'mod({ltyp}, {rtyp})'") elif isinstance(self.expr.op, vy_ast.Pow): if left.typ.positional or right.typ.positional: raise TypeMismatch( "Cannot use positional values as exponential arguments!", self.expr, ) if right.typ.unit: raise TypeMismatch( "Cannot use unit values as exponents", self.expr, ) if ltyp != 'int128' and ltyp != 'uint256' and isinstance( self.expr.right, vy_ast.Name): raise TypeMismatch( "Cannot use dynamic values as exponents, for unit base types", self.expr, ) new_unit = left.typ.unit if left.typ.unit and not isinstance(self.expr.right, vy_ast.Name): new_unit = { left.typ.unit.copy().popitem()[0]: self.expr.right.n } new_typ = BaseType(ltyp, new_unit) if ltyp == rtyp == 'uint256': arith = [ 'seq', [ 'assert', [ 'or', # r == 1 | iszero(r) # could be simplified to ~(r & 1) ['or', ['eq', 'r', 1], ['iszero', 'r']], ['lt', 'l', ['exp', 'l', 'r']] ] ], ['exp', 'l', 'r'] ] elif ltyp == rtyp == 'int128': arith = ['exp', 'l', 'r'] else: raise TypeMismatch('Only whole number exponents are supported', self.expr) else: raise StructureException( f"Unsupported binary operator: {self.expr.op}", self.expr) p = ['seq'] if new_typ.typ == 'int128': p.append([ 'clamp', ['mload', MemoryPositions.MINNUM], arith, ['mload', MemoryPositions.MAXNUM], ]) elif new_typ.typ == 'decimal': p.append([ 'clamp', ['mload', MemoryPositions.MINDECIMAL], arith, ['mload', MemoryPositions.MAXDECIMAL], ]) elif new_typ.typ == 'uint256': p.append(arith) else: raise Exception(f"{arith} {new_typ}") p = ['with', 'l', left, ['with', 'r', right, p]] return LLLnode.from_list(p, typ=new_typ, pos=pos)
def make_setter(left, right, location, pos, in_function_call=False): # Basic types if isinstance(left.typ, BaseType): right = base_type_conversion( right, right.typ, left.typ, pos, in_function_call=in_function_call, ) if location == 'storage': return LLLnode.from_list(['sstore', left, right], typ=None) elif location == 'memory': return LLLnode.from_list(['mstore', left, right], typ=None) # Byte arrays elif isinstance(left.typ, ByteArrayLike): return make_byte_array_copier(left, right, pos) # Can't copy mappings elif isinstance(left.typ, MappingType): raise TypeMismatchException( "Cannot copy mappings; can only copy individual elements", pos) # Arrays elif isinstance(left.typ, ListType): # Cannot do something like [a, b, c] = [1, 2, 3] if left.value == "multi": raise Exception("Target of set statement must be a single item") if not isinstance(right.typ, (ListType, NullType)): raise TypeMismatchException( "Setter type mismatch: left side is array, right side is %r" % right.typ, pos) left_token = LLLnode.from_list('_L', typ=left.typ, location=left.location) if left.location == "storage": left = LLLnode.from_list(['sha3_32', left], typ=left.typ, location="storage_prehashed") left_token.location = "storage_prehashed" # Type checks if not isinstance(right.typ, NullType): if not isinstance(right.typ, ListType): raise TypeMismatchException( "Left side is array, right side is not", pos) if left.typ.count != right.typ.count: raise TypeMismatchException("Mismatched number of elements", pos) # If the right side is a literal if right.value == "multi": if len(right.args) != left.typ.count: raise TypeMismatchException("Mismatched number of elements", pos) subs = [] for i in range(left.typ.count): subs.append( make_setter(add_variable_offset( left_token, LLLnode.from_list(i, typ='int128'), pos=pos, ), right.args[i], location, pos=pos)) return LLLnode.from_list(['with', '_L', left, ['seq'] + subs], typ=None) # If the right side is a null elif isinstance(right.typ, NullType): subs = [] for i in range(left.typ.count): subs.append( make_setter(add_variable_offset( left_token, LLLnode.from_list(i, typ='int128'), pos=pos, ), LLLnode.from_list(None, typ=NullType()), location, pos=pos)) return LLLnode.from_list(['with', '_L', left, ['seq'] + subs], typ=None) # If the right side is a variable else: right_token = LLLnode.from_list('_R', typ=right.typ, location=right.location) subs = [] for i in range(left.typ.count): subs.append( make_setter(add_variable_offset( left_token, LLLnode.from_list(i, typ='int128'), pos=pos, ), add_variable_offset( right_token, LLLnode.from_list(i, typ='int128'), pos=pos, ), location, pos=pos)) return LLLnode.from_list( ['with', '_L', left, ['with', '_R', right, ['seq'] + subs]], typ=None) # Structs elif isinstance(left.typ, (StructType, TupleType)): if left.value == "multi" and isinstance(left.typ, StructType): raise Exception("Target of set statement must be a single item") if not isinstance(right.typ, NullType): if not isinstance(right.typ, left.typ.__class__): raise TypeMismatchException( "Setter type mismatch: left side is %r, right side is %r" % ( left.typ, right.typ, ), pos, ) if isinstance(left.typ, StructType): for k in right.args: if k.value is None: raise InvalidLiteralException( 'Setting struct value to None is not allowed, use a default value.', pos, ) for k in left.typ.members: if k not in right.typ.members: raise TypeMismatchException( "Keys don't match for structs, missing %s" % k, pos, ) for k in right.typ.members: if k not in left.typ.members: raise TypeMismatchException( "Keys don't match for structs, extra %s" % k, pos, ) if left.typ.name != right.typ.name: raise TypeMismatchException( "Expected %r, got %r" % (left.typ, right.typ), pos) else: if len(left.typ.members) != len(right.typ.members): raise TypeMismatchException( "Tuple lengths don't match, %d vs %d" % ( len(left.typ.members), len(right.typ.members), ), pos, ) left_token = LLLnode.from_list('_L', typ=left.typ, location=left.location) if left.location == "storage": left = LLLnode.from_list(['sha3_32', left], typ=left.typ, location="storage_prehashed") left_token.location = "storage_prehashed" if isinstance(left.typ, StructType): keyz = list(left.typ.members.keys()) else: keyz = list(range(len(left.typ.members))) # If the left side is a literal if left.value == 'multi': locations = [arg.location for arg in left.args] else: locations = [location for _ in keyz] # If the right side is a literal if right.value == "multi": if len(right.args) != len(keyz): raise TypeMismatchException("Mismatched number of elements", pos) subs = [] for i, (typ, loc) in enumerate(zip(keyz, locations)): subs.append( make_setter( add_variable_offset(left_token, typ, pos=pos), right.args[i], loc, pos=pos, )) return LLLnode.from_list(['with', '_L', left, ['seq'] + subs], typ=None) # If the right side is a null elif isinstance(right.typ, NullType): subs = [] for typ, loc in zip(keyz, locations): subs.append( make_setter( add_variable_offset(left_token, typ, pos=pos), LLLnode.from_list(None, typ=NullType()), loc, pos=pos, )) return LLLnode.from_list(['with', '_L', left, ['seq'] + subs], typ=None) # If tuple assign. elif isinstance(left.typ, TupleType) and isinstance( right.typ, TupleType): subs = [] static_offset_counter = 0 zipped_components = zip(left.args, right.typ.members, locations) for left_arg, right_arg, loc in zipped_components: if isinstance(right_arg, ByteArrayLike): RType = ByteArrayType if isinstance( right_arg, ByteArrayType) else StringType offset = LLLnode.from_list([ 'add', '_R', ['mload', ['add', '_R', static_offset_counter]] ], typ=RType(right_arg.maxlen), location='memory', pos=pos) static_offset_counter += 32 else: offset = LLLnode.from_list( ['mload', ['add', '_R', static_offset_counter]], typ=right_arg.typ, pos=pos, ) static_offset_counter += get_size_of_type(right_arg) * 32 subs.append(make_setter(left_arg, offset, loc, pos=pos)) return LLLnode.from_list( ['with', '_R', right, ['seq'] + subs], typ=None, annotation='Tuple assignment', ) # If the right side is a variable else: subs = [] right_token = LLLnode.from_list('_R', typ=right.typ, location=right.location) for typ, loc in zip(keyz, locations): subs.append( make_setter(add_variable_offset(left_token, typ, pos=pos), add_variable_offset(right_token, typ, pos=pos), loc, pos=pos)) return LLLnode.from_list( ['with', '_L', left, ['with', '_R', right, ['seq'] + subs]], typ=None, ) else: raise Exception("Invalid type for setters")
def parse_Attribute(self): # x.balance: balance of address x if self.expr.attr == "balance": addr = Expr.parse_value_expr(self.expr.value, self.context) if is_base_type(addr.typ, "address"): if (isinstance(self.expr.value, vy_ast.Name) and self.expr.value.id == "self" and version_check(begin="istanbul")): seq = ["selfbalance"] else: seq = ["balance", addr] return LLLnode.from_list( seq, typ=BaseType("uint256"), location=None, pos=getpos(self.expr), ) # x.codesize: codesize of address x elif self.expr.attr == "codesize" or self.expr.attr == "is_contract": addr = Expr.parse_value_expr(self.expr.value, self.context) if is_base_type(addr.typ, "address"): if self.expr.attr == "codesize": eval_code = ["extcodesize", addr] output_type = "int128" else: eval_code = ["gt", ["extcodesize", addr], 0] output_type = "bool" return LLLnode.from_list( eval_code, typ=BaseType(output_type), location=None, pos=getpos(self.expr), ) # x.codehash: keccak of address x elif self.expr.attr == "codehash": addr = Expr.parse_value_expr(self.expr.value, self.context) if not version_check(begin="constantinople"): raise EvmVersionException( "address.codehash is unavailable prior to constantinople ruleset", self.expr) if is_base_type(addr.typ, "address"): return LLLnode.from_list( ["extcodehash", addr], typ=BaseType("bytes32"), location=None, pos=getpos(self.expr), ) # self.x: global attribute elif isinstance(self.expr.value, vy_ast.Name) and self.expr.value.id == "self": var = self.context.globals[self.expr.attr] return LLLnode.from_list( var.pos, typ=var.typ, location="storage", pos=getpos(self.expr), annotation="self." + self.expr.attr, ) # Reserved keywords elif (isinstance(self.expr.value, vy_ast.Name) and self.expr.value.id in ENVIRONMENT_VARIABLES): key = f"{self.expr.value.id}.{self.expr.attr}" if key == "msg.sender" and not self.context.is_private: return LLLnode.from_list(["caller"], typ="address", pos=getpos(self.expr)) elif key == "msg.value" and self.context.is_payable: return LLLnode.from_list( ["callvalue"], typ=BaseType("uint256"), pos=getpos(self.expr), ) elif key == "msg.gas": return LLLnode.from_list( ["gas"], typ="uint256", pos=getpos(self.expr), ) elif key == "block.difficulty": return LLLnode.from_list( ["difficulty"], typ="uint256", pos=getpos(self.expr), ) elif key == "block.timestamp": return LLLnode.from_list( ["timestamp"], typ=BaseType("uint256"), pos=getpos(self.expr), ) elif key == "block.coinbase": return LLLnode.from_list(["coinbase"], typ="address", pos=getpos(self.expr)) elif key == "block.number": return LLLnode.from_list(["number"], typ="uint256", pos=getpos(self.expr)) elif key == "block.prevhash": return LLLnode.from_list( ["blockhash", ["sub", "number", 1]], typ="bytes32", pos=getpos(self.expr), ) elif key == "tx.origin": return LLLnode.from_list(["origin"], typ="address", pos=getpos(self.expr)) elif key == "chain.id": if not version_check(begin="istanbul"): raise EvmVersionException( "chain.id is unavailable prior to istanbul ruleset", self.expr) return LLLnode.from_list(["chainid"], typ="uint256", pos=getpos(self.expr)) # Other variables else: sub = Expr.parse_variable_location(self.expr.value, self.context) # contract type if isinstance(sub.typ, InterfaceType): return sub if isinstance(sub.typ, StructType) and self.expr.attr in sub.typ.members: return add_variable_offset(sub, self.expr.attr, pos=getpos(self.expr))