def canonicalize_type(t, is_indexed=False): if isinstance(t, ByteArrayLike): # Check to see if maxlen is small enough for events byte_type = 'string' if isinstance(t, StringType) else 'bytes' if is_indexed: return f'{byte_type}{t.maxlen}' else: return f'{byte_type}' if isinstance(t, ListType): if not isinstance(t.subtype, (ListType, BaseType)): raise InvalidTypeException(f"List of {t.subtype}s not allowed") return canonicalize_type(t.subtype) + f"[{t.count}]" if isinstance(t, TupleLike): return f"({','.join(canonicalize_type(x) for x in t.tuple_members())})" if not isinstance(t, BaseType): raise InvalidTypeException(f"Cannot canonicalize non-base type: {t}") t = t.typ if t in ('int128', 'uint256', 'bool', 'address', 'bytes32'): return t elif t == 'decimal': return 'fixed168x10' raise InvalidTypeException(f"Invalid or unsupported type: {repr(t)}")
def parse_unit(item, custom_units): if isinstance(item, ast.Name): if item.id not in valid_units + custom_units: raise InvalidTypeException("Invalid base unit", item) return {item.id: 1} elif isinstance(item, ast.Num) and item.n == 1: return {} elif not isinstance(item, ast.BinOp): raise InvalidTypeException("Invalid unit expression", item) elif isinstance(item.op, ast.Mult): left, right = parse_unit(item.left, custom_units), parse_unit( item.right, custom_units) return combine_units(left, right) elif isinstance(item.op, ast.Div): left, right = parse_unit(item.left, custom_units), parse_unit( item.right, custom_units) return combine_units(left, right, div=True) elif isinstance(item.op, ast.Pow): if not isinstance(item.left, ast.Name): raise InvalidTypeException( "Can only raise a base type to an exponent", item) if not isinstance(item.right, ast.Num) or not isinstance( item.right.n, int) or item.right.n <= 0: raise InvalidTypeException("Exponent must be positive integer", item) return {item.left.id: item.right.n} else: raise InvalidTypeException("Invalid unit expression", item)
def get_static_size_of_type(typ): if isinstance(typ, BaseType): return 1 elif isinstance(typ, ByteArrayLike): return 1 elif isinstance(typ, ListType): return get_size_of_type(typ.subtype) * typ.count elif isinstance(typ, MappingType): raise InvalidTypeException("Maps are not supported for function arguments or outputs.") elif isinstance(typ, TupleLike): return sum([get_size_of_type(v) for v in typ.tuple_members()]) else: raise InvalidTypeException(f"Can not get size of type, Unexpected type: {repr(typ)}")
def get_size_of_type(typ): if isinstance(typ, BaseType): return 1 elif isinstance(typ, ByteArrayLike): # 1 word for offset (in static section), 1 word for length, # up to maxlen words for actual data. return ceil32(typ.maxlen) // 32 + 2 elif isinstance(typ, ListType): return get_size_of_type(typ.subtype) * typ.count elif isinstance(typ, MappingType): raise InvalidTypeException("Maps are not supported for function arguments or outputs.") elif isinstance(typ, TupleLike): return sum([get_size_of_type(v) for v in typ.tuple_members()]) else: raise InvalidTypeException(f"Can not get size of type, Unexpected type: {repr(typ)}")
def canonicalize_type(t, is_indexed=False): if isinstance(t, ByteArrayType): # Check to see if maxlen is small enough for events if is_indexed: return 'bytes{}'.format(t.maxlen) else: return 'bytes' if isinstance(t, ListType): if not isinstance(t.subtype, (ListType, BaseType)): raise Exception("List of byte arrays not allowed") return canonicalize_type(t.subtype) + "[%d]" % t.count if isinstance(t, TupleType): return "({})".format(",".join(canonicalize_type(x) for x in t.members)) if isinstance(t, StructType): # TODO: VIP1019 raise InvalidTypeException( "Structs are not allowed in events, or as args or return values from functions yet (see VIP1019)", t) if not isinstance(t, BaseType): raise Exception("Cannot canonicalize non-base type: %r" % t) t = t.typ if t == 'int128': return 'int128' elif t == 'decimal': return 'fixed168x10' elif t == 'bool': return 'bool' elif t == 'uint256': return 'uint256' elif t == 'address' or t == 'bytes32': return t raise Exception("Invalid or unsupported type: " + repr(t))
def make_struct_type(name, location, members, custom_units, custom_structs, constants): o = OrderedDict() for key, value in members: if not isinstance(key, ast.Name): raise InvalidTypeException( "Invalid member variable for struct %r, expected a name." % key.id, key, ) check_valid_varname( key.id, custom_units, custom_structs, constants, "Invalid member variable for struct", ) o[key.id] = parse_type( value, location, custom_units=custom_units, custom_structs=custom_structs, constants=constants, ) return StructType(o, name)
def make_struct(self, name, body): members = [] for item in body: if isinstance(item, ast.AnnAssign): member_name = item.target member_type = item.annotation # Check well-formedness of member names if not isinstance(member_name, ast.Name): raise InvalidTypeException( "Invalid member name for struct %r, needs to be a valid name. " % name, item) check_valid_varname(member_name.id, self._custom_units, self._structs, self._constants, item, "Invalid member name for struct. ") # Check well-formedness of member types # Note this kicks out mutually recursive structs, # raising an exception instead of stackoverflow. # A struct must be defined before it is referenced. # This feels like a semantic step and maybe should be pushed # to a later compilation stage. parse_type(member_type, 'storage', custom_units=self._custom_units, custom_structs=self._structs, constants=self._constants) members.append((member_name, member_type)) else: raise StructureException("Structs can only contain variables", item) return members
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 has_dynamic_data(typ): if isinstance(typ, BaseType): return False elif isinstance(typ, ByteArrayLike): return True elif isinstance(typ, ListType): return has_dynamic_data(typ.subtype) elif isinstance(typ, TupleLike): return any([has_dynamic_data(v) for v in typ.tuple_members()]) else: raise InvalidTypeException(f"Unexpected type: {repr(typ)}")
def make_struct_type(name, location, members, custom_units, custom_structs): o = OrderedDict() for key, value in members: if not isinstance(key, ast.Name) or not is_varname_valid( key.id, custom_units, custom_structs): raise InvalidTypeException( "Invalid member variable for struct %r" % key.id, key) o[key.id] = parse_type(value, location, custom_units=custom_units, custom_structs=custom_structs) return StructType(o, name)
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 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)
def add_globals_and_events(self, item): item_attributes = {"public": False} if len(self._globals) > NONRENTRANT_STORAGE_OFFSET: raise ParserException( f"Too many globals defined, only {NONRENTRANT_STORAGE_OFFSET} globals are allowed", item, ) # Make sure we have a valid variable name. if not isinstance(item.target, ast.Name): raise StructureException('Invalid global variable name', item.target) # Handle constants. if self.get_call_func_name(item) == "constant": self._constants.add_constant(item, global_ctx=self) return # Handle events. if not (self.get_call_func_name(item) == "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( f'Invalid global keyword used: {item_attributes}', item) if item.value is not None: raise StructureException( 'May not assign value whilst defining type', item) elif self.get_call_func_name(item) == "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 not isinstance(item.target, ast.Name): raise StructureException( "Can only assign type to variable in top-level statement", item) # 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) check_valid_varname(key.id, self._custom_units, self._structs, self._constants, key, "Custom unit invalid.") self._custom_units.add(key.id) self._custom_units_descriptions[key.id] = value.s else: raise VariableDeclarationException( "Custom units can only be defined once", item.target) # Check if variable name is valid. # Don't move this check higher, as unit parsing has to happen first. 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, ) elif item_name in self._contracts or item_name in self._interfaces: if self.get_call_func_name(item) == "address": raise StructureException( f"Persistent address({item_name}) style contract declarations " "are not support anymore." f" Use {item.target.id}: {item_name} instead") 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) elif self.get_call_func_name(item) == "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, custom_structs=self._structs, constants=self._constants, ) 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) elif isinstance(item.annotation, (ast.Name, ast.Call, ast.Subscript)): self._globals[item.target.id] = VariableRecord( item.target.id, len(self._globals), parse_type(item.annotation, 'storage', custom_units=self._custom_units, custom_structs=self._structs, constants=self._constants), True) else: raise InvalidTypeException('Invalid global type specified', item)
def parse_type(item, location, sigs=None, custom_units=None, custom_structs=None, constants=None): # Base and custom types, e.g. num if isinstance(item, ast.Name): if item.id in base_types: return BaseType(item.id) elif item.id in special_types: return special_types[item.id] elif (custom_structs is not None) and (item.id in custom_structs): return make_struct_type(item.id, location, custom_structs[item.id], custom_units, custom_structs, constants) else: raise InvalidTypeException("Invalid base type: " + item.id, item) # Units, e.g. num (1/sec) or contracts elif isinstance(item, ast.Call) and isinstance(item.func, ast.Name): # Mapping type. if item.func.id == 'map': if location == 'memory': raise InvalidTypeException("No mappings allowed for in-memory types, only fixed-size arrays", item) if len(item.args) != 2: raise InvalidTypeException("Mapping requires 2 valid positional arguments.", item) keytype = parse_type(item.args[0], None, custom_units=custom_units, custom_structs=custom_structs, constants=constants) if not isinstance(keytype, (BaseType, ByteArrayLike)): raise InvalidTypeException("Mapping keys must be base or bytes/string types", item) return MappingType(keytype, parse_type(item.args[1], location, custom_units=custom_units, custom_structs=custom_structs, constants=constants)) # Contract_types if item.func.id == 'address': if sigs and item.args[0].id in sigs: return ContractType(item.args[0].id) # Struct types if (custom_structs is not None) and (item.func.id in custom_structs): return make_struct_type(item.id, location, custom_structs[item.id], custom_units, custom_structs, constants) if not isinstance(item.func, ast.Name): raise InvalidTypeException("Malformed unit type:", item) base_type = item.func.id if base_type not in ('int128', 'uint256', 'decimal'): raise InvalidTypeException("You must use int128, uint256, decimal, address, contract, \ for variable declarations and indexed for logging topics ", item) if len(item.args) == 0: raise InvalidTypeException("Malformed unit type", item) if isinstance(item.args[-1], ast.Name) and item.args[-1].id == "positional": positional = True argz = item.args[:-1] else: positional = False argz = item.args if len(argz) != 1: raise InvalidTypeException("Malformed unit type", item) unit = parse_unit(argz[0], custom_units=custom_units) return BaseType(base_type, unit, positional) # Subscripts elif isinstance(item, ast.Subscript): if 'value' not in vars(item.slice): raise InvalidTypeException("Array / ByteArray access must access a single element, not a slice", item) # Fixed size lists or bytearrays, e.g. num[100] is_constant_val = constants.ast_is_constant(item.slice.value) if isinstance(item.slice.value, ast.Num) or is_constant_val: n_val = constants.get_constant(item.slice.value.id, context=None).value if is_constant_val else item.slice.value.n if not isinstance(n_val, int) or n_val <= 0: raise InvalidTypeException("Arrays / ByteArrays must have a positive integral number of elements", item.slice.value) # ByteArray if getattr(item.value, 'id', None) == 'bytes': return ByteArrayType(n_val) elif getattr(item.value, 'id', None) == 'string': return StringType(n_val) # List else: return ListType(parse_type(item.value, location, custom_units=custom_units, custom_structs=custom_structs, constants=constants), n_val) # Mappings, e.g. num[address] else: warnings.warn( "Mapping definitions using subscript have deprecated (see VIP564). " "Use map(type1, type2) instead.", DeprecationWarning ) raise InvalidTypeException('Unknown list type.', item) # Dicts, used to represent mappings, e.g. {uint: uint}. Key must be a base type elif isinstance(item, ast.Dict): warnings.warn( "Anonymous structs have been removed in" " favor of named structs, see VIP300", DeprecationWarning ) raise InvalidTypeException("Invalid type: %r" % ast.dump(item), item) elif isinstance(item, ast.Tuple): members = [parse_type(x, location, custom_units=custom_units, custom_structs=custom_structs, constants=constants) for x in item.elts] return TupleType(members) else: raise InvalidTypeException("Invalid type: %r" % ast.dump(item), item)
def parse_type(item, location, sigs=None, custom_units=None): custom_units = [] if custom_units is None else custom_units sigs = {} if sigs is None else sigs # Base types, e.g. num if isinstance(item, ast.Name): if item.id in base_types: return BaseType(item.id) elif item.id in special_types: return special_types[item.id] else: raise InvalidTypeException("Invalid base type: " + item.id, item) # Units, e.g. num (1/sec) or contracts elif isinstance(item, ast.Call): # Contract_types if item.func.id == 'contract' or item.func.id == 'address': if sigs and item.args[0].id in sigs: return BaseType('address', item.args[0].id) else: raise InvalidTypeException('Invalid contract declaration') if not isinstance(item.func, ast.Name): raise InvalidTypeException("Malformed unit type:", item) base_type = item.func.id if base_type not in ('int128', 'uint256', 'decimal'): raise InvalidTypeException( "You must use int128, decimal, address, contract, \ for variable declarations and indexed for logging topics ", item) if len(item.args) == 0: raise InvalidTypeException("Malformed unit type", item) if isinstance(item.args[-1], ast.Name) and item.args[-1].id == "positional": positional = True argz = item.args[:-1] else: positional = False argz = item.args if len(argz) != 1: raise InvalidTypeException("Malformed unit type", item) # Check for uint256 to num casting if item.func.id == 'int128' and getattr(item.args[0], 'id', '') == 'uint256': return BaseType('int128', override_signature='uint256') unit = parse_unit(argz[0], custom_units=custom_units) return BaseType(base_type, unit, positional) # Subscripts elif isinstance(item, ast.Subscript): if 'value' not in vars(item.slice): raise InvalidTypeException( "Array / ByteArray access must access a single element, not a slice", item) # Fixed size lists or bytearrays, e.g. num[100] elif isinstance(item.slice.value, ast.Num): if not isinstance(item.slice.value.n, int) or item.slice.value.n <= 0: raise InvalidTypeException( "Arrays / ByteArrays must have a positive integral number of elements", item.slice.value) # ByteArray if getattr(item.value, 'id', None) == 'bytes': return ByteArrayType(item.slice.value.n) # List else: return ListType( parse_type(item.value, location, custom_units=custom_units), item.slice.value.n) # Mappings, e.g. num[address] else: if location == 'memory': raise InvalidTypeException( "No mappings allowed for in-memory types, only fixed-size arrays", item) keytype = parse_type(item.slice.value, None) if not isinstance(keytype, (BaseType, ByteArrayType)): raise InvalidTypeException( "Mapping keys must be base or bytes types", item.slice.value) return MappingType( keytype, parse_type(item.value, location, custom_units=custom_units)) # Dicts, used to represent mappings, e.g. {uint: uint}. Key must be a base type elif isinstance(item, ast.Dict): o = {} for key, value in zip(item.keys, item.values): if not isinstance(key, ast.Name) or not is_varname_valid( key.id, custom_units): raise InvalidTypeException( "Invalid member variable for struct", key) o[key.id] = parse_type(value, location, custom_units=custom_units) return StructType(o) elif isinstance(item, ast.Tuple): members = [ parse_type(x, location, custom_units=custom_units) for x in item.elts ] return TupleType(members) else: raise InvalidTypeException("Invalid type: %r" % ast.dump(item), item)
def from_definition(cls, code, sigs=None, custom_units=None, custom_structs=None, contract_def=False, constants=None, constant=False): if not custom_structs: custom_structs = {} name = code.name mem_pos = 0 valid_name, msg = is_varname_valid(name, custom_units, custom_structs, constants) if not valid_name and (not name.lower() in function_whitelist): raise FunctionDeclarationException("Function name invalid. " + msg, code) # Validate default values. for default_value in getattr(code.args, 'defaults', []): allowed_types = (ast.Num, ast.Str, ast.Bytes, ast.List, ast.NameConstant) if not isinstance(default_value, allowed_types): raise FunctionDeclarationException( "Default parameter values have to be literals.") # Determine the arguments, expects something of the form def foo(arg1: # int128, arg2: int128 ... args = [] for arg in code.args.args: # Each arg needs a type specified. typ = arg.annotation if not typ: raise InvalidTypeException("Argument must have type", arg) # Validate arg name. check_valid_varname( arg.arg, custom_units, custom_structs, constants, arg, "Argument name invalid or reserved. ", FunctionDeclarationException, ) # Check for duplicate arg name. 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, custom_structs=custom_structs, constants=constants, ) args.append( VariableRecord( arg.arg, mem_pos, parsed_type, False, defined_at=getpos(arg), )) if isinstance(parsed_type, ByteArrayLike): mem_pos += 32 else: mem_pos += get_size_of_type(parsed_type) * 32 # Apply decorators const, payable, private, public, nonreentrant_key = False, False, False, False, '' for dec in code.decorator_list: if isinstance(dec, ast.Name) and dec.id == "constant": const = True 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 elif isinstance(dec, ast.Call) and dec.func.id == "nonreentrant": if dec.args and len(dec.args) == 1 and isinstance( dec.args[0], ast.Str) and dec.args[0].s: # noqa: E501 nonreentrant_key = dec.args[0].s else: raise StructureException( "@nonreentrant decorator requires a non-empty string to use as a key.", dec) 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 and nonreentrant_key: raise StructureException( "@nonreentrant makes no sense on a @constant function.", 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, custom_structs=custom_structs, constants=constants, ) 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, custom_structs, constants) # Take the first 4 bytes of the hash of the sig to get the method ID method_id = fourbytes_to_int(keccak256(bytes(sig, 'utf-8'))[:4]) return cls(name, args, output_type, const, payable, private, nonreentrant_key, sig, method_id, custom_units, code)
def __init__(self, keytype, valuetype): if not isinstance(keytype, (BaseType, ByteArrayLike)): raise InvalidTypeException("Dictionary keys must be a base type") self.keytype = keytype self.valuetype = valuetype
def add_globals_and_events(self, item): item_attributes = {"public": False} # Make sure we have a valid variable name. if not isinstance(item.target, ast.Name): raise StructureException('Invalid global variable name', item.target) # Handle constants. if self.get_call_func_name(item) == "constant": self._constants.add_constant(item, global_ctx=self) return # Handle events. if not (self.get_call_func_name(item) == "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) elif self.get_call_func_name(item) == "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 not isinstance(item.target, ast.Name): raise StructureException( "Can only assign type to variable in top-level statement", item) # 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) check_valid_varname(key.id, self._custom_units, self._structs, self._constants, key, "Custom unit invalid.") self._custom_units.add(key.id) self._custom_units_descriptions[key.id] = value.s else: raise VariableDeclarationException( "Custom units can only be defined once", item.target) # Check if variable name is valid. # Don't move this check higher, as unit parsing has to happen first. 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 elif self.get_call_func_name(item) == "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.make_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) elif self.get_call_func_name(item) == "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, custom_structs=self._structs, constants=self._constants) 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) elif isinstance(item.annotation, (ast.Name, ast.Call, ast.Subscript)): self._globals[item.target.id] = VariableRecord( item.target.id, len(self._globals), parse_type(item.annotation, 'storage', custom_units=self._custom_units, custom_structs=self._structs, constants=self._constants), True) else: raise InvalidTypeException('Invalid global type specified', item)
def from_declaration(cls, code, global_ctx): name = code.target.id pos = 0 check_valid_varname(name, global_ctx._custom_units, global_ctx._structs, global_ctx._constants, pos=code, error_prefix="Event name invalid. ", exc=EventDeclarationException) # 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] if not isinstance(keys[i], ast.Name): raise EventDeclarationException( 'Invalid key type, expected a valid name.', keys[i], ) if not isinstance(typ, (ast.Name, ast.Call, ast.Subscript)): raise EventDeclarationException( 'Invalid event argument type.', typ) if isinstance(typ, ast.Call) and not isinstance(typ.func, ast.Name): raise EventDeclarationException( 'Invalid event argument type', typ) arg = keys[i].id arg_item = keys[i] 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: # noqa: E501 raise EventDeclarationException( "Indexed arguments are limited to 32 bytes") if topics_count > 4: raise EventDeclarationException( f"Maximum of 3 topics {topics_count - 1} given", arg, ) if not isinstance(arg, str): raise VariableDeclarationException("Argument name invalid", arg) if not typ: raise InvalidTypeException("Argument must have type", arg) check_valid_varname( arg, global_ctx._custom_units, global_ctx._structs, global_ctx._constants, pos=arg_item, error_prefix="Event argument name invalid or reserved.", ) if arg in (x.name for x in args): raise VariableDeclarationException( "Duplicate function argument name: " + arg, arg_item, ) # Can struct be logged? parsed_type = global_ctx.parse_type(typ, None) 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(keccak256(bytes(sig, 'utf-8'))) return cls(name, args, indexed_list, event_id, sig)