def __init__(self, vyper_module: vy_ast.Module, fn_node: vy_ast.FunctionDef, namespace: dict) -> None: self.vyper_module = vyper_module self.fn_node = fn_node self.namespace = namespace self.func = fn_node._metadata["type"] self.annotation_visitor = StatementAnnotationVisitor( fn_node, namespace) namespace.update(self.func.arguments) if self.func.visibility is FunctionVisibility.INTERNAL: node_list = fn_node.get_descendants(vy_ast.Attribute, { "value.id": "msg", "attr": {"data", "sender"} }) if node_list: raise StateAccessViolation( f"msg.{node_list[0].attr} is not allowed in internal functions", node_list[0]) if self.func.mutability == StateMutability.PURE: node_list = fn_node.get_descendants( vy_ast.Attribute, { "value.id": set(CONSTANT_ENVIRONMENT_VARS.keys()).union( set(MUTABLE_ENVIRONMENT_VARS.keys())) }, ) if node_list: raise StateAccessViolation( "not allowed to query contract or environment variables in pure functions", node_list[0], ) if self.func.mutability is not StateMutability.PAYABLE: node_list = fn_node.get_descendants(vy_ast.Attribute, { "value.id": "msg", "attr": "value" }) if node_list: raise NonPayableViolation( "msg.value is not allowed in non-payable functions", node_list[0]) for node in fn_node.body: self.visit(node) if self.func.return_type: if not check_for_terminus(fn_node.body): raise FunctionDeclarationException( f"Missing or unmatched return statements in function '{fn_node.name}'", fn_node, )
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))