def call(self): from .parser import ( pack_logging_data, pack_logging_topics, external_contract_call, ) if isinstance(self.stmt.func, ast.Name): if self.stmt.func.id in stmt_dispatch_table: return stmt_dispatch_table[self.stmt.func.id](self.stmt, self.context) elif self.stmt.func.id in dispatch_table: raise StructureException("Function {} can not be called without being used.".format(self.stmt.func.id), self.stmt) else: raise StructureException("Unknown function: '{}'.".format(self.stmt.func.id), self.stmt) elif isinstance(self.stmt.func, ast.Attribute) and isinstance(self.stmt.func.value, ast.Name) and self.stmt.func.value.id == "self": return self_call.make_call(self.stmt, self.context) elif isinstance(self.stmt.func, ast.Attribute) and isinstance(self.stmt.func.value, ast.Call): contract_name = self.stmt.func.value.func.id contract_address = Expr.parse_value_expr(self.stmt.func.value.args[0], self.context) return external_contract_call(self.stmt, self.context, contract_name, contract_address, pos=getpos(self.stmt)) elif isinstance(self.stmt.func.value, ast.Attribute) and self.stmt.func.value.attr in self.context.sigs: contract_name = self.stmt.func.value.attr var = self.context.globals[self.stmt.func.value.attr] contract_address = unwrap_location(LLLnode.from_list(var.pos, typ=var.typ, location='storage', pos=getpos(self.stmt), annotation='self.' + self.stmt.func.value.attr)) return external_contract_call(self.stmt, self.context, contract_name, contract_address, pos=getpos(self.stmt)) elif isinstance(self.stmt.func.value, ast.Attribute) and self.stmt.func.value.attr in self.context.globals: contract_name = self.context.globals[self.stmt.func.value.attr].typ.unit var = self.context.globals[self.stmt.func.value.attr] contract_address = unwrap_location(LLLnode.from_list(var.pos, typ=var.typ, location='storage', pos=getpos(self.stmt), annotation='self.' + self.stmt.func.value.attr)) return external_contract_call(self.stmt, self.context, contract_name, contract_address, pos=getpos(self.stmt)) elif isinstance(self.stmt.func, ast.Attribute) and self.stmt.func.value.id == 'log': if self.stmt.func.attr not in self.context.sigs['self']: raise EventDeclarationException("Event not declared yet: %s" % self.stmt.func.attr) event = self.context.sigs['self'][self.stmt.func.attr] if len(event.indexed_list) != len(self.stmt.args): raise EventDeclarationException("%s received %s arguments but expected %s" % (event.name, len(self.stmt.args), len(event.indexed_list))) expected_topics, topics = [], [] expected_data, data = [], [] for pos, is_indexed in enumerate(event.indexed_list): if is_indexed: expected_topics.append(event.args[pos]) topics.append(self.stmt.args[pos]) else: expected_data.append(event.args[pos]) data.append(self.stmt.args[pos]) topics = pack_logging_topics(event.event_id, topics, expected_topics, self.context, pos=getpos(self.stmt)) inargs, inargsize, inargsize_node, inarg_start = pack_logging_data(expected_data, data, self.context, pos=getpos(self.stmt)) if inargsize_node is None: sz = inargsize else: sz = ['mload', inargsize_node] return LLLnode.from_list(['seq', inargs, LLLnode.from_list(["log" + str(len(topics)), inarg_start, sz] + topics, add_gas_estimate=inargsize * 10)], typ=None, pos=getpos(self.stmt)) else: raise StructureException("Unsupported operator: %r" % ast.dump(self.stmt), self.stmt)
def from_declaration(cls, code, custom_units=None, custom_structs=None): if not custom_structs: custom_structs = {} name = code.target.id pos = 0 if not is_varname_valid(name, custom_units=custom_units, custom_structs=custom_structs): 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, custom_structs): 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) # Can struct be logged? parsed_type = parse_type(typ, None, custom_units=custom_units, custom_structs=custom_structs) 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 parse_tree_to_lll(code, origcode, runtime_only=False): _contracts, _events, _defs, _globals, _custom_units = get_contracts_and_defs_and_globals(code) _names_def = [_def.name for _def in _defs] # Checks for duplicate function names if len(set(_names_def)) < len(_names_def): raise FunctionDeclarationException("Duplicate function name: %s" % [name for name in _names_def if _names_def.count(name) > 1][0]) _names_events = [_event.target.id for _event in _events] # Checks for duplicate event names if len(set(_names_events)) < len(_names_events): raise EventDeclarationException("Duplicate event name: %s" % [name for name in _names_events if _names_events.count(name) > 1][0]) # Initialization function initfunc = [_def for _def in _defs if is_initializer(_def)] # Default function defaultfunc = [_def for _def in _defs if is_default_func(_def)] # Regular functions otherfuncs = [_def for _def in _defs if not is_initializer(_def) and not is_default_func(_def)] sigs = {} external_contracts = {} # Create the main statement o = ['seq'] if _events: sigs = parse_events(sigs, _events, _custom_units) if _contracts: external_contracts = parse_external_contracts(external_contracts, _contracts) # If there is an init func... if initfunc: o.append(['seq', initializer_lll]) o.append(parse_func(initfunc[0], _globals, {**{'self': sigs}, **external_contracts}, origcode, _custom_units)) # If there are regular functions... if otherfuncs or defaultfunc: o = parse_other_functions( o, otherfuncs, _globals, sigs, external_contracts, origcode, _custom_units, defaultfunc, runtime_only ) return LLLnode.from_list(o, typ=None)
def call(self): from .parser import ( pack_logging_data, pack_logging_topics, ) if isinstance(self.stmt.func, ast.Name): if self.stmt.func.id in stmt_dispatch_table: if self.stmt.func.id == 'clear': return self._clear() else: return stmt_dispatch_table[self.stmt.func.id](self.stmt, self.context) elif self.stmt.func.id in dispatch_table: raise StructureException("Function {} can not be called without being used.".format(self.stmt.func.id), self.stmt) else: raise StructureException("Unknown function: '{}'.".format(self.stmt.func.id), self.stmt) elif isinstance(self.stmt.func, ast.Attribute) and isinstance(self.stmt.func.value, ast.Name) and self.stmt.func.value.id == "self": return self_call.make_call(self.stmt, self.context) elif isinstance(self.stmt.func, ast.Attribute) and isinstance(self.stmt.func.value, ast.Name) and self.stmt.func.value.id == 'log': if self.stmt.func.attr not in self.context.sigs['self']: raise EventDeclarationException("Event not declared yet: %s" % self.stmt.func.attr) event = self.context.sigs['self'][self.stmt.func.attr] if len(event.indexed_list) != len(self.stmt.args): raise EventDeclarationException("%s received %s arguments but expected %s" % (event.name, len(self.stmt.args), len(event.indexed_list))) expected_topics, topics = [], [] expected_data, data = [], [] for pos, is_indexed in enumerate(event.indexed_list): if is_indexed: expected_topics.append(event.args[pos]) topics.append(self.stmt.args[pos]) else: expected_data.append(event.args[pos]) data.append(self.stmt.args[pos]) topics = pack_logging_topics(event.event_id, topics, expected_topics, self.context, pos=getpos(self.stmt)) inargs, inargsize, inargsize_node, inarg_start = pack_logging_data(expected_data, data, self.context, pos=getpos(self.stmt)) if inargsize_node is None: sz = inargsize else: sz = ['mload', inargsize_node] return LLLnode.from_list(['seq', inargs, LLLnode.from_list(["log" + str(len(topics)), inarg_start, sz] + topics, add_gas_estimate=inargsize * 10)], typ=None, pos=getpos(self.stmt)) else: return external_call.make_external_call(self.stmt, self.context)
def parse_tree_to_lll(code, origcode, runtime_only=False, interface_codes=None): global_ctx = GlobalContext.get_global_context(code, interface_codes=interface_codes) _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: %s" % ( [name for name in _names_def if _names_def.count(name) > 1][0] ) ) _names_events = [_event.target.id for _event in global_ctx._events] # Checks for duplicate event names if len(set(_names_events)) < len(_names_events): raise EventDeclarationException( "Duplicate event name: %s" % ( [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 = {} external_contracts = {} # 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_contracts = parse_external_contracts(external_contracts, global_ctx) # If there is an init func... if initfunc: o.append(INITIALIZER_LLL) o.append( parse_function( initfunc[0], {**{'self': sigs}, **external_contracts}, origcode, global_ctx, ) ) # If there are regular functions... if otherfuncs or defaultfunc: o = parse_other_functions( o, otherfuncs, sigs, external_contracts, origcode, global_ctx, defaultfunc, runtime_only ) # Check if interface of contract is correct. check_valid_contract_interface(global_ctx, sigs) return LLLnode.from_list(o, typ=None)
def parse_tree_to_lll(global_ctx: GlobalContext) -> Tuple[LLLnode, LLLnode, FunctionSignatures]: _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 init_function = next((_def for _def in global_ctx._defs if is_initializer(_def)), None) # Default function default_function = next((i for i in global_ctx._defs if is_default_func(i)), None) regular_functions = [ _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: List[Union[str, LLLnode]] = ["seq"] if global_ctx._contracts or global_ctx._interfaces: external_interfaces = parse_external_interfaces(external_interfaces, global_ctx) # TODO: fix for #2251 is to move this after parse_regular_functions if init_function: o.append(init_func_init_lll()) init_func_lll, _frame_start, _frame_size = generate_lll_for_function( init_function, {**{"self": sigs}, **external_interfaces}, global_ctx, False, ) o.append(init_func_lll) if regular_functions or default_function: o, runtime = parse_regular_functions( o, regular_functions, sigs, external_interfaces, global_ctx, default_function, ) else: runtime = o.copy() return LLLnode.from_list(o), LLLnode.from_list(runtime), sigs
def from_EventDef(cls, base_node: vy_ast.EventDef) -> "Event": """ Generate an `Event` object from a Vyper ast node. Arguments --------- base_node : EventDef Vyper ast node defining the event Returns ------- Event """ members: OrderedDict = OrderedDict() indexed: List = [] if len(base_node.body) == 1 and isinstance(base_node.body[0], vy_ast.Pass): return Event(base_node.name, members, indexed) for node in base_node.body: if not isinstance(node, vy_ast.AnnAssign): raise StructureException( "Events can only contain variable definitions", node) if node.value is not None: raise StructureException( "Cannot assign a value during event declaration", node) if not isinstance(node.target, vy_ast.Name): raise StructureException( "Invalid syntax for event member name", node.target) member_name = node.target.id if member_name in members: raise NamespaceCollision( f"Event member '{member_name}' has already been declared", node.target) annotation = node.annotation if isinstance( annotation, vy_ast.Call) and annotation.get("func.id") == "indexed": validate_call_args(annotation, 1) if indexed.count(True) == 3: raise EventDeclarationException( "Event cannot have more than three indexed arguments", annotation) indexed.append(True) annotation = annotation.args[0] else: indexed.append(False) members[member_name] = get_type_from_annotation( annotation, DataLocation.UNSET) return Event(base_node.name, members, indexed)
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 parse_tree_to_lll(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) ] # check if any functions in the contract are payable - if not, we do a single # ASSERT CALLVALUE ISZERO at the start of the bytecode rather than at the start # of each function is_contract_payable = next( (True for i in global_ctx._defs if FunctionSignature.from_definition( i, custom_structs=global_ctx._structs).mutability == "payable"), False, ) sigs: dict = {} external_interfaces: dict = {} # Create the main statement o = ["seq"] 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 }, global_ctx, False, )) # If there are regular functions... if otherfuncs or defaultfunc: o, runtime = parse_other_functions( o, otherfuncs, sigs, external_interfaces, global_ctx, defaultfunc, is_contract_payable, ) else: runtime = o.copy() if not is_contract_payable: # if no functions in the contract are payable, assert that callvalue is # zero at the beginning of the bytecode runtime.insert(1, ["assert", ["iszero", "callvalue"]]) return LLLnode.from_list(o, typ=None), LLLnode.from_list(runtime, typ=None)
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 parse_tree_to_ir( global_ctx: GlobalContext ) -> Tuple[IRnode, IRnode, FunctionSignatures]: _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 init_function = next( (_def for _def in global_ctx._defs if is_initializer(_def)), None) # Default function default_function = next( (i for i in global_ctx._defs if is_default_func(i)), None) regular_functions = [ _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: List[Union[str, IRnode]] = ["seq"] if global_ctx._contracts or global_ctx._interfaces: external_interfaces = parse_external_interfaces( external_interfaces, global_ctx) init_func_ir = None if init_function: init_func_ir, _frame_start, init_frame_size = generate_ir_for_function( init_function, { **{ "self": sigs }, **external_interfaces }, global_ctx, False, ) o.append(init_func_ir) if regular_functions or default_function: runtime = parse_regular_functions( regular_functions, sigs, external_interfaces, global_ctx, default_function, init_func_ir, ) else: # for some reason, somebody may want to deploy a contract with no code, # or more likely, a "pure data" contract which contains immutables runtime = IRnode.from_list(["seq"]) immutables_len = global_ctx.immutable_section_bytes if init_function: memsize = init_func_ir.context.memory_allocator.size_of_mem # type: ignore else: memsize = 0 # note: (deploy mem_ofst, code, extra_padding) o.append(["deploy", memsize, runtime, immutables_len]) # type: ignore return IRnode.from_list(o), IRnode.from_list(runtime), sigs
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 call(self): from .parser import ( pack_arguments, pack_logging_data, pack_logging_topics, external_contract_call, ) if isinstance(self.stmt.func, ast.Name): if self.stmt.func.id in stmt_dispatch_table: return stmt_dispatch_table[self.stmt.func.id](self.stmt, self.context) elif self.stmt.func.id in dispatch_table: raise StructureException("Function {} can not be called without being used.".format(self.stmt.func.id), self.stmt) else: raise StructureException("Unknown function: '{}'.".format(self.stmt.func.id), self.stmt) elif isinstance(self.stmt.func, ast.Attribute) and isinstance(self.stmt.func.value, ast.Name) and self.stmt.func.value.id == "self": method_name = self.stmt.func.attr expr_args = [Expr(arg, self.context).lll_node for arg in self.stmt.args] # full_sig = FunctionSignature.get_full_sig(method_name, expr_args, self.context.sigs, self.context.custom_units) sig = FunctionSignature.lookup_sig(self.context.sigs, method_name, expr_args, self.stmt, self.context) if self.context.is_constant and not sig.const: raise ConstancyViolationException( "May not call non-constant function '%s' within a constant function." % (sig.sig) ) add_gas = self.context.sigs['self'][sig.sig].gas inargs, inargsize = pack_arguments(sig, expr_args, self.context, pos=getpos(self.stmt)) return LLLnode.from_list(['assert', ['call', ['gas'], ['address'], 0, inargs, inargsize, 0, 0]], typ=None, pos=getpos(self.stmt), add_gas_estimate=add_gas, annotation='Internal Call: %s' % sig.sig) elif isinstance(self.stmt.func, ast.Attribute) and isinstance(self.stmt.func.value, ast.Call): contract_name = self.stmt.func.value.func.id contract_address = Expr.parse_value_expr(self.stmt.func.value.args[0], self.context) return external_contract_call(self.stmt, self.context, contract_name, contract_address, pos=getpos(self.stmt)) elif isinstance(self.stmt.func.value, ast.Attribute) and self.stmt.func.value.attr in self.context.sigs: contract_name = self.stmt.func.value.attr var = self.context.globals[self.stmt.func.value.attr] contract_address = unwrap_location(LLLnode.from_list(var.pos, typ=var.typ, location='storage', pos=getpos(self.stmt), annotation='self.' + self.stmt.func.value.attr)) return external_contract_call(self.stmt, self.context, contract_name, contract_address, pos=getpos(self.stmt)) elif isinstance(self.stmt.func.value, ast.Attribute) and self.stmt.func.value.attr in self.context.globals: contract_name = self.context.globals[self.stmt.func.value.attr].typ.unit var = self.context.globals[self.stmt.func.value.attr] contract_address = unwrap_location(LLLnode.from_list(var.pos, typ=var.typ, location='storage', pos=getpos(self.stmt), annotation='self.' + self.stmt.func.value.attr)) return external_contract_call(self.stmt, self.context, contract_name, contract_address, pos=getpos(self.stmt)) elif isinstance(self.stmt.func, ast.Attribute) and self.stmt.func.value.id == 'log': if self.stmt.func.attr not in self.context.sigs['self']: raise EventDeclarationException("Event not declared yet: %s" % self.stmt.func.attr) event = self.context.sigs['self'][self.stmt.func.attr] if len(event.indexed_list) != len(self.stmt.args): raise EventDeclarationException("%s received %s arguments but expected %s" % (event.name, len(self.stmt.args), len(event.indexed_list))) expected_topics, topics = [], [] expected_data, data = [], [] for pos, is_indexed in enumerate(event.indexed_list): if is_indexed: expected_topics.append(event.args[pos]) topics.append(self.stmt.args[pos]) else: expected_data.append(event.args[pos]) data.append(self.stmt.args[pos]) topics = pack_logging_topics(event.event_id, topics, expected_topics, self.context, pos=getpos(self.stmt)) inargs, inargsize, inargsize_node, inarg_start = pack_logging_data(expected_data, data, self.context, pos=getpos(self.stmt)) if inargsize_node is None: sz = inargsize else: sz = ['mload', inargsize_node] return LLLnode.from_list(['seq', inargs, LLLnode.from_list(["log" + str(len(topics)), inarg_start, sz] + topics, add_gas_estimate=inargsize * 10)], typ=None, pos=getpos(self.stmt)) else: raise StructureException("Unsupported operator: %r" % ast.dump(self.stmt), self.stmt)
def call(self): is_self_function = (isinstance( self.stmt.func, ast.Attribute)) and isinstance( self.stmt.func.value, ast.Name) and self.stmt.func.value.id == "self" is_log_call = (isinstance( self.stmt.func, ast.Attribute)) and isinstance( self.stmt.func.value, ast.Name) and self.stmt.func.value.id == 'log' if isinstance(self.stmt.func, ast.Name): if self.stmt.func.id in STMT_DISPATCH_TABLE: if self.stmt.func.id == 'clear': return self._clear() else: return STMT_DISPATCH_TABLE[self.stmt.func.id](self.stmt, self.context) elif self.stmt.func.id in DISPATCH_TABLE: raise StructureException( f"Function {self.stmt.func.id} can not be called without being used.", self.stmt, ) else: raise StructureException( f"Unknown function: '{self.stmt.func.id}'.", self.stmt, ) elif is_self_function: return self_call.make_call(self.stmt, self.context) elif is_log_call: if self.stmt.func.attr not in self.context.sigs['self']: raise EventDeclarationException( f"Event not declared yet: {self.stmt.func.attr}") event = self.context.sigs['self'][self.stmt.func.attr] if len(event.indexed_list) != len(self.stmt.args): raise EventDeclarationException( f"{event.name} received {len(self.stmt.args)} arguments but " f"expected {len(event.indexed_list)}") expected_topics, topics = [], [] expected_data, data = [], [] for pos, is_indexed in enumerate(event.indexed_list): if is_indexed: expected_topics.append(event.args[pos]) topics.append(self.stmt.args[pos]) else: expected_data.append(event.args[pos]) data.append(self.stmt.args[pos]) topics = pack_logging_topics( event.event_id, topics, expected_topics, self.context, pos=getpos(self.stmt), ) inargs, inargsize, inargsize_node, inarg_start = pack_logging_data( expected_data, data, self.context, pos=getpos(self.stmt), ) if inargsize_node is None: sz = inargsize else: sz = ['mload', inargsize_node] return LLLnode.from_list([ 'seq', inargs, LLLnode.from_list( ["log" + str(len(topics)), inarg_start, sz] + topics, add_gas_estimate=inargsize * 10, ) ], typ=None, pos=getpos(self.stmt)) else: return external_call.make_external_call(self.stmt, self.context)
def parse_tree_to_lll( code, origcode, runtime_only=False, interface_codes=None, ): global_ctx = GlobalContext.get_global_context(code, interface_codes) _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: %s" % [name for name in _names_def if _names_def.count(name) > 1][0]) _names_events = [_event.target.id for _event in global_ctx._events] # Checks for duplicate event names if len(set(_names_events)) < len(_names_events): raise EventDeclarationException( "Duplicate event name: %s" % [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 = {} external_contracts = {} # Create the main statement o = ['seq'] if global_ctx._events: sigs = parse_events(sigs, global_ctx) if global_ctx._contracts: external_contracts = parse_external_contracts(external_contracts, global_ctx._contracts, global_ctx._structs, global_ctx._constants) # If there is an init func... if initfunc: o.append(['seq', initializer_lll]) o.append( parse_func(initfunc[0], { **{ 'self': sigs }, **external_contracts }, origcode, global_ctx)) # If there are regular functions... if otherfuncs or defaultfunc: o = parse_other_functions(o, otherfuncs, sigs, external_contracts, origcode, global_ctx, defaultfunc, runtime_only) # Check interface. if global_ctx._interface: funcs_left = global_ctx._interface.copy() for sig, func_sig in sigs.items(): if isinstance(func_sig, FunctionSignature): if sig in funcs_left and not func_sig.private: del funcs_left[sig] if isinstance(func_sig, EventSignature) and func_sig.sig in funcs_left: del funcs_left[func_sig.sig] if funcs_left: error_message = 'Contract does not comply to supplied Interface(s).\n' missing_functions = [ sig_name for sig_name, func_sig in funcs_left.items() if isinstance(func_sig, FunctionSignature) ] missing_events = [ sig_name for sig_name, func_sig in funcs_left.items() if isinstance(func_sig, EventSignature) ] if missing_functions: error_message += 'Missing interface functions:\n\t{}'.format( '\n\t'.join(missing_functions)) if missing_events: error_message += 'Missing interface events:\n\t{}'.format( '\n\t'.join(missing_events)) raise StructureException(error_message) return LLLnode.from_list(o, typ=None)
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)
def add_globals_and_events(_custom_units, _contracts, _defs, _events, _getters, _globals, item): item_attributes = {"public": False} if not (isinstance(item.annotation, ast.Call) and item.annotation.func.id == "event"): item_name, item_attributes = 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 isinstance(item.annotation, ast.Call) and item.annotation.func.id == "event": if _globals or len(_defs): raise EventDeclarationException("Events must all come before global declarations and function definitions", item) _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 _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 unquoted string.", key) if key.id in _custom_units: raise VariableDeclarationException("Custom unit may only be defined once", key) if not is_varname_valid(key.id, custom_units=_custom_units): raise VariableDeclarationException("Custom unit may not be a reserved keyword", key) _custom_units.append(key.id) else: raise VariableDeclarationException("Can units can only defined once.", item.target) # Check if variable name is reserved or invalid elif not is_varname_valid(item.target.id, custom_units=_custom_units): raise VariableDeclarationException("Variable name invalid or reserved: ", item.target) # Check if global already exists, if so error elif item.target.id in _globals: raise VariableDeclarationException("Cannot declare a persistent variable twice!", item.target) elif len(_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 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] _contracts[item.target.id] = add_contract(premade_contract.body) _globals[item.target.id] = VariableRecord(item.target.id, len(_globals), BaseType('address'), True) elif item_name in _contracts: _globals[item.target.id] = ContractRecord(item.target.id, len(_globals), ContractType(item_name), True) if item_attributes["public"]: typ = ContractType(item_name) for getter in mk_getter(item.target.id, typ): _getters.append(parse_line('\n' * (item.lineno - 1) + getter)) _getters[-1].pos = getpos(item) elif isinstance(item.annotation, ast.Call) and item.annotation.func.id == "public": if isinstance(item.annotation.args[0], ast.Name) and item_name in _contracts: typ = ContractType(item_name) else: typ = parse_type(item.annotation.args[0], 'storage', custom_units=_custom_units) _globals[item.target.id] = VariableRecord(item.target.id, len(_globals), typ, True) # Adding getters here for getter in mk_getter(item.target.id, typ): _getters.append(parse_line('\n' * (item.lineno - 1) + getter)) _getters[-1].pos = getpos(item) else: _globals[item.target.id] = VariableRecord( item.target.id, len(_globals), parse_type(item.annotation, 'storage', custom_units=_custom_units), True ) return _custom_units, _contracts, _events, _globals, _getters