def get_type(arg): if isinstance(arg, LLLnode): return canonicalize_type(arg.typ) elif hasattr(arg, 'annotation'): return canonicalize_type( parse_type(arg.annotation, None, sigs, custom_units=custom_units))
def process_arg(index, arg, expected_arg_typelist, function_name, context): if isinstance(expected_arg_typelist, Optional): expected_arg_typelist = expected_arg_typelist.typ if not isinstance(expected_arg_typelist, tuple): expected_arg_typelist = (expected_arg_typelist, ) vsub = None for expected_arg in expected_arg_typelist: if expected_arg == 'num_literal': if isinstance(arg, ast.Num) and get_original_if_0_prefixed( arg, context) is None: return arg.n elif expected_arg == 'str_literal': if isinstance(arg, ast.Str) and get_original_if_0_prefixed( arg, context) is None: bytez = b'' for c in arg.s: if ord(c) >= 256: raise InvalidLiteralException( "Cannot insert special character %r into byte array" % c, arg) bytez += bytes([ord(c)]) return bytez elif expected_arg == 'name_literal': if isinstance(arg, ast.Name): return arg.id elif isinstance(arg, ast.Subscript) and arg.value.id == 'bytes': return 'bytes[%s]' % arg.slice.value.n elif expected_arg == '*': return arg elif expected_arg == 'bytes': sub = Expr(arg, context).lll_node if isinstance(sub.typ, ByteArrayType): return sub else: # Does not work for unit-endowed types inside compound types, e.g. timestamp[2] parsed_expected_type = parse_type( ast.parse(expected_arg).body[0].value, 'memory') if isinstance(parsed_expected_type, BaseType): vsub = vsub or Expr.parse_value_expr(arg, context) if is_base_type(vsub.typ, expected_arg): return vsub elif expected_arg in ('int128', 'uint256') and isinstance(vsub.typ, BaseType) and \ vsub.typ.is_literal and SizeLimits.in_bounds(expected_arg, vsub.value): return vsub else: vsub = vsub or Expr(arg, context).lll_node if vsub.typ == parsed_expected_type: return Expr(arg, context).lll_node if len(expected_arg_typelist) == 1: raise TypeMismatchException( "Expecting %s for argument %r of %s" % (expected_arg, index, function_name), arg) else: raise TypeMismatchException( "Expecting one of %r for argument %r of %s" % (expected_arg_typelist, index, function_name), arg) return arg.id
def ann_assign(self): self.context.set_in_assignment(True) typ = parse_type(self.stmt.annotation, location='memory', custom_units=self.context.custom_units) if isinstance(self.stmt.target, ast.Attribute) and self.stmt.target.value.id == 'self': raise TypeMismatchException('May not redefine storage variables.', self.stmt) varname = self.stmt.target.id pos = self.context.new_variable(varname, typ) o = LLLnode.from_list('pass', typ=None, pos=pos) if self.stmt.value is not None: sub = Expr(self.stmt.value, self.context).lll_node self._check_valid_assign(sub) variable_loc = LLLnode.from_list(pos, typ=typ, location='memory', pos=getpos(self.stmt)) o = make_setter(variable_loc, sub, 'memory', pos=getpos(self.stmt)) self.context.set_in_assignment(False) return o
def from_declaration(cls, code, custom_units=None): name = code.target.id pos = 0 if not is_varname_valid(name, custom_units=custom_units): raise EventDeclarationException("Event name invalid: " + name) # Determine the arguments, expects something of the form def foo(arg1: num, arg2: num ... args = [] indexed_list = [] topics_count = 1 if code.annotation.args: keys = code.annotation.args[0].keys values = code.annotation.args[0].values for i in range(len(keys)): typ = values[i] arg = keys[i].id is_indexed = False # Check to see if argument is a topic if isinstance(typ, ast.Call) and typ.func.id == 'indexed': typ = values[i].args[0] indexed_list.append(True) topics_count += 1 is_indexed = True else: indexed_list.append(False) if isinstance(typ, ast.Subscript) and getattr(typ.value, 'id', None) == 'bytes' and typ.slice.value.n > 32 and is_indexed: raise EventDeclarationException("Indexed arguments are limited to 32 bytes") if topics_count > 4: raise EventDeclarationException("Maximum of 3 topics {} given".format(topics_count - 1), arg) if not isinstance(arg, str): raise VariableDeclarationException("Argument name invalid", arg) if not typ: raise InvalidTypeException("Argument must have type", arg) if not is_varname_valid(arg, custom_units): raise VariableDeclarationException("Argument name invalid or reserved: " + arg, arg) if arg in (x.name for x in args): raise VariableDeclarationException("Duplicate function argument name: " + arg, arg) parsed_type = parse_type(typ, None, custom_units=custom_units) args.append(VariableRecord(arg, pos, parsed_type, False)) if isinstance(parsed_type, ByteArrayType): pos += ceil32(typ.slice.value.n) else: pos += get_size_of_type(parsed_type) * 32 sig = name + '(' + ','.join([canonicalize_type(arg.typ, indexed_list[pos]) for pos, arg in enumerate(args)]) + ')' # noqa F812 event_id = bytes_to_int(sha3(bytes(sig, 'utf-8'))) return cls(name, args, indexed_list, event_id, sig)
def unroll_constant(self, const): # const = self.context.constants[self.expr.id] expr = Expr.parse_value_expr(const.value, Context(vars=None, global_ctx=self, origcode=const.source_code)) annotation_type = parse_type(const.annotation.args[0], None, custom_units=self._custom_units) fail = False if self.is_instances([expr.typ, annotation_type], ByteArrayType): if expr.typ.maxlen < annotation_type.maxlen: return const fail = True elif expr.typ != annotation_type: fail = True # TODO: # special case for literals, which can be uint256 types as well. # if self.is_instances([expr.typ, annotation_type], BaseType) and \ # [annotation_type.typ, expr.typ.typ] == ['uint256', 'int128'] and \ # SizeLimits.in_bounds('uint256', expr.value): # fail = False # elif self.is_instances([expr.typ, annotation_type], BaseType) and \ # [annotation_type.typ, expr.typ.typ] == ['int128', 'int128'] and \ # SizeLimits.in_bounds('int128', expr.value): # fail = False if self.is_instances([expr.typ, annotation_type], BaseType) and \ [annotation_type.typ, expr.typ.typ] == ['integer', 'integer'] and \ SizeLimits.in_bounds('integer', expr.value): fail = False elif self.is_instances([expr.typ, annotation_type], BaseType) and \ [annotation_type.typ, expr.typ.typ] == ['amount', 'amount'] and \ SizeLimits.in_bounds('amount', expr.value): fail = False if fail: raise TypeMismatchException('Invalid value for constant type, expected %r' % annotation_type, const.value) else: expr.typ = annotation_type return expr
def add_globals(self, item): item_attributes = {"public": False} # Handle constants. if isinstance(item.annotation, ast.Call) and item.annotation.func.id == "constant": self.add_constant(item) return # Handle events. # TODO: events has been skipped if not (isinstance(item.annotation, ast.Call) and item.annotation.func.id == "event"): item_name, item_attributes = self.get_item_name_and_attributes(item, item_attributes) if not all([attr in valid_global_keywords for attr in item_attributes.keys()]): raise StructureException('Invalid global keyword used: %s' % item_attributes, item) if item.value is not None: raise StructureException('May not assign value whilst defining type', item) # TODO: remove events # elif isinstance(item.annotation, ast.Call) and item.annotation.func.id == "event": # if self._globals or len(self._defs): # raise EventDeclarationException("Events must all come before global declarations and function definitions", item) # self._events.append(item) elif isinstance(item.annotation, ast.Call) and item.annotation.func.id == "event": raise EventDeclarationException("Events are not supported") elif not isinstance(item.target, ast.Name): raise StructureException("Can only assign type to variable in top-level statement", item) # TODO: custom unit definition # Is this a custom unit definition. elif item.target.id == 'units': if not self._custom_units: if not isinstance(item.annotation, ast.Dict): raise VariableDeclarationException("Define custom units using units: { }.", item.target) for key, value in zip(item.annotation.keys, item.annotation.values): if not isinstance(value, ast.Str): raise VariableDeclarationException("Custom unit description must be a valid string", value) if not isinstance(key, ast.Name): raise VariableDeclarationException("Custom unit name must be a valid string", key) if key.id in self._custom_units: raise VariableDeclarationException("Custom unit name may only be used once", key) if not is_varname_valid(key.id, custom_units=self._custom_units): raise VariableDeclarationException("Custom unit may not be a reserved keyword", key) self._custom_units.append(key.id) else: raise VariableDeclarationException("Custom units can only be defined once", item.target) # Check if variable name is valid. elif not self.is_valid_varname(item.target.id, item): pass elif len(self._defs): raise StructureException("Global variables must all come before function definitions", item) # If the type declaration is of the form public(<type here>), then proceed with # the underlying type but also add getters # TODO: support for premade_contract # elif isinstance(item.annotation, ast.Call) and item.annotation.func.id == "address": # if item.annotation.args[0].id not in premade_contracts: # raise VariableDeclarationException("Unsupported premade contract declaration", item.annotation.args[0]) # premade_contract = premade_contracts[item.annotation.args[0].id] # self._contracts[item.target.id] = self.add_contract(premade_contract.body) # self._globals[item.target.id] = VariableRecord(item.target.id, len(self._globals), BaseType('address'), True) # elif item_name in self._contracts: # self._globals[item.target.id] = ContractRecord(item.target.id, len(self._globals), ContractType(item_name), True) # if item_attributes["public"]: # typ = ContractType(item_name) # for getter in self.mk_getter(item.target.id, typ): # self._getters.append(self.parse_line('\n' * (item.lineno - 1) + getter)) # self._getters[-1].pos = getpos(item) # TODO: supprt contract importing elif isinstance(item.annotation, ast.Call) and item.annotation.func.id == "public": if isinstance(item.annotation.args[0], ast.Name) and item_name in self._contracts: typ = ContractType(item_name) else: typ = parse_type(item.annotation.args[0], 'storage', custom_units=self._custom_units) self._globals[item.target.id] = VariableRecord(item.target.id, len(self._globals), typ, True) # Adding getters here for getter in self.mk_getter(item.target.id, typ): self._getters.append(self.parse_line('\n' * (item.lineno - 1) + getter)) self._getters[-1].pos = getpos(item) else: self._globals[item.target.id] = VariableRecord( item.target.id, len(self._globals), parse_type(item.annotation, 'storage', custom_units=self._custom_units), True )
def _RLPlist(expr, args, kwargs, context): # Second argument must be a list of types if not isinstance(args[1], ast.List): raise TypeMismatchException( "Expecting list of types for second argument", args[1]) if len(args[1].elts) == 0: raise TypeMismatchException("RLP list must have at least one item", expr) if len(args[1].elts) > 32: raise TypeMismatchException("RLP list must have at most 32 items", expr) # Get the output format _format = [] for arg in args[1].elts: if isinstance(arg, ast.Name) and arg.id == "bytes": subtyp = ByteArrayType(args[0].typ.maxlen) else: subtyp = parse_type(arg, 'memory') if not isinstance(subtyp, BaseType): raise TypeMismatchException( "RLP lists only accept BaseTypes and byte arrays", arg) if not is_base_type( subtyp, ('int128', 'uint256', 'bytes32', 'address', 'bool')): raise TypeMismatchException( "Unsupported base type: %s" % subtyp.typ, arg) _format.append(subtyp) output_type = TupleType(_format) output_placeholder_type = ByteArrayType( (2 * len(_format) + 1 + get_size_of_type(output_type)) * 32) output_placeholder = context.new_placeholder(output_placeholder_type) output_node = LLLnode.from_list(output_placeholder, typ=output_placeholder_type, location='memory') # Create a decoder for each element in the tuple decoder = [] for i, typ in enumerate(_format): # Decoder for bytes32 if is_base_type(typ, 'bytes32'): decoder.append( LLLnode.from_list( [ 'seq', [ 'assert', [ 'eq', [ 'mload', [ 'add', output_node, [ 'mload', ['add', output_node, 32 * i] ] ] ], 32 ] ], [ 'mload', [ 'add', 32, [ 'add', output_node, ['mload', ['add', output_node, 32 * i]] ] ] ] ], typ, annotation='getting and checking bytes32 item')) # Decoder for address elif is_base_type(typ, 'address'): decoder.append( LLLnode.from_list( [ 'seq', [ 'assert', [ 'eq', [ 'mload', [ 'add', output_node, [ 'mload', ['add', output_node, 32 * i] ] ] ], 20 ] ], [ 'mod', [ 'mload', [ 'add', 20, [ 'add', output_node, [ 'mload', ['add', output_node, 32 * i] ] ] ] ], ['mload', MemoryPositions.ADDRSIZE] ] ], typ, annotation='getting and checking address item')) # Decoder for bytes elif isinstance(typ, ByteArrayType): decoder.append( LLLnode.from_list([ 'add', output_node, ['mload', ['add', output_node, 32 * i]] ], typ, location='memory', annotation='getting byte array')) # Decoder for num and uint256 elif is_base_type(typ, ('int128', 'uint256')): bytez = LLLnode.from_list( ['add', output_node, ['mload', ['add', output_node, 32 * i]]], typ, location='memory', annotation='getting and checking %s' % typ.typ) decoder.append(byte_array_to_num(bytez, expr, typ.typ)) # Decoder for bools elif is_base_type(typ, ('bool')): # This is basically a really clever way to test for a length-prefixed one or zero. We take the 32 bytes # starting one byte *after* the start of the length declaration; this includes the last 31 bytes of the # length and the first byte of the value. 0 corresponds to length 0, first byte 0, and 257 corresponds # to length 1, first byte \x01 decoder.append( LLLnode.from_list([ 'with', '_ans', [ 'mload', [ 'add', 1, [ 'add', output_node, ['mload', ['add', output_node, 32 * i]] ] ] ], [ 'seq', [ 'assert', ['or', ['eq', '_ans', 0], ['eq', '_ans', 257]] ], ['div', '_ans', 257] ] ], typ, annotation='getting and checking bool')) else: # Should never reach because of top level base level check. raise Exception("Type not yet supported") # pragma: no cover # Copy the input data to memory if args[0].location == "memory": variable_pointer = args[0] elif args[0].location == "storage": placeholder = context.new_placeholder(args[0].typ) placeholder_node = LLLnode.from_list(placeholder, typ=args[0].typ, location='memory') copier = make_byte_array_copier( placeholder_node, LLLnode.from_list('_ptr', typ=args[0].typ, location=args[0].location)) variable_pointer = [ 'with', '_ptr', args[0], ['seq', copier, placeholder_node] ] else: # Should never reach because of top level base level check. raise Exception("Location not yet supported") # pragma: no cover # Decode the input data initial_setter = LLLnode.from_list([ 'seq', [ 'with', '_sub', variable_pointer, [ 'pop', [ 'call', 1500 + 400 * len(_format) + 10 * len(args), LLLnode.from_list(RLP_DECODER_ADDRESS, annotation='RLP decoder'), 0, ['add', '_sub', 32], ['mload', '_sub'], output_node, 64 * len(_format) + 32 + 32 * get_size_of_type(output_type) ] ] ], ['assert', ['eq', ['mload', output_node], 32 * len(_format) + 32]] ], typ=None) # Shove the input data decoder in front of the first variable decoder decoder[0] = LLLnode.from_list(['seq', initial_setter, decoder[0]], typ=decoder[0].typ, location=decoder[0].location) return LLLnode.from_list(["multi"] + decoder, typ=output_type, location='memory', pos=getpos(expr))
def from_definition(cls, code, sigs=None, custom_units=None, contract_def=False, constant=False): name = code.name pos = 0 if not is_varname_valid(name, custom_units=custom_units): raise FunctionDeclarationException("Function name invalid: " + name) # Determine the arguments, expects something of the form def foo(arg1: int128, arg2: int128 ... args = [] for arg in code.args.args: typ = arg.annotation if not typ: raise InvalidTypeException("Argument must have type", arg) if not is_varname_valid(arg.arg, custom_units=custom_units): raise FunctionDeclarationException( "Argument name invalid or reserved: " + arg.arg, arg) if arg.arg in (x.name for x in args): raise FunctionDeclarationException( "Duplicate function argument name: " + arg.arg, arg) parsed_type = parse_type(typ, None, sigs, custom_units=custom_units) args.append(VariableRecord(arg.arg, pos, parsed_type, False)) if isinstance(parsed_type, ByteArrayType): pos += 32 else: pos += get_size_of_type(parsed_type) * 32 # Apply decorators const, payable, private, public = False, False, False, False for dec in code.decorator_list: if isinstance(dec, ast.Name) and dec.id == "constant": const = True # TODO: # elif isinstance(dec, ast.Name) and dec.id == "payable": # payable = True elif isinstance(dec, ast.Name) and dec.id == "private": private = True elif isinstance(dec, ast.Name) and dec.id == "public": public = True else: raise StructureException("Bad decorator", dec) if public and private: raise StructureException( "Cannot use public and private decorators on the same function: {}" .format(name)) # if payable and const: # raise StructureException("Function {} cannot be both constant and payable.".format(name)) # if payable and private: # raise StructureException("Function {} cannot be both private and payable.".format(name)) if (not public and not private) and not contract_def: raise StructureException( "Function visibility must be declared (@public or @private)", code) if constant: const = True # Determine the return type and whether or not it's constant. Expects something # of the form: # def foo(): ... # def foo() -> int128: ... # If there is no return type, ie. it's of the form def foo(): ... # and NOT def foo() -> type: ..., then it's null if not code.returns: output_type = None elif isinstance( code.returns, (ast.Name, ast.Compare, ast.Subscript, ast.Call, ast.Tuple)): output_type = parse_type(code.returns, None, sigs, custom_units=custom_units) else: raise InvalidTypeException( "Output type invalid or unsupported: %r" % parse_type(code.returns, None), code.returns, ) # Output type must be canonicalizable if output_type is not None: assert isinstance(output_type, TupleType) or canonicalize_type(output_type) # Get the canonical function signature sig = cls.get_full_sig(name, code.args.args, sigs, custom_units) # Take the first 4 bytes of the hash of the sig to get the method ID method_id = fourbytes_to_int(sha3(bytes(sig, 'utf-8'))[:4]) return cls(name, args, output_type, const, payable, private, sig, method_id, custom_units)