def external_contract_call_stmt(stmt, context, contract_name, contract_address): if contract_name not in context.sigs: raise VariableDeclarationException("Contract not declared yet: %s" % contract_name) method_name = stmt.func.attr if method_name not in context.sigs[contract_name]: raise VariableDeclarationException( "Function not declared yet: %s (reminder: " "function must be declared in the correct contract)" % method_name) sig = context.sigs[contract_name][method_name] inargs, inargsize = pack_arguments( sig, [parse_expr(arg, context) for arg in stmt.args], context) sub = [ 'seq', ['assert', ['extcodesize', contract_address]], ['assert', ['ne', 'address', contract_address]] ] if context.is_constant: sub.append([ 'assert', ['staticcall', 'gas', contract_address, inargs, inargsize, 0, 0] ]) else: sub.append([ 'assert', ['call', 'gas', contract_address, 0, inargs, inargsize, 0, 0] ]) o = LLLnode.from_list(sub, typ=sig.output_type, location='memory', pos=getpos(stmt)) return o
def assign(self): from .parser import ( make_setter, ) # 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 fetch_call_return(self, node: vy_ast.Call) -> StructDefinition: validate_call_args(node, 1) if not isinstance(node.args[0], vy_ast.Dict): raise VariableDeclarationException( "Struct values must be declared via dictionary", node.args[0]) if next( (i for i in self.members.values() if isinstance(i, MappingDefinition)), False, ): raise VariableDeclarationException( "Struct contains a mapping and so cannot be declared as a literal", node) members = self.members.copy() for key, value in zip(node.args[0].keys, node.args[0].values): if key is None or key.get("id") not in members: raise UnknownAttribute("Unknown or duplicate struct member", key or value) validate_expected_type(value, members.pop(key.id)) if members: raise VariableDeclarationException( f"Struct declaration does not define all fields: {', '.join(list(members))}", node, ) return StructDefinition(self._id, self.members)
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) # Determine if it's an RLPList assignment. if isinstance(self.stmt.value, ast.Call) and getattr( self.stmt.value.func, 'id', '') == '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)) 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) # Do no allow tuple-to-tuple assignment if isinstance(self.stmt.targets[0], ast.Tuple) and isinstance( self.stmt.value, ast.Tuple): 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]) o = make_setter(target, sub, target.location, pos=getpos(self.stmt)) o.pos = getpos(self.stmt) return o
def from_declaration(cls, code, custom_units=None): name = code.target.id pos = 0 if not is_varname_valid(name, custom_units=custom_units): raise EventDeclarationException("Event name invalid: " + name) # Determine the arguments, expects something of the form def foo(arg1: num, arg2: num ... args = [] indexed_list = [] topics_count = 1 if code.annotation.args: keys = code.annotation.args[0].keys values = code.annotation.args[0].values for i in range(len(keys)): typ = values[i] arg = keys[i].id is_indexed = False # Check to see if argument is a topic if isinstance(typ, ast.Call) and typ.func.id == 'indexed': typ = values[i].args[0] indexed_list.append(True) topics_count += 1 is_indexed = True else: indexed_list.append(False) if isinstance(typ, ast.Subscript) and getattr( typ.value, 'id', None ) == 'bytes' and typ.slice.value.n > 32 and is_indexed: raise EventDeclarationException( "Indexed arguments are limited to 32 bytes") if topics_count > 4: raise EventDeclarationException( "Maximum of 3 topics {} given".format(topics_count - 1), arg) if not isinstance(arg, str): raise VariableDeclarationException("Argument name invalid", arg) if not typ: raise InvalidTypeException("Argument must have type", arg) if not is_varname_valid(arg, custom_units): raise VariableDeclarationException( "Argument name invalid or reserved: " + arg, arg) if arg in (x.name for x in args): raise VariableDeclarationException( "Duplicate function argument name: " + arg, arg) parsed_type = parse_type(typ, None, custom_units=custom_units) args.append(VariableRecord(arg, pos, parsed_type, False)) if isinstance(parsed_type, ByteArrayType): pos += ceil32(typ.slice.value.n) else: pos += get_size_of_type(parsed_type) * 32 sig = name + '(' + ','.join([ canonicalize_type(arg.typ, indexed_list[pos]) for pos, arg in enumerate(args) ]) + ')' # noqa F812 event_id = bytes_to_int(sha3(bytes(sig, 'utf-8'))) return cls(name, args, indexed_list, event_id, sig)
def new_variable(self, name, typ): if not is_varname_valid(name, custom_units=self.custom_units): raise VariableDeclarationException("Variable name invalid or reserved: " + name) if name in self.vars or name in self.globals: raise VariableDeclarationException("Duplicate variable name: %s" % name) self.vars[name] = VariableRecord(name, self.next_mem, typ, True, self.blockscopes.copy()) pos = self.next_mem self.next_mem += 32 * get_size_of_type(typ) return pos
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 # 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) 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["self"].add_member(name, 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 assign(self): # Assignment (e.g. x[4] = y) with self.context.assignment_scope(): sub = Expr(self.stmt.value, self.context).lll_node is_valid_rlp_list_assign = ( isinstance(self.stmt.value, vy_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.target.id, sub.typ) variable_loc = LLLnode.from_list( pos, typ=sub.typ, location='memory', pos=getpos(self.stmt), annotation=self.stmt.target.id, ) o = make_setter(variable_loc, sub, 'memory', pos=getpos(self.stmt)) else: # Error check when assigning to declared variable if isinstance(self.stmt.target, vy_ast.Name): # Do not allow assignment to undefined variables without annotation if self.stmt.target.id not in self.context.vars: raise VariableDeclarationException("Variable type not defined", self.stmt) # Check against implicit conversion self._check_implicit_conversion(self.stmt.target.id, sub) is_valid_tuple_assign = ( isinstance(self.stmt.target, vy_ast.Tuple) ) and isinstance(self.stmt.value, vy_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.target) if isinstance(target.typ, ContractType) and not isinstance(sub.typ, ContractType): raise TypeMismatch( 'Contract assignment expects casted address: ' f'{target.typ}(<address_var>)', self.stmt ) o = make_setter(target, sub, target.location, pos=getpos(self.stmt)) o.pos = getpos(self.stmt) return o
def is_valid_varname(self, name, item): if not is_varname_valid(name, custom_units=self._custom_units, custom_structs=self._structs): raise VariableDeclarationException('Invalid name "%s"' % name, item) if name in self._globals: raise VariableDeclarationException('Invalid name "%s", previously defined as global.' % name, item) if name in self._constants: raise VariableDeclarationException('Invalid name "%s", previously defined as constant.' % name, item) if name in self._custom_units: raise VariableDeclarationException('Invalid name "%s", previously defined as custom unit.' % name, item) return True
def external_contract_call(node, context, contract_name, contract_address, is_modifiable, 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 VariableDeclarationException( "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 not is_modifiable: 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 external_contract_call_expr(expr, context, contract_name, contract_address): if contract_name not in context.sigs: raise VariableDeclarationException("Contract not declared yet: %s" % contract_name) method_name = expr.func.attr if method_name not in context.sigs[contract_name]: raise VariableDeclarationException( "Function not declared yet: %s (reminder: " "function must be declared in the correct contract)" % method_name) sig = context.sigs[contract_name][method_name] inargs, inargsize = pack_arguments( sig, [parse_expr(arg, context) for arg in expr.args], context) output_placeholder = context.new_placeholder(typ=sig.output_type) if isinstance(sig.output_type, BaseType): returner = output_placeholder elif isinstance(sig.output_type, ByteArrayType): returner = output_placeholder + 32 else: raise TypeMismatchException( "Invalid output type: %r" % sig.output_type, expr) sub = [ 'seq', ['assert', ['extcodesize', contract_address]], ['assert', ['ne', 'address', contract_address]] ] if context.is_constant: sub.append([ 'assert', [ 'staticcall', 'gas', contract_address, inargs, inargsize, output_placeholder, get_size_of_type(sig.output_type) * 32 ] ]) else: sub.append([ 'assert', [ 'call', 'gas', contract_address, 0, inargs, inargsize, output_placeholder, get_size_of_type(sig.output_type) * 32 ] ]) sub.extend([0, returner]) o = LLLnode.from_list(sub, typ=sig.output_type, location='memory', pos=getpos(expr)) return o
def get_contracts_and_defs_and_globals(code): _contracts = {} _events = [] _globals = {} _defs = [] _getters = [] _custom_units = [] for item in code: # Contract references if isinstance(item, ast.ClassDef): if _events or _globals or _defs: raise StructureException("External contract declarations must come before event declarations, global declarations, and function definitions", item) _contracts[item.name] = add_contract(item.body) # Statements of the form: # variable_name: type elif isinstance(item, ast.AnnAssign): _custom_units, _contracts, _events, _globals, _getters = add_globals_and_events(_custom_units, _contracts, _defs, _events, _getters, _globals, item) # Function definitions elif isinstance(item, ast.FunctionDef): if item.name in _globals: raise VariableDeclarationException("Function name shadowing a variable name: %s" % item.name) _defs.append(item) else: raise StructureException("Invalid top-level statement", item) return _contracts, _events, _defs + _getters, _globals, _custom_units
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, _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: o = parse_other_functions(o, otherfuncs, _globals, sigs, external_contracts, origcode, _custom_units, runtime_only) return LLLnode.from_list(o, typ=None)
def is_valid_varname(self, name, item): """ Valid variable name, checked against global context. """ check_valid_varname(name, self._structs, self._constants, item) if name in self._globals: raise VariableDeclarationException( f'Invalid name "{name}", previously defined as global.', item) return True
def variables(self): 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=var. location, # either 'memory' or 'calldata' storage is handled above. pos=getpos(self.expr), annotation=self.expr.id, mutable=var.mutable, ) elif self.expr.id in BUILTIN_CONSTANTS: obj, typ = BUILTIN_CONSTANTS[self.expr.id] return LLLnode.from_list([obj], typ=BaseType(typ, is_literal=True), pos=getpos(self.expr)) elif self.context.constants.ast_is_constant(self.expr): return self.context.constants.get_constant(self.expr.id, self.context) else: raise VariableDeclarationException( f"Undeclared variable: {self.expr.id}", self.expr)
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 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('int128', {'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('int128', {'wei': 1}), pos=getpos(self.expr)) elif key == "msg.gas": return LLLnode.from_list(['gas'], typ='int128', pos=getpos(self.expr)) elif key == "block.difficulty": return LLLnode.from_list(['difficulty'], typ='int128', pos=getpos(self.expr)) elif key == "block.timestamp": return LLLnode.from_list(['timestamp'], typ=BaseType('int128', {'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='int128', 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) 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 variables(self): if self.expr.id == 'self': return LLLnode.from_list(['address'], typ='address', 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 visit_AnnAssign(self, node): name = node.get("target.id") if name is None: raise VariableDeclarationException("Invalid assignment", node) if not node.value: raise VariableDeclarationException( "Memory variables must be declared with an initial 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 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 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.expr.id in self.context.constants: # check if value is compatible with const = self.context.constants[self.expr.id] if isinstance(const, ast.AnnAssign): # Handle ByteArrays. expr = Expr(const.value, self.context).lll_node return expr # Other types are already unwrapped, no need return self.context.constants[self.expr.id] 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=var.location, # either 'memory' or 'calldata' storage is handled above. 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 parse_func(code, _globals, sigs, origcode, _custom_units, _vars=None): if _vars is None: _vars = {} sig = FunctionSignature.from_definition(code, sigs=sigs, custom_units=_custom_units) # 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, custom_units=_custom_units) # 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 assign(self): # Assignment (e.g. x[4] = y) with self.context.assignment_scope(): sub = Expr(self.stmt.value, self.context).lll_node # Error check when assigning to declared variable if isinstance(self.stmt.target, vy_ast.Name): # Do not allow assignment to undefined variables without annotation if self.stmt.target.id not in self.context.vars: raise VariableDeclarationException( "Variable type not defined", self.stmt) # Check against implicit conversion self._check_implicit_conversion(self.stmt.target.id, sub) is_valid_tuple_assign = (isinstance( self.stmt.target, vy_ast.Tuple)) and isinstance( self.stmt.value, vy_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.target) if isinstance(target.typ, ContractType) and not isinstance( sub.typ, ContractType): raise TypeMismatch( 'Contract assignment expects casted address: ' f'{target.typ}(<address_var>)', self.stmt) o = make_setter(target, sub, target.location, pos=getpos(self.stmt)) o.pos = getpos(self.stmt) return o
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 fetch_call_return(self, node: vy_ast.Call) -> StructDefinition: validate_call_args(node, 1) if not isinstance(node.args[0], vy_ast.Dict): raise VariableDeclarationException( "Struct values must be declared via dictionary", node.args[0]) if next((i for i in self.members.values() if isinstance(i, MappingDefinition)), False): raise VariableDeclarationException( "Struct contains a mapping and so cannot be declared as a literal", node) members = self.members.copy() keys = list(self.members.keys()) for i, (key, value) in enumerate(zip(node.args[0].keys, node.args[0].values)): if key is None or key.get("id") not in members: suggestions_str = get_levenshtein_error_suggestions( key.get("id"), members, 1.0) raise UnknownAttribute( f"Unknown or duplicate struct member. {suggestions_str}", key or value) expected_key = keys[i] if key.id != expected_key: raise InvalidAttribute( "Struct keys are required to be in order, but got " f"`{key.id}` instead of `{expected_key}`. (Reminder: the " f"keys in this struct are {list(self.members.items())})", key, ) validate_expected_type(value, members.pop(key.id)) if members: raise VariableDeclarationException( f"Struct declaration does not define all fields: {', '.join(list(members))}", node) return StructDefinition(self._id, self.members)
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 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)