コード例 #1
0
def validate_default_values(node):
    if isinstance(node,
                  vy_ast.Name) and node.id in parser.expr.BUILTIN_CONSTANTS:
        return
    if isinstance(node, vy_ast.Attribute
                  ) and node.value.id in parser.expr.ENVIRONMENT_VARIABLES:
        return
    if not isinstance(node, (vy_ast.Constant, vy_ast.List)):
        raise FunctionDeclarationException(
            "Default value must be a literal, built-in constant, or environment variable.",
            node)
    if isinstance(node, vy_ast.List):
        for n in node.elts:
            validate_default_values(n)
コード例 #2
0
ファイル: function_signature.py プロジェクト: spencerbh/vyper
    def lookup_sig(cls, sigs, method_name, expr_args, stmt_or_expr, context):
        """ Using a list of args, determine the most accurate signature to use from the given context """
        def synonymise(s):
            return s.replace('int128', 'num').replace('uint256', 'num')

        # for sig in sigs['self']
        full_sig = cls.get_full_sig(stmt_or_expr.func.attr, expr_args, None,
                                    context.custom_units)
        method_names_dict = dict(
            Counter([x.split('(')[0] for x in context.sigs['self']]))
        if method_name not in method_names_dict:
            raise FunctionDeclarationException(
                "Function not declared yet (reminder: functions cannot "
                "call functions later in code than themselves): %s" %
                method_name)

        if method_names_dict[method_name] == 1:
            return next(sig for name, sig in context.sigs['self'].items()
                        if name.split('(')[0] == method_name)
        if full_sig in context.sigs['self']:
            return context.sigs['self'][full_sig]
        else:
            synonym_sig = synonymise(full_sig)
            syn_sigs_test = [synonymise(k) for k in context.sigs.keys()]
            if len(syn_sigs_test) != len(set(syn_sigs_test)):
                raise Exception(
                    'Incompatible default parameter signature,'
                    'can not tell the number type of literal', stmt_or_expr)
            synonym_sigs = [(synonymise(k), v)
                            for k, v in context.sigs['self'].items()]
            ssig = [s[1] for s in synonym_sigs if s[0] == synonym_sig]
            if len(ssig) == 0:
                raise FunctionDeclarationException(
                    "Function not declared yet (reminder: functions cannot "
                    "call functions later in code than themselves): %s" %
                    method_name)
            return ssig[0]
コード例 #3
0
    def __init__(self, vyper_module: vy_ast.Module,
                 fn_node: vy_ast.FunctionDef, namespace: dict) -> None:
        self.vyper_module = vyper_module
        self.fn_node = fn_node
        self.namespace = namespace
        self.func = namespace["self"].get_member(fn_node.name, fn_node)
        namespace.update(self.func.arguments)

        if self.func.visibility is FunctionVisibility.INTERNAL:
            node_list = fn_node.get_descendants(vy_ast.Attribute, {
                "value.id": "msg",
                "attr": "sender"
            })
            if node_list:
                raise StateAccessViolation(
                    "msg.sender is not allowed in internal functions",
                    node_list[0])
        if self.func.mutability == StateMutability.PURE:
            node_list = fn_node.get_descendants(
                vy_ast.Attribute,
                {
                    "value.id":
                    set(CONSTANT_ENVIRONMENT_VARS.keys()).union(
                        set(MUTABLE_ENVIRONMENT_VARS.keys()))
                },
            )
            if node_list:
                raise StateAccessViolation(
                    "not allowed to query contract or environment variables in pure functions",
                    node_list[0],
                )
        if self.func.mutability is not StateMutability.PAYABLE:
            node_list = fn_node.get_descendants(vy_ast.Attribute, {
                "value.id": "msg",
                "attr": "value"
            })
            if node_list:
                raise NonPayableViolation(
                    "msg.value is not allowed in non-payable functions",
                    node_list[0])

        for node in fn_node.body:
            self.visit(node)
        if self.func.return_type:
            if not check_for_terminus(fn_node.body):
                raise FunctionDeclarationException(
                    f"Missing or unmatched return statements in function '{fn_node.name}'",
                    fn_node,
                )
コード例 #4
0
    def get_global_context(cls, code):
        global_ctx = cls()

        for item in code:
            # Contract references
            if isinstance(item, ast.ClassDef):
                if global_ctx._events or global_ctx._globals or global_ctx._defs:
                    raise StructureException(
                        "External contract and struct declarations must come before event declarations, global declarations, and function definitions",
                        item)

                base_classes = [x.id for x in item.bases]
                if base_classes == ['__VYPER_ANNOT_STRUCT__']:
                    if global_ctx._contracts:
                        raise StructureException(
                            "Structs must come before external contract definitions",
                            item)
                    global_ctx._structs[item.name] = global_ctx.make_struct(
                        item.name, item.body)
                elif base_classes == ['__VYPER_ANNOT_CONTRACT__']:
                    global_ctx._contracts[
                        item.name] = GlobalContext.make_contract(item.body)

                elif base_classes == []:
                    raise StructureException(
                        "No base classes for class. This is likely a compiler bug, please report at https://github.com/ethereum/vyper/issues",
                        item)

                else:
                    raise StructureException(
                        "Multiple base classes for class. This is likely a compiler bug, please report at https://github.com/ethereum/vyper/issues",
                        item)

            # Statements of the form:
            # variable_name: type
            elif isinstance(item, ast.AnnAssign):
                global_ctx.add_globals_and_events(item)
            # Function definitions
            elif isinstance(item, ast.FunctionDef):
                if item.name in global_ctx._globals:
                    raise FunctionDeclarationException(
                        "Function name shadowing a variable name: %s" %
                        item.name)
                global_ctx._defs.append(item)
            else:
                raise StructureException("Invalid top-level statement", item)
        # Add getters to _defs
        global_ctx._defs += global_ctx._getters
        return global_ctx
コード例 #5
0
def parse_tree_to_lll(code, origcode, runtime_only=False):
    global_ctx = GlobalContext.get_global_context(code)
    _names_def = [_def.name for _def in global_ctx._defs]
    # Checks for duplicate function names
    if len(set(_names_def)) < len(_names_def):
        raise FunctionDeclarationException(
            "Duplicate function name: %s" %
            [name for name in _names_def if _names_def.count(name) > 1][0])
    _names_events = [_event.target.id for _event in global_ctx._events]
    # Checks for duplicate event names
    if len(set(_names_events)) < len(_names_events):
        raise EventDeclarationException(
            "Duplicate event name: %s" %
            [name
             for name in _names_events if _names_events.count(name) > 1][0])
    # Initialization function
    initfunc = [_def for _def in global_ctx._defs if is_initializer(_def)]
    # Default function
    defaultfunc = [_def for _def in global_ctx._defs if is_default_func(_def)]
    # Regular functions
    otherfuncs = [
        _def for _def in global_ctx._defs
        if not is_initializer(_def) and not is_default_func(_def)
    ]
    sigs = {}
    external_contracts = {}
    # Create the main statement
    o = ['seq']
    if global_ctx._events:
        sigs = parse_events(sigs, global_ctx._events, global_ctx._custom_units)
    if global_ctx._contracts:
        external_contracts = parse_external_contracts(external_contracts,
                                                      global_ctx._contracts)
    # If there is an init func...
    if initfunc:
        o.append(['seq', initializer_lll])
        o.append(
            parse_func(initfunc[0], {
                **{
                    'self': sigs
                },
                **external_contracts
            }, origcode, global_ctx))
    # If there are regular functions...
    if otherfuncs or defaultfunc:
        o = parse_other_functions(o, otherfuncs, sigs, external_contracts,
                                  origcode, global_ctx, defaultfunc,
                                  runtime_only)
    return LLLnode.from_list(o, typ=None)
コード例 #6
0
ファイル: local.py プロジェクト: vyperlang/vyper
    def __init__(self, vyper_module: vy_ast.Module,
                 fn_node: vy_ast.FunctionDef, namespace: dict) -> None:
        self.vyper_module = vyper_module
        self.fn_node = fn_node
        self.namespace = namespace
        self.func = fn_node._metadata["type"]
        self.annotation_visitor = StatementAnnotationVisitor(
            fn_node, namespace)
        self.expr_visitor = _LocalExpressionVisitor()
        namespace.update(self.func.arguments)

        for node in fn_node.body:
            self.visit(node)
        if self.func.return_type:
            if not check_for_terminus(fn_node.body):
                raise FunctionDeclarationException(
                    f"Missing or unmatched return statements in function '{fn_node.name}'",
                    fn_node)

        if self.func.mutability == StateMutability.PURE:
            node_list = fn_node.get_descendants(
                vy_ast.Attribute,
                {
                    "value.id":
                    set(CONSTANT_ENVIRONMENT_VARS.keys()).union(
                        set(MUTABLE_ENVIRONMENT_VARS.keys()))
                },
            )
            for node in node_list:
                t = node._metadata.get("type")
                if isinstance(t, ContractFunction
                              ) and t.mutability == StateMutability.PURE:
                    # allowed
                    continue
                raise StateAccessViolation(
                    "not allowed to query contract or environment variables in pure functions",
                    node_list[0],
                )
        if self.func.mutability is not StateMutability.PAYABLE:
            node_list = fn_node.get_descendants(vy_ast.Attribute, {
                "value.id": "msg",
                "attr": "value"
            })
            if node_list:
                raise NonPayableViolation(
                    "msg.value is not allowed in non-payable functions",
                    node_list[0])
コード例 #7
0
ファイル: module.py プロジェクト: ProGamerCode/vyper
def parse_external_interfaces(external_interfaces, global_ctx):
    for _interfacename in global_ctx._contracts:
        # TODO factor me into helper function
        _interface_defs = global_ctx._contracts[_interfacename]
        _defnames = [_def.name for _def in _interface_defs]
        interface = {}
        if len(set(_defnames)) < len(_interface_defs):
            raise FunctionDeclarationException(
                "Duplicate function name: "
                f"{[name for name in _defnames if _defnames.count(name) > 1][0]}"
            )

        for _def in _interface_defs:
            constant = False
            # test for valid call type keyword.
            if (len(_def.body) == 1 and isinstance(_def.body[0], vy_ast.Expr)
                    and isinstance(_def.body[0].value, vy_ast.Name)
                    # NOTE: Can't import enums here because of circular import
                    and _def.body[0].value.id
                    in ("pure", "view", "nonpayable", "payable")):
                constant = True if _def.body[0].value.id in ("view",
                                                             "pure") else False
            else:
                raise StructureException(
                    "state mutability of call type must be specified", _def)
            # Recognizes already-defined structs
            sig = FunctionSignature.from_definition(
                _def,
                sigs=global_ctx.interface_names,
                interface_def=True,
                constant_override=constant,
                custom_structs=global_ctx._structs,
            )
            interface[sig.name] = sig
        external_interfaces[_interfacename] = interface

    for interface_name, interface in global_ctx._interfaces.items():
        external_interfaces[interface_name] = {
            sig.name: sig
            for sig in interface if isinstance(sig, FunctionSignature)
        }

    return external_interfaces
コード例 #8
0
    def get_global_context(cls, code):
        global_ctx = cls()

        for item in code:
            # Contract references
            if isinstance(item, ast.ClassDef):
                if global_ctx._events or global_ctx._globals or global_ctx._defs:
                    raise StructureException(
                        "External contract and struct declarations must come before event declarations, global declarations, and function definitions",
                        item)

                if item.class_type == 'struct':
                    if global_ctx._contracts:
                        raise StructureException(
                            "Structs must come before external contract definitions",
                            item)
                    global_ctx._structs[item.name] = global_ctx.make_struct(
                        item.name, item.body)
                elif item.class_type == 'contract':
                    global_ctx._contracts[
                        item.name] = GlobalContext.make_contract(item.body)
                else:
                    raise StructureException(
                        "Unknown class_type. This is likely a compiler bug, please report",
                        item)

            # Statements of the form:
            # variable_name: type
            elif isinstance(item, ast.AnnAssign):
                global_ctx.add_globals_and_events(item)
            # Function definitions
            elif isinstance(item, ast.FunctionDef):
                if item.name in global_ctx._globals:
                    raise FunctionDeclarationException(
                        "Function name shadowing a variable name: %s" %
                        item.name)
                global_ctx._defs.append(item)
            else:
                raise StructureException("Invalid top-level statement", item)
        # Add getters to _defs
        global_ctx._defs += global_ctx._getters
        return global_ctx
コード例 #9
0
ファイル: parser.py プロジェクト: yashvadhvani/vyper
def parse_external_contracts(external_contracts, global_ctx):
    for _contractname in global_ctx._contracts:
        _contract_defs = global_ctx._contracts[_contractname]
        _defnames = [_def.name for _def in _contract_defs]
        contract = {}
        if len(set(_defnames)) < len(_contract_defs):
            raise FunctionDeclarationException(
                "Duplicate function name: %s" % (
                    [name for name in _defnames if _defnames.count(name) > 1][0]
                )
            )

        for _def in _contract_defs:
            constant = False
            # test for valid call type keyword.
            if len(_def.body) == 1 and \
               isinstance(_def.body[0], ast.Expr) and \
               isinstance(_def.body[0].value, ast.Name) and \
               _def.body[0].value.id in ('modifying', 'constant'):
                constant = True if _def.body[0].value.id == 'constant' else False
            else:
                raise StructureException('constant or modifying call type must be specified', _def)
            # Recognizes already-defined structs
            sig = FunctionSignature.from_definition(
                _def,
                contract_def=True,
                constant=constant,
                custom_structs=global_ctx._structs,
                constants=global_ctx._constants
            )
            contract[sig.name] = sig
        external_contracts[_contractname] = contract

    for interface_name, interface in global_ctx._interfaces.items():
        external_contracts[interface_name] = {
            sig.name: sig
            for sig in interface
            if isinstance(sig, FunctionSignature)
        }

    return external_contracts
コード例 #10
0
def parse_external_contracts(external_contracts, _contracts):
    for _contractname in _contracts:
        _contract_defs = _contracts[_contractname]
        _defnames = [_def.name for _def in _contract_defs]
        contract = {}
        if len(set(_defnames)) < len(_contract_defs):
            raise FunctionDeclarationException("Duplicate function name: %s" % [name for name in _defnames if _defnames.count(name) > 1][0])

        for _def in _contract_defs:
            constant = False
            # test for valid call type keyword.
            if len(_def.body) == 1 and \
               isinstance(_def.body[0], ast.Expr) and \
               isinstance(_def.body[0].value, ast.Name) and \
               _def.body[0].value.id in ('modifying', 'constant'):
                constant = True if _def.body[0].value.id == 'constant' else False
            else:
                raise StructureException('constant or modifying call type must be specified', _def)
            sig = FunctionSignature.from_definition(_def, contract_def=True, constant=constant)
            contract[sig.name] = sig
        external_contracts[_contractname] = contract
    return external_contracts
コード例 #11
0
def external_contract_call(node, context, contract_name, contract_address, pos, value=None, gas=None):
    if value is None:
        value = 0
    if gas is None:
        gas = 'gas'
    if contract_name not in context.sigs:
        raise VariableDeclarationException("Contract not declared yet: %s" % contract_name)
    method_name = node.func.attr
    if method_name not in context.sigs[contract_name]:
        raise FunctionDeclarationException("Function not declared yet: %s (reminder: "
                                                    "function must be declared in the correct contract)" % method_name, pos)
    sig = context.sigs[contract_name][method_name]
    inargs, inargsize = pack_arguments(sig, [parse_expr(arg, context) for arg in node.args], context, pos=pos)
    output_placeholder, output_size, returner = get_external_contract_call_output(sig, context)
    sub = ['seq', ['assert', ['extcodesize', contract_address]],
                    ['assert', ['ne', 'address', contract_address]]]
    if context.is_constant or sig.const:
        sub.append(['assert', ['staticcall', gas, contract_address, inargs, inargsize, output_placeholder, output_size]])
    else:
        sub.append(['assert', ['call', gas, contract_address, value, inargs, inargsize, output_placeholder, output_size]])
    sub.extend(returner)
    o = LLLnode.from_list(sub, typ=sig.output_type, location='memory', pos=getpos(node))
    return o
コード例 #12
0
ファイル: module.py プロジェクト: ProGamerCode/vyper
def parse_tree_to_ir(
        global_ctx: GlobalContext
) -> Tuple[IRnode, IRnode, FunctionSignatures]:
    _names_def = [_def.name for _def in global_ctx._defs]
    # Checks for duplicate function names
    if len(set(_names_def)) < len(_names_def):
        raise FunctionDeclarationException(
            "Duplicate function name: "
            f"{[name for name in _names_def if _names_def.count(name) > 1][0]}"
        )
    _names_events = [_event.name for _event in global_ctx._events]
    # Checks for duplicate event names
    if len(set(_names_events)) < len(_names_events):
        raise EventDeclarationException(f"""Duplicate event name:
            {[name for name in _names_events if _names_events.count(name) > 1][0]}"""
                                        )
    # Initialization function
    init_function = next(
        (_def for _def in global_ctx._defs if is_initializer(_def)), None)
    # Default function
    default_function = next(
        (i for i in global_ctx._defs if is_default_func(i)), None)

    regular_functions = [
        _def for _def in global_ctx._defs
        if not is_initializer(_def) and not is_default_func(_def)
    ]

    sigs: dict = {}
    external_interfaces: dict = {}
    # Create the main statement
    o: List[Union[str, IRnode]] = ["seq"]
    if global_ctx._contracts or global_ctx._interfaces:
        external_interfaces = parse_external_interfaces(
            external_interfaces, global_ctx)

    init_func_ir = None
    if init_function:
        init_func_ir, _frame_start, init_frame_size = generate_ir_for_function(
            init_function,
            {
                **{
                    "self": sigs
                },
                **external_interfaces
            },
            global_ctx,
            False,
        )
        o.append(init_func_ir)

    if regular_functions or default_function:
        runtime = parse_regular_functions(
            regular_functions,
            sigs,
            external_interfaces,
            global_ctx,
            default_function,
            init_func_ir,
        )
    else:
        # for some reason, somebody may want to deploy a contract with no code,
        # or more likely, a "pure data" contract which contains immutables
        runtime = IRnode.from_list(["seq"])

    immutables_len = global_ctx.immutable_section_bytes

    if init_function:
        memsize = init_func_ir.context.memory_allocator.size_of_mem  # type: ignore
    else:
        memsize = 0

    # note: (deploy mem_ofst, code, extra_padding)
    o.append(["deploy", memsize, runtime, immutables_len])  # type: ignore

    return IRnode.from_list(o), IRnode.from_list(runtime), sigs
コード例 #13
0
ファイル: external_call.py プロジェクト: vilsbole/vyper
def external_contract_call(node,
                           context,
                           contract_name,
                           contract_address,
                           pos,
                           value=None,
                           gas=None):
    from vyper.parser.parser import parse_expr
    if value is None:
        value = 0
    if gas is None:
        gas = 'gas'
    if not contract_name:
        raise StructureException(
            f'Invalid external contract call "{node.func.attr}".',
            node
        )
    if contract_name not in context.sigs:
        raise VariableDeclarationException(
            f'Contract "{contract_name}" not declared yet',
            node
        )
    method_name = node.func.attr
    if method_name not in context.sigs[contract_name]:
        raise FunctionDeclarationException(
            (
                "Function not declared yet: %s (reminder: "
                "function must be declared in the correct contract)"
                " The available methods are: %s"
            ) % (method_name, ",".join(context.sigs[contract_name].keys())),
            node.func
        )
    sig = context.sigs[contract_name][method_name]
    inargs, inargsize, _ = pack_arguments(
        sig,
        [parse_expr(arg, context) for arg in node.args],
        context,
        pos=pos,
    )
    output_placeholder, output_size, returner = get_external_contract_call_output(sig, context)
    sub = [
        'seq',
        ['assert', ['extcodesize', contract_address]],
        ['assert', ['ne', 'address', contract_address]],
    ]
    if context.is_constant() or sig.const:
        sub.append([
            'assert',
            [
                'staticcall',
                gas, contract_address, inargs, inargsize, output_placeholder, output_size,
            ]
        ])
    else:
        sub.append([
            'assert',
            [
                'call',
                gas, contract_address, value, inargs, inargsize, output_placeholder, output_size,
            ]
        ])
    sub.extend(returner)
    o = LLLnode.from_list(sub, typ=sig.output_type, location='memory', pos=getpos(node))
    return o
コード例 #14
0
ファイル: function_signature.py プロジェクト: aihuawu/vyper
    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,
        )
コード例 #15
0
ファイル: parser.py プロジェクト: sambacha/vyper-xcode
def parse_tree_to_lll(global_ctx: GlobalContext) -> Tuple[LLLnode, LLLnode]:
    _names_def = [_def.name for _def in global_ctx._defs]
    # Checks for duplicate function names
    if len(set(_names_def)) < len(_names_def):
        raise FunctionDeclarationException(
            "Duplicate function name: "
            f"{[name for name in _names_def if _names_def.count(name) > 1][0]}"
        )
    _names_events = [_event.name for _event in global_ctx._events]
    # Checks for duplicate event names
    if len(set(_names_events)) < len(_names_events):
        raise EventDeclarationException(f"""Duplicate event name:
            {[name for name in _names_events if _names_events.count(name) > 1][0]}"""
                                        )
    # Initialization function
    initfunc = [_def for _def in global_ctx._defs if is_initializer(_def)]
    # Default function
    defaultfunc = next((i for i in global_ctx._defs if is_default_func(i)),
                       None)
    # Regular functions
    otherfuncs = [
        _def for _def in global_ctx._defs
        if not is_initializer(_def) and not is_default_func(_def)
    ]

    sigs: dict = {}
    external_interfaces: dict = {}
    # Create the main statement
    o = ["seq"]
    if global_ctx._contracts or global_ctx._interfaces:
        external_interfaces = parse_external_interfaces(
            external_interfaces, global_ctx)
    # If there is an init func...
    if initfunc:
        o.append(init_func_init_lll())
        o.append(
            parse_function(
                initfunc[0],
                {
                    **{
                        "self": sigs
                    },
                    **external_interfaces
                },
                global_ctx,
                False,
            ))

    # If there are regular functions...
    if otherfuncs or defaultfunc:
        o, runtime = parse_other_functions(
            o,
            otherfuncs,
            sigs,
            external_interfaces,
            global_ctx,
            defaultfunc,
        )
    else:
        runtime = o.copy()

    return LLLnode.from_list(o, typ=None), LLLnode.from_list(runtime, typ=None)
コード例 #16
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)
コード例 #17
0
def parse_func(code, sigs, origcode, global_ctx, _vars=None):
    if _vars is None:
        _vars = {}
    sig = FunctionSignature.from_definition(
        code,
        sigs=sigs,
        custom_units=global_ctx._custom_units,
        custom_structs=global_ctx._structs,
        constants=global_ctx._constants)
    # Get base args for function.
    total_default_args = len(code.args.defaults)
    base_args = sig.args[:
                         -total_default_args] if total_default_args > 0 else sig.args
    default_args = code.args.args[-total_default_args:]
    default_values = dict(
        zip([arg.arg for arg in default_args], code.args.defaults))
    # __init__ function may not have defaults.
    if sig.name == '__init__' and total_default_args > 0:
        raise FunctionDeclarationException(
            "__init__ function may not have default parameters.")
    # Check for duplicate variables with globals
    for arg in sig.args:
        if arg.name in global_ctx._globals:
            raise FunctionDeclarationException(
                "Variable name duplicated between function arguments and globals: "
                + arg.name)

    nonreentrant_pre = [['pass']]
    nonreentrant_post = [['pass']]
    if sig.nonreentrant_key:
        nkey = global_ctx.get_nonrentrant_counter(sig.nonreentrant_key)
        nonreentrant_pre = [[
            'seq', ['assert', ['iszero', ['sload', nkey]]],
            ['sstore', nkey, 1]
        ]]
        nonreentrant_post = [['sstore', nkey, 0]]

    # Create a local (per function) context.
    context = Context(
        vars=_vars,
        global_ctx=global_ctx,
        sigs=sigs,
        return_type=sig.output_type,
        constancy=Constancy.Constant if sig.const else Constancy.Mutable,
        is_payable=sig.payable,
        origcode=origcode,
        is_private=sig.private,
        method_id=sig.method_id)

    # Copy calldata to memory for fixed-size arguments
    max_copy_size = sum([
        32 if isinstance(arg.typ, ByteArrayLike) else
        get_size_of_type(arg.typ) * 32 for arg in sig.args
    ])
    base_copy_size = sum([
        32 if isinstance(arg.typ, ByteArrayLike) else
        get_size_of_type(arg.typ) * 32 for arg in base_args
    ])
    context.next_mem += max_copy_size

    clampers = []

    # Create callback_ptr, this stores a destination in the bytecode for a private
    # function to jump to after a function has executed.
    _post_callback_ptr = "{}_{}_post_callback_ptr".format(
        sig.name, sig.method_id)
    if sig.private:
        context.callback_ptr = context.new_placeholder(typ=BaseType('uint256'))
        clampers.append(
            LLLnode.from_list(
                ['mstore', context.callback_ptr, 'pass'],
                annotation='pop callback pointer',
            ))
        if total_default_args > 0:
            clampers.append(['label', _post_callback_ptr])

    # private functions without return types need to jump back to
    # the calling function, as there is no return statement to handle the
    # jump.
    stop_func = [['stop']]
    if sig.output_type is None and sig.private:
        stop_func = [['jump', ['mload', context.callback_ptr]]]

    if not len(base_args):
        copier = 'pass'
    elif sig.name == '__init__':
        copier = [
            'codecopy', MemoryPositions.RESERVED_MEMORY, '~codelen',
            base_copy_size
        ]
    else:
        copier = get_arg_copier(sig=sig,
                                total_size=base_copy_size,
                                memory_dest=MemoryPositions.RESERVED_MEMORY)
    clampers.append(copier)

    # Add asserts for payable and internal
    # private never gets payable check.
    if not sig.payable and not sig.private:
        clampers.append(['assert', ['iszero', 'callvalue']])

    # Fill variable positions
    for i, arg in enumerate(sig.args):
        if i < len(base_args) and not sig.private:
            clampers.append(
                make_clamper(
                    arg.pos,
                    context.next_mem,
                    arg.typ,
                    sig.name == '__init__',
                ))
        if isinstance(arg.typ, ByteArrayLike):
            context.vars[arg.name] = VariableRecord(arg.name, context.next_mem,
                                                    arg.typ, False)
            context.next_mem += 32 * get_size_of_type(arg.typ)
        else:
            context.vars[arg.name] = VariableRecord(
                arg.name,
                MemoryPositions.RESERVED_MEMORY + arg.pos,
                arg.typ,
                False,
            )

    # Private function copiers. No clamping for private functions.
    dyn_variable_names = [
        a.name for a in base_args if isinstance(a.typ, ByteArrayLike)
    ]
    if sig.private and dyn_variable_names:
        i_placeholder = context.new_placeholder(typ=BaseType('uint256'))
        unpackers = []
        for idx, var_name in enumerate(dyn_variable_names):
            var = context.vars[var_name]
            ident = "_load_args_%d_dynarg%d" % (sig.method_id, idx)
            o = make_unpacker(ident=ident,
                              i_placeholder=i_placeholder,
                              begin_pos=var.pos)
            unpackers.append(o)

        if not unpackers:
            unpackers = ['pass']

        clampers.append(
            LLLnode.from_list(
                # [0] to complete full overarching 'seq' statement, see private_label.
                ['seq_unchecked'] + unpackers + [0],
                typ=None,
                annotation='dynamic unpacker',
                pos=getpos(code),
            ))

    # Create "clampers" (input well-formedness checkers)
    # Return function body
    if sig.name == '__init__':
        o = LLLnode.from_list(
            ['seq'] + clampers + [parse_body(code.body, context)],
            pos=getpos(code),
        )
    elif is_default_func(sig):
        if len(sig.args) > 0:
            raise FunctionDeclarationException(
                'Default function may not receive any arguments.', code)
        if sig.private:
            raise FunctionDeclarationException(
                'Default function may only be public.',
                code,
            )
        o = LLLnode.from_list(
            ['seq'] + clampers + [parse_body(code.body, context)],
            pos=getpos(code),
        )
    else:

        if total_default_args > 0:  # Function with default parameters.
            function_routine = "{}_{}".format(sig.name, sig.method_id)
            default_sigs = generate_default_arg_sigs(code, sigs, global_ctx)
            sig_chain = ['seq']

            for default_sig in default_sigs:
                sig_compare, private_label = get_sig_statements(
                    default_sig, getpos(code))

                # Populate unset default variables
                populate_arg_count = len(sig.args) - len(default_sig.args)
                set_defaults = []
                if populate_arg_count > 0:
                    current_sig_arg_names = {x.name for x in default_sig.args}
                    missing_arg_names = [
                        arg.arg for arg in default_args
                        if arg.arg not in current_sig_arg_names
                    ]
                    for arg_name in missing_arg_names:
                        value = Expr(default_values[arg_name],
                                     context).lll_node
                        var = context.vars[arg_name]
                        left = LLLnode.from_list(var.pos,
                                                 typ=var.typ,
                                                 location='memory',
                                                 pos=getpos(code),
                                                 mutable=var.mutable)
                        set_defaults.append(
                            make_setter(left,
                                        value,
                                        'memory',
                                        pos=getpos(code)))

                current_sig_arg_names = {x.name for x in default_sig.args}
                base_arg_names = {arg.name for arg in base_args}
                if sig.private:
                    # Load all variables in default section, if private,
                    # because the stack is a linear pipe.
                    copier_arg_count = len(default_sig.args)
                    copier_arg_names = current_sig_arg_names
                else:
                    copier_arg_count = len(default_sig.args) - len(base_args)
                    copier_arg_names = current_sig_arg_names - base_arg_names
                # Order copier_arg_names, this is very important.
                copier_arg_names = [
                    x.name for x in default_sig.args
                    if x.name in copier_arg_names
                ]

                # Variables to be populated from calldata/stack.
                default_copiers = []
                if copier_arg_count > 0:
                    # Get map of variables in calldata, with thier offsets
                    offset = 4
                    calldata_offset_map = {}
                    for arg in default_sig.args:
                        calldata_offset_map[arg.name] = offset
                        offset += (32 if isinstance(arg.typ, ByteArrayLike)
                                   else get_size_of_type(arg.typ) * 32)
                    # Copy set default parameters from calldata
                    dynamics = []
                    for arg_name in copier_arg_names:
                        var = context.vars[arg_name]
                        calldata_offset = calldata_offset_map[arg_name]
                        if sig.private:
                            _offset = calldata_offset
                            if isinstance(var.typ, ByteArrayLike):
                                _size = 32
                                dynamics.append(var.pos)
                            else:
                                _size = var.size * 32
                            default_copiers.append(
                                get_arg_copier(
                                    sig=sig,
                                    memory_dest=var.pos,
                                    total_size=_size,
                                    offset=_offset,
                                ))
                        else:
                            # Add clampers.
                            default_copiers.append(
                                make_clamper(
                                    calldata_offset - 4,
                                    var.pos,
                                    var.typ,
                                ))
                            # Add copying code.
                            if isinstance(var.typ, ByteArrayLike):
                                _offset = [
                                    'add', 4,
                                    ['calldataload', calldata_offset]
                                ]
                            else:
                                _offset = calldata_offset
                            default_copiers.append(
                                get_arg_copier(
                                    sig=sig,
                                    memory_dest=var.pos,
                                    total_size=var.size * 32,
                                    offset=_offset,
                                ))

                    # Unpack byte array if necessary.
                    if dynamics:
                        i_placeholder = context.new_placeholder(
                            typ=BaseType('uint256'))
                        for idx, var_pos in enumerate(dynamics):
                            ident = 'unpack_default_sig_dyn_%d_arg%d' % (
                                default_sig.method_id, idx)
                            default_copiers.append(
                                make_unpacker(
                                    ident=ident,
                                    i_placeholder=i_placeholder,
                                    begin_pos=var_pos,
                                ))
                    default_copiers.append(0)  # for over arching seq, POP

                sig_chain.append([
                    'if', sig_compare,
                    [
                        'seq', private_label, ['pass'] if not sig.private else
                        LLLnode.from_list([
                            'mstore',
                            context.callback_ptr,
                            'pass',
                        ],
                                          annotation='pop callback pointer',
                                          pos=getpos(code)),
                        ['seq'] + set_defaults if set_defaults else ['pass'],
                        ['seq_unchecked'] +
                        default_copiers if default_copiers else ['pass'],
                        [
                            'goto', _post_callback_ptr
                            if sig.private else function_routine
                        ]
                    ]
                ])

            # With private functions all variable loading occurs in the default
            # function sub routine.
            if sig.private:
                _clampers = [['label', _post_callback_ptr]]
            else:
                _clampers = clampers

            # Function with default parameters.
            o = LLLnode.from_list(
                [
                    'seq',
                    sig_chain,
                    [
                        'if',
                        0,  # can only be jumped into
                        [
                            'seq', ['label', function_routine]
                            if not sig.private else ['pass'],
                            ['seq'] + nonreentrant_pre + _clampers +
                            [parse_body(c, context) for c in code.body] +
                            nonreentrant_post + stop_func
                        ],
                    ],
                ],
                typ=None,
                pos=getpos(code))

        else:
            # Function without default parameters.
            sig_compare, private_label = get_sig_statements(sig, getpos(code))
            o = LLLnode.from_list([
                'if', sig_compare,
                ['seq'] + [private_label] + nonreentrant_pre + clampers +
                [parse_body(c, context)
                 for c in code.body] + nonreentrant_post + stop_func
            ],
                                  typ=None,
                                  pos=getpos(code))

    # Check for at leasts one return statement if necessary.
    if context.return_type and context.function_return_count == 0:
        raise FunctionDeclarationException(
            "Missing return statement in function '%s' " % sig.name, code)

    o.context = context
    o.total_gas = o.gas + calc_mem_gas(o.context.next_mem)
    o.func_name = sig.name
    return o
コード例 #18
0
ファイル: function_signature.py プロジェクト: spencerbh/vyper
    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)
コード例 #19
0
ファイル: parser.py プロジェクト: shogochiai/vyper
def parse_tree_to_lll(
    code,
    origcode,
    runtime_only=False,
    interface_codes=None,
):
    global_ctx = GlobalContext.get_global_context(code, interface_codes)
    _names_def = [_def.name for _def in global_ctx._defs]
    # Checks for duplicate function names
    if len(set(_names_def)) < len(_names_def):
        raise FunctionDeclarationException(
            "Duplicate function name: %s" %
            [name for name in _names_def if _names_def.count(name) > 1][0])
    _names_events = [_event.target.id for _event in global_ctx._events]
    # Checks for duplicate event names
    if len(set(_names_events)) < len(_names_events):
        raise EventDeclarationException(
            "Duplicate event name: %s" %
            [name
             for name in _names_events if _names_events.count(name) > 1][0])
    # Initialization function
    initfunc = [_def for _def in global_ctx._defs if is_initializer(_def)]
    # Default function
    defaultfunc = [_def for _def in global_ctx._defs if is_default_func(_def)]
    # Regular functions
    otherfuncs = [
        _def for _def in global_ctx._defs
        if not is_initializer(_def) and not is_default_func(_def)
    ]
    sigs = {}
    external_contracts = {}
    # Create the main statement
    o = ['seq']
    if global_ctx._events:
        sigs = parse_events(sigs, global_ctx)
    if global_ctx._contracts:
        external_contracts = parse_external_contracts(external_contracts,
                                                      global_ctx._contracts,
                                                      global_ctx._structs,
                                                      global_ctx._constants)
    # If there is an init func...
    if initfunc:
        o.append(['seq', initializer_lll])
        o.append(
            parse_func(initfunc[0], {
                **{
                    'self': sigs
                },
                **external_contracts
            }, origcode, global_ctx))
    # If there are regular functions...
    if otherfuncs or defaultfunc:
        o = parse_other_functions(o, otherfuncs, sigs, external_contracts,
                                  origcode, global_ctx, defaultfunc,
                                  runtime_only)
    # Check interface.
    if global_ctx._interface:
        funcs_left = global_ctx._interface.copy()

        for sig, func_sig in sigs.items():
            if isinstance(func_sig, FunctionSignature):
                if sig in funcs_left and not func_sig.private:
                    del funcs_left[sig]
            if isinstance(func_sig,
                          EventSignature) and func_sig.sig in funcs_left:
                del funcs_left[func_sig.sig]

        if funcs_left:
            error_message = 'Contract does not comply to supplied Interface(s).\n'
            missing_functions = [
                sig_name for sig_name, func_sig in funcs_left.items()
                if isinstance(func_sig, FunctionSignature)
            ]
            missing_events = [
                sig_name for sig_name, func_sig in funcs_left.items()
                if isinstance(func_sig, EventSignature)
            ]
            if missing_functions:
                error_message += 'Missing interface functions:\n\t{}'.format(
                    '\n\t'.join(missing_functions))
            if missing_events:
                error_message += 'Missing interface events:\n\t{}'.format(
                    '\n\t'.join(missing_events))
            raise StructureException(error_message)

    return LLLnode.from_list(o, typ=None)
コード例 #20
0
def parse_func(code, _globals, sigs, origcode, _custom_units, _vars=None):
    if _vars is None:
        _vars = {}
    sig = FunctionSignature.from_definition(code, sigs=sigs, custom_units=_custom_units)
    # Check for duplicate variables with globals
    for arg in sig.args:
        if arg.name in _globals:
            raise FunctionDeclarationException("Variable name duplicated between function arguments and globals: " + arg.name)
    # Create a context
    context = Context(vars=_vars, globals=_globals, sigs=sigs,
                      return_type=sig.output_type, is_constant=sig.const, is_payable=sig.payable, origcode=origcode, custom_units=_custom_units)
    # Copy calldata to memory for fixed-size arguments
    copy_size = sum([32 if isinstance(arg.typ, ByteArrayType) else get_size_of_type(arg.typ) * 32 for arg in sig.args])
    context.next_mem += copy_size
    if not len(sig.args):
        copier = 'pass'
    elif sig.name == '__init__':
        copier = ['codecopy', MemoryPositions.RESERVED_MEMORY, '~codelen', copy_size]
    else:
        copier = ['calldatacopy', MemoryPositions.RESERVED_MEMORY, 4, copy_size]
    clampers = [copier]
    # Add asserts for payable and internal
    if not sig.payable:
        clampers.append(['assert', ['iszero', 'callvalue']])
    if sig.private:
        clampers.append(['assert', ['eq', 'caller', 'address']])
    # Fill in variable positions
    for arg in sig.args:
        clampers.append(make_clamper(arg.pos, context.next_mem, arg.typ, sig.name == '__init__'))
        if isinstance(arg.typ, ByteArrayType):
            context.vars[arg.name] = VariableRecord(arg.name, context.next_mem, arg.typ, False)
            context.next_mem += 32 * get_size_of_type(arg.typ)
        else:
            context.vars[arg.name] = VariableRecord(arg.name, MemoryPositions.RESERVED_MEMORY + arg.pos, arg.typ, False)
    # Create "clampers" (input well-formedness checkers)
    # Return function body
    if sig.name == '__init__':
        o = LLLnode.from_list(['seq'] + clampers + [parse_body(code.body, context)], pos=getpos(code))
    elif is_default_func(sig):
        if len(sig.args) > 0:
            raise FunctionDeclarationException('Default function may not receive any arguments.', code)
        if sig.private:
            raise FunctionDeclarationException('Default function may only be public.', code)
        o = LLLnode.from_list(['seq'] + clampers + [parse_body(code.body, context)], pos=getpos(code))
    else:
        method_id_node = LLLnode.from_list(sig.method_id, pos=getpos(code), annotation='%s' % sig.name)
        o = LLLnode.from_list(['if',
                                  ['eq', ['mload', 0], method_id_node],
                                  ['seq'] + clampers + [parse_body(c, context) for c in code.body] + ['stop']
                               ], typ=None, pos=getpos(code))

    # Check for at leasts one return statement if necessary.
    if context.return_type and context.function_return_count == 0:
        raise FunctionDeclarationException(
            "Missing return statement in function '%s' " % sig.name, code
        )

    o.context = context
    o.total_gas = o.gas + calc_mem_gas(o.context.next_mem)
    o.func_name = sig.name
    return o
コード例 #21
0
ファイル: stmt.py プロジェクト: xitter/vyper
    def call(self):
        from .parser import (
            pack_arguments,
            pack_logging_data,
            pack_logging_topics,
            external_contract_call,
        )
        if isinstance(self.stmt.func, ast.Name):
            if self.stmt.func.id in stmt_dispatch_table:
                return stmt_dispatch_table[self.stmt.func.id](self.stmt,
                                                              self.context)
            elif self.stmt.func.id in dispatch_table:
                raise StructureException(
                    "Function {} can not be called without being used.".format(
                        self.stmt.func.id), self.stmt)
            else:
                raise StructureException(
                    "Unknown function: '{}'.".format(self.stmt.func.id),
                    self.stmt)
        elif isinstance(self.stmt.func, ast.Attribute) and isinstance(
                self.stmt.func.value,
                ast.Name) and self.stmt.func.value.id == "self":
            method_name = self.stmt.func.attr
            if method_name not in self.context.sigs['self']:
                raise FunctionDeclarationException(
                    "Function not declared yet (reminder: functions cannot "
                    "call functions later in code than themselves): %s" %
                    self.stmt.func.attr)
            sig = self.context.sigs['self'][method_name]
            if self.context.is_constant and not sig.const:
                raise ConstancyViolationException(
                    "May not call non-constant function '%s' within a constant function."
                    % (method_name))
            add_gas = self.context.sigs['self'][method_name].gas
            inargs, inargsize = pack_arguments(
                sig,
                [Expr(arg, self.context).lll_node for arg in self.stmt.args],
                self.context,
                pos=getpos(self.stmt))
            return LLLnode.from_list([
                'assert',
                ['call', ['gas'], ['address'], 0, inargs, inargsize, 0, 0]
            ],
                                     typ=None,
                                     pos=getpos(self.stmt),
                                     add_gas_estimate=add_gas,
                                     annotation='Internal Call: %s' %
                                     method_name)
        elif isinstance(self.stmt.func, ast.Attribute) and isinstance(
                self.stmt.func.value, ast.Call):
            contract_name = self.stmt.func.value.func.id
            contract_address = Expr.parse_value_expr(
                self.stmt.func.value.args[0], self.context)
            return external_contract_call(self.stmt,
                                          self.context,
                                          contract_name,
                                          contract_address,
                                          pos=getpos(self.stmt))
        elif isinstance(self.stmt.func.value, ast.Attribute
                        ) and self.stmt.func.value.attr in self.context.sigs:
            contract_name = self.stmt.func.value.attr
            var = self.context.globals[self.stmt.func.value.attr]
            contract_address = unwrap_location(
                LLLnode.from_list(var.pos,
                                  typ=var.typ,
                                  location='storage',
                                  pos=getpos(self.stmt),
                                  annotation='self.' +
                                  self.stmt.func.value.attr))
            return external_contract_call(self.stmt,
                                          self.context,
                                          contract_name,
                                          contract_address,
                                          pos=getpos(self.stmt))
        elif isinstance(
                self.stmt.func.value, ast.Attribute
        ) and self.stmt.func.value.attr in self.context.globals:
            contract_name = self.context.globals[
                self.stmt.func.value.attr].typ.unit
            var = self.context.globals[self.stmt.func.value.attr]
            contract_address = unwrap_location(
                LLLnode.from_list(var.pos,
                                  typ=var.typ,
                                  location='storage',
                                  pos=getpos(self.stmt),
                                  annotation='self.' +
                                  self.stmt.func.value.attr))
            return external_contract_call(self.stmt,
                                          self.context,
                                          contract_name,
                                          contract_address,
                                          pos=getpos(self.stmt))
        elif isinstance(self.stmt.func,
                        ast.Attribute) and self.stmt.func.value.id == 'log':
            if self.stmt.func.attr not in self.context.sigs['self']:
                raise EventDeclarationException("Event not declared yet: %s" %
                                                self.stmt.func.attr)
            event = self.context.sigs['self'][self.stmt.func.attr]
            if len(event.indexed_list) != len(self.stmt.args):
                raise EventDeclarationException(
                    "%s received %s arguments but expected %s" %
                    (event.name, len(self.stmt.args), len(event.indexed_list)))
            expected_topics, topics = [], []
            expected_data, data = [], []
            for pos, is_indexed in enumerate(event.indexed_list):
                if is_indexed:
                    expected_topics.append(event.args[pos])
                    topics.append(self.stmt.args[pos])
                else:
                    expected_data.append(event.args[pos])
                    data.append(self.stmt.args[pos])
            topics = pack_logging_topics(event.event_id,
                                         topics,
                                         expected_topics,
                                         self.context,
                                         pos=getpos(self.stmt))
            inargs, inargsize, inargsize_node, inarg_start = pack_logging_data(
                expected_data, data, self.context, pos=getpos(self.stmt))

            if inargsize_node is None:
                sz = inargsize
            else:
                sz = ['mload', inargsize_node]

            return LLLnode.from_list([
                'seq', inargs,
                LLLnode.from_list(
                    ["log" + str(len(topics)), inarg_start, sz] + topics,
                    add_gas_estimate=inargsize * 10)
            ],
                                     typ=None,
                                     pos=getpos(self.stmt))
        else:
            raise StructureException(
                "Unsupported operator: %r" % ast.dump(self.stmt), self.stmt)
コード例 #22
0
def validate_private_function(code: ast.FunctionDef,
                              sig: FunctionSignature) -> None:
    """ Validate private function defintion """
    if sig.is_default_func():
        raise FunctionDeclarationException(
            'Default function may only be public.', code)
コード例 #23
0
    def get_global_context(cls, code, interface_codes=None):
        from vyper.signatures.interface import (
            extract_sigs,
            get_builtin_interfaces,
        )
        interface_codes = {} if interface_codes is None else interface_codes
        global_ctx = cls()

        for item in code:
            # Contract references
            if isinstance(item, ast.ClassDef):
                if global_ctx._events or global_ctx._globals or global_ctx._defs:
                    raise StructureException(
                        ("External contract and struct declarations must come "
                         "before event declarations, global declarations, and "
                         "function definitions"), item)

                if item.class_type == 'struct':
                    if global_ctx._contracts:
                        raise StructureException(
                            "Structs must come before external contract definitions",
                            item)
                    global_ctx._structs[item.name] = global_ctx.make_struct(
                        item.name, item.body)
                elif item.class_type == 'contract':
                    if item.name in global_ctx._contracts or item.name in global_ctx._interfaces:
                        raise StructureException(
                            f"Contract '{item.name}' is already defined",
                            item,
                        )
                    global_ctx._contracts[
                        item.name] = GlobalContext.make_contract(item.body)
                else:
                    raise StructureException(
                        "Unknown class_type. This is likely a compiler bug, please report",
                        item)

            # Statements of the form:
            # variable_name: type
            elif isinstance(item, ast.AnnAssign):
                is_implements_statement = (isinstance(item.target, ast.Name)
                                           and item.target.id
                                           == 'implements') and item.annotation

                # implements statement.
                if is_implements_statement:
                    interface_name = item.annotation.id
                    if interface_name not in global_ctx._interfaces:
                        raise StructureException(
                            f'Unknown interface specified: {interface_name}',
                            item)
                    global_ctx._implemented_interfaces.add(interface_name)
                else:
                    global_ctx.add_globals_and_events(item)
            # Function definitions
            elif isinstance(item, ast.FunctionDef):
                if item.name in global_ctx._globals:
                    raise FunctionDeclarationException(
                        f"Function name shadowing a variable name: {item.name}"
                    )
                global_ctx._defs.append(item)
            elif isinstance(item, ast.ImportFrom):
                if not item.level and item.module == 'vyper.interfaces':
                    built_in_interfaces = get_builtin_interfaces()
                    for item_alias in item.names:
                        interface_name = item_alias.name
                        if interface_name in global_ctx._interfaces:
                            raise StructureException(
                                f'Duplicate import of {interface_name}', item)
                        if interface_name not in built_in_interfaces:
                            raise StructureException(
                                f'Built-In interface {interface_name} does not exist.',
                                item)
                        global_ctx._interfaces[
                            interface_name] = built_in_interfaces[
                                interface_name].copy()  # noqa: E501
                else:
                    for item_alias in item.names:
                        interface_name = item_alias.name

                        if interface_name in global_ctx._interfaces:
                            raise StructureException(
                                f'Duplicate import of {interface_name}', item)
                        if interface_name not in interface_codes:
                            raise StructureException(
                                f'Unknown interface {interface_name}', item)
                        global_ctx._interfaces[interface_name] = extract_sigs(
                            interface_codes[interface_name])  # noqa: E501
            elif isinstance(item, ast.Import):
                for item_alias in item.names:
                    if not item_alias.asname:
                        raise StructureException(
                            'External interface import expects an alias using `as` statement',
                            item)

                    interface_name = item_alias.asname
                    if interface_name in global_ctx._interfaces:
                        raise StructureException(
                            f'Duplicate import of {interface_name}', item)
                    if interface_name not in interface_codes:
                        raise StructureException(
                            f'Unknown interface {interface_name}', item)
                    global_ctx._interfaces[interface_name] = extract_sigs(
                        interface_codes[interface_name])  # noqa: E501
            else:
                raise StructureException("Invalid top-level statement", item)

        # Merge intefaces.
        if global_ctx._interfaces:
            for interface_name, sigs in global_ctx._interfaces.items():
                if interface_name in global_ctx._implemented_interfaces:
                    for func_sig in sigs:
                        func_sig.defined_in_interface = interface_name
                        global_ctx._interface[func_sig.sig] = func_sig

        # Add getters to _defs
        global_ctx._defs += global_ctx._getters
        return global_ctx
コード例 #24
0
    def get_global_context(cls, code, interface_codes=None):
        interface_codes = {} if interface_codes is None else interface_codes
        global_ctx = cls()

        for item in code:
            # Contract references
            if isinstance(item, ast.ClassDef):
                if global_ctx._events or global_ctx._globals or global_ctx._defs:
                    raise StructureException(
                        "External contract and struct declarations must come before event declarations, global declarations, and function definitions",
                        item)

                if item.class_type == 'struct':
                    if global_ctx._contracts:
                        raise StructureException(
                            "Structs must come before external contract definitions",
                            item)
                    global_ctx._structs[item.name] = global_ctx.make_struct(
                        item.name, item.body)
                elif item.class_type == 'contract':
                    global_ctx._contracts[
                        item.name] = GlobalContext.make_contract(item.body)
                else:
                    raise StructureException(
                        "Unknown class_type. This is likely a compiler bug, please report",
                        item)

            # Statements of the form:
            # variable_name: type
            elif isinstance(item, ast.AnnAssign):
                # implements statement.
                if isinstance(
                        item.target, ast.Name
                ) and item.target.id == 'implements' and item.annotation:
                    interface_name = item.annotation.id
                    if interface_name not in global_ctx._interfaces:
                        raise StructureException(
                            'Unknown interface specified: {}'.format(
                                interface_name), item)
                    global_ctx._implemented_interfaces.add(interface_name)
                else:
                    global_ctx.add_globals_and_events(item)
            # Function definitions
            elif isinstance(item, ast.FunctionDef):
                if item.name in global_ctx._globals:
                    raise FunctionDeclarationException(
                        "Function name shadowing a variable name: %s" %
                        item.name)
                global_ctx._defs.append(item)
            elif isinstance(item, ast.ImportFrom):
                if item.module == 'vyper.interfaces':
                    built_in_interfaces = get_builtin_interfaces()
                    for item_alias in item.names:
                        interface_name = item_alias.name
                        if interface_name in global_ctx._interfaces:
                            raise StructureException(
                                'Duplicate import of {}'.format(
                                    interface_name), item)
                        if interface_name not in built_in_interfaces:
                            raise StructureException(
                                'Built-In interface {} does not exist.'.format(
                                    interface_name), item)
                        global_ctx._interfaces[
                            interface_name] = built_in_interfaces[
                                interface_name].copy()
                else:
                    raise StructureException(
                        'Only built-in vyper.interfaces package supported for `from` statement.',
                        item)
            elif isinstance(item, ast.Import):
                for item_alias in item.names:
                    if not item_alias.asname:
                        raise StructureException(
                            'External interface import expects and alias using `as` statement',
                            item)

                    interface_name = item_alias.asname
                    if interface_name in global_ctx._interfaces:
                        raise StructureException(
                            'Duplicate import of {}'.format(interface_name),
                            item)
                    if interface_name not in interface_codes:
                        raise StructureException(
                            'Unknown interface {}'.format(interface_name),
                            item)
                    global_ctx._interfaces[interface_name] = extract_sigs(
                        interface_codes[interface_name])
            else:
                raise StructureException("Invalid top-level statement", item)

        if set(global_ctx._interfaces.keys()
               ) != global_ctx._implemented_interfaces:
            raise StructureException(
                'All interfaces that are imported have to be implemented using the "implements" statement.'
            )

        # Merge intefaces.
        if global_ctx._interfaces:
            for interface_name, sigs in global_ctx._interfaces.items():
                for func_sig in sigs:
                    setattr(func_sig, 'defined_in_interface', interface_name)
                    global_ctx._interface[func_sig.sig] = func_sig

        # Add getters to _defs
        global_ctx._defs += global_ctx._getters
        return global_ctx
コード例 #25
0
ファイル: parser.py プロジェクト: davesque/vyper
def parse_tree_to_lll(code,
                      origcode,
                      runtime_only=False,
                      interface_codes=None):
    global_ctx = GlobalContext.get_global_context(
        code, interface_codes=interface_codes)
    _names_def = [_def.name for _def in global_ctx._defs]
    # Checks for duplicate function names
    if len(set(_names_def)) < len(_names_def):
        raise FunctionDeclarationException(
            "Duplicate function name: "
            f"{[name for name in _names_def if _names_def.count(name) > 1][0]}"
        )
    _names_events = [_event.target.id for _event in global_ctx._events]
    # Checks for duplicate event names
    if len(set(_names_events)) < len(_names_events):
        raise EventDeclarationException(f"""Duplicate event name:
            {[name for name in _names_events if _names_events.count(name) > 1][0]}"""
                                        )
    # Initialization function
    initfunc = [_def for _def in global_ctx._defs if is_initializer(_def)]
    # Default function
    defaultfunc = [_def for _def in global_ctx._defs if is_default_func(_def)]
    # Regular functions
    otherfuncs = [
        _def for _def in global_ctx._defs
        if not is_initializer(_def) and not is_default_func(_def)
    ]
    sigs = {}
    external_contracts = {}
    # Create the main statement
    o = ['seq']
    if global_ctx._events:
        sigs = parse_events(sigs, global_ctx)
    if global_ctx._contracts or global_ctx._interfaces:
        external_contracts = parse_external_contracts(external_contracts,
                                                      global_ctx)
    # If there is an init func...
    if initfunc:
        o.append(INIT_FUNC_INIT_LLL)
        o.append(
            parse_function(
                initfunc[0],
                {
                    **{
                        'self': sigs
                    },
                    **external_contracts
                },
                origcode,
                global_ctx,
            ))

    # If there are regular functions...
    if otherfuncs or defaultfunc:
        o = parse_other_functions(o, otherfuncs, sigs, external_contracts,
                                  origcode, global_ctx, defaultfunc,
                                  runtime_only)

    # Check if interface of contract is correct.
    check_valid_contract_interface(global_ctx, sigs)

    return LLLnode.from_list(o, typ=None)
コード例 #26
0
    def from_FunctionDef(
            cls,
            node: vy_ast.FunctionDef,
            is_interface: Optional[bool] = False) -> "ContractFunction":
        """
        Generate a `ContractFunction` object from a `FunctionDef` node.

        Arguments
        ---------
        node : FunctionDef
            Vyper ast node to generate the function definition from.
        is_interface: bool, optional
            Boolean indicating if the function definition is part of an interface.

        Returns
        -------
        ContractFunction
        """
        kwargs: Dict[str, Any] = {}
        if is_interface:
            # FunctionDef with stateMutability in body (Interface defintions)
            if (len(node.body) == 1 and isinstance(node.body[0], vy_ast.Expr)
                    and isinstance(node.body[0].value, vy_ast.Name)
                    and StateMutability.is_valid_value(node.body[0].value.id)):
                # Interfaces are always public
                kwargs["function_visibility"] = FunctionVisibility.EXTERNAL
                kwargs["state_mutability"] = StateMutability(
                    node.body[0].value.id)
            elif len(node.body) == 1 and node.body[0].get("value.id") in (
                    "constant", "modifying"):
                if node.body[0].value.id == "constant":
                    expected = "view or pure"
                else:
                    expected = "payable or nonpayable"
                raise StructureException(
                    f"State mutability should be set to {expected}",
                    node.body[0])
            else:
                raise StructureException(
                    "Body must only contain state mutability label",
                    node.body[0])

        else:

            # FunctionDef with decorators (normal functions)
            for decorator in node.decorator_list:

                if isinstance(decorator, vy_ast.Call):
                    if "nonreentrant" in kwargs:
                        raise StructureException(
                            "nonreentrant decorator is already set with key: "
                            f"{kwargs['nonreentrant']}",
                            node,
                        )

                    if decorator.get("func.id") != "nonreentrant":
                        raise StructureException("Decorator is not callable",
                                                 decorator)
                    if len(decorator.args) != 1 or not isinstance(
                            decorator.args[0], vy_ast.Str):
                        raise StructureException(
                            "@nonreentrant name must be given as a single string literal",
                            decorator)

                    if node.name == "__init__":
                        msg = "Nonreentrant decorator disallowed on `__init__`"
                        raise FunctionDeclarationException(msg, decorator)

                    kwargs["nonreentrant"] = decorator.args[0].value

                elif isinstance(decorator, vy_ast.Name):
                    if FunctionVisibility.is_valid_value(decorator.id):
                        if "function_visibility" in kwargs:
                            raise FunctionDeclarationException(
                                f"Visibility is already set to: {kwargs['function_visibility']}",
                                node,
                            )
                        kwargs["function_visibility"] = FunctionVisibility(
                            decorator.id)

                    elif StateMutability.is_valid_value(decorator.id):
                        if "state_mutability" in kwargs:
                            raise FunctionDeclarationException(
                                f"Mutability is already set to: {kwargs['state_mutability']}",
                                node)
                        kwargs["state_mutability"] = StateMutability(
                            decorator.id)

                    else:
                        if decorator.id == "constant":
                            warnings.warn(
                                "'@constant' decorator has been removed (see VIP2040). "
                                "Use `@view` instead.",
                                DeprecationWarning,
                            )
                        raise FunctionDeclarationException(
                            f"Unknown decorator: {decorator.id}", decorator)

                else:
                    raise StructureException("Bad decorator syntax", decorator)

        if "function_visibility" not in kwargs:
            raise FunctionDeclarationException(
                f"Visibility must be set to one of: {', '.join(FunctionVisibility.values())}",
                node)

        if node.name == "__default__":
            if kwargs["function_visibility"] != FunctionVisibility.EXTERNAL:
                raise FunctionDeclarationException(
                    "Default function must be marked as `@external`", node)
            if node.args.args:
                raise FunctionDeclarationException(
                    "Default function may not receive any arguments",
                    node.args.args[0])

        if "state_mutability" not in kwargs:
            # Assume nonpayable if not set at all (cannot accept Ether, but can modify state)
            kwargs["state_mutability"] = StateMutability.NONPAYABLE

        if kwargs[
                "state_mutability"] == StateMutability.PURE and "nonreentrant" in kwargs:
            raise StructureException(
                "Cannot use reentrancy guard on pure functions", node)

        # call arguments
        if node.args.defaults and node.name == "__init__":
            raise FunctionDeclarationException(
                "Constructor may not use default arguments",
                node.args.defaults[0])

        arguments = OrderedDict()
        max_arg_count = len(node.args.args)
        min_arg_count = max_arg_count - len(node.args.defaults)
        defaults = [None] * min_arg_count + node.args.defaults

        namespace = get_namespace()
        for arg, value in zip(node.args.args, defaults):
            if arg.arg in ("gas", "value", "skip_contract_check",
                           "default_return_value"):
                raise ArgumentException(
                    f"Cannot use '{arg.arg}' as a variable name in a function input",
                    arg)
            if arg.arg in arguments:
                raise ArgumentException(
                    f"Function contains multiple inputs named {arg.arg}", arg)
            if arg.arg in namespace:
                raise NamespaceCollision(arg.arg, arg)

            if arg.annotation is None:
                raise ArgumentException(
                    f"Function argument '{arg.arg}' is missing a type", arg)

            type_definition = get_type_from_annotation(
                arg.annotation,
                location=DataLocation.CALLDATA,
                is_constant=True)
            if value is not None:
                if not check_kwargable(value):
                    raise StateAccessViolation(
                        "Value must be literal or environment variable", value)
                validate_expected_type(value, type_definition)

            arguments[arg.arg] = type_definition

        # return types
        if node.returns is None:
            return_type = None
        elif node.name == "__init__":
            raise FunctionDeclarationException(
                "Constructor may not have a return type", node.returns)
        elif isinstance(node.returns,
                        (vy_ast.Name, vy_ast.Call, vy_ast.Subscript)):
            return_type = get_type_from_annotation(
                node.returns, location=DataLocation.MEMORY)
        elif isinstance(node.returns, vy_ast.Tuple):
            tuple_types: Tuple = ()
            for n in node.returns.elements:
                tuple_types += (get_type_from_annotation(
                    n, location=DataLocation.MEMORY), )
            return_type = TupleDefinition(tuple_types)
        else:
            raise InvalidType(
                "Function return value must be a type name or tuple",
                node.returns)

        return cls(node.name, arguments, min_arg_count, max_arg_count,
                   return_type, **kwargs)
コード例 #27
0
ファイル: parser.py プロジェクト: vaporlang/vyper
def parse_tree_to_lll(global_ctx: GlobalContext) -> Tuple[LLLnode, LLLnode]:
    _names_def = [_def.name for _def in global_ctx._defs]
    # Checks for duplicate function names
    if len(set(_names_def)) < len(_names_def):
        raise FunctionDeclarationException(
            "Duplicate function name: "
            f"{[name for name in _names_def if _names_def.count(name) > 1][0]}"
        )
    _names_events = [_event.name for _event in global_ctx._events]
    # Checks for duplicate event names
    if len(set(_names_events)) < len(_names_events):
        raise EventDeclarationException(f"""Duplicate event name:
            {[name for name in _names_events if _names_events.count(name) > 1][0]}"""
                                        )
    # Initialization function
    initfunc = [_def for _def in global_ctx._defs if is_initializer(_def)]
    # Default function
    defaultfunc = [_def for _def in global_ctx._defs if is_default_func(_def)]
    # Regular functions
    otherfuncs = [
        _def for _def in global_ctx._defs
        if not is_initializer(_def) and not is_default_func(_def)
    ]

    # check if any functions in the contract are payable - if not, we do a single
    # ASSERT CALLVALUE ISZERO at the start of the bytecode rather than at the start
    # of each function
    is_contract_payable = next(
        (True for i in global_ctx._defs if FunctionSignature.from_definition(
            i, custom_structs=global_ctx._structs).mutability == "payable"),
        False,
    )

    sigs: dict = {}
    external_interfaces: dict = {}
    # Create the main statement
    o = ["seq"]
    if global_ctx._contracts or global_ctx._interfaces:
        external_interfaces = parse_external_interfaces(
            external_interfaces, global_ctx)
    # If there is an init func...
    if initfunc:
        o.append(init_func_init_lll())
        o.append(
            parse_function(
                initfunc[0],
                {
                    **{
                        "self": sigs
                    },
                    **external_interfaces
                },
                global_ctx,
                False,
            ))

    # If there are regular functions...
    if otherfuncs or defaultfunc:
        o, runtime = parse_other_functions(
            o,
            otherfuncs,
            sigs,
            external_interfaces,
            global_ctx,
            defaultfunc,
            is_contract_payable,
        )
    else:
        runtime = o.copy()

    if not is_contract_payable:
        # if no functions in the contract are payable, assert that callvalue is
        # zero at the beginning of the bytecode
        runtime.insert(1, ["assert", ["iszero", "callvalue"]])

    return LLLnode.from_list(o, typ=None), LLLnode.from_list(runtime, typ=None)
コード例 #28
0
ファイル: external_call.py プロジェクト: elrobles/vyper
def external_contract_call(node,
                           context,
                           contract_name,
                           contract_address,
                           pos,
                           value=None,
                           gas=None):
    from vyper.parser.expr import (
        Expr, )
    if value is None:
        value = 0
    if gas is None:
        gas = 'gas'
    if not contract_name:
        raise StructureException(
            f'Invalid external contract call "{node.func.attr}".', node)
    if contract_name not in context.sigs:
        raise VariableDeclarationException(
            f'Contract "{contract_name}" not declared yet', node)
    if contract_address.value == "address":
        raise StructureException(f"External calls to self are not permitted.",
                                 node)
    method_name = node.func.attr
    if method_name not in context.sigs[contract_name]:
        raise FunctionDeclarationException((
            f"Function not declared yet: {method_name} (reminder: "
            "function must be declared in the correct contract)"
            f"The available methods are: {','.join(context.sigs[contract_name].keys())}"
        ), node.func)
    sig = context.sigs[contract_name][method_name]
    inargs, inargsize, _ = pack_arguments(
        sig,
        [Expr(arg, context).lll_node for arg in node.args],
        context,
        node.func,
    )
    output_placeholder, output_size, returner = get_external_contract_call_output(
        sig, context)
    sub = [
        'seq',
        ['assert', ['extcodesize', contract_address]],
        ['assert', ['ne', 'address', contract_address]],
    ]
    if context.is_constant() and not sig.const:
        raise ConstancyViolationException(
            f"May not call non-constant function '{method_name}' within {context.pp_constancy()}."
            " For asserting the result of modifiable contract calls, try assert_modifiable.",
            node)

    if context.is_constant() or sig.const:
        sub.append([
            'assert',
            [
                'staticcall',
                gas,
                contract_address,
                inargs,
                inargsize,
                output_placeholder,
                output_size,
            ]
        ])
    else:
        sub.append([
            'assert',
            [
                'call',
                gas,
                contract_address,
                value,
                inargs,
                inargsize,
                output_placeholder,
                output_size,
            ]
        ])
    sub.extend(returner)
    o = LLLnode.from_list(sub,
                          typ=sig.output_type,
                          location='memory',
                          pos=getpos(node))
    return o
コード例 #29
0
    def from_FunctionDef(
        cls,
        node: vy_ast.FunctionDef,
        is_interface: Optional[bool] = False,
    ) -> "ContractFunction":
        """
        Generate a `ContractFunction` object from a `FunctionDef` node.

        Arguments
        ---------
        node : FunctionDef
            Vyper ast node to generate the function definition from.
        is_interface: bool, optional
            Boolean indicating if the function definition is part of an interface.

        Returns
        -------
        ContractFunction
        """
        kwargs: Dict[str, Any] = {}
        if is_interface:
            # FunctionDef with stateMutability in body (Interface defintions)
            if (len(node.body) == 1 and isinstance(node.body[0], vy_ast.Expr)
                    and isinstance(node.body[0].value, vy_ast.Name)
                    and StateMutability.is_valid_value(node.body[0].value.id)):
                # Interfaces are always public
                kwargs["function_visibility"] = FunctionVisibility.EXTERNAL
                kwargs["state_mutability"] = StateMutability(
                    node.body[0].value.id)
            elif len(node.body) == 1 and node.body[0].get("value.id") in (
                    "constant", "modifying"):
                if node.body[0].value.id == "constant":
                    expected = "view or pure"
                else:
                    expected = "payable or nonpayable"
                raise StructureException(
                    f"State mutability should be set to {expected}",
                    node.body[0])
            else:
                raise StructureException(
                    "Body must only contain state mutability label",
                    node.body[0])

        else:

            # FunctionDef with decorators (normal functions)
            for decorator in node.decorator_list:

                if isinstance(decorator, vy_ast.Call):
                    if "nonreentrant" in kwargs:
                        raise StructureException(
                            "nonreentrant decorator is already set with key: "
                            f"{kwargs['nonreentrant']}",
                            node,
                        )
                    if decorator.get("func.id") != "nonreentrant":
                        raise StructureException("Decorator is not callable",
                                                 decorator)
                    if len(decorator.args) != 1 or not isinstance(
                            decorator.args[0], vy_ast.Str):
                        raise StructureException(
                            "@nonreentrant name must be given as a single string literal",
                            decorator,
                        )
                    kwargs["nonreentrant"] = decorator.args[0].value

                elif isinstance(decorator, vy_ast.Name):
                    if FunctionVisibility.is_valid_value(decorator.id):
                        if "function_visibility" in kwargs:
                            raise FunctionDeclarationException(
                                f"Visibility is already set to: {kwargs['function_visibility']}",
                                node,
                            )
                        kwargs["function_visibility"] = FunctionVisibility(
                            decorator.id)

                    elif StateMutability.is_valid_value(decorator.id):
                        if "state_mutability" in kwargs:
                            raise FunctionDeclarationException(
                                f"Mutability is already set to: {kwargs['state_mutability']}",
                                node)
                        kwargs["state_mutability"] = StateMutability(
                            decorator.id)

                    else:
                        if decorator.id == "constant":
                            warnings.warn(
                                "'@constant' decorator has been removed (see VIP2040). "
                                "Use `@view` instead.",
                                DeprecationWarning,
                            )
                        raise FunctionDeclarationException(
                            f"Unknown decorator: {decorator.id}", decorator)

                else:
                    raise StructureException("Bad decorator syntax", decorator)

        if "function_visibility" not in kwargs:
            raise FunctionDeclarationException(
                f"Visibility must be set to one of: {', '.join(FunctionVisibility.values())}",
                node)

        if "state_mutability" not in kwargs:
            # Assume nonpayable if not set at all (cannot accept Ether, but can modify state)
            kwargs["state_mutability"] = StateMutability.NONPAYABLE

        # call arguments
        arg_count: Union[Tuple[int, int], int] = len(node.args.args)
        if node.args.defaults:
            arg_count = (
                len(node.args.args) - len(node.args.defaults),
                len(node.args.args),
            )

        arguments = OrderedDict()
        defaults = [None] * (len(node.args.args) -
                             len(node.args.defaults)) + node.args.defaults

        namespace = get_namespace()
        for arg, value in zip(node.args.args, defaults):
            if arg.arg in ("gas", "value"):
                raise ArgumentException(
                    f"Cannot use '{arg.arg}' as a variable name in a function input",
                    arg,
                )
            if arg.arg in arguments:
                raise ArgumentException(
                    f"Function contains multiple inputs named {arg.arg}", arg)
            if arg.arg in namespace["self"].members:
                raise NamespaceCollision(
                    "Name shadows an existing storage-scoped value", arg)
            if arg.arg in namespace:
                raise NamespaceCollision(arg.arg, arg)

            if arg.annotation is None:
                raise ArgumentException(
                    f"Function argument '{arg.arg}' is missing a type", arg)

            type_definition = get_type_from_annotation(
                arg.annotation,
                location=DataLocation.CALLDATA,
                is_immutable=True)
            if isinstance(
                    type_definition,
                    StructDefinition) and type_definition.is_dynamic_size:
                # this is a temporary restriction and should be removed once support for dynamically
                # sized structs is implemented - https://github.com/vyperlang/vyper/issues/2190
                raise ArgumentException(
                    "Struct with dynamically sized data cannot be used as a function input",
                    arg)

            if value is not None:
                if not check_constant(value):
                    raise StateAccessViolation(
                        "Value must be literal or environment variable", value)
                validate_expected_type(value, type_definition)

            arguments[arg.arg] = type_definition

        # return types
        if node.returns is None:
            return_type = None
        elif isinstance(node.returns,
                        (vy_ast.Name, vy_ast.Call, vy_ast.Subscript)):
            return_type = get_type_from_annotation(
                node.returns, location=DataLocation.MEMORY)
        elif isinstance(node.returns, vy_ast.Tuple):
            tuple_types: Tuple = ()
            for n in node.returns.elements:
                tuple_types += (get_type_from_annotation(
                    n, location=DataLocation.MEMORY), )
            return_type = TupleDefinition(tuple_types)
        else:
            raise InvalidType(
                "Function return value must be a type name or tuple",
                node.returns)

        return cls(node.name, arguments, arg_count, return_type, **kwargs)
コード例 #30
0
def parse_public_function(code: ast.FunctionDef, sig: FunctionSignature,
                          context: Context) -> LLLnode:
    """
    Parse a public function (FuncDef), and produce full function body.

    :param sig: the FuntionSignature
    :param code: ast of function
    :return: full sig compare & function body
    """

    validate_public_function(code, sig, context.global_ctx)

    # Get nonreentrant lock
    nonreentrant_pre, nonreentrant_post = get_nonreentrant_lock(
        sig, context.global_ctx)

    clampers = []

    # Generate copiers
    copier: List[Any] = ['pass']
    if not len(sig.base_args):
        copier = ['pass']
    elif sig.name == '__init__':
        copier = [
            'codecopy', MemoryPositions.RESERVED_MEMORY, '~codelen',
            sig.base_copy_size
        ]
        context.memory_allocator.increase_memory(sig.max_copy_size)
    clampers.append(copier)

    # Add asserts for payable and internal
    if not sig.payable:
        clampers.append(['assert', ['iszero', 'callvalue']])

    # Fill variable positions
    default_args_start_pos = len(sig.base_args)
    for i, arg in enumerate(sig.args):
        if i < len(sig.base_args):
            clampers.append(
                make_arg_clamper(
                    arg.pos,
                    context.memory_allocator.get_next_memory_position(),
                    arg.typ,
                    sig.name == '__init__',
                ))
        if isinstance(arg.typ, ByteArrayLike):
            mem_pos, _ = context.memory_allocator.increase_memory(
                32 * get_size_of_type(arg.typ))
            context.vars[arg.name] = VariableRecord(arg.name, mem_pos, arg.typ,
                                                    False)
        else:
            if sig.name == '__init__':
                context.vars[arg.name] = VariableRecord(
                    arg.name,
                    MemoryPositions.RESERVED_MEMORY + arg.pos,
                    arg.typ,
                    False,
                )
            elif i >= default_args_start_pos:  # default args need to be allocated in memory.
                default_arg_pos, _ = context.memory_allocator.increase_memory(
                    32)
                context.vars[arg.name] = VariableRecord(
                    name=arg.name,
                    pos=default_arg_pos,
                    typ=arg.typ,
                    mutable=False,
                )
            else:
                context.vars[arg.name] = VariableRecord(name=arg.name,
                                                        pos=4 + arg.pos,
                                                        typ=arg.typ,
                                                        mutable=False,
                                                        location='calldata')

    # Create "clampers" (input well-formedness checkers)
    # Return function body
    if sig.name == '__init__':
        o = LLLnode.from_list(
            ['seq'] + clampers +
            [parse_body(code.body, context)],  # type: ignore
            pos=getpos(code),
        )
    # Is default function.
    elif sig.is_default_func():
        if len(sig.args) > 0:
            raise FunctionDeclarationException(
                'Default function may not receive any arguments.', code)
        o = LLLnode.from_list(
            ['seq'] + clampers +
            [parse_body(code.body, context)],  # type: ignore
            pos=getpos(code),
        )
    # Is a normal function.
    else:
        # Function with default parameters.
        if sig.total_default_args > 0:
            function_routine = f"{sig.name}_{sig.method_id}"
            default_sigs = sig_utils.generate_default_arg_sigs(
                code, context.sigs, context.global_ctx)
            sig_chain: List[Any] = ['seq']

            for default_sig in default_sigs:
                sig_compare, _ = get_sig_statements(default_sig, getpos(code))

                # Populate unset default variables
                set_defaults = []
                for arg_name in get_default_names_to_set(sig, default_sig):
                    value = Expr(sig.default_values[arg_name],
                                 context).lll_node
                    var = context.vars[arg_name]
                    left = LLLnode.from_list(var.pos,
                                             typ=var.typ,
                                             location='memory',
                                             pos=getpos(code),
                                             mutable=var.mutable)
                    set_defaults.append(
                        make_setter(left, value, 'memory', pos=getpos(code)))

                current_sig_arg_names = {x.name for x in default_sig.args}
                base_arg_names = {arg.name for arg in sig.base_args}
                copier_arg_count = len(default_sig.args) - len(sig.base_args)
                copier_arg_names = list(current_sig_arg_names - base_arg_names)

                # Order copier_arg_names, this is very important.
                copier_arg_names = [
                    x.name for x in default_sig.args
                    if x.name in copier_arg_names
                ]

                # Variables to be populated from calldata/stack.
                default_copiers: List[Any] = []
                if copier_arg_count > 0:
                    # Get map of variables in calldata, with thier offsets
                    offset = 4
                    calldata_offset_map = {}
                    for arg in default_sig.args:
                        calldata_offset_map[arg.name] = offset
                        offset += (32 if isinstance(arg.typ, ByteArrayLike)
                                   else get_size_of_type(arg.typ) * 32)

                    # Copy default parameters from calldata.
                    for arg_name in copier_arg_names:
                        var = context.vars[arg_name]
                        calldata_offset = calldata_offset_map[arg_name]

                        # Add clampers.
                        default_copiers.append(
                            make_arg_clamper(
                                calldata_offset - 4,
                                var.pos,
                                var.typ,
                            ))
                        # Add copying code.
                        _offset: Union[int, List[Any]] = calldata_offset
                        if isinstance(var.typ, ByteArrayLike):
                            _offset = [
                                'add', 4, ['calldataload', calldata_offset]
                            ]
                        default_copiers.append(
                            get_public_arg_copier(
                                memory_dest=var.pos,
                                total_size=var.size * 32,
                                offset=_offset,
                            ))

                    default_copiers.append(0)  # for over arching seq, POP

                sig_chain.append([
                    'if', sig_compare,
                    [
                        'seq',
                        ['seq'] + set_defaults if set_defaults else ['pass'],
                        ['seq_unchecked'] +
                        default_copiers if default_copiers else ['pass'],
                        ['goto', function_routine]
                    ]
                ])

            # Function with default parameters.
            o = LLLnode.from_list(
                [
                    'seq',
                    sig_chain,
                    [
                        'if',
                        0,  # can only be jumped into
                        [
                            'seq', ['label', function_routine
                                    ], ['seq'] + nonreentrant_pre + clampers +
                            [parse_body(c, context) for c in code.body] +
                            nonreentrant_post + [['stop']]
                        ],
                    ],
                ],
                typ=None,
                pos=getpos(code))

        else:
            # Function without default parameters.
            sig_compare, _ = get_sig_statements(sig, getpos(code))
            o = LLLnode.from_list([
                'if', sig_compare, ['seq'] + nonreentrant_pre + clampers +
                [parse_body(c, context)
                 for c in code.body] + nonreentrant_post + [['stop']]
            ],
                                  typ=None,
                                  pos=getpos(code))
    return o