Beispiel #1
0
    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 make_struct(self, name, body):
     members = []
     for item in body:
         if isinstance(item, ast.AnnAssign):
             member_name = item.target
             member_type = item.annotation
             # Check well-formedness of member names
             if not (isinstance(member_name, ast.Name)
                     and is_varname_valid(member_name.id,
                                          custom_units=self._custom_units,
                                          custom_structs=self._structs)):
                 raise InvalidTypeException(
                     "Invalid member name for struct %r" % name, item)
             # Check well-formedness of member types
             # Note this kicks out mutually recursive structs,
             # raising an exception instead of stackoverflow.
             # A struct must be defined before it is referenced.
             # This feels like a semantic step and maybe should be pushed
             # to a later compilation stage.
             parse_type(member_type,
                        'storage',
                        custom_units=self._custom_units,
                        custom_structs=self._structs)
             members.append((member_name, member_type))
         else:
             raise StructureException("Structs can only contain variables",
                                      item)
     return members
Beispiel #3
0
 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
Beispiel #4
0
 def struct_literals(self):
     o = {}
     members = {}
     for key, value in zip(self.expr.keys, self.expr.values):
         if not isinstance(key, ast.Name) or not is_varname_valid(key.id, self.context.custom_units):
             raise TypeMismatchException("Invalid member variable for struct: %r" % vars(key).get('id', key), key)
         if key.id in o:
             raise TypeMismatchException("Member variable duplicated: " + key.id, key)
         o[key.id] = Expr(value, self.context).lll_node
         members[key.id] = o[key.id].typ
     return LLLnode.from_list(["multi"] + [o[key] for key in sorted(list(o.keys()))], typ=StructType(members), pos=getpos(self.expr))
Beispiel #5
0
    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
Beispiel #6
0
def make_struct_type(name, location, members, custom_units, custom_structs):
    o = OrderedDict()
    for key, value in members:
        if not isinstance(key, ast.Name) or not is_varname_valid(
                key.id, custom_units, custom_structs):
            raise InvalidTypeException(
                "Invalid member variable for struct %r" % key.id, key)
        o[key.id] = parse_type(value,
                               location,
                               custom_units=custom_units,
                               custom_structs=custom_structs)
    return StructType(o, name)
Beispiel #7
0
    def from_definition(cls,
                        code,
                        sigs=None,
                        custom_units=None,
                        contract_def=False,
                        constant=False):
        name = code.name
        pos = 0

        if not is_varname_valid(name, custom_units=custom_units):
            raise FunctionDeclarationException("Function name invalid: " +
                                               name)
        # Determine the arguments, expects something of the form def foo(arg1: int128, arg2: int128 ...
        args = []
        for arg in code.args.args:
            typ = arg.annotation
            if not typ:
                raise InvalidTypeException("Argument must have type", arg)
            if not is_varname_valid(arg.arg, custom_units=custom_units):
                raise FunctionDeclarationException(
                    "Argument name invalid or reserved: " + arg.arg, arg)
            if arg.arg in (x.name for x in args):
                raise FunctionDeclarationException(
                    "Duplicate function argument name: " + arg.arg, arg)
            parsed_type = parse_type(typ,
                                     None,
                                     sigs,
                                     custom_units=custom_units)
            args.append(VariableRecord(arg.arg, pos, parsed_type, False))
            if isinstance(parsed_type, ByteArrayType):
                pos += 32
            else:
                pos += get_size_of_type(parsed_type) * 32

        # Apply decorators
        const, payable, private, public = False, False, False, False
        for dec in code.decorator_list:
            if isinstance(dec, ast.Name) and dec.id == "constant":
                const = True
            elif isinstance(dec, ast.Name) and dec.id == "payable":
                payable = True
            elif isinstance(dec, ast.Name) and dec.id == "private":
                private = True
            elif isinstance(dec, ast.Name) and dec.id == "public":
                public = True
            else:
                raise StructureException("Bad decorator", dec)

        if public and private:
            raise StructureException(
                "Cannot use public and private decorators on the same function: {}"
                .format(name))
        if payable and const:
            raise StructureException(
                "Function {} cannot be both constant and payable.".format(
                    name))
        if payable and private:
            raise StructureException(
                "Function {} cannot be both private and payable.".format(name))
        if (not public and not private) and not contract_def:
            raise StructureException(
                "Function visibility must be declared (@public or @private)",
                code)
        if constant:
            const = True
        # Determine the return type and whether or not it's constant. Expects something
        # of the form:
        # def foo(): ...
        # def foo() -> int128: ...
        # If there is no return type, ie. it's of the form def foo(): ...
        # and NOT def foo() -> type: ..., then it's null
        if not code.returns:
            output_type = None
        elif isinstance(
                code.returns,
            (ast.Name, ast.Compare, ast.Subscript, ast.Call, ast.Tuple)):
            output_type = parse_type(code.returns,
                                     None,
                                     sigs,
                                     custom_units=custom_units)
        else:
            raise InvalidTypeException(
                "Output type invalid or unsupported: %r" %
                parse_type(code.returns, None),
                code.returns,
            )
        # Output type must be canonicalizable
        if output_type is not None:
            assert isinstance(output_type,
                              TupleType) or canonicalize_type(output_type)
        # Get the canonical function signature
        sig = cls.get_full_sig(name, code.args.args, sigs, custom_units)

        # Take the first 4 bytes of the hash of the sig to get the method ID
        method_id = fourbytes_to_int(sha3(bytes(sig, 'utf-8'))[:4])
        return cls(name, args, output_type, const, payable, private, sig,
                   method_id, custom_units)
    def add_globals_and_events(self, item):
        item_attributes = {"public": False}

        # Handle constants.
        if isinstance(item.annotation,
                      ast.Call) and item.annotation.func.id == "constant":
            self.add_constant(item)
            return

        # Handle events.
        if not (isinstance(item.annotation, ast.Call)
                and item.annotation.func.id == "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 isinstance(item.annotation,
                        ast.Call) and item.annotation.func.id == "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)
                    if key.id in self._custom_units:
                        raise VariableDeclarationException(
                            "Custom unit name may only be used once", key)
                    if not is_varname_valid(key.id,
                                            custom_units=self._custom_units,
                                            custom_structs=self._structs):
                        raise VariableDeclarationException(
                            "Custom unit may not be a reserved keyword", key)
                    self._custom_units.append(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.
        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 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]
            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 isinstance(item.annotation,
                        ast.Call) and item.annotation.func.id == "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)
            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)

        else:
            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), True)
Beispiel #9
0
def parse_type(item, location, sigs=None, custom_units=None):
    custom_units = [] if custom_units is None else custom_units
    sigs = {} if sigs is None else sigs

    # Base types, e.g. num
    if isinstance(item, ast.Name):
        if item.id in base_types:
            return BaseType(item.id)
        elif item.id in special_types:
            return special_types[item.id]
        else:
            raise InvalidTypeException("Invalid base type: " + item.id, item)
    # Units, e.g. num (1/sec) or contracts
    elif isinstance(item, ast.Call):
        # Contract_types
        if item.func.id == 'contract' or item.func.id == 'address':
            if sigs and item.args[0].id in sigs:
                return BaseType('address', item.args[0].id)
            else:
                raise InvalidTypeException('Invalid contract declaration')
        if not isinstance(item.func, ast.Name):
            raise InvalidTypeException("Malformed unit type:", item)
        base_type = item.func.id
        if base_type not in ('int128', 'uint256', 'decimal'):
            raise InvalidTypeException(
                "You must use int128, decimal, address, contract, \
                for variable declarations and indexed for logging topics ",
                item)
        if len(item.args) == 0:
            raise InvalidTypeException("Malformed unit type", item)
        if isinstance(item.args[-1],
                      ast.Name) and item.args[-1].id == "positional":
            positional = True
            argz = item.args[:-1]
        else:
            positional = False
            argz = item.args
        if len(argz) != 1:
            raise InvalidTypeException("Malformed unit type", item)
        # Check for uint256 to num casting
        if item.func.id == 'int128' and getattr(item.args[0], 'id',
                                                '') == 'uint256':
            return BaseType('int128', override_signature='uint256')
        unit = parse_unit(argz[0], custom_units=custom_units)
        return BaseType(base_type, unit, positional)
    # Subscripts
    elif isinstance(item, ast.Subscript):
        if 'value' not in vars(item.slice):
            raise InvalidTypeException(
                "Array / ByteArray access must access a single element, not a slice",
                item)
        # Fixed size lists or bytearrays, e.g. num[100]
        elif isinstance(item.slice.value, ast.Num):
            if not isinstance(item.slice.value.n,
                              int) or item.slice.value.n <= 0:
                raise InvalidTypeException(
                    "Arrays / ByteArrays must have a positive integral number of elements",
                    item.slice.value)
            # ByteArray
            if getattr(item.value, 'id', None) == 'bytes':
                return ByteArrayType(item.slice.value.n)
            # List
            else:
                return ListType(
                    parse_type(item.value, location,
                               custom_units=custom_units), item.slice.value.n)
        # Mappings, e.g. num[address]
        else:
            if location == 'memory':
                raise InvalidTypeException(
                    "No mappings allowed for in-memory types, only fixed-size arrays",
                    item)
            keytype = parse_type(item.slice.value, None)
            if not isinstance(keytype, (BaseType, ByteArrayType)):
                raise InvalidTypeException(
                    "Mapping keys must be base or bytes types",
                    item.slice.value)
            return MappingType(
                keytype,
                parse_type(item.value, location, custom_units=custom_units))
    # Dicts, used to represent mappings, e.g. {uint: uint}. Key must be a base type
    elif isinstance(item, ast.Dict):
        o = {}
        for key, value in zip(item.keys, item.values):
            if not isinstance(key, ast.Name) or not is_varname_valid(
                    key.id, custom_units):
                raise InvalidTypeException(
                    "Invalid member variable for struct", key)
            o[key.id] = parse_type(value, location, custom_units=custom_units)
        return StructType(o)
    elif isinstance(item, ast.Tuple):
        members = [
            parse_type(x, location, custom_units=custom_units)
            for x in item.elts
        ]
        return TupleType(members)
    else:
        raise InvalidTypeException("Invalid type: %r" % ast.dump(item), item)
Beispiel #10
0
    def from_definition(
        cls,
        code,
        sigs=None,
        custom_structs=None,
        interface_def=False,
        constants=None,
        constant_override=False,
        is_from_json=False,
    ):
        if not custom_structs:
            custom_structs = {}

        name = code.name
        mem_pos = 0

        valid_name, msg = is_varname_valid(name, custom_structs, constants)
        if not valid_name and (not name.lower() in FUNCTION_WHITELIST):
            raise FunctionDeclarationException("Function name invalid. " + msg, code)

        # Validate default values.
        for default_value in getattr(code.args, "defaults", []):
            validate_default_values(default_value)

        # Determine the arguments, expects something of the form def foo(arg1:
        # int128, arg2: int128 ...
        args = []
        for arg in code.args.args:
            # Each arg needs a type specified.
            typ = arg.annotation
            if not typ:
                raise InvalidType("Argument must have type", arg)
            # Validate arg name.
            check_valid_varname(
                arg.arg,
                custom_structs,
                constants,
                arg,
                "Argument name invalid or reserved. ",
                FunctionDeclarationException,
            )
            # Check for duplicate arg name.
            if arg.arg in (x.name for x in args):
                raise FunctionDeclarationException(
                    "Duplicate function argument name: " + arg.arg, arg,
                )
            parsed_type = parse_type(
                typ, None, sigs, custom_structs=custom_structs, constants=constants,
            )
            args.append(
                VariableRecord(arg.arg, mem_pos, parsed_type, False, defined_at=getpos(arg),)
            )

            if isinstance(parsed_type, ByteArrayLike):
                mem_pos += 32
            else:
                mem_pos += get_size_of_type(parsed_type) * 32

        mutability = "nonpayable"  # Assume nonpayable by default
        internal = False
        external = False
        nonreentrant_key = ""

        # Update function properties from decorators
        # NOTE: Can't import enums here because of circular import
        for dec in code.decorator_list:
            if isinstance(dec, vy_ast.Name) and dec.id in ("payable", "view", "pure"):
                mutability = dec.id
            elif isinstance(dec, vy_ast.Name) and dec.id == "internal":
                internal = True
            elif isinstance(dec, vy_ast.Name) and dec.id == "external":
                external = True
            elif isinstance(dec, vy_ast.Call) and dec.func.id == "nonreentrant":
                if nonreentrant_key:
                    raise StructureException(
                        "Only one @nonreentrant decorator allowed per function", dec
                    )
                if (
                    dec.args
                    and len(dec.args) == 1
                    and isinstance(dec.args[0], vy_ast.Str)
                    and dec.args[0].s
                ):  # noqa: E501
                    nonreentrant_key = dec.args[0].s
                else:
                    raise StructureException(
                        "@nonreentrant decorator requires a non-empty string to use as a key.", dec
                    )
            else:
                raise StructureException("Bad decorator", dec)

        if constant_override:
            # In case this override is abused, match previous behavior
            if mutability == "payable":
                raise StructureException(f"Function {name} cannot be both constant and payable.")
            mutability = "view"

        if external and internal:
            raise StructureException(
                f"Cannot use external and internal decorators on the same function: {name}"
            )
        if mutability == "payable" and internal:
            raise StructureException(f"Function {name} cannot be both internal and payable.")
        if (not external and not internal) and not interface_def:
            raise StructureException(
                "Function visibility must be declared (@external or @internal)", code,
            )
        if mutability in ("view", "pure") and nonreentrant_key:
            raise StructureException(
                f"@nonreentrant makes no sense on a @{mutability} function.", code
            )

        # Determine the return type and whether or not it's constant. Expects something
        # of the form:
        # def foo(): ...
        # def foo() -> int128: ...
        # If there is no return type, ie. it's of the form def foo(): ...
        # and NOT def foo() -> type: ..., then it's null
        if not code.returns:
            output_type = None
        elif isinstance(
            code.returns, (vy_ast.Name, vy_ast.Compare, vy_ast.Subscript, vy_ast.Call, vy_ast.Tuple)
        ):
            output_type = parse_type(
                code.returns, None, sigs, custom_structs=custom_structs, constants=constants,
            )
        else:
            raise InvalidType(
                f"Output type invalid or unsupported: {parse_type(code.returns, None)}",
                code.returns,
            )
        # Output type must be canonicalizable
        if output_type is not None:
            assert isinstance(output_type, TupleType) or canonicalize_type(output_type)
        # Get the canonical function signature
        sig = cls.get_full_sig(name, code.args.args, sigs, custom_structs, constants)

        # Take the first 4 bytes of the hash of the sig to get the method ID
        method_id = fourbytes_to_int(keccak256(bytes(sig, "utf-8"))[:4])
        return cls(
            name,
            args,
            output_type,
            mutability,
            internal,
            nonreentrant_key,
            sig,
            method_id,
            code,
            is_from_json,
        )
Beispiel #11
0
def add_globals_and_events(_custom_units, _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, 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)
    # 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:
        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), 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
Beispiel #12
0
    def from_definition(cls,
                        code,
                        sigs=None,
                        custom_units=None,
                        custom_structs=None,
                        contract_def=False,
                        constants=None,
                        constant=False):
        if not custom_structs:
            custom_structs = {}

        name = code.name
        mem_pos = 0

        valid_name, msg = is_varname_valid(name, custom_units, custom_structs,
                                           constants)
        if not valid_name and (not name.lower() in function_whitelist):
            raise FunctionDeclarationException("Function name invalid. " + msg,
                                               code)

        # Validate default values.
        for default_value in getattr(code.args, 'defaults', []):
            allowed_types = (ast.Num, ast.Str, ast.Bytes, ast.List,
                             ast.NameConstant)
            if not isinstance(default_value, allowed_types):
                raise FunctionDeclarationException(
                    "Default parameter values have to be literals.")

        # Determine the arguments, expects something of the form def foo(arg1:
        # int128, arg2: int128 ...
        args = []
        for arg in code.args.args:
            # Each arg needs a type specified.
            typ = arg.annotation
            if not typ:
                raise InvalidTypeException("Argument must have type", arg)
            # Validate arg name.
            check_valid_varname(
                arg.arg,
                custom_units,
                custom_structs,
                constants,
                arg,
                "Argument name invalid or reserved. ",
                FunctionDeclarationException,
            )
            # Check for duplicate arg name.
            if arg.arg in (x.name for x in args):
                raise FunctionDeclarationException(
                    "Duplicate function argument name: " + arg.arg,
                    arg,
                )
            parsed_type = parse_type(
                typ,
                None,
                sigs,
                custom_units=custom_units,
                custom_structs=custom_structs,
                constants=constants,
            )
            args.append(
                VariableRecord(
                    arg.arg,
                    mem_pos,
                    parsed_type,
                    False,
                    defined_at=getpos(arg),
                ))

            if isinstance(parsed_type, ByteArrayLike):
                mem_pos += 32
            else:
                mem_pos += get_size_of_type(parsed_type) * 32

        # Apply decorators
        const, payable, private, public, nonreentrant_key = False, False, False, False, ''
        for dec in code.decorator_list:
            if isinstance(dec, ast.Name) and dec.id == "constant":
                const = True
            elif isinstance(dec, ast.Name) and dec.id == "payable":
                payable = True
            elif isinstance(dec, ast.Name) and dec.id == "private":
                private = True
            elif isinstance(dec, ast.Name) and dec.id == "public":
                public = True
            elif isinstance(dec, ast.Call) and dec.func.id == "nonreentrant":
                if dec.args and len(dec.args) == 1 and isinstance(
                        dec.args[0], ast.Str) and dec.args[0].s:  # noqa: E501
                    nonreentrant_key = dec.args[0].s
                else:
                    raise StructureException(
                        "@nonreentrant decorator requires a non-empty string to use as a key.",
                        dec)
            else:
                raise StructureException("Bad decorator", dec)

        if public and private:
            raise StructureException(
                "Cannot use public and private decorators on the same function: {}"
                .format(name))
        if payable and const:
            raise StructureException(
                "Function {} cannot be both constant and payable.".format(
                    name))
        if payable and private:
            raise StructureException(
                "Function {} cannot be both private and payable.".format(name))
        if (not public and not private) and not contract_def:
            raise StructureException(
                "Function visibility must be declared (@public or @private)",
                code,
            )
        if constant and nonreentrant_key:
            raise StructureException(
                "@nonreentrant makes no sense on a @constant function.", code)
        if constant:
            const = True
        # Determine the return type and whether or not it's constant. Expects something
        # of the form:
        # def foo(): ...
        # def foo() -> int128: ...
        # If there is no return type, ie. it's of the form def foo(): ...
        # and NOT def foo() -> type: ..., then it's null
        if not code.returns:
            output_type = None
        elif isinstance(
                code.returns,
            (ast.Name, ast.Compare, ast.Subscript, ast.Call, ast.Tuple)):
            output_type = parse_type(
                code.returns,
                None,
                sigs,
                custom_units=custom_units,
                custom_structs=custom_structs,
                constants=constants,
            )
        else:
            raise InvalidTypeException(
                "Output type invalid or unsupported: %r" %
                parse_type(code.returns, None),
                code.returns,
            )
        # Output type must be canonicalizable
        if output_type is not None:
            assert isinstance(output_type,
                              TupleType) or canonicalize_type(output_type)
        # Get the canonical function signature
        sig = cls.get_full_sig(name, code.args.args, sigs, custom_units,
                               custom_structs, constants)

        # Take the first 4 bytes of the hash of the sig to get the method ID
        method_id = fourbytes_to_int(keccak256(bytes(sig, 'utf-8'))[:4])
        return cls(name, args, output_type, const, payable, private,
                   nonreentrant_key, sig, method_id, custom_units, code)
Beispiel #13
0
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