Ejemplo n.º 1
0
 def get_type(arg):
     if isinstance(arg, LLLnode):
         return canonicalize_type(arg.typ)
     elif hasattr(arg, "annotation"):
         return canonicalize_type(
             parse_type(arg.annotation, None, sigs, custom_structs=custom_structs,)
         )
Ejemplo n.º 2
0
    def _generate_output_abi(self, custom_units_descriptions=None):
        t = self.output_type
        if not t:
            return []
        elif isinstance(t, TupleType):
            res = [(canonicalize_type(x),
                    print_unit(unit_from_type(x), custom_units_descriptions))
                   for x in t.members]
        elif isinstance(t, TupleLike):
            res = [(canonicalize_type(x),
                    print_unit(unit_from_type(x), custom_units_descriptions))
                   for x in t.tuple_members()]
        else:
            res = [(canonicalize_type(t),
                    print_unit(unit_from_type(t), custom_units_descriptions))]

        abi_outputs = [{
            "type": x,
            "name": "out",
            "unit": unit
        } for x, unit in res]

        for abi_output in abi_outputs:
            delete_unit_if_empty(abi_output)

        return abi_outputs
Ejemplo n.º 3
0
 def get_type(arg):
     if isinstance(arg, LLLnode):
         return canonicalize_type(arg.typ)
     elif hasattr(arg, 'annotation'):
         return canonicalize_type(
             parse_type(arg.annotation,
                        None,
                        sigs,
                        custom_units=custom_units))
Ejemplo n.º 4
0
def test_canonicalize_type():
    # Non-basetype not allowed
    with raises(Exception):
        canonicalize_type(int)
    # List of byte arrays not allowed
    a = ListType(ByteArrayType(12), 2)
    with raises(Exception):
        canonicalize_type(a)
    # Test ABI format of multiple args.
    c = TupleType([BaseType('int128'), BaseType('address')])
    assert canonicalize_type(c) == "(int128,address)"
Ejemplo n.º 5
0
    def _generate_output_abi(self):
        t = self.output_type

        if not t:
            return []
        elif isinstance(t, TupleType):
            res = [canonicalize_type(x) for x in t.members]
        else:
            res = [canonicalize_type(t)]

        return [{"type": x, "name": "out"} for x in res]
Ejemplo n.º 6
0
    def to_abi_dict(self, custom_units_descriptions=None):
        func_type = "function"
        if self.name == "__init__":
            func_type = "constructor"
        if self.name == "__default__":
            func_type = "fallback"

        abi_dict = {
            "name": self.name,
            "outputs": self._generate_output_abi(custom_units_descriptions),
            "inputs": [{
                "type": canonicalize_type(arg.typ),
                "name": arg.name,
                "unit": print_unit(unit_from_type(arg.typ), custom_units_descriptions)
            } for arg in self.args],
            "constant": self.const,
            "payable": self.payable,
            "type": func_type
        }

        for abi_input in abi_dict['inputs']:
            delete_unit_if_empty(abi_input)

        if self.name in ('__default__', '__init__'):
            del abi_dict['name']
        if self.name == '__default__':
            del abi_dict['inputs']
            del abi_dict['outputs']

        return abi_dict
Ejemplo n.º 7
0
    def to_abi_dict(self, custom_units_descriptions=None):
        abi_dict = {
            "name":
            self.name,
            "outputs":
            self._generate_output_abi(custom_units_descriptions),
            "inputs": [{
                "type":
                canonicalize_type(arg.typ),
                "name":
                arg.name,
                "unit":
                print_unit(unit_from_type(arg.typ), custom_units_descriptions)
            } for arg in self.args],
            "constant":
            self.const,
            "payable":
            self.payable,
            "type":
            "constructor" if self.name == "__init__" else "function"
        }

        for abi_input in abi_dict['inputs']:
            delete_unit_if_empty(abi_input)

        return abi_dict
Ejemplo n.º 8
0
 def _generate_base_type(self, arg_type, name=None, custom_units_descriptions=None):
     yield "type", canonicalize_type(arg_type)
     u = unit_from_type(arg_type)
     if u:
         yield "unit", print_unit(u, custom_units_descriptions)
     name = "out" if not name else name
     yield "name", name
Ejemplo n.º 9
0
 def to_abi_dict(self):
     return {
         "name": self.name,
         "inputs": [{"type": canonicalize_type(arg.typ, self.indexed_list[pos]), "name": arg.name, "indexed": self.indexed_list[pos]} for pos, arg in enumerate(self.args)] if self.args else [],
         "anonymous": False,
         "type": "event"
     }
Ejemplo n.º 10
0
 def to_abi_event_dict(self, arg, pos, custom_units_descriptions):
     yield "type", canonicalize_type(arg.typ, self.indexed_list[pos]),
     yield "name", arg.name,
     yield "indexed", self.indexed_list[pos],
     u = unit_from_type(arg.typ)
     if u:
         yield "unit", print_unit(u, custom_units_descriptions)
Ejemplo n.º 11
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)
Ejemplo n.º 12
0
 def to_abi_dict(self):
     return {
         "name": self.name,
         "outputs": self._generate_output_abi(),
         "inputs": [{"type": canonicalize_type(arg.typ), "name": arg.name} for arg in self.args],
         "constant": self.const,
         "payable": self.payable,
         "type": "constructor" if self.name == "__init__" else "function"
     }
Ejemplo n.º 13
0
    def from_declaration(cls, class_node, global_ctx):
        name = class_node.name
        pos = 0

        check_valid_varname(
            name,
            global_ctx._structs,
            global_ctx._constants,
            pos=class_node,
            error_prefix="Event name invalid. ",
            exc=EventDeclarationException,
        )

        args = []
        indexed_list = []
        if len(class_node.body) != 1 or not isinstance(class_node.body[0],
                                                       vy_ast.Pass):
            for node in class_node.body:
                arg_item = node.target
                arg = node.target.id
                typ = node.annotation

                if isinstance(typ,
                              vy_ast.Call) and typ.get("func.id") == "indexed":
                    indexed_list.append(True)
                    typ = typ.args[0]
                else:
                    indexed_list.append(False)
                check_valid_varname(
                    arg,
                    global_ctx._structs,
                    global_ctx._constants,
                    pos=arg_item,
                    error_prefix="Event argument name invalid or reserved.",
                )
                if arg in (x.name for x in args):
                    raise TypeCheckFailure(
                        f"Duplicate function argument name: {arg}")
                # Can struct be logged?
                parsed_type = global_ctx.parse_type(typ, None)
                args.append(VariableRecord(arg, pos, parsed_type, False))
                if isinstance(parsed_type, ByteArrayType):
                    pos += ceil32(typ.slice.value.n)
                else:
                    pos += get_size_of_type(parsed_type) * 32

        sig = (name + "(" + ",".join([
            canonicalize_type(arg.typ, indexed_list[pos])
            for pos, arg in enumerate(args)
        ]) + ")")  # noqa F812
        event_id = bytes_to_int(keccak256(bytes(sig, "utf-8")))
        return cls(name, args, indexed_list, event_id, sig)
Ejemplo n.º 14
0
    def to_abi_dict(self, custom_units_descriptions=None):
        abi_dict = {
            "name": self.name,
            "inputs": [{
                "type": canonicalize_type(arg.typ, self.indexed_list[pos]),
                "name": arg.name,
                "indexed": self.indexed_list[pos],
                "unit": print_unit(unit_from_type(arg.typ), custom_units_descriptions)
            } for pos, arg in enumerate(self.args)] if self.args else [],
            "anonymous": False,
            "type": "event"
        }

        for abi_input in abi_dict['inputs']:
            delete_unit_if_empty(abi_input)

        return abi_dict
Ejemplo n.º 15
0
    def from_definition(
        cls,
        code,
        sigs=None,
        custom_structs=None,
        interface_def=False,
        constant_override=False,
        is_from_json=False,
    ):
        if not custom_structs:
            custom_structs = {}

        name = code.name
        mem_pos = 0

        # 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
            parsed_type = parse_type(
                typ,
                None,
                sigs,
                custom_structs=custom_structs,
            )
            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
        nonreentrant_key = ""
        is_internal = False

        # 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":
                is_internal = True
            elif isinstance(dec, vy_ast.Name) and dec.id == "external":
                is_internal = False
            elif isinstance(dec,
                            vy_ast.Call) and dec.func.id == "nonreentrant":
                nonreentrant_key = dec.args[0].s

        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"

        # 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
        output_type = None
        if code.returns:
            output_type = parse_type(
                code.returns,
                None,
                sigs,
                custom_structs=custom_structs,
            )
            # Output type must be canonicalizable
            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)

        # 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,
            is_internal,
            nonreentrant_key,
            sig,
            method_id,
            code,
            is_from_json,
        )
Ejemplo n.º 16
0
 def to_abi_event_dict(self, arg, pos):
     yield "type", canonicalize_type(arg.typ, self.indexed_list[pos]),
     yield "name", arg.name,
     yield "indexed", self.indexed_list[pos],
Ejemplo n.º 17
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)
Ejemplo n.º 18
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)
Ejemplo n.º 19
0
    def from_declaration(cls, code, global_ctx):
        name = code.target.id
        pos = 0

        check_valid_varname(name,
                            global_ctx._custom_units,
                            global_ctx._structs,
                            global_ctx._constants,
                            pos=code,
                            error_prefix="Event name invalid. ",
                            exc=EventDeclarationException)

        # Determine the arguments, expects something of the form def foo(arg1: num, arg2: num ...
        args = []
        indexed_list = []
        topics_count = 1
        if code.annotation.args:
            keys = code.annotation.args[0].keys
            values = code.annotation.args[0].values
            for i in range(len(keys)):
                typ = values[i]
                if not isinstance(keys[i], ast.Name):
                    raise EventDeclarationException(
                        'Invalid key type, expected a valid name.',
                        keys[i],
                    )
                if not isinstance(typ, (ast.Name, ast.Call, ast.Subscript)):
                    raise EventDeclarationException(
                        'Invalid event argument type.', typ)
                if isinstance(typ,
                              ast.Call) and not isinstance(typ.func, ast.Name):
                    raise EventDeclarationException(
                        'Invalid event argument type', typ)
                arg = keys[i].id
                arg_item = keys[i]
                is_indexed = False

                # Check to see if argument is a topic
                if isinstance(typ, ast.Call) and typ.func.id == 'indexed':
                    typ = values[i].args[0]
                    indexed_list.append(True)
                    topics_count += 1
                    is_indexed = True
                else:
                    indexed_list.append(False)
                if isinstance(typ, ast.Subscript) and getattr(
                        typ.value, 'id', None
                ) == 'bytes' and typ.slice.value.n > 32 and is_indexed:  # noqa: E501
                    raise EventDeclarationException(
                        "Indexed arguments are limited to 32 bytes")
                if topics_count > 4:
                    raise EventDeclarationException(
                        f"Maximum of 3 topics {topics_count - 1} given",
                        arg,
                    )
                if not isinstance(arg, str):
                    raise VariableDeclarationException("Argument name invalid",
                                                       arg)
                if not typ:
                    raise InvalidTypeException("Argument must have type", arg)
                check_valid_varname(
                    arg,
                    global_ctx._custom_units,
                    global_ctx._structs,
                    global_ctx._constants,
                    pos=arg_item,
                    error_prefix="Event argument name invalid or reserved.",
                )
                if arg in (x.name for x in args):
                    raise VariableDeclarationException(
                        "Duplicate function argument name: " + arg,
                        arg_item,
                    )
                # Can struct be logged?
                parsed_type = global_ctx.parse_type(typ, None)
                args.append(VariableRecord(arg, pos, parsed_type, False))
                if isinstance(parsed_type, ByteArrayType):
                    pos += ceil32(typ.slice.value.n)
                else:
                    pos += get_size_of_type(parsed_type) * 32
        sig = name + '(' + ','.join([
            canonicalize_type(arg.typ, indexed_list[pos])
            for pos, arg in enumerate(args)
        ]) + ')'  # noqa F812
        event_id = bytes_to_int(keccak256(bytes(sig, 'utf-8')))
        return cls(name, args, indexed_list, event_id, sig)
Ejemplo n.º 20
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,
        )
Ejemplo n.º 21
0
 def _generate_base_type(self, arg_type, name=""):
     yield "type", canonicalize_type(arg_type)
     yield "name", name