def parse_Subscript(self): sub = Expr.parse_variable_location(self.expr.value, self.context) if isinstance(sub.typ, (MappingType, ListType)): index = Expr.parse_value_expr(self.expr.slice.value, self.context) elif isinstance(sub.typ, TupleType): index = self.expr.slice.value.n if not 0 <= index < len(sub.typ.members): return else: return lll_node = add_variable_offset(sub, index, pos=getpos(self.expr)) lll_node.mutable = sub.mutable return lll_node
def subscript(self): sub = Expr.parse_variable_location(self.expr.value, self.context) if isinstance(sub.typ, (MappingType, ListType)): if 'value' not in vars(self.expr.slice): raise StructureException("Array access must access a single element, not a slice", self.expr) index = Expr.parse_value_expr(self.expr.slice.value, self.context) elif isinstance(sub.typ, TupleType): if not isinstance(self.expr.slice.value, ast.Num) or self.expr.slice.value.n < 0 or self.expr.slice.value.n >= len(sub.typ.members): raise TypeMismatchException("Tuple index invalid", self.expr.slice.value) index = self.expr.slice.value.n else: raise TypeMismatchException("Bad subscript attempt", self.expr.value) o = add_variable_offset(sub, index, pos=getpos(self.expr)) o.mutable = sub.mutable return o
def o_list(lll_node, pos=None): lll_t = lll_node.typ if isinstance(lll_t, (TupleLike, ListType)): if lll_node.value == 'multi': # is literal ret = lll_node.args else: ks = lll_t.tuple_keys() if isinstance(lll_t, TupleLike) else \ [LLLnode.from_list(i) for i in range(lll_t.count)] ret = [ add_variable_offset(lll_node, k, pos, array_bounds_check=False) for k in ks ] return ret else: return [lll_node]
def parse_Subscript(self): sub = Expr.parse_variable_location(self.expr.value, self.context) if isinstance(sub.typ, (MappingType, ListType)): index = Expr.parse_value_expr(self.expr.slice.value, self.context) if isinstance( index.typ, ByteArrayLike) and index.args[0].location == "storage": # Special case - if the key value is a bytes-array type located in # storage, we have to copy it to memory prior to calculating the hash placeholder = self.context.new_internal_variable(index.typ) placeholder_node = LLLnode.from_list(placeholder, typ=index.typ, location="memory") copier = make_byte_array_copier( placeholder_node, LLLnode.from_list(index.args[0], typ=index.typ, location="storage"), ) return LLLnode.from_list( [ "seq", copier, [ "sha3_64", sub, [ "sha3", ["add", placeholder, 32], ["mload", placeholder] ], ], ], typ=sub.typ.valuetype, pos=getpos(self.expr), location="storage", ) elif isinstance(sub.typ, TupleType): index = self.expr.slice.value.n if not 0 <= index < len(sub.typ.members): return else: return lll_node = add_variable_offset(sub, index, pos=getpos(self.expr)) lll_node.mutable = sub.mutable return lll_node
def avo(arg, ind, pos): return unwrap_location(add_variable_offset(arg, LLLnode.from_list(ind, 'int128'), pos=pos))
def avo(arg, ind): return unwrap_location( add_variable_offset(arg, LLLnode.from_list(ind, 'num')))
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 parse_Attribute(self): # x.balance: balance of address x if self.expr.attr == "balance": addr = Expr.parse_value_expr(self.expr.value, self.context) if is_base_type(addr.typ, "address"): 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 is_base_type(addr.typ, "address"): if self.expr.attr == "codesize": if self.expr.value.id == "self": eval_code = ["codesize"] else: eval_code = ["extcodesize", addr] output_type = "uint256" 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 version_check(begin="constantinople"): raise EvmVersionException( "address.codehash is unavailable prior to constantinople ruleset", self.expr) if is_base_type(addr.typ, "address"): 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": type_ = self.expr._metadata["type"] var = self.context.globals[self.expr.attr] return LLLnode.from_list( type_.position.position, 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 = f"{self.expr.value.id}.{self.expr.attr}" if key == "msg.sender" and not self.context.is_internal: return LLLnode.from_list(["caller"], typ="address", pos=getpos(self.expr)) elif key == "msg.data" and not self.context.is_internal: is_len = self.expr._metadata.get("is_len") if is_len is True: typ = ByteArrayType(32) pos = self.context.new_internal_variable(typ) node = ["seq", ["mstore", pos, "calldatasize"], pos] return LLLnode.from_list(node, typ=typ, pos=getpos(self.expr), location="memory") size = self.expr._metadata.get("size") typ = ByteArrayType(size + 32) pos = self.context.new_internal_variable(typ) node = [ "seq", ["assert", ["le", size, "calldatasize"]], ["mstore", pos, size], ["calldatacopy", pos + 32, 0, size], pos, ] return LLLnode.from_list(node, typ=typ, pos=getpos(self.expr), location="memory") elif key == "msg.value" and self.context.is_payable: 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)) # Other variables else: sub = Expr.parse_variable_location(self.expr.value, self.context) # contract type if isinstance(sub.typ, InterfaceType): return sub if isinstance(sub.typ, StructType) and self.expr.attr in sub.typ.members: return add_variable_offset(sub, self.expr.attr, pos=getpos(self.expr))
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": if self.context.is_private: raise ParserException( "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 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 ParserException("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 TypeMismatchException( "Type mismatch: member variable access not expected", self.expr.value, ) attrs = list(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 make_setter(left, right, location, pos): # Basic types if isinstance(left.typ, BaseType): right = base_type_conversion(right, right.typ, left.typ, pos) if location == 'storage': return LLLnode.from_list(['sstore', left, right], typ=None) elif location == 'memory': return LLLnode.from_list(['mstore', left, right], typ=None) # Byte arrays elif isinstance(left.typ, ByteArrayType): return make_byte_array_copier(left, right) # Can't copy mappings elif isinstance(left.typ, MappingType): raise TypeMismatchException("Cannot copy mappings; can only copy individual elements", pos) # Arrays elif isinstance(left.typ, ListType): # Cannot do something like [a, b, c] = [1, 2, 3] if left.value == "multi": raise Exception("Target of set statement must be a single item") if not isinstance(right.typ, (ListType, NullType)): raise TypeMismatchException("Setter type mismatch: left side is array, right side is %r" % right.typ, pos) left_token = LLLnode.from_list('_L', typ=left.typ, location=left.location) if left.location == "storage": left = LLLnode.from_list(['sha3_32', left], typ=left.typ, location="storage_prehashed") left_token.location = "storage_prehashed" # Type checks if not isinstance(right.typ, NullType): if not isinstance(right.typ, ListType): raise TypeMismatchException("Left side is array, right side is not", pos) if left.typ.count != right.typ.count: raise TypeMismatchException("Mismatched number of elements", pos) # If the right side is a literal if right.value == "multi": if len(right.args) != left.typ.count: raise TypeMismatchException("Mismatched number of elements", pos) subs = [] for i in range(left.typ.count): subs.append(make_setter(add_variable_offset(left_token, LLLnode.from_list(i, typ='int128'), pos=pos), right.args[i], location, pos=pos)) return LLLnode.from_list(['with', '_L', left, ['seq'] + subs], typ=None) # If the right side is a null elif isinstance(right.typ, NullType): subs = [] for i in range(left.typ.count): subs.append(make_setter(add_variable_offset(left_token, LLLnode.from_list(i, typ='int128'), pos=pos), LLLnode.from_list(None, typ=NullType()), location, pos=pos)) return LLLnode.from_list(['with', '_L', left, ['seq'] + subs], typ=None) # If the right side is a variable else: right_token = LLLnode.from_list('_R', typ=right.typ, location=right.location) subs = [] for i in range(left.typ.count): subs.append(make_setter(add_variable_offset(left_token, LLLnode.from_list(i, typ='int128'), pos=pos), add_variable_offset(right_token, LLLnode.from_list(i, typ='int128'), pos=pos), location, pos=pos)) return LLLnode.from_list(['with', '_L', left, ['with', '_R', right, ['seq'] + subs]], typ=None) # Structs elif isinstance(left.typ, (StructType, TupleType)): if left.value == "multi" and isinstance(left.typ, StructType): raise Exception("Target of set statement must be a single item") if not isinstance(right.typ, NullType): if not isinstance(right.typ, left.typ.__class__): raise TypeMismatchException("Setter type mismatch: left side is %r, right side is %r" % (left.typ, right.typ), pos) if isinstance(left.typ, StructType): for k in left.typ.members: if k not in right.typ.members: raise TypeMismatchException("Keys don't match for structs, missing %s" % k, pos) for k in right.typ.members: if k not in left.typ.members: raise TypeMismatchException("Keys don't match for structs, extra %s" % k, pos) else: if len(left.typ.members) != len(right.typ.members): raise TypeMismatchException("Tuple lengths don't match, %d vs %d" % (len(left.typ.members), len(right.typ.members)), pos) left_token = LLLnode.from_list('_L', typ=left.typ, location=left.location) if left.location == "storage": left = LLLnode.from_list(['sha3_32', left], typ=left.typ, location="storage_prehashed") left_token.location = "storage_prehashed" if isinstance(left.typ, StructType): keyz = sorted(list(left.typ.members.keys())) else: keyz = list(range(len(left.typ.members))) # If the right side is a literal if right.value == "multi": if len(right.args) != len(keyz): raise TypeMismatchException("Mismatched number of elements", pos) subs = [] for i, typ in enumerate(keyz): subs.append(make_setter(add_variable_offset(left_token, typ, pos=pos), right.args[i], location, pos=pos)) return LLLnode.from_list(['with', '_L', left, ['seq'] + subs], typ=None) # If the right side is a null elif isinstance(right.typ, NullType): subs = [] for typ in keyz: subs.append(make_setter(add_variable_offset(left_token, typ, pos=pos), LLLnode.from_list(None, typ=NullType()), location, pos=pos)) return LLLnode.from_list(['with', '_L', left, ['seq'] + subs], typ=None) # If tuple assign. elif isinstance(left.typ, TupleType) and isinstance(right.typ, TupleType): right_token = LLLnode.from_list('_R', typ=right.typ, location="memory") subs = [] static_offset_counter = 0 for idx, (left_arg, right_arg) in enumerate(zip(left.args, right.typ.members)): # if left_arg.typ.typ != right_arg.typ: # raise TypeMismatchException("Tuple assignment mismatch position %d, expected '%s'" % (idx, right.typ), pos) if isinstance(right_arg, ByteArrayType): offset = LLLnode.from_list(['add', '_R', ['mload', ['add', '_R', static_offset_counter]]], typ=ByteArrayType(right_arg.maxlen), location='memory') static_offset_counter += 32 else: offset = LLLnode.from_list(['mload', ['add', '_R', static_offset_counter]], typ=right_arg.typ) static_offset_counter += get_size_of_type(right_arg) * 32 subs.append( make_setter( left_arg, offset, location="memory", pos=pos ) ) return LLLnode.from_list(['with', '_R', right, ['seq'] + subs], typ=None, annotation='Tuple assignment') # If the right side is a variable else: subs = [] right_token = LLLnode.from_list('_R', typ=right.typ, location=right.location) for typ in keyz: subs.append(make_setter( add_variable_offset(left_token, typ, pos=pos), add_variable_offset(right_token, typ, pos=pos), location, pos=pos )) return LLLnode.from_list(['with', '_L', left, ['with', '_R', right, ['seq'] + subs]], typ=None) else: raise Exception("Invalid type for setters")