def external_contract_call(node, context, contract_name, contract_address, pos, value=None, gas=None): if value is None: value = 0 if gas is None: gas = 'gas' if contract_name not in context.sigs: raise VariableDeclarationException("Contract not declared yet: %s" % contract_name) method_name = node.func.attr if method_name not in context.sigs[contract_name]: raise FunctionDeclarationException( "Function not declared yet: %s (reminder: " "function must be declared in the correct contract)" % method_name, pos) sig = context.sigs[contract_name][method_name] inargs, inargsize, _ = pack_arguments( sig, [parse_expr(arg, context) for arg in node.args], context, pos=pos) output_placeholder, output_size, returner = get_external_contract_call_output( sig, context) sub = [ 'seq', ['assert', ['extcodesize', contract_address]], ['assert', ['ne', 'address', contract_address]] ] if context.is_constant or sig.const: sub.append([ 'assert', [ 'staticcall', gas, contract_address, inargs, inargsize, output_placeholder, output_size ] ]) else: sub.append([ 'assert', [ 'call', gas, contract_address, value, inargs, inargsize, output_placeholder, output_size ] ]) sub.extend(returner) o = LLLnode.from_list(sub, typ=sig.output_type, location='memory', pos=getpos(node)) return o
def assign(self): # Assignment (e.g. x[4] = y) if len(self.stmt.targets) != 1: raise StructureException( "Assignment statement must have one target", self.stmt) self.context.set_in_assignment(True) sub = Expr(self.stmt.value, self.context).lll_node # Determine if it's an RLPList assignment. if isinstance(self.stmt.value, ast.Call) and getattr( self.stmt.value.func, 'id', '') is 'RLPList': pos = self.context.new_variable(self.stmt.targets[0].id, sub.typ) variable_loc = LLLnode.from_list( pos, typ=sub.typ, location='memory', pos=getpos(self.stmt), annotation=self.stmt.targets[0].id) o = make_setter(variable_loc, sub, 'memory', pos=getpos(self.stmt)) # All other assignments are forbidden. elif isinstance( self.stmt.targets[0], ast.Name) and self.stmt.targets[0].id not in self.context.vars: raise VariableDeclarationException("Variable type not defined", self.stmt) elif isinstance(self.stmt.targets[0], ast.Tuple) and isinstance( self.stmt.value, ast.Tuple): raise VariableDeclarationException( "Tuple to tuple assignment not supported", self.stmt) else: # Checks to see if assignment is valid target = self.get_target(self.stmt.targets[0]) o = make_setter(target, sub, target.location, pos=getpos(self.stmt)) o.pos = getpos(self.stmt) self.context.set_in_assignment(False) return o
def variables(self): if self.expr.id == 'self': return LLLnode.from_list(['address'], typ='address', pos=getpos(self.expr)) if self.expr.id == 'true': return LLLnode.from_list(1, typ='bool', pos=getpos(self.expr)) if self.expr.id == 'false': return LLLnode.from_list(0, typ='bool', pos=getpos(self.expr)) if self.expr.id == 'null': return LLLnode.from_list(None, typ=NullType(), pos=getpos(self.expr)) if self.expr.id in self.context.vars: var = self.context.vars[self.expr.id] return LLLnode.from_list(var.pos, typ=var.typ, location='memory', pos=getpos(self.expr), annotation=self.expr.id, mutable=var.mutable) else: raise VariableDeclarationException("Undeclared variable: " + self.expr.id, self.expr)
def variables(self): builtin_constants = { 'EMPTY_BYTES32': LLLnode.from_list( [0], typ=BaseType('bytes32', None, is_literal=True), pos=getpos(self.expr) ), 'ZERO_ADDRESS': LLLnode.from_list( [0], typ=BaseType('address', None, is_literal=True), pos=getpos(self.expr) ), 'MAX_INT128': LLLnode.from_list( [SizeLimits.MAXNUM], typ=BaseType('int128', None, is_literal=True), pos=getpos(self.expr) ), 'MIN_INT128': LLLnode.from_list( [SizeLimits.MINNUM], typ=BaseType('int128', None, is_literal=True), pos=getpos(self.expr) ), 'MAX_DECIMAL': LLLnode.from_list( [SizeLimits.MAXDECIMAL], typ=BaseType('decimal', None, is_literal=True), pos=getpos(self.expr) ), 'MIN_DECIMAL': LLLnode.from_list( [SizeLimits.MINDECIMAL], typ=BaseType('decimal', None, is_literal=True), pos=getpos(self.expr) ), 'MAX_UINT256': LLLnode.from_list( [SizeLimits.MAX_UINT256], typ=BaseType('uint256', None, is_literal=True), pos=getpos(self.expr) ), } if self.expr.id == 'self': return LLLnode.from_list(['address'], typ='address', pos=getpos(self.expr)) elif self.expr.id in self.context.vars: var = self.context.vars[self.expr.id] return LLLnode.from_list(var.pos, typ=var.typ, location='memory', pos=getpos(self.expr), annotation=self.expr.id, mutable=var.mutable) elif self.expr.id in builtin_constants: return builtin_constants[self.expr.id] elif self.context.constants.ast_is_constant(self.expr): return self.context.constants.get_constant(self.expr.id, self.context) else: raise VariableDeclarationException("Undeclared variable: " + self.expr.id, self.expr)
def is_valid_varname(self, name, pos): # Global context check first. if self.global_ctx.is_valid_varname(name, pos): check_valid_varname(name, custom_units=self.custom_units, custom_structs=self.structs, constants=self.constants, pos=pos) # Local context duplicate context check. if any((name in self.vars, name in self.globals, name in self.constants)): raise VariableDeclarationException( "Duplicate variable name: %s" % name, name) return True
def parse_external_contracts(external_contracts, _contracts): for _contractname in _contracts: _contract_defs = _contracts[_contractname] _defnames = [_def.name for _def in _contract_defs] contract = {} if len(set(_defnames)) < len(_contract_defs): raise VariableDeclarationException( "Duplicate function name: %s" % [name for name in _defnames if _defnames.count(name) > 1][0]) for _def in _contract_defs: sig = FunctionSignature.from_definition(_def) contract[sig.name] = sig external_contracts[_contractname] = contract return external_contracts
def visit_AnnAssign(self, node): if not node.value: raise VariableDeclarationException( "Memory variables must be declared with an initial value", node ) name = node.target.id if name in self.namespace["self"].members: raise NamespaceCollision("Variable name shadows an existing storage-scoped value", node) type_definition = get_type_from_annotation(node.annotation, DataLocation.MEMORY) validate_expected_type(node.value, type_definition) try: self.namespace[name] = type_definition except VyperException as exc: raise exc.with_annotation(node) from None
def get_constant(self, const_name, context): """ Return unrolled const """ # check if value is compatible with const = self._constants[const_name] if isinstance(const, ast.AnnAssign): # Handle ByteArrays. if context: expr = Expr(const.value, context).lll_node return expr else: raise VariableDeclarationException( "ByteArray: Can not be used outside of a function context: %s" % const_name) # Other types are already unwrapped, no need return self._constants[const_name]
def variables(self): constants = { 'ZERO_ADDRESS': LLLnode.from_list([0], typ=BaseType('address', None, is_literal=True), pos=getpos(self.expr)), 'MAX_INT128': LLLnode.from_list(['mload', MemoryPositions.MAXNUM], typ=BaseType('int128', None, is_literal=True), pos=getpos(self.expr)), 'MIN_INT128': LLLnode.from_list(['mload', MemoryPositions.MINNUM], typ=BaseType('int128', None, is_literal=True), pos=getpos(self.expr)), 'MAX_DECIMAL': LLLnode.from_list(['mload', MemoryPositions.MAXDECIMAL], typ=BaseType('decimal', None, is_literal=True), pos=getpos(self.expr)), 'MIN_DECIMAL': LLLnode.from_list(['mload', MemoryPositions.MINDECIMAL], typ=BaseType('decimal', None, is_literal=True), pos=getpos(self.expr)), 'MAX_UINT256': LLLnode.from_list([2**256 - 1], typ=BaseType('uint256', None, is_literal=True), pos=getpos(self.expr)), } if self.expr.id == 'self': return LLLnode.from_list(['address'], typ='address', pos=getpos(self.expr)) elif self.expr.id in self.context.vars: var = self.context.vars[self.expr.id] return LLLnode.from_list(var.pos, typ=var.typ, location='memory', pos=getpos(self.expr), annotation=self.expr.id, mutable=var.mutable) elif self.expr.id in constants: return constants[self.expr.id] else: raise VariableDeclarationException( "Undeclared variable: " + self.expr.id, self.expr)
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.name for _def in _defs] + [_event.target.id for _event in _events] # Checks for duplicate function / event names if len(set(_names)) < len(_names): raise VariableDeclarationException( "Duplicate function or event name: %s" % [name for name in _names if _names.count(name) > 1][0]) # Initialization function initfunc = [_def for _def in _defs if is_initializer(_def)] # Regular functions otherfuncs = [_def for _def in _defs if not is_initializer(_def)] sigs = {} external_contracts = {} # Create the main statement o = ['seq'] if _events: sigs = parse_events(sigs, _events) 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: o = parse_other_functions(o, otherfuncs, _globals, sigs, external_contracts, origcode, _custom_units, runtime_only) return LLLnode.from_list(o, typ=None)
def assign(self): # Assignment (e.g. x[4] = y) if len(self.stmt.targets) != 1: raise StructureException( "Assignment statement must have one target", self.stmt) with self.context.assignment_scope(): sub = Expr(self.stmt.value, self.context).lll_node # Disallow assignment to None if isinstance(sub.typ, NullType): raise InvalidLiteralException( ('Assignment to None is not allowed, use a default value ' 'or built-in `clear()`.'), self.stmt, ) is_valid_rlp_list_assign = (isinstance( self.stmt.value, ast.Call)) and getattr( self.stmt.value.func, 'id', '') == 'RLPList' # Determine if it's an RLPList assignment. if is_valid_rlp_list_assign: pos = self.context.new_variable(self.stmt.targets[0].id, sub.typ) variable_loc = LLLnode.from_list( pos, typ=sub.typ, location='memory', pos=getpos(self.stmt), annotation=self.stmt.targets[0].id, ) o = make_setter(variable_loc, sub, 'memory', pos=getpos(self.stmt)) else: # Error check when assigning to declared variable if isinstance(self.stmt.targets[0], ast.Name): # Do not allow assignment to undefined variables without annotation if self.stmt.targets[0].id not in self.context.vars: raise VariableDeclarationException( "Variable type not defined", self.stmt) # Check against implicit conversion self._check_implicit_conversion(self.stmt.targets[0].id, sub) is_valid_tuple_assign = (isinstance( self.stmt.targets[0], ast.Tuple)) and isinstance( self.stmt.value, ast.Tuple) # Do no allow tuple-to-tuple assignment if is_valid_tuple_assign: raise VariableDeclarationException( "Tuple to tuple assignment not supported", self.stmt, ) # Checks to see if assignment is valid target = self.get_target(self.stmt.targets[0]) if isinstance(target.typ, ContractType) and not isinstance( sub.typ, ContractType): raise TypeMismatchException( 'Contract assignment expects casted address: ' f'{target.typ.unit}(<address_var>)', self.stmt) o = make_setter(target, sub, target.location, pos=getpos(self.stmt)) o.pos = getpos(self.stmt) return o
def attribute(self): # x.balance: balance of address x if self.expr.attr == 'balance': addr = Expr.parse_value_expr(self.expr.value, self.context) if not is_base_type(addr.typ, 'address'): raise TypeMismatch( "Type mismatch: balance keyword expects an address as input", self.expr) if (isinstance(self.expr.value, vy_ast.Name) and self.expr.value.id == "self" and version_check(begin="istanbul")): seq = ['selfbalance'] else: seq = ['balance', addr] return LLLnode.from_list( seq, typ=BaseType('uint256'), location=None, pos=getpos(self.expr), ) # x.codesize: codesize of address x elif self.expr.attr == 'codesize' or self.expr.attr == 'is_contract': addr = Expr.parse_value_expr(self.expr.value, self.context) if not is_base_type(addr.typ, 'address'): raise TypeMismatch( "Type mismatch: codesize keyword expects an address as input", self.expr, ) if self.expr.attr == 'codesize': eval_code = ['extcodesize', addr] output_type = 'int128' else: eval_code = ['gt', ['extcodesize', addr], 0] output_type = 'bool' return LLLnode.from_list( eval_code, typ=BaseType(output_type), location=None, pos=getpos(self.expr), ) # x.codehash: keccak of address x elif self.expr.attr == 'codehash': addr = Expr.parse_value_expr(self.expr.value, self.context) if not is_base_type(addr.typ, 'address'): raise TypeMismatch( "codehash keyword expects an address as input", self.expr, ) if not version_check(begin="constantinople"): raise EvmVersionException( "address.codehash is unavailable prior to constantinople ruleset", self.expr) return LLLnode.from_list(['extcodehash', addr], typ=BaseType('bytes32'), location=None, pos=getpos(self.expr)) # self.x: global attribute elif isinstance(self.expr.value, vy_ast.Name) and self.expr.value.id == "self": if self.expr.attr not in self.context.globals: raise VariableDeclarationException( "Persistent variable undeclared: " + self.expr.attr, self.expr, ) var = self.context.globals[self.expr.attr] return LLLnode.from_list( var.pos, typ=var.typ, location='storage', pos=getpos(self.expr), annotation='self.' + self.expr.attr, ) # Reserved keywords elif (isinstance(self.expr.value, vy_ast.Name) and self.expr.value.id in ENVIRONMENT_VARIABLES): key = self.expr.value.id + "." + self.expr.attr if key == "msg.sender": if self.context.is_private: raise StructureException( "msg.sender not allowed in private functions.", self.expr) return LLLnode.from_list(['caller'], typ='address', pos=getpos(self.expr)) elif key == "msg.value": if not self.context.is_payable: raise NonPayableViolation( "Cannot use msg.value in a non-payable function", self.expr, ) return LLLnode.from_list( ['callvalue'], typ=BaseType('uint256'), pos=getpos(self.expr), ) elif key == "msg.gas": return LLLnode.from_list( ['gas'], typ='uint256', pos=getpos(self.expr), ) elif key == "block.difficulty": return LLLnode.from_list( ['difficulty'], typ='uint256', pos=getpos(self.expr), ) elif key == "block.timestamp": return LLLnode.from_list( ['timestamp'], typ=BaseType('uint256'), pos=getpos(self.expr), ) elif key == "block.coinbase": return LLLnode.from_list(['coinbase'], typ='address', pos=getpos(self.expr)) elif key == "block.number": return LLLnode.from_list(['number'], typ='uint256', pos=getpos(self.expr)) elif key == "block.prevhash": return LLLnode.from_list( ['blockhash', ['sub', 'number', 1]], typ='bytes32', pos=getpos(self.expr), ) elif key == "tx.origin": return LLLnode.from_list(['origin'], typ='address', pos=getpos(self.expr)) elif key == "chain.id": if not version_check(begin="istanbul"): raise EvmVersionException( "chain.id is unavailable prior to istanbul ruleset", self.expr) return LLLnode.from_list(['chainid'], typ='uint256', pos=getpos(self.expr)) else: raise StructureException("Unsupported keyword: " + key, self.expr) # Other variables else: sub = Expr.parse_variable_location(self.expr.value, self.context) # contract type if isinstance(sub.typ, ContractType): return sub if not isinstance(sub.typ, StructType): raise TypeMismatch( "Type mismatch: member variable access not expected", self.expr.value, ) attrs = list(sub.typ.members.keys()) if self.expr.attr not in attrs: raise TypeMismatch( f"Member {self.expr.attr} not found. Only the following available: " f"{' '.join(attrs)}", self.expr, ) return add_variable_offset(sub, self.expr.attr, pos=getpos(self.expr))
def add_globals_and_events(_contracts, _defs, _events, _getters, _globals, 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 StructureException( "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) # Check if variable name is reserved or invalid elif not is_varname_valid(item.target.id): 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 len(item.annotation.args) != 1: raise StructureException("Address expects one arg (the type)") 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 isinstance(item, ast.AnnAssign) and isinstance( item.annotation, ast.Name) and item.annotation.id in _contracts: _globals[item.target.id] = VariableRecord( item.target.id, len(_globals), BaseType('address', item.annotation.id), True) elif isinstance(item.annotation, ast.Call) and item.annotation.func.id == "public": if len(item.annotation.args) != 1: raise StructureException("Public expects one arg (the type)") if isinstance(item.annotation.args[0], ast.Name) and item.annotation.args[0].id in _contracts: typ = BaseType('address', item.annotation.args[0].id) else: typ = parse_type(item.annotation.args[0], 'storage') _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'), True) return _contracts, _events, _globals, _getters
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 visit_AnnAssign(self, node): name = node.get("target.id") if name is None: raise VariableDeclarationException( "Invalid module-level assignment", node) if name == "implements": interface_name = node.annotation.id self.namespace[interface_name].validate_implements(node) return is_immutable, is_public = False, False annotation = node.annotation if isinstance(annotation, vy_ast.Call): # the annotation is a function call, e.g. `foo: constant(uint256)` call_name = annotation.get("func.id") if call_name in ("constant", "public"): validate_call_args(annotation, 1) if call_name == "constant": # declaring a constant is_immutable = True elif call_name == "public": # declaring a public variable is_public = True # generate function type and add to metadata # we need this when builing the public getter node._metadata[ "func_type"] = ContractFunction.from_AnnAssign(node) # remove the outer call node, to handle cases such as `public(map(..))` annotation = annotation.args[0] type_definition = get_type_from_annotation(annotation, DataLocation.STORAGE, is_immutable, is_public) node._metadata["type"] = type_definition if is_immutable: if not node.value: raise VariableDeclarationException( "Constant must be declared with a value", node) if not check_literal(node.value): raise StateAccessViolation("Value must be a literal", node.value) validate_expected_type(node.value, type_definition) try: self.namespace[name] = type_definition except VyperException as exc: raise exc.with_annotation(node) from None return if node.value: raise VariableDeclarationException( "Storage variables cannot have an initial value", node.value) try: self.namespace.validate_assignment(name) except NamespaceCollision as exc: raise exc.with_annotation(node) from None try: self.namespace["self"].add_member(name, type_definition) node.target._metadata["type"] = type_definition except NamespaceCollision: raise NamespaceCollision( f"Value '{name}' has already been declared", node) from None except VyperException as exc: raise exc.with_annotation(node) from None
def attribute(self): # x.balance: balance of address x if self.expr.attr == 'balance': addr = Expr.parse_value_expr(self.expr.value, self.context) if not is_base_type(addr.typ, 'address'): raise TypeMismatchException( "Type mismatch: balance keyword expects an address as input", self.expr) return LLLnode.from_list(['balance', addr], typ=BaseType('uint256', {'wei': 1}), location=None, pos=getpos(self.expr)) # x.codesize: codesize of address x elif self.expr.attr == 'codesize' or self.expr.attr == 'is_contract': addr = Expr.parse_value_expr(self.expr.value, self.context) if not is_base_type(addr.typ, 'address'): raise TypeMismatchException( "Type mismatch: codesize keyword expects an address as input", self.expr) if self.expr.attr == 'codesize': eval_code = ['extcodesize', addr] output_type = 'int128' else: eval_code = ['gt', ['extcodesize', addr], 0] output_type = 'bool' return LLLnode.from_list(eval_code, typ=BaseType(output_type), location=None, pos=getpos(self.expr)) # self.x: global attribute elif isinstance(self.expr.value, ast.Name) and self.expr.value.id == "self": if self.expr.attr not in self.context.globals: raise VariableDeclarationException( "Persistent variable undeclared: " + self.expr.attr, self.expr) var = self.context.globals[self.expr.attr] return LLLnode.from_list(var.pos, typ=var.typ, location='storage', pos=getpos(self.expr), annotation='self.' + self.expr.attr) # Reserved keywords elif isinstance( self.expr.value, ast.Name) and self.expr.value.id in ("msg", "block", "tx"): key = self.expr.value.id + "." + self.expr.attr if key == "msg.sender": return LLLnode.from_list(['caller'], typ='address', pos=getpos(self.expr)) elif key == "msg.value": if not self.context.is_payable: raise NonPayableViolationException( "Cannot use msg.value in a non-payable function", self.expr) return LLLnode.from_list(['callvalue'], typ=BaseType('uint256', {'wei': 1}), pos=getpos(self.expr)) elif key == "msg.gas": return LLLnode.from_list(['gas'], typ='uint256', pos=getpos(self.expr)) elif key == "block.difficulty": return LLLnode.from_list(['difficulty'], typ='uint256', pos=getpos(self.expr)) elif key == "block.timestamp": return LLLnode.from_list(['timestamp'], typ=BaseType('uint256', {'sec': 1}, True), pos=getpos(self.expr)) elif key == "block.coinbase": return LLLnode.from_list(['coinbase'], typ='address', pos=getpos(self.expr)) elif key == "block.number": return LLLnode.from_list(['number'], typ='uint256', pos=getpos(self.expr)) elif key == "block.prevhash": return LLLnode.from_list(['blockhash', ['sub', 'number', 1]], typ='bytes32', pos=getpos(self.expr)) elif key == "tx.origin": return LLLnode.from_list(['origin'], typ='address', pos=getpos(self.expr)) else: raise Exception("Unsupported keyword: " + key) # Other variables else: sub = Expr.parse_variable_location(self.expr.value, self.context) # contract type if isinstance(sub.typ, ContractType): return sub if not isinstance(sub.typ, StructType): raise TypeMismatchException( "Type mismatch: member variable access not expected", self.expr.value) attrs = sorted(sub.typ.members.keys()) if self.expr.attr not in attrs: raise TypeMismatchException( "Member %s not found. Only the following available: %s" % (self.expr.attr, " ".join(attrs)), self.expr) return add_variable_offset(sub, self.expr.attr, pos=getpos(self.expr))
def external_contract_call(node, context, contract_name, contract_address, pos, value=None, gas=None): from vyper.parser.expr import ( Expr, ) if value is None: value = 0 if gas is None: gas = 'gas' if not contract_name: raise StructureException( f'Invalid external contract call "{node.func.attr}".', node) if contract_name not in context.sigs: raise VariableDeclarationException( f'Contract "{contract_name}" not declared yet', node) if contract_address.value == "address": raise StructureException(f"External calls to self are not permitted.", node) method_name = node.func.attr if method_name not in context.sigs[contract_name]: raise FunctionDeclarationException(( f"Function not declared yet: {method_name} (reminder: " "function must be declared in the correct contract)" f"The available methods are: {','.join(context.sigs[contract_name].keys())}" ), node.func) sig = context.sigs[contract_name][method_name] inargs, inargsize, _ = pack_arguments( sig, [Expr(arg, context).lll_node for arg in node.args], context, node.func, ) output_placeholder, output_size, returner = get_external_contract_call_output( sig, context) sub = [ 'seq', ['assert', ['extcodesize', contract_address]], ['assert', ['ne', 'address', contract_address]], ] if context.is_constant() and not sig.const: raise ConstancyViolationException( f"May not call non-constant function '{method_name}' within {context.pp_constancy()}." " For asserting the result of modifiable contract calls, try assert_modifiable.", node) if context.is_constant() or sig.const: sub.append([ 'assert', [ 'staticcall', gas, contract_address, inargs, inargsize, output_placeholder, output_size, ] ]) else: sub.append([ 'assert', [ 'call', gas, contract_address, value, inargs, inargsize, output_placeholder, output_size, ] ]) sub.extend(returner) o = LLLnode.from_list(sub, typ=sig.output_type, location='memory', pos=getpos(node)) return o
def visit_AnnAssign(self, node): name = node.get("target.id") if name is None: raise VariableDeclarationException("Invalid module-level assignment", node) if name == "implements": interface_name = node.annotation.id self.namespace[interface_name].validate_implements(node) return is_constant, is_public, is_immutable = False, False, False annotation = node.annotation if isinstance(annotation, vy_ast.Call): # the annotation is a function call, e.g. `foo: constant(uint256)` call_name = annotation.get("func.id") if call_name in ("constant", "public", "immutable"): validate_call_args(annotation, 1) if call_name == "constant": # declaring a constant is_constant = True elif call_name == "public": # declaring a public variable is_public = True # generate function type and add to metadata # we need this when builing the public getter node._metadata["func_type"] = ContractFunction.from_AnnAssign(node) elif call_name == "immutable": # declaring an immutable variable is_immutable = True # mutability is checked automatically preventing assignment # outside of the constructor, here we just check a value is assigned, # not necessarily where assignments = self.ast.get_descendants( vy_ast.Assign, filters={"target.id": node.target.id} ) if not assignments: # Special error message for common wrong usages via `self.<immutable name>` wrong_self_attribute = self.ast.get_descendants( vy_ast.Attribute, {"value.id": "self", "attr": node.target.id} ) message = ( "Immutable variables must be accessed without 'self'" if len(wrong_self_attribute) > 0 else "Immutable definition requires an assignment in the constructor" ) raise SyntaxException( message, node.node_source_code, node.lineno, node.col_offset ) # remove the outer call node, to handle cases such as `public(map(..))` annotation = annotation.args[0] data_loc = DataLocation.CODE if is_immutable else DataLocation.STORAGE type_definition = get_type_from_annotation( annotation, data_loc, is_constant, is_public, is_immutable ) node._metadata["type"] = type_definition if is_constant: if not node.value: raise VariableDeclarationException("Constant must be declared with a value", node) if not check_constant(node.value): raise StateAccessViolation("Value must be a literal", node.value) validate_expected_type(node.value, type_definition) try: self.namespace[name] = type_definition except VyperException as exc: raise exc.with_annotation(node) from None return if node.value: var_type = "Immutable" if is_immutable else "Storage" raise VariableDeclarationException( f"{var_type} variables cannot have an initial value", node.value ) if is_immutable: try: # block immutable if storage variable already exists if name in self.namespace["self"].members: raise NamespaceCollision( f"Value '{name}' has already been declared", node ) from None self.namespace[name] = type_definition except VyperException as exc: raise exc.with_annotation(node) from None return try: self.namespace.validate_assignment(name) except NamespaceCollision as exc: raise exc.with_annotation(node) from None try: self.namespace["self"].add_member(name, type_definition) node.target._metadata["type"] = type_definition except NamespaceCollision: raise NamespaceCollision(f"Value '{name}' has already been declared", node) from None except VyperException as exc: raise exc.with_annotation(node) from None
def external_contract_call(node, context, contract_name, contract_address, pos, value=None, gas=None): from vyper.parser.parser import parse_expr if value is None: value = 0 if gas is None: gas = 'gas' if not contract_name: raise StructureException( f'Invalid external contract call "{node.func.attr}".', node ) if contract_name not in context.sigs: raise VariableDeclarationException( f'Contract "{contract_name}" not declared yet', node ) method_name = node.func.attr if method_name not in context.sigs[contract_name]: raise FunctionDeclarationException( ( "Function not declared yet: %s (reminder: " "function must be declared in the correct contract)" " The available methods are: %s" ) % (method_name, ",".join(context.sigs[contract_name].keys())), node.func ) sig = context.sigs[contract_name][method_name] inargs, inargsize, _ = pack_arguments( sig, [parse_expr(arg, context) for arg in node.args], context, pos=pos, ) output_placeholder, output_size, returner = get_external_contract_call_output(sig, context) sub = [ 'seq', ['assert', ['extcodesize', contract_address]], ['assert', ['ne', 'address', contract_address]], ] if context.is_constant() or sig.const: sub.append([ 'assert', [ 'staticcall', gas, contract_address, inargs, inargsize, output_placeholder, output_size, ] ]) else: sub.append([ 'assert', [ 'call', gas, contract_address, value, inargs, inargsize, output_placeholder, output_size, ] ]) sub.extend(returner) o = LLLnode.from_list(sub, typ=sig.output_type, location='memory', pos=getpos(node)) return o
def _check_same_variable_assign(self, sub): lhs_var_name = self.stmt.target.id rhs_names = self._check_rhs_var_assn_recur(self.stmt.value) if lhs_var_name in rhs_names: raise VariableDeclarationException('Invalid variable assignment, same variable not allowed on LHS and RHS: %s' % lhs_var_name)
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
def parse_func(code, _globals, sigs, origcode, _vars=None): if _vars is None: _vars = {} sig = FunctionSignature.from_definition(code, sigs) # Check for duplicate variables with globals for arg in sig.args: if arg.name in _globals: raise VariableDeclarationException( "Variable name duplicated between function arguments and globals: " + arg.name) # Create a context context = Context(vars=_vars, globals=_globals, sigs=sigs, return_type=sig.output_type, is_constant=sig.const, is_payable=sig.payable, origcode=origcode) # Copy calldata to memory for fixed-size arguments copy_size = sum([ 32 if isinstance(arg.typ, ByteArrayType) else get_size_of_type(arg.typ) * 32 for arg in sig.args ]) context.next_mem += copy_size if not len(sig.args): copier = 'pass' elif sig.name == '__init__': copier = [ 'codecopy', MemoryPositions.RESERVED_MEMORY, '~codelen', copy_size ] else: copier = [ 'calldatacopy', MemoryPositions.RESERVED_MEMORY, 4, copy_size ] clampers = [copier] # Add asserts for payable and internal if not sig.payable: clampers.append(['assert', ['iszero', 'callvalue']]) if sig.private: clampers.append(['assert', ['eq', 'caller', 'address']]) # Fill in variable positions for arg in sig.args: clampers.append( make_clamper(arg.pos, context.next_mem, arg.typ, sig.name == '__init__')) if isinstance(arg.typ, ByteArrayType): context.vars[arg.name] = VariableRecord(arg.name, context.next_mem, arg.typ, False) context.next_mem += 32 * get_size_of_type(arg.typ) else: context.vars[arg.name] = VariableRecord( arg.name, MemoryPositions.RESERVED_MEMORY + arg.pos, arg.typ, False) # Create "clampers" (input well-formedness checkers) # Return function body if sig.name == '__init__': o = LLLnode.from_list(['seq'] + clampers + [parse_body(code.body, context)], pos=getpos(code)) else: method_id_node = LLLnode.from_list(sig.method_id, pos=getpos(code), annotation='%s' % sig.name) o = LLLnode.from_list([ 'if', ['eq', ['mload', 0], method_id_node], ['seq'] + clampers + [parse_body(c, context) for c in code.body] + ['stop'] ], typ=None, pos=getpos(code)) # Check for at leasts one return statement if necessary. if context.return_type and context.function_return_count == 0: raise StructureException( "Missing return statement in function '%s' " % sig.name, code) o.context = context o.total_gas = o.gas + calc_mem_gas(o.context.next_mem) o.func_name = sig.name return o
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( "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) 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(sha3(bytes(sig, 'utf-8'))) return cls(name, args, indexed_list, event_id, sig)
def call(self): from .parser import ( pack_arguments, pack_logging_data, pack_logging_topics, external_contract_call, ) if isinstance(self.stmt.func, ast.Name) and self.stmt.func.id in stmt_dispatch_table: return stmt_dispatch_table[self.stmt.func.id](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 == "self": method_name = self.stmt.func.attr if method_name not in self.context.sigs['self']: raise VariableDeclarationException( "Function not declared yet (reminder: functions cannot " "call functions later in code than themselves): %s" % self.stmt.func.attr) sig = self.context.sigs['self'][method_name] if self.context.is_constant and not sig.const: raise ConstancyViolationException( "May not call non-constant function '%s' within a constant function." % (method_name)) add_gas = self.context.sigs['self'][method_name].gas inargs, inargsize = pack_arguments( sig, [Expr(arg, self.context).lll_node for arg in self.stmt.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' % method_name) 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 VariableDeclarationException( "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 VariableDeclarationException( "%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): from .parser import ( external_contract_call, pack_arguments, ) from vyper.functions import ( dispatch_table, ) if isinstance(self.expr.func, ast.Name): function_name = self.expr.func.id if function_name in dispatch_table: return dispatch_table[function_name](self.expr, self.context) else: err_msg = "Not a top-level function: {}".format(function_name) if function_name in self.context.sigs['self']: err_msg += ". Did you mean self.{}?".format(function_name) raise StructureException(err_msg, self.expr) elif isinstance(self.expr.func, ast.Attribute) and isinstance( self.expr.func.value, ast.Name) and self.expr.func.value.id == "self": method_name = self.expr.func.attr if method_name not in self.context.sigs['self']: raise VariableDeclarationException( "Function not declared yet (reminder: functions cannot " "call functions later in code than themselves): %s" % self.expr.func.attr) sig = self.context.sigs['self'][method_name] if self.context.is_constant and not sig.const: raise ConstancyViolationException( "May not call non-constant function '%s' within a constant function." % (method_name), getpos(self.expr)) add_gas = self.context.sigs['self'][method_name].gas # gas of call inargs, inargsize = pack_arguments( sig, [Expr(arg, self.context).lll_node for arg in self.expr.args], self.context, pos=getpos(self.expr)) output_placeholder = self.context.new_placeholder( typ=sig.output_type) multi_arg = [] if isinstance(sig.output_type, BaseType): returner = output_placeholder elif isinstance(sig.output_type, ByteArrayType): returner = output_placeholder + 32 elif self.context.in_assignment and isinstance( sig.output_type, TupleType): returner = output_placeholder else: raise TypeMismatchException( "Invalid output type: %r" % sig.output_type, self.expr) o = LLLnode.from_list(multi_arg + [ 'seq', [ 'assert', [ 'call', ['gas'], ['address'], 0, inargs, inargsize, output_placeholder, get_size_of_type(sig.output_type) * 32 ] ], returner ], typ=sig.output_type, location='memory', pos=getpos(self.expr), add_gas_estimate=add_gas, annotation='Internal Call: %s' % method_name) o.gas += sig.gas return o elif isinstance(self.expr.func, ast.Attribute) and isinstance( self.expr.func.value, ast.Call): contract_name = self.expr.func.value.func.id contract_address = Expr.parse_value_expr( self.expr.func.value.args[0], self.context) value, gas = self._get_external_contract_keywords() return external_contract_call(self.expr, self.context, contract_name, contract_address, pos=getpos(self.expr), value=value, gas=gas) elif isinstance(self.expr.func.value, ast.Attribute ) and self.expr.func.value.attr in self.context.sigs: contract_name = self.expr.func.value.attr var = self.context.globals[self.expr.func.value.attr] contract_address = unwrap_location( LLLnode.from_list(var.pos, typ=var.typ, location='storage', pos=getpos(self.expr), annotation='self.' + self.expr.func.value.attr)) value, gas = self._get_external_contract_keywords() return external_contract_call(self.expr, self.context, contract_name, contract_address, pos=getpos(self.expr), value=value, gas=gas) elif isinstance( self.expr.func.value, ast.Attribute ) and self.expr.func.value.attr in self.context.globals: contract_name = self.context.globals[ self.expr.func.value.attr].typ.unit var = self.context.globals[self.expr.func.value.attr] contract_address = unwrap_location( LLLnode.from_list(var.pos, typ=var.typ, location='storage', pos=getpos(self.expr), annotation='self.' + self.expr.func.value.attr)) value, gas = self._get_external_contract_keywords() return external_contract_call(self.expr, self.context, contract_name, contract_address, pos=getpos(self.expr), value=value, gas=gas) else: raise StructureException( "Unsupported operator: %r" % ast.dump(self.expr), self.expr)
def add_globals_and_events(_contracts, _defs, _events, _getters, _globals, item): item_attributes = {"public": False, "modifiable": False, "static": 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) 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 StructureException( "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) # Check if variable name is reserved or invalid elif not is_varname_valid(item.target.id): 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: if not item_attributes["modifiable"] and not item_attributes["static"]: raise StructureException( "All contracts must have `modifiable` or `static` keywords: %s" % item_attributes) _globals[item.target.id] = ContractRecord( item_attributes["modifiable"], item.target.id, len(_globals), BaseType('address', item_name), True) if item_attributes["public"]: typ = BaseType('address', 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 = BaseType('address', item_name) else: typ = parse_type(item.annotation.args[0], 'storage') _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'), True) return _contracts, _events, _globals, _getters